块设备与软中断

来源:互联网 发布:网络课程毛笔字 编辑:程序博客网 时间:2024/06/16 06:16

<!--@page { margin: 2cm }P { margin-bottom: 0.21cm }-->

块设备层无非两件事情,一件是下发,即submit,一件是返回,callback

下发过程举个例子:在进程上下文,用户通过系统调用向文件系统层写数据。文件系统层向下到pagecache层封装成bio发下来,submit_bio调用generic_make_requestbio“加”到设备的队列上,这个过程就是下发。我们已经很清楚了,可是对回调部分呢。

 

回调部分比较复杂,以scsi设备为例,先看一眼scsi设备的queue。当然设备队列的初始化是经过了一系列N长的函数,感兴趣可以去看一下blk_init_queue函数。最后的结果形成了如下的scsiqueue结构q:

q->request_fn= scsi_request_fn;

q->unplug_fn= generic_unplug_device;

q->softirq_done_fn= scsi_softirq_done;

q->make_request_fn= __make_request;

request中的这几个域非常重要,后面所有的论述都是在讲这几个函数:

不用问,generic_make_request用到的队列函数必然是make_request_fn,即__make_request

 

而回调过程我们从generic_unplug_device函数开始说起:这个函数调用了下一层的__generic_unplug_device,在此函数中调用blk_remove_plug,然后调用q->request_fn(q),其实就是scsi_request_fn

进入scsi_request_fn函数,他调用了两个比较重要的函数,一个是elv_next_request,作用是从队列中取出一个request,然后“制作”一个scsicmd。然后调用scsi_dispatch_cmd来处理这个cmd。在这个函数中,对处理完成的cmd调用了scsi_done,开启了实际的返回过程。

进入scsi_done首先映入眼帘的是__scsi_done,再进入,再次映入眼帘的是blk_complete_request函数,这个函数中只做了一件非常有意义的事情:raise_softirq_irqoff(BLOCK_SOFTIRQ),实际上是向软中断提交了一个任务。

在这里要用一下插叙的手法,看看blk_dev_init函数,这个函数是整个block层的初始化函数,在这个函数中做了这样一件事:open_softirq(BLOCK_SOFTIRQ,blk_done_softirq,NULL),这件事的意思是,以后凡是提交到BLOCK_SOFTIRQ软中断的任务,一律用blk_done_softirq处理。

再关注一下软中断中BLOCK_SOFTIRQ的处理函数:blk_done_softirq。注意从这开始,已经是在软中断的上下文中执行了。好吧,不知道过了多长时间,软中断开始执行(这也就是异步的含义)函数中有一句:rq->q->softirq_done_fn(rq),严丝合缝吧,就是scsi_softirq_done。毫不犹豫,进入函数:

我们只对这句感兴趣:scsi_finish_command,这句函数最后会调用cmd->done(cmd)。这个显然是在scsi_setup_blk_pc_cmnd函数中设置的scsi_blk_pc_done。在这个函数中,scsi_io_completion会对cmd做最后的处理:scsi_end_request,这个函数最重要的是end_request_request_last函数,完成的任务是req->end_io(req,error)。这步处理的实际调用的是blk_end_sync_rq。最后的函数里完成了__blk_put_request,在这个函数中req的返回是这样的:e->ops->elevator_completed_req_fn,可以看到,这里又交给了调度策略。

 

原创粉丝点击