]> 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 61a85ff8e459f429b7090cbaa77d4712a7e854c7..9bd98e5be78e1f1a09eac8e1c1b3efaf8d5db4d6 100644 (file)
@@ -22,6 +22,8 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
                             struct domain_device *device,
                             int abort_flag, int tag);
 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);
 
 u8 hisi_sas_get_ata_protocol(u8 cmd, int direction)
 {
@@ -192,7 +194,8 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task,
 
                if (!sas_protocol_ata(task->task_proto))
                        if (slot->n_elem)
-                               dma_unmap_sg(dev, task->scatter, slot->n_elem,
+                               dma_unmap_sg(dev, task->scatter,
+                                            task->num_scatter,
                                             task->data_dir);
 
                if (sas_dev)
@@ -431,7 +434,8 @@ err_out:
        dev_err(dev, "task prep: failed[%d]!\n", rc);
        if (!sas_protocol_ata(task->task_proto))
                if (n_elem)
-                       dma_unmap_sg(dev, task->scatter, n_elem,
+                       dma_unmap_sg(dev, task->scatter,
+                                    task->num_scatter,
                                     task->data_dir);
 prep_out:
        return rc;
@@ -578,6 +582,9 @@ static int hisi_sas_dev_found(struct domain_device *device)
                }
        }
 
+       dev_info(dev, "dev[%d:%x] found\n",
+               sas_dev->device_id, sas_dev->dev_type);
+
        return 0;
 }
 
@@ -617,7 +624,7 @@ static int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time)
 static void hisi_sas_phyup_work(struct work_struct *work)
 {
        struct hisi_sas_phy *phy =
-               container_of(work, struct hisi_sas_phy, phyup_ws);
+               container_of(work, typeof(*phy), works[HISI_PHYE_PHY_UP]);
        struct hisi_hba *hisi_hba = phy->hisi_hba;
        struct asd_sas_phy *sas_phy = &phy->sas_phy;
        int phy_no = sas_phy->id;
@@ -626,10 +633,37 @@ static void hisi_sas_phyup_work(struct work_struct *work)
        hisi_sas_bytes_dmaed(hisi_hba, phy_no);
 }
 
+static void hisi_sas_linkreset_work(struct work_struct *work)
+{
+       struct hisi_sas_phy *phy =
+               container_of(work, typeof(*phy), works[HISI_PHYE_LINK_RESET]);
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+       hisi_sas_control_phy(sas_phy, PHY_FUNC_LINK_RESET, NULL);
+}
+
+static const work_func_t hisi_sas_phye_fns[HISI_PHYES_NUM] = {
+       [HISI_PHYE_PHY_UP] = hisi_sas_phyup_work,
+       [HISI_PHYE_LINK_RESET] = hisi_sas_linkreset_work,
+};
+
+bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy,
+                               enum hisi_sas_phy_event event)
+{
+       struct hisi_hba *hisi_hba = phy->hisi_hba;
+
+       if (WARN_ON(event >= HISI_PHYES_NUM))
+               return false;
+
+       return queue_work(hisi_hba->wq, &phy->works[event]);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_notify_phy_event);
+
 static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no)
 {
        struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
        struct asd_sas_phy *sas_phy = &phy->sas_phy;
+       int i;
 
        phy->hisi_hba = hisi_hba;
        phy->port = NULL;
@@ -647,7 +681,8 @@ static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no)
        sas_phy->ha = (struct sas_ha_struct *)hisi_hba->shost->hostdata;
        sas_phy->lldd_phy = phy;
 
-       INIT_WORK(&phy->phyup_ws, hisi_sas_phyup_work);
+       for (i = 0; i < HISI_PHYES_NUM; i++)
+               INIT_WORK(&phy->works[i], hisi_sas_phye_fns[i]);
 }
 
 static void hisi_sas_port_notify_formed(struct asd_sas_phy *sas_phy)
@@ -733,17 +768,22 @@ static void hisi_sas_dev_gone(struct domain_device *device)
        struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
        struct device *dev = hisi_hba->dev;
 
