]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
scsi: hisi_sas: check host frozen before calling "done" function
authorXiang Chen <chenxiang66@hisilicon.com>
Wed, 2 May 2018 15:56:26 +0000 (23:56 +0800)
committerKhalid Elmously <khalid.elmously@canonical.com>
Wed, 6 Jun 2018 19:13:11 +0000 (15:13 -0400)
BugLink: https://bugs.launchpad.net/bugs/1774466
When the host is frozen in SCSI EH state, at any point after the LLDD
sets SAS_TASK_STATE_DONE for the sas_task task state, libsas may free
the task; see sas_scsi_find_task().

This puts the LLDD in a difficult position, in that once it sets
SAS_TASK_STATE_DONE for the task state it should not reference the
sas_task again. But the LLDD needs will check the sas_task indirectly in
calling task->task_done()->sas_scsi_task_done() or sas_ata_task_done()
(to check if the host is frozen state actually).

And the LLDD cannot set SAS_TASK_STATE_DONE for the task state after
task->task_done() is called (as the sas_task is free'd at this point).

This situation would seem to be a problem made by libsas.

To work around, check in the LLDD whether the host is in frozen state to
ensure it is ok to call task->task_done() function. If in the frozen
state, we rely on SCSI EH and libsas to free the sas_task directly.

We do not do this for the following IO types:

 - SMP - they are managed in libsas directly, outside SCSI EH
 - Any internally originated IO, for similar reason

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 cd938e535e909b80948f26d284ba7475adee0c08 linux-next)
Signed-off-by: dann frazier <dann.frazier@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
Acked-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com>
Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
drivers/scsi/hisi_sas/hisi_sas.h
drivers/scsi/hisi_sas/hisi_sas_main.c
drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
drivers/scsi/hisi_sas/hisi_sas_v3_hw.c

index d1153e8e846bda304b8a071d4bfa170553d1d49f..d3916f50946c1033123b2a527b531f131ae19ad5 100644 (file)
@@ -198,6 +198,7 @@ struct hisi_sas_slot {
        dma_addr_t cmd_hdr_dma;
        struct work_struct abort_slot;
        struct timer_list internal_abort_timer;
+       bool is_internal;
 };
 
 struct hisi_sas_tmf_task {
index 27b1494f1b4f3d0677b01bc6e9798751d5a77498..06d8438614f34ebb6c4fa88f084c69d53095ccfe 100644 (file)
@@ -381,6 +381,8 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq
        slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot];
        slot->task = task;
        slot->port = port;
+       if (is_tmf)
+               slot->is_internal = true;
        task->lldd_task = slot;
        INIT_WORK(&slot->abort_slot, hisi_sas_slot_abort);
 
@@ -1487,6 +1489,7 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id,
        slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot];
        slot->task = task;
        slot->port = port;
+       slot->is_internal = true;
        task->lldd_task = slot;
 
        slot->buf = dma_pool_alloc(hisi_hba->buffer_pool,
index e7e2829bbdccb32887887c0d4c2976e84e2e7046..3f1546dafee525b18ff2a727fc9da6f0edef475e 100644 (file)
@@ -2380,18 +2380,21 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
        struct device *dev = hisi_hba->dev;
        struct task_status_struct *ts;
        struct domain_device *device;
+       struct sas_ha_struct *ha;
        enum exec_status sts;
        struct hisi_sas_complete_v2_hdr *complete_queue =
                        hisi_hba->complete_hdr[slot->cmplt_queue];
        struct hisi_sas_complete_v2_hdr *complete_hdr =
                        &complete_queue[slot->cmplt_queue_slot];
        unsigned long flags;
+       bool is_internal = slot->is_internal;
 
        if (unlikely(!task || !task->lldd_task || !task->dev))
                return -EINVAL;
 
        ts = &task->task_status;
        device = task->dev;
+       ha = device->port->ha;
        sas_dev = device->lldd_dev;
 
        spin_lock_irqsave(&task->task_state_lock, flags);
@@ -2523,6 +2526,17 @@ out:
        task->task_state_flags |= SAS_TASK_STATE_DONE;
        spin_unlock_irqrestore(&task->task_state_lock, flags);
 
+       if (!is_internal && (task->task_proto != SAS_PROTOCOL_SMP)) {
+               spin_lock_irqsave(&device->done_lock, flags);
+               if (test_bit(SAS_HA_FROZEN, &ha->state)) {
+                       spin_unlock_irqrestore(&device->done_lock, flags);
+                       dev_info(dev, "slot complete: task(%p) ignored\n ",
+                                task);
+                       return sts;
+               }
+               spin_unlock_irqrestore(&device->done_lock, flags);
+       }
+
        if (task->task_done)
                task->task_done(task);
 
index 2bab2171fae19755bbac7ea8ed798eec34af765e..090eb99405eca48fde26506712b93a576870af23 100644 (file)
@@ -1571,18 +1571,21 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
        struct device *dev = hisi_hba->dev;
        struct task_status_struct *ts;
        struct domain_device *device;
+       struct sas_ha_struct *ha;
        enum exec_status sts;
        struct hisi_sas_complete_v3_hdr *complete_queue =
                        hisi_hba->complete_hdr[slot->cmplt_queue];
        struct hisi_sas_complete_v3_hdr *complete_hdr =
                        &complete_queue[slot->cmplt_queue_slot];
        unsigned long flags;
+       bool is_internal = slot->is_internal;
 
        if (unlikely(!task || !task->lldd_task || !task->dev))
                return -EINVAL;
 
        ts = &task->task_status;
        device = task->dev;
+       ha = device->port->ha;
        sas_dev = device->lldd_dev;
 
        spin_lock_irqsave(&task->task_state_lock, flags);
@@ -1700,6 +1703,17 @@ out:
        task->task_state_flags |= SAS_TASK_STATE_DONE;
        spin_unlock_irqrestore(&task->task_state_lock, flags);
 
+       if (!is_internal && (task->task_proto != SAS_PROTOCOL_SMP)) {
+               spin_lock_irqsave(&device->done_lock, flags);
+               if (test_bit(SAS_HA_FROZEN, &ha->state)) {
+                       spin_unlock_irqrestore(&device->done_lock, flags);
+                       dev_info(dev, "slot complete: task(%p) ignored\n ",
+                                task);
+                       return sts;
+               }
+               spin_unlock_irqrestore(&device->done_lock, flags);
+       }
+
        if (task->task_done)
                task->task_done(task);