linux 3.1.5
blk-exec.c: 起动request
1. blk_end_sync_rq: request end 函数, 被blk_finish_request的end_io处理调用, 而blk_finish_request会被blk的软中断处理, 软中端被具体的块驱动的中断唤醒。
该函数__blk_put_request, 然后唤醒等待队列,
2. blk_execute_rq: 增加rq ref_count, 调用blk_execute_rq_nowait发起request, 并注册完成处理函数blk_end_sync_rq: request end, 等待request完成。
blk-softirq.c : blk软中断处理
blk_done_softirq: 调用queue的软中断的softirq_done_fn函数, 由具体的driver注册, 比如scsi_alloc_queue会注册scsi_softirq_done。 这些是在probe的时候调用的
该函数由__blk_complete_request唤醒, 由blk_softirq_init注册。
raise_blk_irq/trigger_softirq: 唤醒软中断
blk_cpu_notifier: 热插, 唤醒软中断
blk_complete_request: 由具体驱动处理函数在request处理完成的中断里面调用。
它调用__blk_complete_request, 该函数唤醒软中断, 该软中断会调用具体的驱动的软中断处理函数, 最终回调用blk_finish_request。
blk_softirq_init: 注册BLOCK_SOFTIRQ,软中断,注册熱插拔blk_cpu_notifier处理函数。
blk-flush.c: flush sequnce处理
blk_flush_policy: 生成flush policy, 被blk_insert_flush调用
如果request有数据, 设置REQ_FSEQ_DATA,
如果是REQ_FLUSH请求, 且如果命令标志REQ_FLUSH, 设置REQ_FSEQ_PREFLUSH, 否则如果命令标志REQ_FUA, 设置REQ_FSEQ_POSTFLUSH。
blk_flush_cur_seq: 返回第一个非0的seq值,
REQ_FSEQ_DATA: 2
REQ_FSEQ_POSTFLUSH: 4
按照顺序处理, 根据策略, 不是每个flush都包含所有的seq值。由blk_flush_policy决定。
blk_flush_restore_request: 回复flush之前的request的设置, 比如end_io函数, 清楚flush request等, 在flush done或者abort的时候调用
blk_flush_complete_seq: 处理不同阶段的flush list, 并调用blk_kick_flush将flush加入queue head。
根据不同seq值, 使用不同的处理:
REQ_FSEQ_PREFLUSH:
REQ_FSEQ_POSTFLUSH:
将lush.list加入pending
REQ_FSEQ_DATA:
将flush.list加入flight
将queuelist加入queue_head
REQ_FSEQ_DONE:
删除flush.list
调用blk_flush_restore_request恢复原来的设置
调用__blk_end_request_all执行完成处理
最后, 调用blk_kick_flush:
初始化flush_rq, 设置 它的cmd_type REQ_TYPE_FS, end_io为flush_end_io。将flush_rq.queuelist加入queue_head
flush_end_io:
elv_completed_request: 更新elv
blk_flush_complete_seq: 如果是REQ_FSEQ_PREFLUSH|REQ_FSEQ_POSTFLUSH调用blk_flush_complete_seq继续处理
blk_run_queue_async: 防止queue stall
blk_insert_flush: 启动flush,
调用blk_flush_policy, 得到policy,
清除cmd_flags的flush标志
如果policy是0, 调用__blk_end_bidi_request完成flush, 并返回
如果policy仅为data, 那么就当成普通数据请求, 将request queuelist加入到queue_head, 返回
否则初始化flush.list, 设置cmd_flags为REQ_FLUSH_SEQ, 保存end_io到flush.saved_end_io, 设置新的end_io为flush_data_end_io。
嗲用blk_flush_complete_seq开始处理policy seq,
在第一阶段处理完成, 会调用flush_data_end_io
flush_data_end_io:
调用blk_flush_complete_seq,
如有需要(数据), 调用blk_run_queue_async启动queue处理
blk_abort_flushes:
删除flush.list
调用blk_flush_restore_request恢复设置
如有必要, 将queuelist插入queue_head
bio_end_flush:bio end处理,
唤醒bi wait
bio_put bio
blkdev_issue_flush: 由文件系统调用, 比如sync的时候
初始化bio,设置bi_end_io为bio_end_flush,等。 bio_end_flush被fs的bio_endio调用。bio_endio由具体驱动或者blk_core调用
调用submit_bio来submit flush, 这个回调用generic_make_request
调用wait_for_completion等待flush完成
释放bio
blk-integrity.c
blk_rq_count_integrity_sg: count bio的bi_vect的size的和,并返回其所对应的segment的数量,这里输入参数bio会更新的
BIOVEC_PHYS_MERGEABLE: 检查是否是可merge的, 两个是否相邻, 不是的话, 不能merge,
BIOVEC_SEG_BOUNDARY: 判断是否在segment边界
seg_size + iv->bv_len > queue_max_segment_size(q): 超出边界
以上3中情况都算新segment
blk_rq_map_integrity_sg:将bio的segment的起始bio_vect映射到scatter list中, 并返回segment数量。segment计量方法与前相同。
scatter list提供了一些例如next等的方法操作scatterlist链表
blk_integrity_compare:比较两个disk的integrity. 相等返回0, 否则-1.
比较sector_size, tuple_size, tag_size, name,(都是什么意思?), 有一个不同就返回-1.
//integrity主要由driver调用。未必
blk_integrity_merge_rq: 如果req的bio和next的bio不同, 返回-1(不可merge), 如果req的nr_integrity_segments与next的nr_integrity_segments的和大于max_integrity_segments, 返回-1, 也不许merge, 否则, 可以merge。
内核中到处充斥着没有入口参数判断的函数
blk_integrity_merge_bio: 计算bio的segment, 并把原来的bio的bi_next赋值给跟新后的bio, 如果合并后的总segment size大于最大值, 返回-1, 否则将request的nr_integrity_segments加上bio的segments。
这个函数改变了bio和request的segments。
blk_integrity_register: 给disk生成integrity, 并注册sysfs的属性, show, store方法
blk_integrity_unregister: 取消
blk-ioc.c:
blk_ioc_init: 分配ioc_context变量iocontext_cachep, io_context能在进程之间被共享
current_io_context: 得到当前进程的io_context, 没有就创建
get_io_context: 调用current_io_context, 并增加ref
alloc_io_context: 从iocontext_cachep中分配io_context
cfq_dtor: 析构ioc中的cfq_io_context(组成hlist, 每个cfq_io_context的析构函数dtor被调用),
put_io_context: 递减ioc_context的ref, 减到0时, 调用cfq_dtor释放cfq_io_context, 再释放ioc_context到iocontext_cachep
cfq_exit: ioc中的cfq_io_context的退出处理(组成hlist, 每个cfq_io_context的exit被调用),
exit_io_context: 得到进程的io_context, 减少ioc的nr_tasks,如果为0,调用 cfq_exit做退出处理, 然后调用put_io_context来析构ioc_context,有个奇怪的地方, (这里有个问题, put_io_context总是被调用, 包括释放io_context, 应该是因为一定只有所有task从这个iocontext上退出, 才会ref count为0)
ioc_context的私有数据在别的函数里面处理。
blk-iopoll.c: 在scsi中用
DEFINE_PER_CPU(struct list_head, blk_cpu_iopoll);
blk_iopoll_sched: 增加blk_iopoll到__get_cpu_var(blk_cpu_iopoll) pending list,并唤醒BLOCK_IOPOLL_SOFTIRQ软中端
__raise_softirq_irqoff
给当前处理器的软中断位图 -- irq_stat[cpu].__softirq_pending的相应位置位,表示第nr个softirq等待执行(处理)
__blk_iopoll_complete: 把blk_iopoll从pending list中取下, 调用clear_bit_unlock清除iop->state的IOPOLL_F_SCHED状态
smp_mb__before_clear_bit: 提供屏障
http://apps.hi.baidu.com/share/detail/48241343
http://oss.org.cn/kernel-book/%E5%9F%BA%E6%9C%ACC%E5%BA%93%E5%87%BD%E6%95%B0.htm
blk_iopoll_softirq: poll软中断处理函数
blk_iopoll_disable: disable io poll, 等待pending的处理完成
blk_iopoll_enable: 使能io poll
blk_iopoll_init: 初始化io poll, 及poll函数
blk_iopoll_cpu_notify: 当某cpu dead或者frezon, 这个通知会被触发, 引起 iopoll软中断唤起
blk_iopoll_setup: open io poll软中断, 注册cpu hotplug notify函数
blk-lib.c:
bio_batch_end_io: bio batch end io处理
complete等待队列
调用bio_put释放bio
blkdev_issue_discard: 主要被fs调用, 处理discard sectors, 生成bio, 并设置bio_batch_end_io, 递交并等待处理完成。
blkdev_issue_zeroout: fs/ext4使用, 类似上述函数, 不过这个的submit bio的标志不同,前者是WRITE|REQ_DISCARD, 这个只是REQ_DISCARD
blk-map.c:
blk_rq_append_bio: 挂bio到request,
如果request的bio队列为空, 调用blk_rq_bio_prep把bio赋给request
否则调用ll_back_merge_fn, 苍是merge bio到request, 通过blk_integrity_merge_bio进行merge, 如果不能merge, 返回0.
如果也不能merge, 就bio增加到request的bio next
__blk_rq_unmap_user: unmap user mapoed bio,
如果bio的bio_flagged指名是BIO_USER_MAPPED(由user_map映射), 调用bio_unmap_user(fs/bio)去set_page dirty, 并page_cache_release
否则调用bio_uncopy_user, 将bio拷贝到user 空间(这里因为用的是内核的空间所以需要使用copytouser的方法从内核空间把数据返回用户空间, 这个内核空间的buffer也称为回弹buffer)
__blk_rq_map_user:
1. 如果用户buffer和长度是符合dma alignment的, 那么直接调用bio_map_user映射用户空间, 否则调用bio_copy_user拷贝user空间数据到内核回弹buffer()
2. 调用blk_queue_bounce回弹bio到dma pool
3. bio_get, 增加bio引用计数
4. 如果调用blk_rq_append_bio加载bio到request没有错, 返回。
否则bio_endio, unmap user
blk_rq_map_user: 多次调用__blk_rq_map_user map用户空间到bio请求 (driver调用)
blk_rq_map_user_iov: 跟前述函数差不多, 但调用bio_copy_user_iov/bio_map_user_iov进行map
blk_rq_unmap_user:多次调用__blk_rq_unmap_user
blk_rq_map_kern: map kernel数据到request,与前面不同的是调用bio_copy_kern/bio_map_kern进行map,(driver调用)