scsi: hisi_sas: Fix the conflict between dev gone and host reset
authorXiaofei Tan <tanxiaofei@huawei.com>
Thu, 31 May 2018 12:50:44 +0000 (20:50 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 20 Jun 2018 02:02:25 +0000 (22:02 -0400)
There is a possible conflict when a device is removed and host reset occurs
concurrently.

The reason is that then the device is notified as gone, we try to clear the
ITCT, which is notified via an interrupt. The dev gone function pends on
this event with a completion, which is completed when the ITCT interrupt
occurs.

But host reset will disable all interrupts, the wait_for_completion() may
wait indefinitely.

This patch adds an semaphore to synchronise this two processes. The
semaphore is taken by the host reset as the basis of synchronising.

Signed-off-by: Xiaofei Tan <tanxiaofei@huawei.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

index 7052a5d45f7f98fddcaf9adc16df764df888f060..78e5a9254143f40629a05dd3387af6491623a11e 100644 (file)
@@ -277,6 +277,7 @@ struct hisi_hba {
 
        int n_phy;
        spinlock_t lock;
+       struct semaphore sem;
 
        struct timer_list timer;
        struct workqueue_struct *wq;
index 1c424bb363fd53910e1b371653f78cb638efaaa5..823386ff940cc2dd9bcacc8458e1a14f10fc149e 100644 (file)
@@ -914,7 +914,9 @@ static void hisi_sas_dev_gone(struct domain_device *device)
 
                hisi_sas_dereg_device(hisi_hba, device);
 
+               down(&hisi_hba->sem);
                hisi_hba->hw->clear_itct(hisi_hba, sas_dev);
+               up(&hisi_hba->sem);
                device->lldd_dev = NULL;
        }
 
@@ -1364,6 +1366,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
        if (test_and_set_bit(HISI_SAS_RESET_BIT, &hisi_hba->flags))
                return -1;
 
+       down(&hisi_hba->sem);
        dev_info(dev, "controller resetting...\n");
        old_state = hisi_hba->hw->get_phys_state(hisi_hba);
 
@@ -1378,6 +1381,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
        if (rc) {
                dev_warn(dev, "controller reset failed (%d)\n", rc);
                clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags);
+               up(&hisi_hba->sem);
                scsi_unblock_requests(shost);
                goto out;
        }
@@ -1388,6 +1392,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba)
        hisi_hba->hw->phys_init(hisi_hba);
        msleep(1000);
        hisi_sas_refresh_port_id(hisi_hba);
+       up(&hisi_hba->sem);
 
        if (hisi_hba->reject_stp_links_msk)
                hisi_sas_terminate_stp_reject(hisi_hba);
@@ -2016,6 +2021,7 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost)
        struct device *dev = hisi_hba->dev;
        int i, s, max_command_entries = hisi_hba->hw->max_command_entries;
 
+       sema_init(&hisi_hba->sem, 1);
        spin_lock_init(&hisi_hba->lock);
        for (i = 0; i < hisi_hba->n_phy; i++) {
                hisi_sas_phy_init(hisi_hba, i);