OSDN Git Service

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)
committerMartin K. Petersen <martin.petersen@oracle.com>
Fri, 25 Oct 2019 01:31:13 +0000 (21:31 -0400)
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>
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 720c4d6..83232e8 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 ceba101..621eebb 100644 (file)
@@ -1045,6 +1045,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);
@@ -1056,13 +1057,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 b861a0f..3af53cc 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 8e96a25..61b1e26 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 cc59493..19a8cfe 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,