static int
 hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
                             struct domain_device *device,
-                            int abort_flag, int tag);
+                            int abort_flag, int tag, bool rst_to_recover);
 static int hisi_sas_softreset_ata_disk(struct domain_device *device);
 static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
                                void *funcdata);
        down(&hisi_hba->sem);
        if (!test_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)) {
                hisi_sas_internal_task_abort(hisi_hba, device,
-                                            HISI_SAS_INT_ABT_DEV, 0);
+                                            HISI_SAS_INT_ABT_DEV, 0, true);
 
                hisi_sas_dereg_device(hisi_hba, device);
 
                        continue;
 
                rc = hisi_sas_internal_task_abort(hisi_hba, device,
-                                                 HISI_SAS_INT_ABT_DEV, 0);
+                                                 HISI_SAS_INT_ABT_DEV, 0,
+                                                 false);
                if (rc < 0)
                        dev_err(dev, "STP reject: abort dev failed %d\n", rc);
        }
                                                  &tmf_task);
 
                rc2 = hisi_sas_internal_task_abort(hisi_hba, device,
-                                                  HISI_SAS_INT_ABT_CMD, tag);
+                                                  HISI_SAS_INT_ABT_CMD, tag,
+                                                  false);
                if (rc2 < 0) {
                        dev_err(dev, "abort task: internal abort (%d)\n", rc2);
                        return TMF_RESP_FUNC_FAILED;
                if (task->dev->dev_type == SAS_SATA_DEV) {
                        rc = hisi_sas_internal_task_abort(hisi_hba, device,
                                                          HISI_SAS_INT_ABT_DEV,
-                                                         0);
+                                                         0, false);
                        if (rc < 0) {
                                dev_err(dev, "abort task: internal abort failed\n");
                                goto out;
                struct hisi_sas_cq *cq = &hisi_hba->cq[slot->dlvry_queue];
 
                rc = hisi_sas_internal_task_abort(hisi_hba, device,
-                                                 HISI_SAS_INT_ABT_CMD, tag);
+                                                 HISI_SAS_INT_ABT_CMD, tag,
+                                                 false);
                if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) &&
                                        task->lldd_task) {
                        /*
        int rc;
 
        rc = hisi_sas_internal_task_abort(hisi_hba, device,
-                                         HISI_SAS_INT_ABT_DEV, 0);
+                                         HISI_SAS_INT_ABT_DEV, 0, false);
        if (rc < 0) {
                dev_err(dev, "abort task set: internal abort rc=%d\n", rc);
                return TMF_RESP_FUNC_FAILED;
        int rc;
 
        rc = hisi_sas_internal_task_abort(hisi_hba, device,
-                                         HISI_SAS_INT_ABT_DEV, 0);
+                                         HISI_SAS_INT_ABT_DEV, 0, false);
        if (rc < 0) {
                dev_err(dev, "I_T nexus reset: internal abort (%d)\n", rc);
                return TMF_RESP_FUNC_FAILED;
 
        /* Clear internal IO and then lu reset */
        rc = hisi_sas_internal_task_abort(hisi_hba, device,
-                                         HISI_SAS_INT_ABT_DEV, 0);
+                                         HISI_SAS_INT_ABT_DEV, 0, false);
        if (rc < 0) {
                dev_err(dev, "lu_reset: internal abort failed\n");
                goto out;
  * @tag: tag of IO to be aborted (only relevant to single
  *       IO mode)
  * @dq: delivery queue for this internal abort command
+ * @rst_to_recover: If rst_to_recover set, queue a controller
+ *                 reset if an internal abort times out.
  */
 static int
 _hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
                              struct domain_device *device, int abort_flag,
-                             int tag, struct hisi_sas_dq *dq)
+                             int tag, struct hisi_sas_dq *dq, bool rst_to_recover)
 {
        struct sas_task *task;
        struct hisi_sas_device *sas_dev = device->lldd_dev;
                                synchronize_irq(cq->irq_no);
                                slot->task = NULL;
                        }
-                       dev_err(dev, "internal task abort: timeout and not done.\n");
+
+                       if (rst_to_recover) {
+                               dev_err(dev, "internal task abort: timeout and not done. Queuing reset.\n");
+                               queue_work(hisi_hba->wq, &hisi_hba->rst_work);
+                       } else {
+                               dev_err(dev, "internal task abort: timeout and not done.\n");
+                       }
 
                        res = -EIO;
                        goto exit;
 static int
 hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
                             struct domain_device *device,
-                            int abort_flag, int tag)
+                            int abort_flag, int tag, bool rst_to_recover)
 {
        struct hisi_sas_slot *slot;
        struct device *dev = hisi_hba->dev;
                slot = &hisi_hba->slot_info[tag];
                dq = &hisi_hba->dq[slot->dlvry_queue];
                return _hisi_sas_internal_task_abort(hisi_hba, device,
-                                                    abort_flag, tag, dq);
+                                                    abort_flag, tag, dq,
+                                                    rst_to_recover);
        case HISI_SAS_INT_ABT_DEV:
                for (i = 0; i < hisi_hba->cq_nvecs; i++) {
                        struct hisi_sas_cq *cq = &hisi_hba->cq[i];
                        dq = &hisi_hba->dq[i];
                        rc = _hisi_sas_internal_task_abort(hisi_hba, device,
                                                           abort_flag, tag,
-                                                          dq);
+                                                          dq, rst_to_recover);
                        if (rc)
                                return rc;
                }