一旦这种关系建立好了以后,就可以开始执行请求了。来看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_nowait和wait_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_fn,struct 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_OK。prep表示prepare的意思,用我们的母语说就是准备的意思,最后BLKPREP_OK就说明准备好了,或者说准备就绪。而scsi_prep_fn()也将返回这个值,返回之前还设置了cmd_flags中的REQ_DONTPREP。(注意elv_next_request()函数741行判断的就是设这个flag。)
后面的工作就和前面“scsi块设备驱动层处理”的内容一样了。