免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 6051 | 回复: 6
打印 上一主题 下一主题

块设备的IO流程 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-08-09 11:06 |只看该作者 |倒序浏览
1.正常的IO流程:
文件系统层的submit_bio :把相应的读写标志加到bio结构中。
generic_make_request
检查bio的合法性,每个bio里都有一个块设备结构bi_bdev,每个块设备结构都有个队列
指针。如果bio指向的是相对分区的地址,即要转换成整个硬盘的相对地址:
blk_partition_remap, 这之后到了q->make_request_fn.这个函数指针在创建初始化
queue的时候初始化成了:__make_request.为做不了DMA的page做弹性缓冲区:
blk_queue_bounce,如果此bio为barrier,即为此bio单独创建一个request.不合并不代
立刻下盘。如果不是,即调用电梯算法尝试合并多个bio.
get_request_wait:获取一个空的request;
init_request_from_bio:利用bio来初始化一个request.
如果已经设置sync:__generic_unplug_device->blk_remove_plug->q->request_fn;

论坛徽章:
0
2 [报告]
发表于 2011-08-09 11:07 |只看该作者
回复 1# tacoe


    2. 电梯合并:elv_merge
如果之前已经有合并,即last_merge不为NULL时,只要简单计算sector就可以知道是否
可以前合并或者后合并。当然还要比较相应的标志位。
每个不能合并的bio都会先组装成一个rquest放到电梯中的hash[这里特指是deadline]
表中:add_request
如果last_merge为NULL即表示queue没有合并的request,合并的物理段不能超过最大
段数:q->max_phys_segments.如果超过即last_merge要重装配置为NULL. 在
elv_insert的时候如果认为该request是可merge的且last_merge是NULL时,该值指
向该request.
hash表的是以rq->sector+rq->nr_sectors 为key值为保存hash的,所以以bio的开始
扇区能在hash表中能找到相应的request即表示此bio可以后合并的,前合并也是类似
的道理,当然还要检查相应的标志位,像读写要相同等。

论坛徽章:
0
3 [报告]
发表于 2011-08-09 11:07 |只看该作者
回复 2# tacoe


    scsi_request_fn
这个函数是在 scsi_alloc_queue 的时候初始化的,每一个scsi device设备都有一个
queue队列,在创建scsi_alloc_sdev的时候创建此队列,最开始创建scsi设备的函数
数是从驱动里开始的(mptsas_scan_sas_topology)里的scsi_add_device。
这个函数即[q->request_fn(q)],可能在电梯调度算法中被调用,也可能在文件
ll_rw_blk.c 中被调用:__generic_unplug_device/blk_start_queue/blk_run_queue
/blk_insert_request.
io下盘的两条路径:
(1)
submit_bio->generic_make_request->__make_request->elv_insert->
__blk_run_queue->scsi_request_fn
(2)线程路径:
kthread->journald->journal_commit_transaction->out_of_line_wait_on_bit->
__wait_on_bit->sync_buffer->gerneric_unplug_device->scsi_request_fn
3毫秒定时器unplug:blk_unplug_timeout
在blk_plug_device函数中设置QUEUE_FLAG_PLUGGED标志时加入定时器,3毫秒如果还
没有unplug的话,定时器函数会调用下IO。blk_unplug_work

scsi_dispatch_cmd
主要是在command下发到驱动的时候做的一些预处理。

queuecommand
这个实际是在驱动里执行了,每个IOC HAB 的处理都不同。这个函数里传入了一个重
要的函数指针scsi_done.把该函数指针放到scsi_cmnd结构中,最后会在中断中调用
这个函数。

scsi_done
该函数的功能是触发软件中断。异步通知此IO已经完成。

论坛徽章:
0
4 [报告]
发表于 2011-08-09 11:08 |只看该作者
回复 3# tacoe


    blk_done_softirq
这个是软件中断处理函数,它从队列中取出已经完成的request,再调用相应queue中的
softirq_done_fn, 每个request中都有一个queue结构。
同样这个函数指针也是在创建queue的时候初始化到queue结构中的。
通用的为:scsi_softirq_done,它主要处理的是命令的完成情况。
根据相应的标志,做出三种反应a.正常完成。b.需要重新入队。c.不能正常完成,
request 中止。