-       dev_info(dev, "found dev[%d:%x] is gone\n",
+       dev_info(dev, "dev[%d:%x] is gone\n",
                 sas_dev->device_id, sas_dev->dev_type);
 
-       hisi_sas_internal_task_abort(hisi_hba, device,
+       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_dereg_device(hisi_hba, device);
+               hisi_sas_dereg_device(hisi_hba, device);
 
-       hisi_hba->hw->free_device(hisi_hba, sas_dev);
-       device->lldd_dev = NULL;
-       memset(sas_dev, 0, sizeof(*sas_dev));
+               hisi_hba->hw->clear_itct(hisi_hba, sas_dev);
+               device->lldd_dev = NULL;
+               memset(sas_dev, 0, sizeof(*sas_dev));
+       }
+
+       if (hisi_hba->hw->free_device)
+               hisi_hba->hw->free_device(sas_dev);
        sas_dev->dev_type = SAS_PHY_UNUSED;
 }
 
@@ -839,7 +879,7 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device,
                }
                task->task_done = hisi_sas_task_done;
 
-               task->slow_task->timer.function = (TIMER_FUNC_TYPE)hisi_sas_tmf_timedout;
+               task->slow_task->timer.function = hisi_sas_tmf_timedout;
                task->slow_task->timer.expires = jiffies + TASK_TIMEOUT*HZ;
                add_timer(&task->slow_task->timer);
 
@@ -859,12 +899,13 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device,
                        if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
                                struct hisi_sas_slot *slot = task->lldd_task;
 
-                               dev_err(dev, "abort tmf: TMF task timeout\n");
+                               dev_err(dev, "abort tmf: TMF task timeout and not done\n");
                                if (slot)
                                        slot->task = NULL;
 
                                goto ex_err;
-                       }
+                       } else
+                               dev_err(dev, "abort tmf: TMF task timeout\n");
                }
 
                if (task->task_status.resp == SAS_TASK_COMPLETE &&
@@ -985,27 +1026,42 @@ static int hisi_sas_debug_issue_ssp_tmf(struct domain_device *device,
                                sizeof(ssp_task), tmf);
 }
 
-static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba,
-               struct asd_sas_port *sas_port, enum sas_linkrate linkrate)
+static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba)
 {
-       struct hisi_sas_device  *sas_dev;
-       struct domain_device *device;
+       u32 state = hisi_hba->hw->get_phys_state(hisi_hba);
        int i;
 
        for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
-               sas_dev = &hisi_hba->devices[i];
-               device = sas_dev->sas_device;
+               struct hisi_sas_device *sas_dev = &hisi_hba->devices[i];
+               struct domain_device *device = sas_dev->sas_device;
+               struct asd_sas_port *sas_port;
+               struct hisi_sas_port *port;
+               struct hisi_sas_phy *phy = NULL;
+               struct asd_sas_phy *sas_phy;
+
                if ((sas_dev->dev_type == SAS_PHY_UNUSED)
-                               || !device || (device->port != sas_port))
+                               || !device || !device->port)
                        continue;
 
-               hisi_hba->hw->free_device(hisi_hba, sas_dev);
+               sas_port = device->port;
+               port = to_hisi_sas_port(sas_port);
 
-               /* Update linkrate of directly attached device. */
-               if (!device->parent)
-                       device->linkrate = linkrate;
+               list_for_each_entry(sas_phy, &sas_port->phy_list, port_phy_el)
+                       if (state & BIT(sas_phy->id)) {
+                               phy = sas_phy->lldd_phy;
+                               break;
+                       }
+
+               if (phy) {
+                       port->id = phy->port_id;
 
-               hisi_hba->hw->setup_itct(hisi_hba, sas_dev);
+                       /* Update linkrate of directly attached device. */
+                       if (!device->parent)
+                               device->linkrate = phy->sas_phy.linkrate;
+
+                       hisi_hba->hw->setup_itct(hisi_hba, sas_dev);
+               } else
+                       port->id = 0xff;
        }
 }
 
@@ -1020,21 +1076,17 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state,
                struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
                struct asd_sas_phy *sas_phy = &phy->sas_phy;
                struct asd_sas_port *sas_port = sas_phy->port;
-               struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
                bool do_port_check = !!(_sas_port != sas_port);
 
                if (!sas_phy->phy->enabled)
                        continue;
 
                /* Report PHY state change to libsas */
