]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
scsi: hisi_sas: use wait_for_completion_timeout() when clearing ITCT
authorXiang Chen <chenxiang66@hisilicon.com>
Thu, 24 Oct 2019 14:08:10 +0000 (22:08 +0800)
committerSeth Forshee <seth.forshee@canonical.com>
Tue, 7 Jan 2020 21:29:36 +0000 (15:29 -0600)
BugLink: https://bugs.launchpad.net/bugs/1853999
When injecting 2bit ecc errors, it will cause confusion inside SAS
controller which needs host reset to recover it. If a device is gone at the
same times inject 2bit ecc errors, we may not receive the ITCT interrupt so
it will wait for completion in clear_itct_v3_hw() all the time. And host
reset will also not occur because it can't require hisi_hba->sem, so the
system will be suspended.

To solve the issue, use wait_for_completion_timeout() instead of
wait_for_completion(), and also don't mark the gone device as
SAS_PHY_UNUSED when device gone.

Link: https://lore.kernel.org/r/1571926105-74636-4-git-send-email-john.garry@huawei.com
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
(cherry picked from commit 8fa9a7bd3099a96194d767ce681c68dbcb8a957e)
Signed-off-by: dann frazier <dann.frazier@canonical.com>
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
drivers/scsi/hisi_sas/hisi_sas.h
drivers/scsi/hisi_sas/hisi_sas_main.c
drivers/scsi/hisi_sas/hisi_sas_v1_hw.c
drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
drivers/scsi/hisi_sas/hisi_sas_v3_hw.c

index 720c4d6be939df480265e6db0b8bd54727b34090..83232e8472fbf0c7063d0a9b8053c38dd3ef755f 100644 (file)
@@ -84,6 +84,7 @@
 #define HISI_SAS_PROT_MASK (HISI_SAS_DIF_PROT_MASK | HISI_SAS_DIX_PROT_MASK)
 
 #define HISI_SAS_WAIT_PHYUP_TIMEOUT 20
+#define CLEAR_ITCT_TIMEOUT     20
 
 struct hisi_hba;
 
@@ -296,8 +297,8 @@ struct hisi_sas_hw {
        void (*phy_set_linkrate)(struct hisi_hba *hisi_hba, int phy_no,
                        struct sas_phy_linkrates *linkrates);
        enum sas_linkrate (*phy_get_max_linkrate)(void);
-       void (*clear_itct)(struct hisi_hba *hisi_hba,
-                           struct hisi_sas_device *dev);
+       int (*clear_itct)(struct hisi_hba *hisi_hba,
+                         struct hisi_sas_device *dev);
        void (*free_device)(struct hisi_sas_device *sas_dev);
        int (*get_wideport_bitmap)(struct hisi_hba *hisi_hba, int port_id);
        void (*dereg_device)(struct hisi_hba *hisi_hba,
index 6f4692f0d7143974378af0880ad764bfb069dbcf..d620d3f4df306c79307d16a31a0259d33ff04e47 100644 (file)
@@ -1052,6 +1052,7 @@ static void hisi_sas_dev_gone(struct domain_device *device)
        struct hisi_sas_device *sas_dev = device->lldd_dev;
        struct hisi_hba *hisi_hba = dev_to_hisi_hba(device);
        struct device *dev = hisi_hba->dev;
+       int ret = 0;
 
        dev_info(dev, "dev[%d:%x] is gone\n",
                 sas_dev->device_id, sas_dev->dev_type);
@@ -1063,13 +1064,16 @@ static void hisi_sas_dev_gone(struct domain_device *device)
 
                hisi_sas_dereg_device(hisi_hba, device);
 
-               hisi_hba->hw->clear_itct(hisi_hba, sas_dev);
+               ret = hisi_hba->hw->clear_itct(hisi_hba, sas_dev);
                device->lldd_dev = NULL;
        }
 
        if (hisi_hba->hw->free_device)
                hisi_hba->hw->free_device(sas_dev);
-       sas_dev->dev_type = SAS_PHY_UNUSED;
+
+       /* Don't mark it as SAS_PHY_UNUSED if failed to clear ITCT */
+       if (!ret)
+               sas_dev->dev_type = SAS_PHY_UNUSED;
        sas_dev->sas_device = NULL;
        up(&hisi_hba->sem);
 }
index b861a0f14c9d8f7f8d65385d3b34f1a255bd5ee6..3af53cc42bd6bc1f550144f2dae09fb44c2158b6 100644 (file)
@@ -531,8 +531,8 @@ static void setup_itct_v1_hw(struct hisi_hba *hisi_hba,
                                (0xff00ULL << ITCT_HDR_REJ_OPEN_TL_OFF));
 }
 
