UFS读写请求完成的处理流程

      UFS传输分为三个过程:制作并下发Request阶段 , 处理Request请求阶段 ,  Request请求完成后的阶段,这里主要是简单说明一下Request请求完成后的阶段,即在ufs request请求处理完成之后系统还需要做清理的工作,释放资源等,ufs host需要接收ufs devices返回的请求完成的状态走对应的流程, ufs devices返回一些状态status.

 

/*
 *function:    scsi_request_fn()
 *
 * Purpose:     Main strategy routine for SCSI.
 *
 * Arguments:   q       - Pointer to actual queue.
 *
 * Returns:     Nothing
 *
 * Lock status: IO request lock assumed to be held when called.
 */
static void scsi_request_fn(struct request_queue *q)
    __releases(q->queue_lock)
    __acquires(q->queue_lock)
{
    struct scsi_device *sdev = q->queuedata;
    struct Scsi_Host *shost;
    struct scsi_cmnd *cmd;
    struct request *req;

    /*
     * To start with, we keep looping until the queue is empty, or until
     * the host is no longer able to accept any more requests.
     */
    shost = sdev->host;
    for (;;) {
        int rtn;
        /*
         * get next queueable request.  We do this early to make sure
         * that the request is fully prepared even if we cannot
         * accept it.
         */
        req = blk_peek_request(q);
        if (!req)
            break;

        if (unlikely(!scsi_device_online(sdev))) {
            sdev_printk(KERN_ERR, sdev,
                    "rejecting I/O to offline device\n");
            scsi_kill_request(req, q);
            continue;
        }

        if (!scsi_dev_queue_ready(q, sdev))
            break;

        /*
         * Remove the request from the request list.
         */
        if (!(blk_queue_tagged(q) && !blk_queue_start_tag(q, req)))
            blk_start_request(req);

        spin_unlock_irq(q->queue_lock);
        cmd = blk_mq_rq_to_pdu(req);
        if (cmd != req->special) {
            printk(KERN_CRIT "impossible request in %s.\n"
                     "please mail a stack trace to "
                     "linux-scsi@vger.kernel.org\n",
                     __func__);
            blk_dump_rq_flags(req, "foo");
            BUG();
        }

        /*
         * We hit this when the driver is using a host wide
         * tag map. For device level tag maps the queue_depth check
         * in the device ready fn would prevent us from trying
         * to allocate a tag. Since the map is a shared host resource
         * we add the dev to the starved list so it eventually gets
         * a run when a tag is freed.
         */
        if (blk_queue_tagged(q) && !(req->rq_flags & RQF_QUEUED)) {
            spin_lock_irq(shost->host_lock);
            if (list_empty(&sdev->starved_entry))
                list_add_tail(&sdev->starved_entry,
                          &shost->starved_list);
            spin_unlock_irq(shost->host_lock);
            goto not_ready;
        }

        if (!scsi_target_queue_ready(shost, sdev))
            goto not_ready;

        if (!scsi_host_queue_ready(q, shost, sdev))
            goto host_not_ready;
    
        if (sdev->simple_tags)
            cmd->flags |= SCMD_TAGGED;
        else
            cmd->flags &= ~SCMD_TAGGED;

        /*
         * Finally, initialize any error handling parameters, and set up
         * the timers for timeouts.
         */
        scsi_init_cmd_errh(cmd);

        /*
         * Dispatch the command to the low-level driver.
         */
        cmd->scsi_done = scsi_done;
        rtn = scsi_dispatch_cmd(cmd);
        if (rtn) {
            scsi_queue_insert(cmd, rtn);
            spin_lock_irq(q->queue_lock);
            goto out_delay;
        }
        spin_lock_irq(q->queue_lock);
    }

    return;

 host_not_ready:
    if (scsi_target(sdev)->can_queue > 0)
        atomic_dec(&scsi_target(sdev)->target_busy);
 not_ready:
    /*
     * lock q, handle tag, requeue req, and decrement device_busy. We
     * must return with queue_lock held.
     *
     * Decrementing device_busy without checking it is OK, as all such
     * cases (host limits or settings) should run the queue at some
     * later time.
     */
    spin_lock_irq(q->queue_lock);
    blk_requeue_request(q, req);
    atomic_dec(&sdev->device_busy);
out_delay:
    if (!atomic_read(&sdev->device_busy) && !scsi_device_blocked(sdev))
        blk_delay_queue(q, SCSI_QUEUE_DELAY);
}

