scsi命令的第二次转变

1.6.6 scsi命令的第二次转变

一旦这种关系建立好了以后,就可以开始执行请求了。来看blk_execute_rq(),来自block/ll_rw_blk.c

 

   2616 int blk_execute_rq(request_queue_t *q, struct gendisk *bd_disk,

   2617                    struct request *rq, int at_head)

   2618 {

   2619         DECLARE_COMPLETION_ONSTACK(wait);

   2620         char sense[SCSI_SENSE_BUFFERSIZE];

   2621         int err = 0;

   2622

   2623         /*

   2624          * we need an extra reference to the request, so we can look at

   2625          * it after io completion

   2626          */

   2627         rq->ref_count++;

   2628

   2629         if (!rq->sense) {

   2630                 memset(sense, 0, sizeof(sense));

   2631                 rq->sense = sense;

   2632                 rq->sense_len = 0;

   2633         }

   2634

   2635         rq->end_io_data = &wait;

   2636         blk_execute_rq_nowait(q, bd_disk, rq, at_head, blk_end_sync_rq);

   2637         wait_for_completion(&wait);

   2638

   2639         if (rq->errors)

   2640                 err = -EIO;

   2641

   2642         return err;

   2643 }

 

抛去那些用于错误处理的代码,这个函数真正有意义的代码就是两行,blk_execute_rq_nowaitwait_for_completion。先看前者,来自block/ll_rw_blk.c

 

   2588 void blk_execute_rq_nowait(request_queue_t *q, struct gendisk *bd_disk,

   2589                            struct request *rq, int at_head,

   2590                            rq_end_io_fn *done)

   2591 {

   2592        int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK;

   2593

   2594         rq->rq_disk = bd_disk;

   2595         rq->cmd_flags |= REQ_NOMERGE;

   2596         rq->end_io = done;

   2597         WARN_ON(irqs_disabled());

   2598         spin_lock_irq(q->queue_lock);

   2599         __elv_add_request(q, rq, where, 1);

   2600         __generic_unplug_device(q);

   2601         spin_unlock_irq(q->queue_lock);

   2602 }

 

首先at_head是表示往哪插。

 

where用来记录at_head的值。在我们这个上下文中,at_head是从scsi_execute()中调用blk_execute_rq的时候传递下来的,当时我们设置的是1。于是where被设置为ELEVATOR_INSERT_FRONT

 

回到blk_execute_rq_nowait()中,下一个被调用的函数是__generic_unplug_device,依然是来自block/ll_rw_blk.c

 

   1589 void __generic_unplug_device(request_queue_t *q)

   1590 {

   1591         if (unlikely(blk_queue_stopped(q)))

   1592                 return;

   1593

   1594         if (!blk_remove_plug(q))

   1595                 return;

   1596

   1597         q->request_fn(q);

   1598 }

 

其实最有看点的就是1597行调用这个request_fnstruct request_queue中的一个成员request_fn_proc *request_fn,而至于request_fn_proc,其实又是typedef的小伎俩,来自include/linux/blkdev.h

334 typedef void (request_fn_proc) (request_queue_t *q);

 

那么这个request_fn是多少呢还记得当初那个scsi子系统中申请队列的函数了么没错,就是__scsi_alloc_queue()设置成的scsi_request_fn函数。这个函数调用elv_next_request跟我们前面看到的一样,只不过在执行scsi_prep_fn的时候,由于request的标识已经不是

 

按正路,我们会走到1229行这个switch语句,并且会根据scsi命令的类型而执行不同的函数:scsi_setup_blk_pc_cmnd或者scsi_setup_fs_cmnd。那么我们cmd_type究竟是什么呢?跟前面不同了,前面是REQ_CMD,而我们这次是在scsi_execute()中有这么一行:

199         req->cmd_type = REQ_BLOCK_PC;

 

所以,没什么好说的,我们会执行scsi_setup_blk_pc_cmnd,来自drivers/scsi/scsi_lib.c

 

   1090 static int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req)

   1091 {

   1092         struct scsi_cmnd *cmd;

   1093

   1094         cmd = scsi_get_cmd_from_req(sdev, req);

   1095         if (unlikely(!cmd))

   1096                 return BLKPREP_DEFER;

   1097

   1098         /*

   1099          * BLOCK_PC requests may transfer data, in which case they must

   1100          * a bio attached to them.  Or they might contain a SCSI command

   1101          * that does not transfer data, in which case they may optionally

   1102          * submit a request without an attached bio.

   1103          */

   1104         if (req->bio) {

   1105                 int ret;

   1106

   1107                 BUG_ON(!req->nr_phys_segments);

   1108

   1109                 ret = scsi_init_io(cmd);

   1110                 if (unlikely(ret))

   1111                         return ret;

   1112         } else {

   1113                 BUG_ON(req->data_len);

   1114                 BUG_ON(req->data);

   1115

   1116                 cmd->request_bufflen = 0;

   1117                 cmd->request_buffer = NULL;

   1118                 cmd->use_sg = 0;

   1119                 req->buffer = NULL;

   1120         }

   1121

   1122         BUILD_BUG_ON(sizeof(req->cmd) > sizeof(cmd->cmnd));

   1123         memcpy(cmd->cmnd, req->cmd, sizeof(cmd->cmnd));

   1124         cmd->cmd_len = req->cmd_len;

   1125         if (!req->data_len)

   1126                 cmd->sc_data_direction = DMA_NONE;

   1127         else if (rq_data_dir(req) == WRITE)

   1128                 cmd->sc_data_direction = DMA_TO_DEVICE;

   1129         else

   1130                 cmd->sc_data_direction = DMA_FROM_DEVICE;

   1131

   1132         cmd->transfersize = req->data_len;

   1133         cmd->allowed = req->retries;

   1134         cmd->timeout_per_command = req->timeout;

   1135         cmd->done = scsi_blk_pc_done;

   1136         return BLKPREP_OK;

   1137 }

 

如果曾经的你还对scsi cmd是如何形成的颇有疑义的话,那么相信此刻,你应该会明白了吧,尤其是当你在usb-storage那个故事中看到对它sc_data_direction的判断的时候,你不理解这个值是如何设定的,那么此刻,这代码活生生的展现在你面前,想必已经揭开了你心中那谜团吧。

 

最终,正常的话,函数返回BLKPREP_OKprep表示prepare的意思,用我们的母语说就是准备的意思,最后BLKPREP_OK就说明准备好了,或者说准备就绪。而scsi_prep_fn()也将返回这个值,返回之前还设置了cmd_flags中的REQ_DONTPREP(注意elv_next_request()函数741行判断的就是设这个flag)

 

后面的工作就和前面“scsi块设备驱动层处理”的内容一样了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值