提交 c4a22c21 编写于 作者: C chenxiang 提交者: Xie XiuQi

scsi: hisi_sas: fix the race between IO completion and IO timeout for SMP/internal IO

If SMP/internal IO is timeout, it will free slot/task immediately, but if
it enters IO completion at the same time, it may refer to task which is
freed. So to solve the issue, flush tasklet to finish IO completion before
free'ing slot/task.
Signed-off-by: NXiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: NJohn Garry <john.garry@huawei.com>
上级 259ada04
...@@ -995,8 +995,7 @@ static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, ...@@ -995,8 +995,7 @@ static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func,
static void hisi_sas_task_done(struct sas_task *task) static void hisi_sas_task_done(struct sas_task *task)
{ {
if (!del_timer(&task->slow_task->timer)) del_timer(&task->slow_task->timer);
return;
complete(&task->slow_task->completion); complete(&task->slow_task->completion);
} }
...@@ -1005,13 +1004,17 @@ static void hisi_sas_tmf_timedout(struct timer_list *t) ...@@ -1005,13 +1004,17 @@ static void hisi_sas_tmf_timedout(struct timer_list *t)
struct sas_task_slow *slow = from_timer(slow, t, timer); struct sas_task_slow *slow = from_timer(slow, t, timer);
struct sas_task *task = slow->task; struct sas_task *task = slow->task;
unsigned long flags; unsigned long flags;
bool is_completed = true;
spin_lock_irqsave(&task->task_state_lock, flags); spin_lock_irqsave(&task->task_state_lock, flags);
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
task->task_state_flags |= SAS_TASK_STATE_ABORTED; task->task_state_flags |= SAS_TASK_STATE_ABORTED;
is_completed = false;
}
spin_unlock_irqrestore(&task->task_state_lock, flags); spin_unlock_irqrestore(&task->task_state_lock, flags);
complete(&task->slow_task->completion); if (!is_completed)
complete(&task->slow_task->completion);
} }
#define TASK_TIMEOUT 20 #define TASK_TIMEOUT 20
...@@ -1062,10 +1065,18 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device, ...@@ -1062,10 +1065,18 @@ static int hisi_sas_exec_internal_tmf_task(struct domain_device *device,
if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
struct hisi_sas_slot *slot = task->lldd_task; struct hisi_sas_slot *slot = task->lldd_task;
struct hisi_sas_cq *cq =
&hisi_hba->cq[slot->dlvry_queue];
dev_err(dev, "abort tmf: TMF task timeout and not done\n"); dev_err(dev, "abort tmf: TMF task timeout and not done\n");
if (slot) if (slot) {
/*
* flush tasklet to avoid free'ing task
* before using task in IO completion
*/
tasklet_kill(&cq->tasklet);
slot->task = NULL; slot->task = NULL;
}
goto ex_err; goto ex_err;
} else } else
...@@ -1441,6 +1452,17 @@ static int hisi_sas_abort_task(struct sas_task *task) ...@@ -1441,6 +1452,17 @@ static int hisi_sas_abort_task(struct sas_task *task)
spin_lock_irqsave(&task->task_state_lock, flags); spin_lock_irqsave(&task->task_state_lock, flags);
if (task->task_state_flags & SAS_TASK_STATE_DONE) { if (task->task_state_flags & SAS_TASK_STATE_DONE) {
struct hisi_sas_slot *slot = task->lldd_task;
struct hisi_sas_cq *cq;
if (slot) {
/*
* flush tasklet to avoid free'ing task
* before using task in IO completion
*/
cq = &hisi_hba->cq[slot->dlvry_queue];
tasklet_kill(&cq->tasklet);
}
spin_unlock_irqrestore(&task->task_state_lock, flags); spin_unlock_irqrestore(&task->task_state_lock, flags);
rc = TMF_RESP_FUNC_COMPLETE; rc = TMF_RESP_FUNC_COMPLETE;
goto out; goto out;
...@@ -1496,12 +1518,19 @@ static int hisi_sas_abort_task(struct sas_task *task) ...@@ -1496,12 +1518,19 @@ static int hisi_sas_abort_task(struct sas_task *task)
/* SMP */ /* SMP */
struct hisi_sas_slot *slot = task->lldd_task; struct hisi_sas_slot *slot = task->lldd_task;
u32 tag = slot->idx; u32 tag = slot->idx;
struct hisi_sas_cq *cq = &hisi_hba->cq[slot->dlvry_queue];
rc = hisi_sas_internal_task_abort(hisi_hba, device, rc = hisi_sas_internal_task_abort(hisi_hba, device,
HISI_SAS_INT_ABT_CMD, tag); HISI_SAS_INT_ABT_CMD, tag);
if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) && if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) &&
task->lldd_task) task->lldd_task) {
hisi_sas_do_release_task(hisi_hba, task, slot); /*
* flush tasklet to avoid free'ing task
* before using task in IO completion
*/
tasklet_kill(&cq->tasklet);
slot->task = NULL;
}
} }
out: out:
...@@ -1861,9 +1890,17 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, ...@@ -1861,9 +1890,17 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba,
if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
struct hisi_sas_slot *slot = task->lldd_task; struct hisi_sas_slot *slot = task->lldd_task;
struct hisi_sas_cq *cq =
if (slot) &hisi_hba->cq[slot->dlvry_queue];
if (slot) {
/*
* flush tasklet to avoid free'ing task
* before using task in IO completion
*/
tasklet_kill(&cq->tasklet);
slot->task = NULL; slot->task = NULL;
}
dev_err(dev, "internal task abort: timeout and not done.\n"); dev_err(dev, "internal task abort: timeout and not done.\n");
res = -EIO; res = -EIO;
goto exit; goto exit;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册