aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSujit Reddy Thumma <sthumma@codeaurora.org>2014-05-26 01:29:13 -0400
committerChristoph Hellwig <hch@lst.de>2014-05-28 06:25:13 -0400
commitf20810d8d0bbe70dc6bb526213c31171f7e54751 (patch)
tree138e6bf419064b20da29634de401433617ebf1de
parente293313262d3c780632f7888878c982fa0a9bf7e (diff)
scsi: ufs: Fix hardware race conditions while aborting a command
There is a possible race condition in the hardware when the abort command is issued to terminate the ongoing SCSI command as described below: - A bit in the door-bell register is set in the controller for a new SCSI command. - In some rare situations, before controller get a chance to issue the command to the device, the software issued an abort command. - If the device recieves abort command first then it returns success because the command itself is not present. - Now if the controller commits the command to device it will be processed. - Software thinks that command is aborted and proceed while still the device is processing it. - The software, controller and device may go out of sync because of this race condition. To avoid this, query task presence in the device before sending abort task command so that after the abort operation, the command is guaranteed to be non-existent in both controller and the device. Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org> Reviewed-by: Yaniv Gardi <ygardi@codeaurora.org> Tested-by: Dolev Raviv <draviv@codeaurora.org> Acked-by: Vinayak Holikatti <vinholikatti@gmail.com> Signed-off-by: Christoph Hellwig <hch@lst.de>
-rw-r--r--drivers/scsi/ufs/ufshcd.c70
1 files changed, 55 insertions, 15 deletions
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c3acadc2cf0a..52f66e4c8b47 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -2695,6 +2695,12 @@ static int ufshcd_host_reset(struct scsi_cmnd *cmd)
2695 * ufshcd_abort - abort a specific command 2695 * ufshcd_abort - abort a specific command
2696 * @cmd: SCSI command pointer 2696 * @cmd: SCSI command pointer
2697 * 2697 *
2698 * Abort the pending command in device by sending UFS_ABORT_TASK task management
2699 * command, and in host controller by clearing the door-bell register. There can
2700 * be race between controller sending the command to the device while abort is
2701 * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is
2702 * really issued and then try to abort it.
2703 *
2698 * Returns SUCCESS/FAILED 2704 * Returns SUCCESS/FAILED
2699 */ 2705 */
2700static int ufshcd_abort(struct scsi_cmnd *cmd) 2706static int ufshcd_abort(struct scsi_cmnd *cmd)
@@ -2703,7 +2709,8 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
2703 struct ufs_hba *hba; 2709 struct ufs_hba *hba;
2704 unsigned long flags; 2710 unsigned long flags;
2705 unsigned int tag; 2711 unsigned int tag;
2706 int err; 2712 int err = 0;
2713 int poll_cnt;
2707 u8 resp = 0xF; 2714 u8 resp = 0xF;
2708 struct ufshcd_lrb *lrbp; 2715 struct ufshcd_lrb *lrbp;
2709 2716
@@ -2711,33 +2718,59 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
2711 hba = shost_priv(host); 2718 hba = shost_priv(host);
2712 tag = cmd->request->tag; 2719 tag = cmd->request->tag;
2713 2720
2714 spin_lock_irqsave(host->host_lock, flags); 2721 /* If command is already aborted/completed, return SUCCESS */
2722 if (!(test_bit(tag, &hba->outstanding_reqs)))
2723 goto out;
2715 2724
2716 /* check if command is still pending */ 2725 lrbp = &hba->lrb[tag];
2717 if (!(test_bit(tag, &hba->outstanding_reqs))) { 2726 for (poll_cnt = 100; poll_cnt; poll_cnt--) {
2718 err = FAILED; 2727 err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
2719 spin_unlock_irqrestore(host->host_lock, flags); 2728 UFS_QUERY_TASK, &resp);
2729 if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) {
2730 /* cmd pending in the device */
2731 break;
2732 } else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
2733 u32 reg;
2734
2735 /*
2736 * cmd not pending in the device, check if it is
2737 * in transition.
2738 */
2739 reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
2740 if (reg & (1 << tag)) {
2741 /* sleep for max. 200us to stabilize */
2742 usleep_range(100, 200);
2743 continue;
2744 }
2745 /* command completed already */
2746 goto out;
2747 } else {
2748 if (!err)
2749 err = resp; /* service response error */
2750 goto out;
2751 }
2752 }
2753
2754 if (!poll_cnt) {
2755 err = -EBUSY;
2720 goto out; 2756 goto out;
2721 } 2757 }
2722 spin_unlock_irqrestore(host->host_lock, flags);
2723 2758
2724 lrbp = &hba->lrb[tag];
2725 err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag, 2759 err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
2726 UFS_ABORT_TASK, &resp); 2760 UFS_ABORT_TASK, &resp);
2727 if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { 2761 if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
2728 err = FAILED; 2762 if (!err)
2763 err = resp; /* service response error */
2729 goto out; 2764 goto out;
2730 } else {
2731 err = SUCCESS;
2732 } 2765 }
2733 2766
2767 err = ufshcd_clear_cmd(hba, tag);
2768 if (err)
2769 goto out;
2770
2734 scsi_dma_unmap(cmd); 2771 scsi_dma_unmap(cmd);
2735 2772
2736 spin_lock_irqsave(host->host_lock, flags); 2773 spin_lock_irqsave(host->host_lock, flags);
2737
2738 /* clear the respective UTRLCLR register bit */
2739 ufshcd_utrl_clear(hba, tag);
2740
2741 __clear_bit(tag, &hba->outstanding_reqs); 2774 __clear_bit(tag, &hba->outstanding_reqs);
2742 hba->lrb[tag].cmd = NULL; 2775 hba->lrb[tag].cmd = NULL;
2743 spin_unlock_irqrestore(host->host_lock, flags); 2776 spin_unlock_irqrestore(host->host_lock, flags);
@@ -2745,6 +2778,13 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
2745 clear_bit_unlock(tag, &hba->lrb_in_use); 2778 clear_bit_unlock(tag, &hba->lrb_in_use);
2746 wake_up(&hba->dev_cmd.tag_wq); 2779 wake_up(&hba->dev_cmd.tag_wq);
2747out: 2780out:
2781 if (!err) {
2782 err = SUCCESS;
2783 } else {
2784 dev_err(hba->dev, "%s: failed with err %d\n", __func__, err);
2785 err = FAILED;
2786 }
2787
2748 return err; 2788 return err;
2749} 2789}
2750 2790