linux块设备4

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_PREFLUSH: 1

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调用)

你可能感兴趣的:(linux块设备4)