全部博文(127)
分类: LINUX
2012-11-23 00:08:40
Libata driver里用struct ata_queued_cmd结构来描述一个命令,libata driver的ata命令来自libata自身初始化和错误处理以及scsi middle layer。
Libata driver 将ata_scsi_queuecmd作为hostt->queuecommand的回调函数,scsi命令在libata driver里面分两部分执行,一种是真的转换为ata命令称作translate,一种是借用ata命令执行完的结果填充相应命令的返回参数称作simulate;对于simulate而言不会牵涉到qc的处理,而translate类型的命令,调用ata_qc_new_init()来分配qc,并且scsi命令会转换为qc,转换完成后,ata_qc_issue()函数会将qc发送到ata设备,当qc完成后qc->complete_fn()回调函数里qc->scsidone()会通知scsi middle layer。
int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
{
struct ata_port *ap;
struct ata_device *dev;
struct scsi_device *scsidev = cmd->device;
struct Scsi_Host *shost = scsidev->host;
int rc = 0;
ap = ata_shost_to_port(shost);
spin_unlock(shost->host_lock);
spin_lock(ap->lock);
ata_scsi_dump_cdb(ap, cmd);
dev = ata_scsi_find_dev(ap, scsidev);//查找ata device
if (likely(dev))
rc = __ata_scsi_queuecmd(cmd, done, dev);
else {
cmd->result = (DID_BAD_TARGET << 16);
done(cmd);
}
spin_unlock(ap->lock);
spin_lock(shost->host_lock);
return rc;
}
static inline int __ata_scsi_queuecmd(struct scsi_cmnd *scmd,
void (*done)(struct scsi_cmnd *),
struct ata_device *dev)
{
u8 scsi_op = scmd->cmnd[0];
ata_xlat_func_t xlat_func;
int rc = 0;
if (dev->class == ATA_DEV_ATA) {//ata设备类型
if (unlikely(!scmd->cmd_len || scmd->cmd_len > dev->cdb_len))
goto bad_cdb_len;
xlat_func = ata_get_xlat_func(dev, scsi_op);
/*对于scsi命令转换ata命令时分两种类型,一种是真的转换为了ata命令,返
*回要转换的函数地址;另一种只是借用了一下libata库加载后ata命令执行时的*结果,返回NULL;
*/
} else {
if (unlikely(!scmd->cmd_len))
goto bad_cdb_len;
xlat_func = NULL;
if (likely((scsi_op != ATA_16) || !atapi_passthru16)) {
/* relay SCSI command to ATAPI device */
int len = COMMAND_SIZE(scsi_op);
if (unlikely(len > scmd->cmd_len || len > dev->cdb_len))
goto bad_cdb_len;
xlat_func = atapi_xlat;
} else {
/* ATA_16 passthru, treat as an ATA command */
if (unlikely(scmd->cmd_len > 16))
goto bad_cdb_len;
xlat_func = ata_get_xlat_func(dev, scsi_op);
}
}
if (xlat_func)
rc = ata_scsi_translate(dev, scmd, done, xlat_func);
else
ata_scsi_simulate(dev, scmd, done);
return rc;
bad_cdb_len:
DPRINTK("bad CDB len=%u, scsi_op=0x%02x, max=%u\n",
scmd->cmd_len, scsi_op, dev->cdb_len);
scmd->result = DID_ERROR << 16;
done(scmd);
return 0;
}
ata_get_xlat_func函数的处理如下,对于真的转换为ata命令时,返回相应的函数地址,否则返回NULL,具体的函数就是相应的ata命令转换
static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd)
{
switch (cmd) {
case READ_6:
case READ_10:
case READ_16:
case WRITE_6:
case WRITE_10:
case WRITE_16:
return ata_scsi_rw_xlat;
case SYNCHRONIZE_CACHE:
if (ata_try_flush_cache(dev))
return ata_scsi_flush_xlat;
break;
case VERIFY:
case VERIFY_16:
return ata_scsi_verify_xlat;
case ATA_12:
case ATA_16:
return ata_scsi_pass_thru;
case START_STOP:
return ata_scsi_start_stop_xlat;
}
return NULL;
}
Scis命令转换为ata命令的流程
static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
void (*done)(struct scsi_cmnd *),
ata_xlat_func_t xlat_func)
{
struct ata_port *ap = dev->link->ap;
struct ata_queued_cmd *qc;
int rc;
VPRINTK("ENTER\n");
qc = ata_scsi_qc_new(dev, cmd, done);//获取可用的ata_queued_cmd
if (!qc)
goto err_mem;
/* data is present; dma-map it */
if (cmd->sc_data_direction == DMA_FROM_DEVICE ||
cmd->sc_data_direction == DMA_TO_DEVICE) {
if (unlikely(scsi_bufflen(cmd) < 1)) {
ata_dev_printk(dev, KERN_WARNING,
"WARNING: zero len r/w req\n");
goto err_did;
}
ata_sg_init(qc, scsi_sglist(cmd), scsi_sg_count(cmd));
qc->dma_dir = cmd->sc_data_direction;
}
qc->complete_fn = ata_scsi_qc_complete;
/*
*调用ata命令打包函数
*/
if (xlat_func(qc))
goto early_finish;
/*
*命令的延后发送,如果你的代码里面需要同时执行NCQ和NON-NCQ命令的
*话,这里的操作就有必要了,否则会disable NCQ
*/
if (ap->ops->qc_defer) {
if ((rc = ap->ops->qc_defer(qc)))
goto defer;
}
/* select device, send command to hardware */
ata_qc_issue(qc);//命令发送到ata设备
VPRINTK("EXIT\n");
return 0;
early_finish:
ata_qc_free(qc);
qc->scsidone(cmd);
DPRINTK("EXIT - early finish (good or error)\n");
return 0;
err_did:
ata_qc_free(qc);
cmd->result = (DID_ERROR << 16);
qc->scsidone(cmd);
err_mem:
DPRINTK("EXIT - internal\n");
return 0;
defer:
ata_qc_free(qc);
DPRINTK("EXIT - defer\n");
if (rc == ATA_DEFER_LINK)
return SCSI_MLQUEUE_DEVICE_BUSY;
else
return SCSI_MLQUEUE_HOST_BUSY;
}
命令发送完成后,要么超时要么完成;ata host会产生中断来完成命令,会调用ata_qc_complete函数,之后会调用回调函数qc->complete_fn(qc),ata命令的完成回调函数有两种,内部命令和scsi转换ata命令;内部命令的回调函数在ata_exec_internal_sg函数里,qc->complete_fn = ata_qc_complete_internal;
Scsi转换ata命令在ata_scsi_translate函数里,qc->complete_fn = ata_scsi_qc_complete;
static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct scsi_cmnd *cmd = qc->scsicmd;
u8 *cdb = cmd->cmnd;
int need_sense = (qc->err_mask != 0);
/* For ATA pass thru (SAT) commands, generate a sense block if
* user mandated it or if there's an error. Note that if we
* generate because the user forced us to, a check condition
* is generated and the ATA register values are returned
* whether the command completed successfully or not. If there
* was no error, SK, ASC and ASCQ will all be zero.
*/
if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) &&
((cdb[2] & 0x20) || need_sense)) {
ata_gen_passthru_sense(qc);
} else {
if (!need_sense) {
/*
*命令执行的最好结果
*/
cmd->result = SAM_STAT_GOOD;
} else {
/* TODO: decide which descriptor format to use
* for 48b LBA devices and call that here
* instead of the fixed desc, which is only
* good for smaller LBA (and maybe CHS?)
* devices.
*/
ata_gen_ata_sense(qc);//根据ata命令执行完后的结果,将ata错误转换为scsi错误
}
}
/* XXX: track spindown state for spindown skipping and warning */
if (unlikely(qc->tf.command == ATA_CMD_STANDBY ||
qc->tf.command == ATA_CMD_STANDBYNOW1))
qc->dev->flags |= ATA_DFLAG_SPUNDOWN;
else if (likely(system_state != SYSTEM_HALT &&
system_state != SYSTEM_POWER_OFF))
qc->dev->flags &= ~ATA_DFLAG_SPUNDOWN;
if (need_sense && !ap->ops->error_handler)
ata_dump_status(ap->print_id, &qc->result_tf);
qc->scsidone(cmd);//回调函数,调用scsi_done函数
ata_qc_free(qc);//释放执行完的ata命令
}
Scsi命令没有转换为ata命令的流程
void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd,
void (*done)(struct scsi_cmnd *))
{
struct ata_scsi_args args;
const u8 *scsicmd = cmd->cmnd;
u8 tmp8;
args.dev = dev;
args.id = dev->id;
args.cmd = cmd;
args.done = done;//回调函数赋值,最终会调用scsi_done
switch(scsicmd[0]) {
/* TODO: worth improving? */
case FORMAT_UNIT:
ata_scsi_invalid_field(cmd, done);
break;
case INQUIRY:
if (scsicmd[1] & 2) /* is CmdDt set? */
ata_scsi_invalid_field(cmd, done);
else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std);
else switch (scsicmd[2]) {
case 0x00:
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_00);
break;
case 0x80:
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_80);
break;
case 0x83:
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83);
break;
case 0x89:
ata_scsi_rbuf_fill(&args, ata_scsiop_inq_89);
break;
default:
ata_scsi_invalid_field(cmd, done);
break;
}
break;
case MODE_SENSE:
case MODE_SENSE_10:
ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense);
break;
case MODE_SELECT: /* unconditionally return */
case MODE_SELECT_10: /* bad-field-in-cdb */
ata_scsi_invalid_field(cmd, done);
break;
case READ_CAPACITY:
ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
break;
case SERVICE_ACTION_IN:
if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16)
ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
else
ata_scsi_invalid_field(cmd, done);
break;
case REPORT_LUNS:
ata_scsi_rbuf_fill(&args, ata_scsiop_report_luns);
&n