Chinaunix首页 | 论坛 | 博客
  • 博客访问: 788064
  • 博文数量: 127
  • 博客积分: 2669
  • 博客等级: 少校
  • 技术积分: 1680
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-23 11:39
文章分类

全部博文(127)

文章存档

2014年(5)

2013年(19)

2012年(25)

2011年(9)

2010年(25)

2009年(44)

分类: LINUX

2012-11-23 00:08:40

Libata driver里用struct ata_queued_cmd结构来描述一个命令,libata driverata命令来自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;

/*

*命令的延后发送,如果你的代码里面需要同时执行NCQNON-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

阅读(1742) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~