OSDN Git Service

scsi: hisi_sas: Fix some issues related to asd_sas_port->phy_list
authorXiang Chen <chenxiang66@hisilicon.com>
Mon, 20 Dec 2021 11:21:28 +0000 (19:21 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 23 Dec 2021 04:38:29 +0000 (23:38 -0500)
Most places that use asd_sas_port->phy_list are protected by spinlock
asd_sas_port->phy_list_lock, however there are still some places which miss
grabbing the lock. Add it in function hisi_sas_refresh_port_id() when
accessing asd_sas_port->phy_list. This carries a risk that list mutates
while at the same time dropping the lock in function
hisi_sas_send_ata_reset_each_phy(). Read asd_sas_port->phy_mask instead of
accessing asd_sas_port->phy_list to avoid this risk.

Link: https://lore.kernel.org/r/1639999298-244569-6-git-send-email-chenxiang66@hisilicon.com
Acked-by: John Garry <john.garry@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/hisi_sas/hisi_sas_main.c

index ad64ccd..051092e 100644 (file)
@@ -1428,11 +1428,13 @@ static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba)
                sas_port = device->port;
                port = to_hisi_sas_port(sas_port);
 
+               spin_lock(&sas_port->phy_list_lock);
                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;
                        }
+               spin_unlock(&sas_port->phy_list_lock);
 
                if (phy) {
                        port->id = phy->port_id;
@@ -1509,22 +1511,25 @@ static void hisi_sas_send_ata_reset_each_phy(struct hisi_hba *hisi_hba,
        struct ata_link *link;
        u8 fis[20] = {0};
        u32 state;
+       int i;
 
        state = hisi_hba->hw->get_phys_state(hisi_hba);
-       list_for_each_entry(sas_phy, &sas_port->phy_list, port_phy_el) {
+       for (i = 0; i < hisi_hba->n_phy; i++) {
                if (!(state & BIT(sas_phy->id)))
                        continue;
+               if (!(sas_port->phy_mask & BIT(i)))
+                       continue;
 
                ata_for_each_link(link, ap, EDGE) {
                        int pmp = sata_srst_pmp(link);
 
-                       tmf_task.phy_id = sas_phy->id;
+                       tmf_task.phy_id = i;
                        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);
+                                       i, rc);
                                break;
                        }
                }