mirror of
https://github.com/torvalds/linux.git
synced 2024-11-18 01:51:53 +00:00
scsi: hisi_sas: use wait_for_completion_timeout() when clearing ITCT
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>
This commit is contained in:
parent
65a3b8bd56
commit
8fa9a7bd30
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user