]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/scsi/hisi_sas/hisi_sas_main.c
scsi: hisi_sas: fix reset and port ID refresh issues
[mirror_ubuntu-artful-kernel.git] / drivers / scsi / hisi_sas / hisi_sas_main.c
index f8fd9f09468506a4878f97e3921788eb6c2c544c..68bc6e8c6f580ffe936d34fe6fc5f7dc27b81b33 100644 (file)
@@ -435,7 +435,7 @@ static int hisi_sas_task_exec(struct sas_task *task, gfp_t gfp_flags,
        struct hisi_sas_device *sas_dev = device->lldd_dev;
        struct hisi_sas_dq *dq = sas_dev->dq;
 
-       if (unlikely(test_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)))
+       if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags)))
                return -EINVAL;
 
        /* protect task_prep and start_delivery sequence */
@@ -969,37 +969,117 @@ 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)
+{
+       struct hisi_sas_device  *sas_dev;
+       struct domain_device *device;
+       int i;
+
+       for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) {
+               sas_dev = &hisi_hba->devices[i];
+               device = sas_dev->sas_device;
+               if ((sas_dev->dev_type == SAS_PHY_UNUSED)
+                               || !device || (device->port != sas_port))
+                       continue;
+
+               hisi_hba->hw->free_device(hisi_hba, sas_dev);
+
+               /* Update linkrate of directly attached device. */
+               if (!device->parent)
+                       device->linkrate = linkrate;
+
+               hisi_hba->hw->setup_itct(hisi_hba, sas_dev);
+       }
+}
+
+static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state,
+                             u32 state)
+{
+       struct sas_ha_struct *sas_ha = &hisi_hba->sha;
+       struct asd_sas_port *_sas_port = NULL;
+       int phy_no;
+
+       for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
+               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) {
+                               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,
+                                                       PORTE_BROADCAST_RCVD);
+                       }
+               } else if (old_state & (1 << phy_no))
+                       /* PHY down but was up before */
+                       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)
 {
+       struct sas_ha_struct *sas_ha = &hisi_hba->sha;
+       struct device *dev = hisi_hba->dev;
+       struct Scsi_Host *shost = hisi_hba->shost;
+       u32 old_state, state;
+       unsigned long flags;
        int rc;
 
        if (!hisi_hba->hw->soft_reset)
                return -1;
 
-       if (!test_and_set_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)) {
-               struct device *dev = hisi_hba->dev;
-               struct sas_ha_struct *sas_ha = &hisi_hba->sha;
-               unsigned long flags;
+       if (test_and_set_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags))
+               return -1;
 
-               dev_dbg(dev, "controller reset begins!\n");
-               scsi_block_requests(hisi_hba->shost);
-               rc = hisi_hba->hw->soft_reset(hisi_hba);
-               if (rc) {
-                       dev_warn(dev, "controller reset failed (%d)\n", rc);
-                       goto out;
-               }
-               spin_lock_irqsave(&hisi_hba->lock, flags);
-               hisi_sas_release_tasks(hisi_hba);
-               spin_unlock_irqrestore(&hisi_hba->lock, flags);
+       dev_dbg(dev, "controller resetting...\n");
+       old_state = hisi_hba->hw->get_phys_state(hisi_hba);
 
-               sas_ha->notify_ha_event(sas_ha, HAE_RESET);
-               dev_dbg(dev, "controller reset successful!\n");
-       } else
-               return -1;
+       scsi_block_requests(shost);
+       set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+       rc = hisi_hba->hw->soft_reset(hisi_hba);
+       if (rc) {
+               dev_warn(dev, "controller reset failed (%d)\n", rc);
+               clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+               goto out;
+       }
+       spin_lock_irqsave(&hisi_hba->lock, flags);
+       hisi_sas_release_tasks(hisi_hba);
+       spin_unlock_irqrestore(&hisi_hba->lock, flags);
+
+       sas_ha->notify_ha_event(sas_ha, HAE_RESET);
+       clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+
+       /* 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);
+
+       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");
 
 out:
-       scsi_unblock_requests(hisi_hba->shost);
+       scsi_unblock_requests(shost);
        clear_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags);
+
        return rc;
 }
 
@@ -1243,7 +1323,7 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id,
        int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx;
        unsigned long flags, flags_dq;
 
-       if (unlikely(test_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags)))
+       if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags)))
                return -EINVAL;
 
        if (!device->port)
@@ -1439,36 +1519,6 @@ void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy)
 }
 EXPORT_SYMBOL_GPL(hisi_sas_phy_down);
 
-void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state,
-                             u32 state)
-{
-       struct sas_ha_struct *sas_ha = &hisi_hba->sha;
-       int phy_no;
-
-       for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
-               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 domain_device *dev;
-
-               if (sas_phy->enabled) {
-                       /* Report PHY state change to libsas */
-                       if (state & (1 << phy_no))
-                               continue;
-
-                       if (old_state & (1 << phy_no))
-                               /* PHY down but was up before */
-                               hisi_sas_phy_down(hisi_hba, phy_no, 0);
-               }
-               if (!sas_port)
-                       continue;
-               dev = sas_port->port_dev;
-
-               if (DEV_IS_EXPANDER(dev->dev_type))
-                       sas_ha->notify_phy_event(sas_phy, PORTE_BROADCAST_RCVD);
-       }
-}
-EXPORT_SYMBOL_GPL(hisi_sas_rescan_topology);
 
 struct scsi_transport_template *hisi_sas_stt;
 EXPORT_SYMBOL_GPL(hisi_sas_stt);