linux/debian/patches/bugfix/arm64/huawei-taishan/0003-scsi-hisi_sas-Fix-the-...

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;