在device mapper中,需要执行某些异步拷贝。例如,在第一次写快照源前,需要将快照源的数据拷贝到COW设备上;又如,在镜像模块进行恢复时,需要将某个设备的数据拷贝到其他镜像设备上。这些异步拷贝都由“内核拷贝进程”进行。
“内核拷贝进程”提供了一些所谓的“客户端”API函数,由要进行异步拷贝的内核模块使用。其中
int kcopyd_client_create(unsigned int num_pages, struct kcopyd_client **result);
和
void kcopyd_client_destroy(struct kcopyd_client *kc);
分配和释放“kcopyd客户端”结构。而
int kcopyd_copy(struct kcopyd_client *kc, struct io_region *from, unsigned int num_dests, struct io_region *dests, unsigned int flags, kcopyd_notify_fn fn, void *context);
用于向“kcopyd内核拷贝进程”提交一个拷贝任务。其中kc为提交拷贝任务的“kcopyd客户端”,from为拷贝源, num_dests和dests为拷贝目标数目以及拷贝目标数组。在拷贝完成之后,作为“客户端”的内核模块通常要执行某些善后工作,这是在参数中指定了回调函数和回调上下文。
每个提交上来的拷贝任务都会被“内核拷贝进程”添加到任务链表中。根据任务所处的阶段不同,有三个这样的链表:i)等待页面的任务链表 _pages_jobs;ii)有页面、等待发起I/O的任务_io_jobs;iii)已经完成的任务_complete_jobs。“内核拷贝进程” 的主要任务是循环处理这三个任务链表。
任务刚提交时,被加入到_pages_jobs链表,在从“kcopyd客户端”的页面池中分配所需个数的页面后,这个任务会转移到_ios_jobs链表。
任务刚开始加入到_ios_jobs链表时,rw标志为读,一旦调度,将从拷贝源读取数据到任务的页面,然后通过设定的回调函数将rw标志改为写,并重新插入到_ios_jobs链表。再次调度时,会将任务的页面中的数据拷贝到所有的拷贝目标,在完成之后,回调函数把这个任务又转移到 _complete_jobs链表。
_complete_job链表中的任务是需要进行善后处理的,包括将使用的页面重新回收到“kcopyd客户端”的页面池中,并将kcopyd任务从链表中取出,并释放,最终调用“客户端”内核模块指定的回调函数,即kcopyd_copy函数的fn参数。
阅读(1976) | 评论(0) | 转发(0) |