这篇延续上一篇的内容,通过几个典型场景来分析dm-ioband的工作流程。
第一个场景是 http://sourceforge.net/apps/trac/ioband/wiki/dm-ioband/man/examples 中的example 1,首先调用命令创建两个ioband device,
# echo "0 $(blockdev --getsize /dev/sda1) ioband /dev/sda1 1 0 0 none weight 0 :80" | dmsetup create ioband1 # echo "0 $(blockdev --getsize /dev/sda2) ioband /dev/sda2 1 0 0 none weight 0 :40" | dmsetup create ioband2
dmsetup会调用ioband_ctr来创建ioband设备,从ioband_ctr的注释可以看出参数顺序
/*
* Create a new band device:
* parameters: <device> <device-group-id> <io_throttle> <io_limit>
* <type> <policy> <policy-param...> <group-id:group-param...>
*/
其中 <device-group-id> 是 ioband_device->g_name,用以唯一标识一个ioband设备。P.S. 这里ioband1, ioband2两个设备其实对应同一个ioband_device,其ioband-group-id为1 。而传入的<device> 为底层真实的块设备,如 /dev/sda1, /dev/sda2。会调用dm_get_device把这个块设备加到dm_table->devices 列表中。下面调用alloc_ioband_device( <device-group-id>, <io_throttle>, <io_limit> ) 来创建ioband_device,由于此时已经有了<device-group-id>为 1 的ioband_device,因此只是简单的把ioband_device->g_ref++就结束了。内核全局的有一个 ioband_device_list 的list_head,所有的ioband_device都属于这个list_head,这些ioband_device通过ioband_device->g_list 组织起来。
下面调用policy_init,传入的参数包括 <policy> <policy-param...> <group-id:group_param...>,首先进行policy 名字的比对,在全局变量 dm_ioband_policy_type 中查询名字叫weight的policy,之后会调用对应的policy_weight_init 函数。 P.S. ioband_device的g_groups链接了attach在上面的所有ioband_group,这个list_head的变量对应于ioband_group的 c_list 成员,通过container_of宏可以得到ioband_group。 得到的struct ioband_policy_type 赋值给 ioband_device->g_policy。
调用policy_weight_init的时候,传入的参数为 <policy-param...> <group-id: group-param...>这些参数。weight policy 第一个参数是<token base>,之后是<group-id:group-param> 的数组,policy_weight_init 不对 <group-id:group-param> 的队列做处理。
下面会调到 ioband_group_init 首先创建 default ioband group,在这个场景中,会有同一个ioband_device出现了两个default ioband_group,其中c_id为 IOBAND_ID_ANY。ioband_device->g_root_groups保存了同一个 <device-group-id> 中所有的根group,ioband_group_init 会通过list_add_tail,把ioband_device->g_root_groups加到ioband_group->c_sibling里面,可以看出每个ioband_group->c_sibling保存了同级别的所有ioband_group,而ioband_device->g_root_groups里所有的ioband_group互相都是sibling。本场景下,两个ioband_group的c_parent, c_children都为空,c_sibling有两个ioband_group。
对于ioband_group其他的成员: c_children表示这个group之下的所有子group,c_parent表示父group,c_sibling表示兄弟group,由于ioband_group是按照红黑树的结构组织的,c_group_root表示这颗红黑树的树根,c_group_node表示当前group在这颗红黑树上的节点。
最后调用ioband_group_type_select 选择 ioband_group_type,这里的 type为 none
############################
OK,现在我们来看请求执行流,请求进入device mapper之后,最终会落到 ioband_map 函数中,ioband_map执行步骤如下:
-------------------------------------------------华丽的分割线-----------------------------------------------------
第二个场景是 http://sourceforge.net/apps/trac/ioband/wiki/dm-ioband/man/examples 的 example 3,这种场景下,ioband2上多了个uid=1000,weight=20的group。
首先是__ioband_message把type设置为uid
其次在__ioband_message中调用ioband_group_attach(struct ioband_group* head, 0, 1000, NULL) 时,head为原有default group的指针,只是现在head->c_type变成了dm_ioband_group_type中的uid group type。parent_id为0,group id为1000。之后再调用ioband_group_init(dp, head, NULL, gp, 1000, NULL),这里gp为新分配出来的一块内存,用于保存ioband_group,这里gp->c_id = id,这时ioband_group的id就不是default group中的IOBAND_ID_ANY了,而是uid 1000。这个新分配的ioband_group被挂在ioband_device的g_groups下,因此这个ioband_device目前就有了3个ioband_group。同时ioband2这个group的红黑树下面,有两个node,一个属于default group,一个属于uid group。这两个group的c_dev,c_target都是相同的。
最后调用__ioband_message中的ioband_set_param来设置weight。最后还是调用policy_weight_param(gp, "weight", "20") 。
现在ioband_device下面有3个ioband_group了,那么如何知道请求到底是属于哪个group呢?还记得ioband_map中的ioband_group_get 么,对于目的为ioband2的user=1000的进程发出的请求,传入ioband_group_get 中第一个参数为 default group 的指针。由于此时default group 的c_type已经变成了uid group type,会调用ioband_group_find 在红黑树上查找 c_id =1000 的ioband_group,这个查找算法要么返回 c_id 为1000的 rb_node,要么返回c_id 为 IOBAND_ID_ANY的default group的rb_node,因此不是uid=1000的进程发出的bio,最后都会落到default group上。而uid=1000的进程发出的bio会落到c_id=1000的ioband_group上。
最后总结下来,这个<device-group-id>包含了g_ref为2的ioband_device,里面有两个块设备/dev/mapper/ioband1, /dev/mapper/ioband2,同时有3个ioband_group,按照权重来分享token