linux scatter dma传输,硬盘DMA过程分析--scatter/gather映射

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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值