136 lines
4.3 KiB
Diff
136 lines
4.3 KiB
Diff
From d754707455238c59350c70ce51123b586fefac52 Mon Sep 17 00:00:00 2001
|
|
From: Xiang Chen <chenxiang66@hisilicon.com>
|
|
Date: Mon, 24 Sep 2018 23:06:30 +0800
|
|
Subject: [PATCH 03/31] scsi: hisi_sas: Fix the race between IO completion and
|
|
timeout for SMP/internal IO
|
|
Origin: https://git.kernel.org/linus/584f53fe5f529d877968c711a095923c1ed12307
|
|
|
|
If SMP/internal IO times out, we will possibly free the task immediately.
|
|
|
|
However if the IO actually completes at the same time, the IO completion
|
|
may refer to task which has been freed.
|
|
|
|
So to solve the issue, flush the tasklet to finish IO completion before
|
|
free'ing slot/task.
|
|
|
|
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>
|
|
---
|
|
drivers/scsi/hisi_sas/hisi_sas_main.c | 55 ++++++++++++++++++++++-----
|
|
1 file changed, 46 insertions(+), 9 deletions(-)
|
|
|
|
Index: linux/drivers/scsi/hisi_sas/hisi_sas_main.c
|
|
===================================================================
|
|
--- linux.orig/drivers/scsi/hisi_sas/hisi_sas_main.c
|
|
+++ linux/drivers/scsi/hisi_sas/hisi_sas_main.c
|
|
@@ -958,8 +958,7 @@ static int hisi_sas_control_phy(struct a
|
|
|
|
static void hisi_sas_task_done(struct sas_task *task)
|
|
{
|
|
- if (!del_timer(&task->slow_task->timer))
|
|
- return;
|
|
+ del_timer(&task->slow_task->timer);
|
|
complete(&task->slow_task->completion);
|
|
}
|
|
|
|
@@ -968,13 +967,17 @@ static void hisi_sas_tmf_timedout(struct
|
|
struct sas_task_slow *slow = from_timer(slow, t, timer);
|
|
struct sas_task *task = slow->task;
|
|
unsigned long flags;
|
|
+ bool is_completed = true;
|
|
|
|
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;
|
|
+ is_completed = false;
|
|
+ }
|
|
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
|
|
@@ -1025,10 +1028,18 @@ static int hisi_sas_exec_internal_tmf_ta
|
|
if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
|
|
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
|
|
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");
|
|
- if (slot)
|
|
+ if (slot) {
|
|
+ /*
|
|
+ * flush tasklet to avoid free'ing task
|
|
+ * before using task in IO completion
|
|
+ */
|
|
+ tasklet_kill(&cq->tasklet);
|
|
slot->task = NULL;
|
|
+ }
|
|
|
|
goto ex_err;
|
|
} else
|
|
@@ -1404,6 +1415,17 @@ static int hisi_sas_abort_task(struct sa
|
|
|
|
spin_lock_irqsave(&task->task_state_lock, flags);
|
|
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);
|
|
rc = TMF_RESP_FUNC_COMPLETE;
|
|
goto out;
|
|
@@ -1459,12 +1481,19 @@ static int hisi_sas_abort_task(struct sa
|
|
/* SMP */
|
|
struct hisi_sas_slot *slot = task->lldd_task;
|
|
u32 tag = slot->idx;
|
|
+ struct hisi_sas_cq *cq = &hisi_hba->cq[slot->dlvry_queue];
|
|
|
|
rc = hisi_sas_internal_task_abort(hisi_hba, device,
|
|
HISI_SAS_INT_ABT_CMD, tag);
|
|
if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) &&
|
|
- task->lldd_task)
|
|
- hisi_sas_do_release_task(hisi_hba, task, slot);
|
|
+ task->lldd_task) {
|
|
+ /*
|
|
+ * flush tasklet to avoid free'ing task
|
|
+ * before using task in IO completion
|
|
+ */
|
|
+ tasklet_kill(&cq->tasklet);
|
|
+ slot->task = NULL;
|
|
+ }
|
|
}
|
|
|
|
out:
|
|
@@ -1830,9 +1859,17 @@ hisi_sas_internal_task_abort(struct hisi
|
|
if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
|
|
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
|
|
struct hisi_sas_slot *slot = task->lldd_task;
|
|
+ struct hisi_sas_cq *cq =
|
|
+ &hisi_hba->cq[slot->dlvry_queue];
|
|
|
|
- if (slot)
|
|
+ if (slot) {
|
|
+ /*
|
|
+ * flush tasklet to avoid free'ing task
|
|
+ * before using task in IO completion
|
|
+ */
|
|
+ tasklet_kill(&cq->tasklet);
|
|
slot->task = NULL;
|
|
+ }
|
|
dev_err(dev, "internal task abort: timeout and not done.\n");
|
|
res = -EIO;
|
|
goto exit;
|