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

全部博文(127)

文章存档

2014年(5)

2013年(19)

2012年(25)

2011年(9)

2010年(25)

2009年(44)

分类: LINUX

2011-05-20 16:49:14

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);

              break;

 

       case REQUEST_SENSE:

              ata_scsi_set_sense(cmd, 0, 0, 0);

              cmd->result = (DRIVER_SENSE << 24);

              done(cmd);

              break;

 

       /* if we reach this, then writeback caching is disabled,

        * turning this into a no-op.

        */

       case SYNCHRONIZE_CACHE:

              /* fall through */

 

       /* no-op's, complete with success */

       case REZERO_UNIT:

       case SEEK_6:

       case SEEK_10:

       case TEST_UNIT_READY:

              ata_scsi_rbuf_fill(&args, ata_scsiop_noop);

              break;

 

       case SEND_DIAGNOSTIC:

              tmp8 = scsicmd[1] & ~(1 << 3);

              if ((tmp8 == 0x4) && (!scsicmd[3]) && (!scsicmd[4]))

                     ata_scsi_rbuf_fill(&args, ata_scsiop_noop);

              else

                     ata_scsi_invalid_field(cmd, done);

              break;

 

       /* all other commands */

       default:

              ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x20, 0x0);

              /* "Invalid command operation code" */

              done(cmd);

              break;

       }

}

 

 

void ata_scsi_rbuf_fill(struct ata_scsi_args *args,

                     unsigned int (*actor) (struct ata_scsi_args *args,

                                          u8 *rbuf, unsigned int buflen))

{

       u8 *rbuf;

       unsigned int buflen, rc;

       struct scsi_cmnd *cmd = args->cmd;

      

       /* 分配命令应答缓冲区,具体代码scatter-gather DMA modules */

       buflen = ata_scsi_rbuf_get(cmd, &rbuf);

       memset(rbuf, 0, buflen);

       rc = actor(args, rbuf, buflen);

       ata_scsi_rbuf_put(cmd, rbuf);

 

       if (rc == 0)

              cmd->result = SAM_STAT_GOOD;

       args->done(cmd);

       //执行scsi_done函数

}

针对READ_CAPACITY命令

Scsi10个字节读容量命令结构:

 

10个字节读容量返回结构:

 

unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf,

                             unsigned int buflen)

{

       u64 last_lba = args->dev->n_sectors - 1; /* LBA of the last block */

       //sata硬盘的最大块,即容量

       VPRINTK("ENTER\n");

 

       if (args->cmd->cmnd[0] == READ_CAPACITY) {

       10字节的读容量rbuf填充

              if (last_lba >= 0xffffffffULL)

                     last_lba = 0xffffffff;

 

              /* sector count, 32-bit */

              依次按照上述结构填充rbuf

              ATA_SCSI_RBUF_SET(0, last_lba >> (8 * 3));

              ATA_SCSI_RBUF_SET(1, last_lba >> (8 * 2));

              ATA_SCSI_RBUF_SET(2, last_lba >> (8 * 1));

              ATA_SCSI_RBUF_SET(3, last_lba);

 

              /* sector size */

              ATA_SCSI_RBUF_SET(6, ATA_SECT_SIZE >> 8);

              ATA_SCSI_RBUF_SET(7, ATA_SECT_SIZE);

       } else {

       16字节的读容量rbuf填充

              /* sector count, 64-bit */

              ATA_SCSI_RBUF_SET(0, last_lba >> (8 * 7));

              ATA_SCSI_RBUF_SET(1, last_lba >> (8 * 6));

              ATA_SCSI_RBUF_SET(2, last_lba >> (8 * 5));

              ATA_SCSI_RBUF_SET(3, last_lba >> (8 * 4));

              ATA_SCSI_RBUF_SET(4, last_lba >> (8 * 3));

              ATA_SCSI_RBUF_SET(5, last_lba >> (8 * 2));

              ATA_SCSI_RBUF_SET(6, last_lba >> (8 * 1));

              ATA_SCSI_RBUF_SET(7, last_lba);

 

              /* sector size */

              ATA_SCSI_RBUF_SET(10, ATA_SECT_SIZE >> 8);

              ATA_SCSI_RBUF_SET(11, ATA_SECT_SIZE);

       }

 

       return 0;

}

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

udb66882012-03-09 15:16:54

清辉到些一游。。。。。。。。。