• Linux3.10.0块IO子系统流程(6)-- 派发SCSI命令到低层驱动


    在SCSI策略例程中最后调用scsi_dispatch_cmd将SCSI命令描述符派发给低层驱动进行处理

      1 /**
      2 * scsi_dispatch_command - Dispatch a command to the low-level driver.
      3 * @cmd: command block we are dispatching.
      4 *
      5 * Return: nonzero return request was rejected and device's queue needs to be
      6 * plugged.
      7 */
      8 int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
      9 {
     10     struct Scsi_Host *host = cmd->device->host;
     11     unsigned long timeout;
     12     int rtn = 0;
     13 
     14     // 递增SCSI设备的IO请求统计数 
     15     atomic_inc(&cmd->device->iorequest_cnt);
     16 
     17     /* 
     18      * check if the device is still usable 
     19      * 检测设备是否还可用。在此时,可能别的地方已经将设备状态置为SDEV_DEL,如果这样,我们将所有命令出错,将命令的结果标记为DID_NO_CONNECT,调用scsi_done结束
     20      */
     21     if (unlikely(cmd->device->sdev_state == SDEV_DEL)) {
     22         /* in SDEV_DEL we error all commands. DID_NO_CONNECT
     23          * returns an immediate error upwards, and signals
     24          * that the device is no longer present */
     25         cmd->result = DID_NO_CONNECT << 16;
     26         scsi_done(cmd);
     27         /* return 0 (because the command has been processed) */
     28         goto out;
     29     }
     30 
     31     /* Check to see if the scsi lld made this device blocked. 检查SCSI低层驱动是否已经阻塞了这个设备*/
     32     if (unlikely(scsi_device_blocked(cmd->device))) {
     33         /*
     34          * in blocked state, the command is just put back on
     35          * the device queue.  The suspend state has already
     36          * blocked the queue so future requests should not
     37          * occur until the device transitions out of the
     38          * suspend state.
     39          */
     40 
     41         scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
     42 
     43         SCSI_LOG_MLQUEUE(3, printk("queuecommand : device blocked 
    "));
     44 
     45         /*
     46          * NOTE: rtn is still zero here because we don't need the
     47          * queue to be plugged on return (it's already stopped)
     48          */
     49         goto out;
     50     }
     51 
     52     /*
     53      * If SCSI-2 or lower, store the LUN value in cmnd.
     54      */
     55     if (cmd->device->scsi_level <= SCSI_2 &&
     56         cmd->device->scsi_level != SCSI_UNKNOWN) {
     57         cmd->cmnd[1] = (cmd->cmnd[1] & 0x1f) |
     58                    (cmd->device->lun << 5 & 0xe0);
     59     }
     60 
     61     /*
     62      * We will wait MIN_RESET_DELAY clock ticks after the last reset so we can avoid the drive not being ready.
     63      * 如果主机适配器的resetting被设置为1,表示其last_Reset域有效。也就是说,后者记录了主机适配器上次复位的时间。
     64      * 在复位后必须有2秒钟的延时,才能向这个主机适配器发送命令。
     65      */
     66     timeout = host->last_reset + MIN_RESET_DELAY;
     67 
     68     if (host->resetting && time_before(jiffies, timeout)) {
     69         int ticks_remaining = timeout - jiffies;
     70         /*
     71          * NOTE: This may be executed from within an interrupt
     72          * handler!  This is bad, but for now, it'll do.  The irq
     73          * level of the interrupt handler has been masked out by the
     74          * platform dependent interrupt handling code already, so the
     75          * sti() here will not cause another call to the SCSI host's
     76          * interrupt handler (assuming there is one irq-level per
     77          * host).
     78          */
     79         while (--ticks_remaining >= 0)
     80             mdelay(1 + 999 / HZ);
     81         host->resetting = 0;
     82     }
     83 
     84     // 在提交SCSI命令之前输出信息
     85     scsi_log_send(cmd);
     86 
     87     /*
     88      * Before we queue this command, check if the command length exceeds what the host adapter can handle.
     89      * 在将命令排入队列之前,检查命令长度是否超过了主机适配器可以处理的最大长度,如果这样,将命令结果标记为DID_ABORT并返回
     90      */
     91     if (cmd->cmd_len > cmd->device->host->max_cmd_len) {
     92         SCSI_LOG_MLQUEUE(3,
     93             printk("queuecommand : command too long. "
     94                    "cdb_size=%d host->max_cmd_len=%d
    ",
     95                    cmd->cmd_len, cmd->device->host->max_cmd_len));
     96         cmd->result = (DID_ABORT << 16);
     97 
     98         scsi_done(cmd);
     99         goto out;
    100     }
    101 
    102     /* 在获取锁的过程中,可能别的地方已经将主机适配器的状态设置为“删除”。如果这样,标记为DID_NO_CONNECT并返回*/
    103     if (unlikely(host->shost_state == SHOST_DEL)) {
    104         cmd->result = (DID_NO_CONNECT << 16);
    105         scsi_done(cmd);
    106     } else {
    107         trace_scsi_dispatch_cmd_start(cmd);
    108         cmd->scsi_done = scsi_done;
    109         rtn = host->hostt->queuecommand(host, cmd);    // 调用主机适配器模板的queuecommand回调,将SCSI命令排入低层设备驱动的队列,调用时传入scsi_done,以便低层驱动完成后回调
    110     }
    111 
    112     if (rtn) {
    113         trace_scsi_dispatch_cmd_error(cmd, rtn);
    114         if (rtn != SCSI_MLQUEUE_DEVICE_BUSY &&
    115             rtn != SCSI_MLQUEUE_TARGET_BUSY)
    116             rtn = SCSI_MLQUEUE_HOST_BUSY;
    117 
    118         scsi_queue_insert(cmd, rtn);
    119 
    120         SCSI_LOG_MLQUEUE(3,
    121             printk("queuecommand : request rejected
    "));
    122     }
    123 
    124 out:
    125     SCSI_LOG_MLQUEUE(3, printk("leaving scsi_dispatch_cmnd()
    "));
    126     return rtn;
    127 }
  • 相关阅读:
    JS之RegExp对象(一)
    八大排序算法总结
    mysql创建数据库指定编码格式
    Java学习笔记_22_Set接口的实现类
    HDU--杭电--3415--Max Sum of Max-K-sub-sequence--暴力或单调队列
    第六届蓝桥杯JavaB组省赛真题
    第六届蓝桥杯JavaA组省赛真题
    第六届蓝桥杯JavaA组省赛真题
    第六届蓝桥杯JavaA组省赛真题
    第六届蓝桥杯JavaA组省赛真题
  • 原文地址:https://www.cnblogs.com/luxiaodai/p/9266318.html
Copyright © 2020-2023  润新知