正常或者出错的scsi_finish_command: 最终会调用scsi_cmnd 中的done,注意这里的
done与scsi_done的区别。
非读写的done:
[./drivers/scsi/scsi_lib.c:1170: cmd->done = scsi_blk_pc_done;]
读写的done:
sd_rw_intr 具体的块设备不相同, 这个函数在sd_init_command函数里初始化。
这是scsi_driver结构体中的函数。
最后会回到scsi中层函数:scsi_io_completion,在这个函数里可能会根据sensekey
再做相应的处理,或者重新入队。

论坛徽章:
0
5 [报告]
发表于 2011-08-09 11:08 |只看该作者
回复 4# tacoe


    2.电梯算法结构[struct elevator_type]中各个函数的功能:
.elevator_merge_fn:判断bio是否可以合并,是前合并还是后合并。
.elevator_merged_fn:合并后的后续处理。
.elevator_merge_req_fn:合并request时,算法对两个request所做的处理.
.elevator_dispatch_fn:找到最适合的request放到q->head后面。
.elevator_add_req_fn:算法把request加到具体自己的结构队列中
.elevator_queue_empty_fn:查看算法队列中是否还有request
.elevator_former_req_fn:前merge时所要取的request.
.elevator_latter_req_fn:后merge时所要取的request.
.elevator_set_req_fn:
.elevator_put_req_fn:
.elevator_init_fn:块设备调用,初始化与算法相关的具体数据结构,或者与queue
相关。
.elevator_exit_fn,上面函数的反操作。

论坛徽章:
0
6 [报告]
发表于 2011-08-09 11:09 |只看该作者
回复 5# tacoe


    3.deadline IO调度算法:
模块注册:elv_register,即只是把elevator_type结构add到全局链表elv_list中,以
便后期调用可以被找到。
块设备如何找到调度算法:块设备在初始化队列的时候,[blk_init_queue_node] 调
用了elevator_init. 每个队列里都有一个struct elevator_queue,这个结构里有个
重要的结构hash表,用来存放request的。
块设备的队列与具体的调度算法联系起来:elevator_attach,因为每个设备都都有一
个独立的queue,所以每个独立的设备可以对应一个独立的调度算法。
26 struct deadline_data {
27     /*
28      * run time data
29      */
30
31     /*
32      * requests (deadline_rq s) are present on both sort_list and fifo_list
33      */
34     struct rb_root sort_list[2]; 读写红黑树,作用:
35     struct list_head fifo_list[2]; 读写fifo,作用:
36
37     /*
38      * next in sort order. read, write or both are NULL
39      */
40     struct request *next_rq[2]; 什么时候填充?
41     unsigned int batching;      /* number of sequential requests made */
42     sector_t last_sector;       /* head position */
43     unsigned int starved;       /* times reads have starved writes */
44
45     /*
46      * settings that change how the i/o scheduler behaves
47      */
48     int fifo_expire[2];
49     int fifo_batch;
50     int writes_starved;
51     int front_merges;
52 };
add_request按序把相应的request加入到相应的读或者写rbtree中。如果rbtree已经
有键值一样的节点,如何处理?从rbtree及fifo中删除再dispatch到队列中去。为什
新的request没有加到rbtree中了呢???,新的request只放到fifo中而没有放到
rbtree中。是认为再也没有合并的必要了吗?
hash表中存放的是有可能merge的request,如果rq_mergeable()为假即从hash表中删除
可以从hash表中查找可以后merge的request.前merge即从rbtree 中找,看
deadline_merge.

elv_merged_request:已经合并后所作的处理,如果是前merge即重新计算rbtree,以
request的[__secto]r为键值,即开始位置为键值。如果是后merge即重新调整hash表中
的位置。以开始位置+它的大小[即块的终点]为键值.
所以rbtree中查找的是前合并的,而hash表查找的是后合并的。

论坛徽章:
0
7 [报告]
发表于 2011-08-09 11:10 |只看该作者
本帖最后由 tacoe 于 2011-08-09 11:17 编辑

回复 6# tacoe


    4.cfq IO调度算法
.elevator_merge_fn:
每个线程都有一个struct cfq_queue结构。
每个线程结构struct task_struct 中都保存有一个struct io_context成员。
struct cfq_io_context 中有两个struct cfq_queue队列,分别是同步和异步。
从io_context结构可以找到cfq_io_context结构,再根据bio的类型可以找到
cfq_queue结构。同样cfq_queue中有红黑树链表。根据bio的sector计算是否可以前
merge.

--------------
在看代码中的一些笔记,还有很多没看,欢迎指教。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP