mirror of https://gitee.com/openkylin/linux.git
scsi: hisi_sas: check host frozen before calling "done" function
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>
This commit is contained in:
parent
b81b6cce58
commit
cd938e535e
|
@ -196,6 +196,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 {
|
||||
|
|
|
@ -382,6 +382,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);
|
||||
|
||||
|
@ -1486,6 +1488,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,
|
||||
|
|
|
@ -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 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
|
|||
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);
|
||||
|
||||
|
|
|
@ -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 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot)
|
|||
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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue