]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/scsi/hisi_sas/hisi_sas_main.c
scsi: hisi_sas: Terminate STP reject quickly for v2 hw
[mirror_ubuntu-bionic-kernel.git] / drivers / scsi / hisi_sas / hisi_sas_main.c
index c1624c1aa7a760b286e8c709906db8d1f3a42481..fd92b7e9d794e28198076d4c11838fbb4b3abb3c 100644 (file)
@@ -265,10 +265,9 @@ static void hisi_sas_task_prep_smp(struct hisi_hba *hisi_hba,
 }
 
 static void hisi_sas_task_prep_ssp(struct hisi_hba *hisi_hba,
-                                 struct hisi_sas_slot *slot, int is_tmf,
-                                 struct hisi_sas_tmf_task *tmf)
+                                 struct hisi_sas_slot *slot)
 {
-       hisi_hba->hw->prep_ssp(hisi_hba, slot, is_tmf, tmf);
+       hisi_hba->hw->prep_ssp(hisi_hba, slot);
 }
 
 static void hisi_sas_task_prep_ata(struct hisi_hba *hisi_hba,
@@ -321,7 +320,7 @@ out:
 
 static int hisi_sas_task_prep(struct sas_task *task,
                              struct hisi_sas_dq **dq_pointer,
-                             int is_tmf, struct hisi_sas_tmf_task *tmf,
+                             bool is_tmf, struct hisi_sas_tmf_task *tmf,
                              int *pass)
 {
        struct domain_device *device = task->dev;
@@ -460,8 +459,8 @@ static int hisi_sas_task_prep(struct sas_task *task,
        slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot];
        slot->task = task;
        slot->port = port;
-       if (is_tmf)
-               slot->is_internal = true;
+       slot->tmf = tmf;
+       slot->is_internal = is_tmf;
        task->lldd_task = slot;
        INIT_WORK(&slot->abort_slot, hisi_sas_slot_abort);
 
@@ -474,7 +473,7 @@ static int hisi_sas_task_prep(struct sas_task *task,
                hisi_sas_task_prep_smp(hisi_hba, slot);
                break;
        case SAS_PROTOCOL_SSP:
-               hisi_sas_task_prep_ssp(hisi_hba, slot, is_tmf, tmf);
+               hisi_sas_task_prep_ssp(hisi_hba, slot);
                break;
        case SAS_PROTOCOL_SATA:
        case SAS_PROTOCOL_STP:
@@ -526,7 +525,7 @@ prep_out:
 }
 
 static int hisi_sas_task_exec(struct sas_task *task, gfp_t gfp_flags,
-                             int is_tmf, struct hisi_sas_tmf_task *tmf)
+                             bool is_tmf, struct hisi_sas_tmf_task *tmf)
 {
        u32 rc;
        u32 pass = 0;
@@ -1250,6 +1249,81 @@ static void hisi_sas_reset_init_all_devices(struct hisi_hba *hisi_hba)
        }
 }
 
+static void hisi_sas_send_ata_reset_each_phy(struct hisi_hba *hisi_hba,
+                                            struct asd_sas_port *sas_port,
+                                            struct domain_device *device)
+{
+       struct hisi_sas_tmf_task tmf_task = { .force_phy = 1 };
+       struct ata_port *ap = device->sata_dev.ap;
+       struct device *dev = hisi_hba->dev;
+       int s = sizeof(struct host_to_dev_fis);
+       int rc = TMF_RESP_FUNC_FAILED;
+       struct asd_sas_phy *sas_phy;
+       struct ata_link *link;
+       u8 fis[20] = {0};
+       u32 state;
+
+       state = hisi_hba->hw->get_phys_state(hisi_hba);
+       list_for_each_entry(sas_phy, &sas_port->phy_list, port_phy_el) {
+               if (!(state & BIT(sas_phy->id)))
+                       continue;
+
+               ata_for_each_link(link, ap, EDGE) {
+                       int pmp = sata_srst_pmp(link);
+
+                       tmf_task.phy_id = sas_phy->id;
+                       hisi_sas_fill_ata_reset_cmd(link->device, 1, pmp, fis);
+                       rc = hisi_sas_exec_internal_tmf_task(device, fis, s,
+                                                            &tmf_task);
+                       if (rc != TMF_RESP_FUNC_COMPLETE) {
+                               dev_err(dev, "phy%d ata reset failed rc=%d\n",
+                                       sas_phy->id, rc);
+                               break;
+                       }
+               }
+       }
+}
+
+static void hisi_sas_terminate_stp_reject(struct hisi_hba *hisi_hba)
+{
+       struct device *dev = hisi_hba->dev;
+       int port_no, rc, i;
+
+       for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+               struct hisi_sas_device *sas_dev = &hisi_hba->devices[i];
+               struct domain_device *device = sas_dev->sas_device;
+
+               if ((sas_dev->dev_type == SAS_PHY_UNUSED) || !device)
+                       continue;
+
+               rc = hisi_sas_internal_task_abort(hisi_hba, device,
+                                                 HISI_SAS_INT_ABT_DEV, 0);
+               if (rc < 0)
+                       dev_err(dev, "STP reject: abort dev failed %d\n", rc);
+       }
+
+       for (port_no = 0; port_no < hisi_hba->n_phy; port_no++) {
+               struct hisi_sas_port *port = &hisi_hba->port[port_no];
+               struct asd_sas_port *sas_port = &port->sas_port;
+               struct domain_device *port_dev = sas_port->port_dev;
+               struct domain_device *device;
+
+               if (!port_dev || !DEV_IS_EXPANDER(port_dev->dev_type))
+                       continue;
+
+               /* Try to find a SATA device */
+               list_for_each_entry(device, &sas_port->dev_list,
+                                   dev_list_node) {
+                       if (dev_is_sata(device)) {
+                               hisi_sas_send_ata_reset_each_phy(hisi_hba,
+                                                                sas_port,
+                                                                device);
+                               break;
+                       }
+               }
+       }
+}
+
 static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
 {
        struct device *dev = hisi_hba->dev;
@@ -1267,6 +1341,8 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
        old_state = hisi_hba->hw->get_phys_state(hisi_hba);
 
        scsi_block_requests(shost);
+       hisi_hba->hw->wait_cmds_complete_timeout(hisi_hba, 100, 5000);
+
        if (timer_pending(&hisi_hba->timer))
                del_timer_sync(&hisi_hba->timer);
 
@@ -1285,6 +1361,9 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
        hisi_hba->hw->phys_init(hisi_hba);
        msleep(1000);
        hisi_sas_refresh_port_id(hisi_hba);
+
+       if (hisi_hba->reject_stp_links_msk)
+               hisi_sas_terminate_stp_reject(hisi_hba);
        hisi_sas_reset_init_all_devices(hisi_hba);
        scsi_unblock_requests(shost);