注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

gmd20的个人空间

// 编程和生活

 
 
 

日志

 
 

linux怎么把block层的bio request发给scsi底层驱动(从submit_bio到scsi_request_fn)  

2013-06-07 14:01:51|  分类: linux相关 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Understanding Linux kernel一书大概说了bio 是怎么传给设备底层驱动的。

14.3.3. Activating the Block Device Driver

http://140.120.7.20/LinuxRef/LinuxKernel/understandlk-chp-14.html#understandlk-chp-14-sect-3.3 


这里会解释block层是怎么把request发给底层的设备驱动的。不过新的3.9的内核这个已经跟书上说的变了好多,

比如block的plug接口应该变的不重要了。参考下面这个文章。

----------------------------

Explicit block device plugging

http://lwn.net/Articles/438256/


plug的用于延时处理,以便收集多个request之后再批量发送给底层磁盘。

不过现在磁盘越来越快,这个plug引入延时缓存的技术已经是没必要的啦? 至少ssd磁盘应该不会再用了。

所以很多plug延时相关的接口都变了,struct request_queue 里面plug_fn都没有了,相关的定时器也被删了。

如果少数情况要使用plug的话,应该是类似下面这种on-stack plug的用法了。


struct blk_plug plug;

blk_start_plug(&plug);

submit_batch_of_io();

blk_finish_plug(&plug);

这种使用栈上的临时变量plug继续被支持,应该很少用了? 它把栈上 plug变量指针保存到 current 的tast_struct的plug里面去。后面

blk_flush_plug_list

    queue_unplugged   

访问这个plug,然后也会调用哪个 __blk_run_queue



底层的驱动如果需要延时调用 __blk_run_queue ,比如初始化时设备还没准备好? 可以使用blk_delay_queue 通知block隔一会再调用blk_run_queue把request发送过来。

---------------------------

scsi层的创建的request_queue 注册的request处理函数是scsi_request_fn。 

block层的__blk_run_queue 函数调用scsi层的scsi_request_fn 函数把request分发给scsi层。



 297/**

 298 * __blk_run_queue_uncond - run a queue whether or not it has been stopped

 299 * @q:  The queue to run

 300 *

 301 * Description:

 302 *    Invoke request handling on a queue if there are any pending requests.

 303 *    May be used to restart request handling after a request has completed.

 304 *    This variant runs the queue whether or not the queue has been

 305 *    stopped. Must be called with the queue lock held and interrupts

 306 *    disabled. See also @blk_run_queue.

 307 */

 308inline void __blk_run_queue_uncond(struct request_queue *q)

 309{

 310        if (unlikely(blk_queue_dead(q)))

 311                return;

 312

 313        /*

 314         * Some request_fn implementations, e.g. scsi_request_fn(), unlock

 315         * the queue lock internally. As a result multiple threads may be

 316         * running such a request function concurrently. Keep track of the

 317         * number of active request_fn invocations such that blk_drain_queue()

 318         * can wait until all these request_fn calls have finished.

 319         */

 320        q->request_fn_active++;

 321        q->request_fn(q);                   ///调用scsi_request_fn 函数

 322        q->request_fn_active--;

 323}

 324

 325/**

 326 * __blk_run_queue - run a single device queue

 327 * @q:  The queue to run

 328 *

 329 * Description:

 330 *    See @blk_run_queue. This variant must be called with the queue lock

 331 *    held and interrupts disabled.

 332 */

 333void __blk_run_queue(struct request_queue *q)

 334{

 335        if (unlikely(blk_queue_stopped(q)))

 336                return;

 337

 338        __blk_run_queue_uncond(q);

 339}

 340EXPORT_SYMBOL(__blk_run_queue);

 341




bio下发的主要调用路径应该和书上讲的还是没多大变化的,还是这样

submit_bio ->

    generic_make_request ->  // 这个函数注释就写的把 bio buffer发送给底层驱动进行IO操作。

       q->make_request_fn    //调用 request_queue的make_request_fn 函数把bio request 插入队列


应该很多request合并,触发request下发到底层驱动的工作都是这个make_request_fn 函数来做的。



scsi 中间层创建request_queue时把这个make_request_fn 函数初始化为blk_queue_bio 函数。

scsi_alloc_queue-> blk_init_allocated_queue ->  blk_queue_make_request -> blk_queue_bio ->__blk_run_queue


blk_queue_bio 里面会尝试合并request的,不过合并不成功,就调用__blk_run_queue把request发给底层驱动。

       __blk_run_queue

如果合并成功了,应该就直接返回了,request在队列里面等待底层驱动自己来取。


不过scsi自己也会从request_queue里面取出request来执行。看源码有两种调用关系是自己主动的blk_run_queue的。

scsi_next_command -> scsi_run_queue -> blk_run_queue -> scsi_request_fn()

scsi_run_host_queues -> scsi_run_queue 


scsi_requeue_command scsi_device_resume等函数也都会调用到scsi_run_queue去取request来运行。


scsi_end_request里面会调用scsi_next_command,然后到blk_run_queue通知block层把下一个命令发送过来。

所以说底层scsi驱动每次自己执行完一个磁盘命令,自己就回去队列里面取下一个来执行的。直到队列为空。

scsi_run_host_queues 这些函数,看样子,会在磁盘启动之类的情况下被执行的吧。

处理request的策略还是靠底层驱动自己来控制了。block层只是第一个request的时候会触发下发动作? request合并那些动作,会在驱动从队列里面取request的时候,由io scheduler 的各种电梯算法执行。


其他的也有很多触发blk_run_queue的地方,知道的

blk_queue_bio

blk_run_queue_async

flush_end_io

elv_add_request ->__elv_add_request

elevator_switch ->  blk_queue_bypass_start -> __blk_drain_queue

cfq_rq_enqueued -> __blk_run_queue

cfq_schedule_dispatch->cfq_kick_queue




这篇文章好像也不错。

Linux IO 调度层分析 

http://www.360doc.com/content/12/0201/22/2459_183505470.shtml

  评论这张
 
阅读(1296)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017