UFS发生命令超时处理流程

1. 在UFS的上层发送命令下来,如果上层30s没有收到ufs devices的response, 就会出现超时,此时会去查询命令是否真正的发送以及是否需要Abort 超时的命令

首先  我们先看数据传输是驱动程序的本质目的,通过数据的传输,来完成作为存储介质的使命,read & write,在read流程中,ufs向应用程序提供数据,在write流程中,应用程序向ufs存放数据。

本节分三个阶段关注数据的流向,分别是:系统调用数据到bio的组成,bio到电梯队列request的组成,request到controller的数据传输。主要围绕buffer和存储地址两个数据流要素,根据读流程描述各阶段这两个数据流要素的数据结构。

UFS发生命令超时处理流程_第1张图片

sys_read->submit_bio的流程

 

bio生成后,就plug到进程的plug_list里面,如果失败,则进电梯merge,如果依然失败,则只能等待新的request被释放出来,两次蓄流的目的是为了尽可能的让bio请求得到merge,减少往磁盘驱动下发的频度,提升综合性能

UFS发生命令超时处理流程_第2张图片

bio层->request的流程

 

当plug_list达到设定阈值或进程主动发起或设定时间已到或手机将要关机等,则会进行泄流。从elevator的queue里面取出一个request请求,下发到底层UFS驱动,这个流程从request转化为scsi协议包,最后封装在UFS的协议包UPIU包里面,于是就完成了一个请求的host内存准备,最后通过置这个请求对应的UFSHCI的doorbell寄存器中的mask,实现请求往驱动层的发送。

UFS发生命令超时处理流程_第3张图片

2. 接下来我们看看超时的具体流程和操作

```c
/**
 * scsi_alloc_sdev - allocate and setup a scsi_Device
 * @starget: which target to allocate a &scsi_device for
 * @lun: which lun
 * @hostdata: usually NULL and set by ->slave_alloc instead
 *
 * Description:
 *     Allocate, initialize for io, and return a pointer to a scsi_Device.
 *     Stores the @shost, @channel, @id, and @lun in the scsi_Device, and
 *     adds scsi_Device to the appropriate list.
 *
 * Return value:
 *     scsi_Device pointer, or NULL on failure.
 **/
 
```

```c
scsi_alloc_sdev
   |
   
 if (shost_use_blk_mq(shost))
  sdev->request_queue = scsi_mq_alloc_queue(sdev);
 else
  sdev->request_queue = scsi_old_alloc_queue(sdev);
  |
 blk_queue_rq_timed_out(q, scsi_times_out);
  |
scsi_abort_command
  |
 queue_delayed_work(shost->tmf_work_q, &scmd->abort_work, HZ / 100);
  |
 scmd_eh_abort_handler
  |
 scsi_try_to_abort_cmd
  |
 return hostt->eh_abort_handler(scmd);
  |
 .eh_abort_handler = ufshcd_abort
  |
  ufshcd_abort
```

/**
 * ufshcd_abort - abort a specific command
 * @cmd: SCSI command pointer
 *
 * Abort the pending command in device by sending UFS_ABORT_TASK task management
 * command, and in host controller by clearing the door-bell register. There can
 * be race between controller sending the command to the device while abort is
 * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is
 * really issued and then try to abort it.
 *
 * Returns SUCCESS/FAILED
 */
