在vfs层:
首先还是统一的vfs_write入口,调用统一的aio接口,如果是direct io则直接下发,如果是buffer io则要走vfs中的chche机制。
在blcok层:
以buffer io为例,常规的写需要执行3步:write_begin---copy_data---write_end
write_begin主要是在address_space查找该page,如果找到则lock,没找到则分配一个page,并lock。
copy_data为常规的用户态数据copy到内核中,访问page时需要kmap。
write_end则目的是更新buffer_head,page, inode的状态(主要是标脏),并unlock该page.
然后唤醒pdflush,此时,异步IO就返回了。
在scsi层:
对于pdflush:
当dirty page数量大于阈值、或者dirty page在内存中存在过长时间,pdflush会被激活。
首先遍历所有dirty的inode,然后依次回写mapping中的数据到设备中。
在write_cache_pages中,lock page后,直到调用__block_write_full_page,遍历出脏缓冲区,set_page_writeback。
之后submit_bh,unlock page,但此时bh已经被lock,设置回调end_bio_bh_io_sync。
之后submit_bio,将bio提交到磁盘驱动维护的请求队列中,现在重点关注__generic_make_request
__generic_make_request:
获取bio对应的块设备文件对应的磁盘对象的请求队列,如md设备,或者vdisk设备等,实现bio的多次转发,直到最终的物理磁盘。
块设备初始化时,注册make_request_fn为__make_request,如果__make_request返回0,则说明bio走到物理磁盘了。
看此IO能否和queue合并,如果不能merge,则分配新的request(可能会睡眠,做流控限制),加入队列中等待merge和调度。
题外话:常说的direct io就是此bio不进入队列合并,直接触发unplug机制向底层设备下发,barrier io类似,只是多了sync操作。
unplug机制:
IO超时激活,或者IO超过4个激活,或者对实时性要求很高的IO激活unplug机制,其request_fn实现以此处理queue中的requset。
request_fn机制(Main strategy routine for SCSI):
在scsi_alloc_queue时,注册了scsi_request_fn,执行elv_next_request,翻译request为scsi cmd,核心为prep_rq_fn,直到dispatch_cmd.
prep_rq_fn的两次绑定:
scsi_prep_fn当scsi设备在scsi层被初次识别时,由于此时SCSI设备只能处理来自SCSI公共服务层的请求,如inq和report lun等命令。
sd_prep_fn当该scsi设备被进一步识别后,preq_rq_fn被再次重新绑定,它即能处理公共服务层请求,也能处理上层的bio请求。
dispatch_cmd:
将scsi cmd下发给具体的scsi host(即hba),并注册回调scsi_done,queuecomand函数的实现由scsi host driver完成
在scsi的LLDD层:
lsi将scsi cmd加入到自己内部的queue进行维护,timeout处理走scsi系统默认流程(某个设备的timeout处理,会导致整个host的io被挂起)。
mv用状态机维护scsi cmd,timeout处理走自己内部的实现。
callback:
blk_softirq_init注册blk_done_softirq
rq->q->softirq_done_fn(rq); //scsi_softirq_done
scsi_finish_command
scsi_end_request
blk_end_request
req_bio_endio
blk_end_io
end_that_request_data
__end_that_request_first
req_bio_endio
bio_endio
bio->bi_end_io(bio, error); //end_bio_bh_io_sync
bh->b_end_io = end_buffer_async_write; //这里unlock bh
end_page_writeback(page);
附言:
当某个page已经为writeback,此时上层的再次写该页,其只有等上次的writeback完成后,才能进行本次操作。
因此就有了write_cache_pages中
if (PageWriteback(page)) {
if (wbc->sync_mode != WB_SYNC_NONE)
wait_on_page_writeback(page);
}
在end_page_writeback(page)中就有wake_up_page(page, PG_writeback);
阅读(2754) | 评论(0) | 转发(0) |