]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blobdiff - drivers/scsi/hisi_sas/hisi_sas_main.c
scsi: hisi_sas: fix SAS_QUEUE_FULL problem while running IO
[mirror_ubuntu-focal-kernel.git] / drivers / scsi / hisi_sas / hisi_sas_main.c
index 7446a39f9442f948b9db1531e62f571afc5a84d2..9bd98e5be78e1f1a09eac8e1c1b3efaf8d5db4d6 100644 (file)
@@ -1184,6 +1184,11 @@ static int hisi_sas_abort_task(struct sas_task *task)
 
                rc2 = hisi_sas_internal_task_abort(hisi_hba, device,
                                                   HISI_SAS_INT_ABT_CMD, tag);
+               if (rc2 < 0) {
+                       dev_err(dev, "abort task: internal abort (%d)\n", rc2);
+                       return TMF_RESP_FUNC_FAILED;
+               }
+
                /*
                 * If the TMF finds that the IO is not in the device and also
                 * the internal abort does not succeed, then it is safe to
@@ -1201,8 +1206,12 @@ static int hisi_sas_abort_task(struct sas_task *task)
        } else if (task->task_proto & SAS_PROTOCOL_SATA ||
                task->task_proto & SAS_PROTOCOL_STP) {
                if (task->dev->dev_type == SAS_SATA_DEV) {
-                       hisi_sas_internal_task_abort(hisi_hba, device,
-                                                    HISI_SAS_INT_ABT_DEV, 0);
+                       rc = hisi_sas_internal_task_abort(hisi_hba, device,
+                                               HISI_SAS_INT_ABT_DEV, 0);
+                       if (rc < 0) {
+                               dev_err(dev, "abort task: internal abort failed\n");
+                               goto out;
+                       }
                        hisi_sas_dereg_device(hisi_hba, device);
                        rc = hisi_sas_softreset_ata_disk(device);
                }
@@ -1213,7 +1222,8 @@ static int hisi_sas_abort_task(struct sas_task *task)
 
                rc = hisi_sas_internal_task_abort(hisi_hba, device,
                             HISI_SAS_INT_ABT_CMD, tag);
-               if (rc == TMF_RESP_FUNC_FAILED && task->lldd_task) {
+               if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) &&
+                                       task->lldd_task) {
                        spin_lock_irqsave(&hisi_hba->lock, flags);
                        hisi_sas_do_release_task(hisi_hba, task, slot);
                        spin_unlock_irqrestore(&hisi_hba->lock, flags);
@@ -1228,12 +1238,29 @@ out:
 
 static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun)
 {
+       struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
+       struct device *dev = hisi_hba->dev;
        struct hisi_sas_tmf_task tmf_task;
        int rc = TMF_RESP_FUNC_FAILED;
+       unsigned long flags;
+
+       rc = hisi_sas_internal_task_abort(hisi_hba, device,
+                                       HISI_SAS_INT_ABT_DEV, 0);
+       if (rc < 0) {
+               dev_err(dev, "abort task set: internal abort rc=%d\n", rc);
+               return TMF_RESP_FUNC_FAILED;
+       }
+       hisi_sas_dereg_device(hisi_hba, device);
 
        tmf_task.tmf = TMF_ABORT_TASK_SET;
        rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task);
 
+       if (rc == TMF_RESP_FUNC_COMPLETE) {
+               spin_lock_irqsave(&hisi_hba->lock, flags);
+               hisi_sas_release_task(hisi_hba, device);
+               spin_unlock_irqrestore(&hisi_hba->lock, flags);
+       }
+
        return rc;
 }
 
@@ -1263,20 +1290,25 @@ static int hisi_sas_I_T_nexus_reset(struct domain_device *device)
 {
        struct hisi_sas_device *sas_dev = device->lldd_dev;
        struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
-       unsigned long flags;
+       struct device *dev = hisi_hba->dev;
        int rc = TMF_RESP_FUNC_FAILED;
+       unsigned long flags;
 
        if (sas_dev->dev_status != HISI_SAS_DEV_EH)
                return TMF_RESP_FUNC_FAILED;
        sas_dev->dev_status = HISI_SAS_DEV_NORMAL;
 
-       hisi_sas_internal_task_abort(hisi_hba, device,
+       rc = hisi_sas_internal_task_abort(hisi_hba, device,
                                        HISI_SAS_INT_ABT_DEV, 0);
+       if (rc < 0) {
+               dev_err(dev, "I_T nexus reset: internal abort (%d)\n", rc);
+               return TMF_RESP_FUNC_FAILED;
+       }
        hisi_sas_dereg_device(hisi_hba, device);
 
        rc = hisi_sas_debug_I_T_nexus_reset(device);
 
-       if (rc == TMF_RESP_FUNC_COMPLETE) {
+       if ((rc == TMF_RESP_FUNC_COMPLETE) || (rc == -ENODEV)) {
                spin_lock_irqsave(&hisi_hba->lock, flags);
                hisi_sas_release_task(hisi_hba, device);
                spin_unlock_irqrestore(&hisi_hba->lock, flags);
@@ -1299,8 +1331,10 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun)
                /* Clear internal IO and then hardreset */
                rc = hisi_sas_internal_task_abort(hisi_hba, device,
                                                  HISI_SAS_INT_ABT_DEV, 0);
-               if (rc == TMF_RESP_FUNC_FAILED)
+               if (rc < 0) {
+                       dev_err(dev, "lu_reset: internal abort failed\n");
                        goto out;
+               }
                hisi_sas_dereg_device(hisi_hba, device);
 
                phy = sas_get_local_phy(device);
@@ -1316,6 +1350,14 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun)
        } else {
                struct hisi_sas_tmf_task tmf_task = { .tmf =  TMF_LU_RESET };
 
+               rc = hisi_sas_internal_task_abort(hisi_hba, device,
+                                               HISI_SAS_INT_ABT_DEV, 0);
+               if (rc < 0) {
+                       dev_err(dev, "lu_reset: internal abort failed\n");
+                       goto out;
+               }
+               hisi_sas_dereg_device(hisi_hba, device);
+
                rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task);
                if (rc == TMF_RESP_FUNC_COMPLETE) {
                        spin_lock_irqsave(&hisi_hba->lock, flags);
@@ -1497,8 +1539,14 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
        struct device *dev = hisi_hba->dev;
        int res;
 
+       /*
+        * The interface is not realized means this HW don't support internal
+        * abort, or don't need to do internal abort. Then here, we return
+        * TMF_RESP_FUNC_FAILED and let other steps go on, which depends that
+        * the internal abort has been executed and returned CQ.
+        */
        if (!hisi_hba->hw->prep_abort)
-               return -EOPNOTSUPP;
+               return TMF_RESP_FUNC_FAILED;
 
        task = sas_alloc_slow_task(GFP_KERNEL);
        if (!task)
@@ -1530,6 +1578,7 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
                        if (slot)
                                slot->task = NULL;
                        dev_err(dev, "internal task abort: timeout and not done.\n");
+                       res = -EIO;
                        goto exit;
                } else
                        dev_err(dev, "internal task abort: timeout.\n");