static int ufshcd_abort(struct scsi_cmnd *cmd)
{
    struct Scsi_Host *host;
    struct ufs_hba *hba;
    unsigned long flags;
    unsigned int tag;
    int err = 0;
    int poll_cnt;
    u8 resp = 0xF;
    struct ufshcd_lrb *lrbp;
    u32 reg;

    host = cmd->device->host;
    hba = shost_priv(host);
    tag = cmd->request->tag;
    lrbp = &hba->lrb[tag];
    if (!ufshcd_valid_tag(hba, tag)) {
        dev_err(hba->dev,
            "%s: invalid command tag %d: cmd=0x%p, cmd->request=0x%p",
            __func__, tag, cmd, cmd->request);
        BUG();
    }

    /*
     * Task abort to the device W-LUN is illegal. When this command
     * will fail, due to spec violation, scsi err handling next step
     * will be to send LU reset which, again, is a spec violation.
     * To avoid these unnecessary/illegal step we skip to the last error
     * handling stage: reset and restore.
     */
    if (lrbp->lun == UFS_UPIU_UFS_DEVICE_WLUN)
        return ufshcd_eh_host_reset_handler(cmd);

    ufshcd_hold(hba, false);
    reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
    /* If command is already aborted/completed, return SUCCESS */
    if (!(test_bit(tag, &hba->outstanding_reqs))) {
        dev_err(hba->dev,
            "%s: cmd at tag %d already completed, outstanding=0x%lx, doorbell=0x%x\n",
            __func__, tag, hba->outstanding_reqs, reg);
        goto out;
    }

    if (!(reg & (1 << tag))) {
        dev_err(hba->dev,
        "%s: cmd was completed, but without a notifying intr, tag = %d",
        __func__, tag);
    }

    /* Print Transfer Request of aborted task */
    dev_err(hba->dev, "%s: Device abort task at tag %d\n", __func__, tag);

    /*
     * Print detailed info about aborted request.
     * As more than one request might get aborted at the same time,
     * print full information only for the first aborted request in order
     * to reduce repeated printouts. For other aborted requests only print
     * basic details.
     */
    scsi_print_command(hba->lrb[tag].cmd);
    if (!hba->req_abort_count) {
        ufshcd_print_host_regs(hba);
        ufshcd_print_host_state(hba);
        ufshcd_print_pwr_info(hba);
        ufshcd_print_trs(hba, 1 << tag, true);
    } else {
        ufshcd_print_trs(hba, 1 << tag, false);
    }
    hba->req_abort_count++;

    /* Skip task abort in case previous aborts failed and report failure */
    if (lrbp->req_abort_skip) {
        err = -EIO;
        goto out;
    }

    for (poll_cnt = 100; poll_cnt; poll_cnt--) {
        err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
                UFS_QUERY_TASK, &resp);
        if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) {
            /* cmd pending in the device */
            dev_err(hba->dev, "%s: cmd pending in the device. tag = %d\n",
                __func__, tag);
            break;
        } else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
            /*
             * cmd not pending in the device, check if it is
             * in transition.
             */
            dev_err(hba->dev, "%s: cmd at tag %d not pending in the device.\n",
                __func__, tag);
            reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
            if (reg & (1 << tag)) {
                /* sleep for max. 200us to stabilize */
                usleep_range(100, 200);
                continue;
            }
            /* command completed already */
            dev_err(hba->dev, "%s: cmd at tag %d successfully cleared from DB.\n",
                __func__, tag);
            goto out;
        } else {
            dev_err(hba->dev,
                "%s: no response from device. tag = %d, err %d\n",
                __func__, tag, err);
            if (!err)
                err = resp; /* service response error */
            goto out;
        }
    }

    if (!poll_cnt) {
        err = -EBUSY;
        goto out;
    }

    err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
            UFS_ABORT_TASK, &resp);
    if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
        if (!err) {
            err = resp; /* service response error */
            dev_err(hba->dev, "%s: issued. tag = %d, err %d\n",
                __func__, tag, err);
        }
        goto out;
    }

    err = ufshcd_clear_cmd(hba, tag);
    if (err) {
        dev_err(hba->dev, "%s: Failed clearing cmd at tag %d, err %d\n",
            __func__, tag, err);
        goto out;
    }

    scsi_dma_unmap(cmd);

    spin_lock_irqsave(host->host_lock, flags);
    ufshcd_outstanding_req_clear(hba, tag);
    hba->lrb[tag].cmd = NULL;
    spin_unlock_irqrestore(host->host_lock, flags);

    clear_bit_unlock(tag, &hba->lrb_in_use);
    wake_up(&hba->dev_cmd.tag_wq);

out:
    if (!err) {
        err = SUCCESS;
    } else {
        dev_err(hba->dev, "%s: failed with err %d\n", __func__, err);
        ufshcd_set_req_abort_skip(hba, hba->outstanding_reqs);
        err = FAILED;
    }

    /*
     * This ufshcd_release() corresponds to the original scsi cmd that got
     * aborted here (as we won't get any IRQ for it).
     */
    ufshcd_release(hba);
    return err;
}

 

 

 

 

 

 

 

 

你可能感兴趣的:(ufs,文件系统,linux)