注册事件(REGISTER_FOR_EVENT):
在初始化的时候去注册一个事件。包括注册事件进程ID ,q序列号,初始化事件定义为DM_EVENT_CMD_HELLO。注册的id和q都是关于进程的 ,是为了作为客户端的dmevrntd进程在获取到动态连接库的调用之后,才事件有真正的事件注册,即:
int _register_for_event(struct message_data *message_data);
函数主要参数struct message_data *message_data,传递了
struct message_data {
char *id;
char *dso_name; /* Name of DSO. */
char *device_uuid; /* Mapped device path. */
char *events_str; /* Events string as fetched from message. */
enum dm_event_mask events_field; /* Events bitfield. */
char *timeout_str;
uint32_t timeout_secs;
struct dm_event_daemon_message *msg; /* Pointer to message buffer. */
};
events_str表示一个事件,比如是DM_EVENT_ERROR_MASK事件标志。 dm_event_mask events_field表示事件的某一个,具体举例子比如事件 dm_event_mask events_field的值等于
DM_EVENT_ERROR_MASK | DM_EVENT_TIMEOUT。
该函数实现的功能是注册一个事件设备用于记录事件的标志,启用一个计时线程,用于监控设备的元数据和数据的读写情况和百分比。调用到的函数有
_fill_device_data(thread_new)获取线程需要的设备名device_name,主次设备号,UUID。_do_register_device(thread_new)获取设备的lv和vg名以及最小扩展的百分比50%。
metadata_percent_check = CHECK_MINIMUM;
state->data_percent_check = CHECK_MINIMUM;
_create_thread(thread)启用一个监控线程,用来循环等待或解析扩展或者缩减事件
注销事件(UNREGISTER_FOR_EVENT)和注册事件(REGISTER_FOR_EVENT,)事件一起在lvm初始的时候被定义和注销/注册。
获取注册事件的设备(GET_REGISTERED_DEVICE):
evmask是实践标志和进程启用和关闭情况,从lvchange(struct cmd_context *cmd, int argc, char **argv)调用查看dmeventd线程是否在active或者其他的状态,以便获取到设备事件
设备事件监督线程:
_pthread_create_smallstack(&thread->thread, _monitor_thread, thread);
一个参数是创建线程需要的线程id,第二参数是创建线程的执行函数,参数是创建线程的函数调用的传参。
_monitor_thread线程功能:循环等待和解析设备事件。
Dmeventd线程的工作任务:等待事件(_event_wait)/判断事件(if.....)/处理事件
1事件等待_event_wait
过程:为事件的task设置task设备uuid和事件event_nr。
dmt = dm_task_create(DM_DEVICE_WAITEVENT)表示创建的事件为设备循环等待事件。
dm_task_set_event_nr(dmt, thread->event_nr)
在设备元数据或者设备使用完毕,即设备元数据和数据的使用率有一个达到百分之百的时候dm_task_run(dmt)调用从内核返回一个Infor事件,设置线程事件为thread->current_events |= DM_EVENT_DEVICE_ERROR;在正常设备元数据和设备写占用率未达到百分之百的时候设置当前线程thread->current_events |= DM_EVENT_TIMEOUT;
在循环等待事件之后获取设备的状态
_get_device_status(thread)然后_do_process_event
2处理事件_do_process_event;
thread->current_events |= DM_EVENT_TIMEOUT事件超时的时候,等待事件的判断依据:
ioctl_errno = dm_task_get_errno(dmt);
if (thread->events & DM_EVENT_TIMEOUT && ioctl_errno == EINTR) {
thread->current_events |= DM_EVENT_TIMEOUT;
对这个不断的判断目的就是为了检测线程是否退出 ,退出有退出的处理,如果不退出则:if (thread->events & thread->current_events) {
thread->processing = 1;
_unlock_mutex();
_do_process_event(thread, task);
dm_task_destroy(task);
.............................................
也就是才真正的实践处理函数用来处理事件:
process_event(struct dm_task *dmt, enum dm_event_mask event __attribute__((unused)),
void **private)
处理的步骤:
1>获取设备写元数据的占用情况和数据的占用情况,以便计算占用的百分比
dm_get_status_thin_pool(state->mem, params, &tps)对从内核获取参数,(包括设备读写属性discacrd,空间使用情况(no space))按照一定的格式转化保存在tps中。之所以这么做,是为了接下来方便做磁盘使用情况的对比。
比如1 645/1024 81728/81728 存在params中,分别表示 transaction_id,used_metadata_blocks,total_metadata_blocks,used_data_blocks,total_data_blocks。
对比的参数有一下:
state->known_metadata_size/tps->total_metadata_blocks
state->known_data_size/tps->total_data_blocks
tps->used_metadata_blocks / tps->total_metadata_blocks;//表示使用元数据占thin pool 元数据的百分比。
tps->used_data_blocks / tps->total_data_blocks;//表示thin pool写数据占thin pool的百分比,如果达到百分之百,表示thin pool被写满,这个时候,就不能再扩展。
对于metadata_percent_check和data_percent_check最小百分比50,也就是在conf文件中设置小于50的扩展百分比,最终的扩展值还是按照百分之五十来进行,就是写数据占到了创建存储池的大小的百分之五十就开始按照某一个百分比来扩展存储池的。在metadata_percent_check和data_percent_check超过扩展的最小值的时候每5%数据的递增检测一次数据和元数据所占的百分比。当数据和原数据超过百分之八十的时候会报警数据即将写满。percent = 100 * tps->used_data_blocks / tps->total_data_blocks;
if (percent >= state->data_percent_check) {
_extend(state);
}
如果是扩展失败:比如存储池写满,就是扩展失败,不能在扩展,这个时候的做法就是:
_umount(dmt, device)->_umount_device->execvp(cmd, (char **)argv).最后的函数就是umount命令的执行函数,找到文件执行解挂载。
同时设置扩展的百分比为0,让设备不能再扩展。
2>进行扩展。
static struct logical_volume *_lvresize_volume 发生扩展的地方是:
} else if ((lp->extents > lv->le_count) && /* Ensure we extend */
!lv_extend(lv, lp->segtype,
lp->stripes, lp->stripe_size,
lp->mirrors, first_seg(lv)->region_size,
lp->extents - lv->le_count,
pvh, alloc, lp->approx_alloc))
return_NULL;
if (lp->sizeargs &&
!(lock_lv = _lvresize_volume(cmd, lv, lp, pvh)))//精简卷和精简池扩展
return_0;
if (lp->poolmetadataextents) {//扩展
if (!_lvresize_poolmetadata(cmd, vg, lp, lv, pvh))//扩展找不到空间返回0 直接再次返回
return_0;
lock_lv = lv;
}