scsi_request是scsi读写策略的主要处理例程函数,上层经过FS, BIO层,IO调度层下发的读写请求都会调用到这里去进行处理

 

/**
 * scsi_dispatch_command - Dispatch a command to the low-level driver.
 * @cmd: command block we are dispatching.
 *
 * Return: nonzero return request was rejected and device's queue needs to be
 * plugged.
 */
static int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
{
    struct Scsi_Host *host = cmd->device->host;
    int rtn = 0;

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

    /* check if the device is still usable */
    if (unlikely(cmd->device->sdev_state == SDEV_DEL)) {
        /* in SDEV_DEL we error all commands. DID_NO_CONNECT
         * returns an immediate error upwards, and signals
         * that the device is no longer present */
        cmd->result = DID_NO_CONNECT << 16;
        goto done;
    }

    /* Check to see if the scsi lld made this device blocked. */
    if (unlikely(scsi_device_blocked(cmd->device))) {
        /*
         * in blocked state, the command is just put back on
         * the device queue.  The suspend state has already
         * blocked the queue so future requests should not
         * occur until the device transitions out of the
         * suspend state.
         */
        SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd,
            "queuecommand : device blocked\n"));
        return SCSI_MLQUEUE_DEVICE_BUSY;
    }

    /* Store the LUN value in cmnd, if needed. */
    if (cmd->device->lun_in_cdb)
        cmd->cmnd[1] = (cmd->cmnd[1] & 0x1f) |
                   (cmd->device->lun << 5 & 0xe0);

    scsi_log_send(cmd);

    /*
     * Before we queue this command, check if the command
     * length exceeds what the host adapter can handle.
     */
    if (cmd->cmd_len > cmd->device->host->max_cmd_len) {
        SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd,
                   "queuecommand : command too long. "
                   "cdb_size=%d host->max_cmd_len=%d\n",
                   cmd->cmd_len, cmd->device->host->max_cmd_len));
        cmd->result = (DID_ABORT << 16);
        goto done;
    }

    if (unlikely(host->shost_state == SHOST_DEL)) {
        cmd->result = (DID_NO_CONNECT << 16);
        goto done;

    }

    trace_scsi_dispatch_cmd_start(cmd);
    rtn = host->hostt->queuecommand(host, cmd);
    if (rtn) {
        trace_scsi_dispatch_cmd_error(cmd, rtn);
        if (rtn != SCSI_MLQUEUE_DEVICE_BUSY &&
            rtn != SCSI_MLQUEUE_TARGET_BUSY)
            rtn = SCSI_MLQUEUE_HOST_BUSY;

        SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd,
            "queuecommand : request rejected\n"));
    }

    return rtn;
 done:
    cmd->scsi_done(cmd);
    return 0;
}
scsi_dispatch_command 这个是SCSI层下发一个读写请求的scsi command到low level driver(即ufs host driver, 如ufs-qcom.c)

 

/**
 * scsi_done - Invoke completion on finished SCSI command.
 * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives
 * ownership back to SCSI Core -- i.e. the LLDD has finished with it.
 *
 * Description: This function is the mid-level's (SCSI Core) interrupt routine,
 * which regains ownership of the SCSI command (de facto) from a LLDD, and


 * calls blk_complete_request() for further processing.
 *
 * This function is interrupt context safe.
 */

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值