-               if (state & (1 << phy_no)) {
-                       if (do_port_check && sas_port) {
+               if (state & BIT(phy_no)) {
+                       if (do_port_check && sas_port && sas_port->port_dev) {
                                struct domain_device *dev = sas_port->port_dev;
 
                                _sas_port = sas_port;
-                               port->id = phy->port_id;
-                               hisi_sas_refresh_port_id(hisi_hba,
-                                               sas_port, sas_phy->linkrate);
 
                                if (DEV_IS_EXPANDER(dev->dev_type))
                                        sas_ha->notify_port_event(sas_phy,
@@ -1045,8 +1097,6 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state,
                        hisi_sas_phy_down(hisi_hba, phy_no, 0);
 
        }
-
-       drain_workqueue(hisi_hba->shost->work_q);
 }
 
 static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
@@ -1063,7 +1113,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
        if (test_and_set_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags))
                return -1;
 
-       dev_dbg(dev, "controller resetting...\n");
+       dev_info(dev, "controller resetting...\n");
        old_state = hisi_hba->hw->get_phys_state(hisi_hba);
 
        scsi_block_requests(shost);
@@ -1072,6 +1122,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
        if (rc) {
                dev_warn(dev, "controller reset failed (%d)\n", rc);
                clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+               scsi_unblock_requests(shost);
                goto out;
        }
        spin_lock_irqsave(&hisi_hba->lock, flags);
@@ -1083,15 +1134,14 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
        /* Init and wait for PHYs to come up and all libsas event finished. */
        hisi_hba->hw->phys_init(hisi_hba);
        msleep(1000);
-       drain_workqueue(hisi_hba->wq);
-       drain_workqueue(shost->work_q);
+       hisi_sas_refresh_port_id(hisi_hba);
+       scsi_unblock_requests(shost);
 
        state = hisi_hba->hw->get_phys_state(hisi_hba);
        hisi_sas_rescan_topology(hisi_hba, old_state, state);
-       dev_dbg(dev, "controller reset complete\n");
+       dev_info(dev, "controller reset complete\n");
 
 out:
-       scsi_unblock_requests(shost);
        clear_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags);
 
        return rc;
@@ -1134,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
@@ -1151,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);
                }
@@ -1163,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);
@@ -1178,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;
 }
 
@@ -1213,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);
@@ -1249,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);
@@ -1266,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);
@@ -1283,8 +1375,14 @@ out:
 static int hisi_sas_clear_nexus_ha(struct sas_ha_struct *sas_ha)
 {
        struct hisi_hba *hisi_hba = sas_ha->lldd_ha;
+       HISI_SAS_DECLARE_RST_WORK_ON_STACK(r);
+
+       queue_work(hisi_hba->wq, &r.work);
+       wait_for_completion(r.completion);
+       if (r.done)
+               return TMF_RESP_FUNC_COMPLETE;
 
-       return hisi_sas_controller_reset(hisi_hba);
+       return TMF_RESP_FUNC_FAILED;
 }
 
 static int hisi_sas_query_task(struct sas_task *task)
@@ -1441,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)
@@ -1451,7 +1555,7 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
        task->dev = device;
        task->task_proto = device->tproto;
        task->task_done = hisi_sas_task_done;
-       task->slow_task->timer.function = (TIMER_FUNC_TYPE)hisi_sas_tmf_timedout;
+       task->slow_task->timer.function = hisi_sas_tmf_timedout;
        task->slow_task->timer.expires = jiffies + msecs_to_jiffies(110);
        add_timer(&task->slow_task->timer);
 
@@ -1473,9 +1577,11 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
 
                        if (slot)
                                slot->task = NULL;
-                       dev_err(dev, "internal task abort: timeout.\n");
+                       dev_err(dev, "internal task abort: timeout and not done.\n");
+                       res = -EIO;
                        goto exit;
-               }
+               } else
+                       dev_err(dev, "internal task abort: timeout.\n");
        }
 
        if (task->task_status.resp == SAS_TASK_COMPLETE &&
@@ -1657,6 +1763,7 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
                cq->hisi_hba = hisi_hba;
 
                /* Delivery queue structure */
+               spin_lock_init(&dq->lock);
                dq->id = i;
                dq->hisi_hba = hisi_hba;
 
@@ -1803,6 +1910,17 @@ void hisi_sas_rst_work_handler(struct work_struct *work)
 }
 EXPORT_SYMBOL_GPL(hisi_sas_rst_work_handler);
 
+void hisi_sas_sync_rst_work_handler(struct work_struct *work)
+{
+       struct hisi_sas_rst *rst =
+               container_of(work, struct hisi_sas_rst, work);
+
+       if (!hisi_sas_controller_reset(rst->hisi_hba))
+               rst->done = true;
+       complete(rst->completion);
+}
+EXPORT_SYMBOL_GPL(hisi_sas_sync_rst_work_handler);
+
 int hisi_sas_get_fw_info(struct hisi_hba *hisi_hba)
 {
        struct device *dev = hisi_hba->dev;