scsi命令的第二次转变

    技术2024-07-27  57

    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)