-static void clear_itct_v1_hw(struct hisi_hba *hisi_hba,
-                             struct hisi_sas_device *sas_dev)
+static int clear_itct_v1_hw(struct hisi_hba *hisi_hba,
+                           struct hisi_sas_device *sas_dev)
 {
        u64 dev_id = sas_dev->device_id;
        struct hisi_sas_itct *itct = &hisi_hba->itct[dev_id];
@@ -551,6 +551,8 @@ static void clear_itct_v1_hw(struct hisi_hba *hisi_hba,
        qw0 = le64_to_cpu(itct->qw0);
        qw0 &= ~ITCT_HDR_VALID_MSK;
        itct->qw0 = cpu_to_le64(qw0);
+
+       return 0;
 }
 
 static int reset_hw_v1_hw(struct hisi_hba *hisi_hba)
index 8e96a257e439383acb5033fd4eca565668ee237f..61b1e2693b08d8264bd293254c31892dee0cb1fe 100644 (file)
@@ -974,13 +974,14 @@ static void setup_itct_v2_hw(struct hisi_hba *hisi_hba,
                                        (0x1ULL << ITCT_HDR_RTOLT_OFF));
 }
 
-static void clear_itct_v2_hw(struct hisi_hba *hisi_hba,
-                             struct hisi_sas_device *sas_dev)
+static int clear_itct_v2_hw(struct hisi_hba *hisi_hba,
+                           struct hisi_sas_device *sas_dev)
 {
        DECLARE_COMPLETION_ONSTACK(completion);
        u64 dev_id = sas_dev->device_id;
        struct hisi_sas_itct *itct = &hisi_hba->itct[dev_id];
        u32 reg_val = hisi_sas_read32(hisi_hba, ENT_INT_SRC3);
+       struct device *dev = hisi_hba->dev;
        int i;
 
        sas_dev->completion = &completion;
@@ -990,13 +991,19 @@ static void clear_itct_v2_hw(struct hisi_hba *hisi_hba,
                hisi_sas_write32(hisi_hba, ENT_INT_SRC3,
                                 ENT_INT_SRC3_ITC_INT_MSK);
 
+       /* need to set register twice to clear ITCT for v2 hw */
        for (i = 0; i < 2; i++) {
                reg_val = ITCT_CLR_EN_MSK | (dev_id & ITCT_DEV_MSK);
                hisi_sas_write32(hisi_hba, ITCT_CLR, reg_val);
-               wait_for_completion(sas_dev->completion);
+               if (!wait_for_completion_timeout(sas_dev->completion,
+                                                CLEAR_ITCT_TIMEOUT * HZ)) {
+                       dev_warn(dev, "failed to clear ITCT\n");
+                       return -ETIMEDOUT;
+               }
 
                memset(itct, 0, sizeof(struct hisi_sas_itct));
        }
+       return 0;
 }
 
 static void free_device_v2_hw(struct hisi_sas_device *sas_dev)
index c4f76d7c29dbc911b0bc5cec5fc997e25d4e6e58..e4da309009c0f184e154695cbe78d7a958bfdc47 100644 (file)
@@ -795,13 +795,14 @@ static void setup_itct_v3_hw(struct hisi_hba *hisi_hba,
                                        (0x1ULL << ITCT_HDR_RTOLT_OFF));
 }
 
-static void clear_itct_v3_hw(struct hisi_hba *hisi_hba,
-                             struct hisi_sas_device *sas_dev)
+static int clear_itct_v3_hw(struct hisi_hba *hisi_hba,
+                           struct hisi_sas_device *sas_dev)
 {
        DECLARE_COMPLETION_ONSTACK(completion);
        u64 dev_id = sas_dev->device_id;
        struct hisi_sas_itct *itct = &hisi_hba->itct[dev_id];
        u32 reg_val = hisi_sas_read32(hisi_hba, ENT_INT_SRC3);
+       struct device *dev = hisi_hba->dev;
 
        sas_dev->completion = &completion;
 
@@ -814,8 +815,14 @@ static void clear_itct_v3_hw(struct hisi_hba *hisi_hba,
        reg_val = ITCT_CLR_EN_MSK | (dev_id & ITCT_DEV_MSK);
        hisi_sas_write32(hisi_hba, ITCT_CLR, reg_val);
 
-       wait_for_completion(sas_dev->completion);
+       if (!wait_for_completion_timeout(sas_dev->completion,
+                                        CLEAR_ITCT_TIMEOUT * HZ)) {
+               dev_warn(dev, "failed to clear ITCT\n");
+               return -ETIMEDOUT;
+       }
+
        memset(itct, 0, sizeof(struct hisi_sas_itct));
+       return 0;
 }
 
 static void dereg_device_v3_hw(struct hisi_hba *hisi_hba,