static int scsi_prep_fn(struct request_queue *q, struct request *req)
{
struct scsi_device *sdev = q->queuedata;
int ret = BLKPREP_OK;
/*
* If the device is not in running state we will reject some
* or all commands.
*/
if (unlikely(sdev->sdev_state != SDEV_RUNNING)) {
switch (sdev->sdev_state) {
case SDEV_OFFLINE:
/*
* If the device is offline we refuse to process any
* commands. The device must be brought online
* before trying any recovery commands.
*/
sdev_printk(KERN_ERR, sdev,
"rejecting I/O to offline device\n");
ret = BLKPREP_KILL;
break;
case SDEV_DEL:
/*
* If the device is fully deleted, we refuse to
* process any commands as well.
*/
sdev_printk(KERN_ERR, sdev,
"rejecting I/O to dead device\n");
ret = BLKPREP_KILL;
break;
case SDEV_QUIESCE:
case SDEV_BLOCK:
/*
* If the devices is blocked we defer normal commands.
*/
if (!(req->cmd_flags & REQ_PREEMPT))
ret = BLKPREP_DEFER;
break;
default:
/*
* For any other not fully online state we only allow
* special commands. In particular any user initiated
* command is not allowed.
*/
if (!(req->cmd_flags & REQ_PREEMPT))
ret = BLKPREP_KILL;
break;
}
if (ret != BLKPREP_OK)
goto out;
}
switch (req->cmd_type) {
case REQ_TYPE_BLOCK_PC:
ret = scsi_setup_blk_pc_cmnd(sdev, req);
break;
case REQ_TYPE_FS:
//建立scsi命令
ret = scsi_setup_fs_cmnd(sdev, req);
break;
default:
/*
* All other command types are not supported.
*
* Note that these days the SCSI subsystem does not use
* REQ_TYPE_SPECIAL requests anymore. These are only used
* (directly or via blk_insert_request) by non-SCSI drivers.
*/
blk_dump_rq_flags(req, "SCSI bad req");
ret = BLKPREP_KILL;
break;
}
out:
switch (ret) {
case BLKPREP_KILL:
req->errors = DID_NO_CONNECT << 16;
break;
case BLKPREP_DEFER:
/*
* If we defer, the elv_next_request() returns NULL, but the
* queue must be restarted, so we plug here if no returning
* command will automatically do that.
*/
if (sdev->device_busy == 0)
blk_plug_device(q);
break;
default:
req->cmd_flags |= REQ_DONTPREP;
}
return ret;
}
static int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req)
{
struct scsi_cmnd *cmd;
struct scsi_driver *drv;
int ret;
/*
* Filesystem requests must transfer data.
*/
BUG_ON(!req->nr_phys_segments);
cmd = scsi_get_cmd_from_req(sdev, req);
if (unlikely(!cmd))
return BLKPREP_DEFER;
ret = scsi_init_io(cmd);
if (unlikely(ret))
return ret;
/*
* Initialize the actual SCSI command for this request.
*/
drv = *(struct scsi_driver **)req->rq_disk->private_data;
if (unlikely(!drv->init_command(cmd))) {
scsi_release_buffers(cmd);
scsi_put_command(cmd);
return BLKPREP_KILL;
}
return BLKPREP_OK;
}
/*
* Function: scsi_init_io()
*
* Purpose: SCSI I/O initialize function.
*
* Arguments: cmd - Command descriptor we wish to initialize
*
* Returns: 0 on success
* BLKPREP_DEFER if the failure is retryable
* BLKPREP_KILL if the failure is fatal
*/
static int scsi_init_io(struct scsi_cmnd *cmd)
{
struct request *req = cmd->request;
struct scatterlist *sgpnt;
int count;
/*
* We used to not use scatter-gather for single segment request,
* but now we do (it makes highmem I/O easier to support without
* kmapping pages)
*/
cmd->use_sg = req->nr_phys_segments;
/*
* If sg table allocation fails, requeue request later.
*/
sgpnt = scsi_alloc_sgtable(cmd, GFP_ATOMIC);
if (unlikely(!sgpnt)) {
scsi_unprep_request(req);
return BLKPREP_DEFER;
}
req->buffer = NULL;
cmd->request_buffer = (char *) sgpnt;
if (blk_pc_request(req))
cmd->request_bufflen = req->data_len;
else
cmd->request_bufflen = req->nr_sectors << 9;
/*
* Next, walk the list, and fill in the addresses and sizes of
* each segment.
*/
count = blk_rq_map_sg(req->q, req, cmd->request_buffer);
if (likely(count <= cmd->use_sg)) {
//scatter list的个数
cmd->use_sg = count;
return BLKPREP_OK;
}
printk(KERN_ERR "Incorrect number of segments after building list\n");
printk(KERN_ERR "counted %d, received %d\n", count, cmd->use_sg);
printk(KERN_ERR "req nr_sec %lu, cur_nr_sec %u\n", req->nr_sectors,
req->current_nr_sectors);
/* release the command and kill it */
scsi_release_buffers(cmd);
scsi_put_command(cmd);
return BLKPREP_KILL;
}