scsi命令的执行

1.6.4 scsi命令的执行

负责执行具体scsi命令的函数是scsi_dispatch_cmd,来自drivers/scsi/scsi.c

 

    468 int scsi_dispatch_cmd(struct scsi_cmnd *cmd)

    469 {

    470         struct Scsi_Host *host = cmd->device->host;

    471         unsigned long flags = 0;

    472         unsigned long timeout;

    473         int rtn = 0;

    474

    475         /* check if the device is still usable */

    476         if (unlikely(cmd->device->sdev_state == SDEV_DEL)) {

    477                 /* in SDEV_DEL we error all commands. DID_NO_CONNECT

    478                  * returns an immediate error upwards, and signals

    479                  * that the device is no longer present */

    480                 cmd->result = DID_NO_CONNECT << 16;

    481                 atomic_inc(&cmd->device->iorequest_cnt);

    482                 __scsi_done(cmd);

    483                 /* return 0 (because the command has been processed) */

    484                 goto out;

    485         }

    486

    487         /* Check to see if the scsi lld put this device into state SDEV_BLOCK. */

    488         if (unlikely(cmd->device->sdev_state == SDEV_BLOCK)) {

    489                 /*

    490                  * in SDEV_BLOCK, the command is just put back on the device

    491                  * queue.  The suspend state has already blocked the queue so

    492                  * future requests should not occur until the device

    493                  * transitions out of the suspend state.

    494                  */

    495                 scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);

    496

    497                 SCSI_LOG_MLQUEUE(3, printk("queuecommand : device blocked /n"));

    498

    499                 /*

    500                  * NOTE: rtn is still zero here because we don't need the

    501                  * queue to be plugged on return (it's already stopped)

    502                  */

    503                 goto out;

    504         }

    505

    506         /*

    507          * If SCSI-2 or lower, store the LUN value in cmnd.

    508          */

    509         if (cmd->device->scsi_level <= SCSI_2 &&

    510             cmd->device->scsi_level != SCSI_UNKNOWN) {

    511                 cmd->cmnd[1] = (cmd->cmnd[1] & 0x1f) |

    512                                (cmd->device->lun << 5 & 0xe0);

    513         }

    514

    515         /*

    516          * We will wait MIN_RESET_DELAY clock ticks after the last reset so

    517          * we can avoid the drive not being ready.

    518          */

    519         timeout = host->last_reset + MIN_RESET_DELAY;

    520

    521         if (host->resetting && time_before(jiffies, timeout)) {

    522                 int ticks_remaining = timeout - jiffies;

    523                 /*

    524                  * NOTE: This may be executed from within an interrupt

    525                  * handler!  This is bad, but for now, it'll do.  The irq

    526                  * level of the interrupt handler has been masked out by the

    527                  * platform dependent interrupt handling code already, so the

    528                  * sti() here will not cause another call to the SCSI host's

    529                  * interrupt handler (assuming there is one irq-level per

    530                  * host).

    531                  */

    532                 while (--ticks_remaining >= 0)

    533                         mdelay(1 + 999 / HZ);

    534                 host->resetting = 0;

    535         }

    536

    537         /*

    538          * AK: unlikely race here: for some reason the timer could

    539          * expire before the serial number is set up below.

    540          */

    541         scsi_add_timer(cmd, cmd->timeout_per_command, scsi_times_out);

    542

    543         scsi_log_send(cmd);

    544

    545         /*

    546          * We will use a queued command if possible, otherwise we will

    547          * emulate the queuing and calling of completion function ourselves.

    548          */

    549         atomic_inc(&cmd->device->iorequest_cnt);

    550

    551         /*

    552          * Before we queue this command, check if the command

    553          * length exceeds what the host adapter can handle.

    554          */

    555         if (CDB_SIZE(cmd) > cmd->device->host->max_cmd_len) {

    556                 SCSI_LOG_MLQUEUE(3,

    557                                 printk("queuecommand : command too long./n"));

    558                 cmd->result = (DID_ABORT << 16);

    559

    560                 scsi_done(cmd);

    561                 goto out;

    562         }

    563

    564         spin_lock_irqsave(host->host_lock, flags);

    565         scsi_cmd_get_serial(host, cmd);

    566

    567         if (unlikely(host->shost_state == SHOST_DEL)) {

    568                 cmd->result = (DID_NO_CONNECT << 16);

    569                 scsi_done(cmd);

    570         } else {

    571                 rtn = host->hostt->queuecommand(cmd, scsi_done);

    572         }

    573         spin_unlock_irqrestore(host->host_lock, flags);

    574         if (rtn) {

    575                 if (scsi_delete_timer(cmd)) {

    576                         atomic_inc(&cmd->device->iodone_cnt);

    577                         scsi_queue_insert(cmd,

    578                                     (rtn == SCSI_MLQUEUE_DEVICE_BUSY) ?

    579                                      rtn : SCSI_MLQUEUE_HOST_BUSY);

    580                 }

    581                 SCSI_LOG_MLQUEUE(3,

    582                     printk("queuecommand : request rejected/n"));

    583         }

    584

    585  out:

    586         SCSI_LOG_MLQUEUE(3, printk("leaving scsi_dispatch_cmnd()/n"));

    587         return rtn;

    588 }

 

scsi_dispatch_cmd函数将一个scsi命令提交给底层scsi host驱动。在命令dispatch的过程中,middle level会检查scsi host是否出于busy状态,是否还有空间存放新的scsi command。如果所有条件都满足,那么会调用上下层之间的接口函数queuecommand函数转发请求。

 

queuecomand函数的实现由scsi host driver完成。通常该函数的实现很简单,只需要将传下来的scsi命令挂载到hostscsi命令队列中。由于queuecommand函数在持有spinlock的上下文中运行,所以不宜做过多复杂的操作,否则很容易导致程序睡眠,从而使程序运行不稳定。

 

一路走来的兄弟一定会一眼就看出这里我们最期待的一行代码就是571那个queuecommand()的调用。因为这之后我们就知道该发生什么了。比如对于U盘驱动来说,命令就从这里接过去开始执行。而对于实际的scsi控制器,其对应的驱动中的queuecommand也会被调用,剩下的事情我们就不用操心了。正常情况下queuecommand返回0。于是紧接着scsi_dispatch_cmd也返回0。这样就算是执行了一条scsi命令了。

 

queuecomand函数的实现由scsi host driver完成。通常该函数的实现很简单,只需要将传下来的scsi命令scsi_cmnd挂载到hostscsi命令队列中。由于queuecommand函数在持有 spinlock的上下文中运行,所以不宜做过多复杂的操作,否则很容易导致程序睡眠,从而使程序运行不稳定。

 

scsi_request_fn()是否结束还得看while循环的条件是否满足,而这就得看blk_queue_plugged()的脸色了。那么我们从字面上来分析,什么叫queue plugged?比如说,北四环上上下班高峰期,许许多多的车辆排成一队又一队,但是可能半天都前进不了,这就叫plugged,或者说堵车,也叫塞车。为此咱们使用一个flag来标志堵车与否,来自include/linux/blkdev.h

523 #define blk_queue_plugged(q)    test_bit(QUEUE_FLAG_PLUGGED, &(q)->queue_flags)

 

改变这个这个flag的函数有两个,一个是设置,一个是取消。负责设置的是blk_plug_device,负责取消的是blk_remove_plug(),前面讲的理论知识,这里就用到了。

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值