Commit d712d3fb authored by Igor Pylypiv's avatar Igor Pylypiv Committed by Martin K. Petersen
Browse files

scsi: pm80xx: Fix TMF task completion race condition

The TMF timeout timer may trigger at the same time when the response from a
controller is being handled. When this happens the SAS task may get freed
before the response processing is finished.

Fix this by calling complete() only when SAS_TASK_STATE_DONE is not set.

A similar race condition was fixed in commit b90cd6f2 ("scsi: libsas:
fix a race condition when smp task timeout")

Link: https://lore.kernel.org/r/20210707185945.35559-1-ipylypiv@google.com


Reviewed-by: default avatarVishakha Channapattan <vishakhavc@google.com>
Acked-by: default avatarJack Wang <jinpu.wang@ionos.com>
Signed-off-by: default avatarIgor Pylypiv <ipylypiv@google.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent a47fa413
Loading
Loading
Loading
Loading
+15 −17
Original line number Diff line number Diff line
@@ -684,8 +684,7 @@ int pm8001_dev_found(struct domain_device *dev)

void pm8001_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);
}

@@ -693,10 +692,15 @@ static void pm8001_tmf_timedout(struct timer_list *t)
{
	struct sas_task_slow *slow = from_timer(slow, t, timer);
	struct sas_task *task = slow->task;
	unsigned long flags;

	spin_lock_irqsave(&task->task_state_lock, flags);
	if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
		task->task_state_flags |= SAS_TASK_STATE_ABORTED;
		complete(&task->slow_task->completion);
	}
	spin_unlock_irqrestore(&task->task_state_lock, flags);
}

#define PM8001_TASK_TIMEOUT 20
/**
@@ -748,14 +752,11 @@ static int pm8001_exec_internal_tmf_task(struct domain_device *dev,
		}
		res = -TMF_RESP_FUNC_FAILED;
		/* Even TMF timed out, return direct. */
		if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
			if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
				pm8001_dbg(pm8001_ha, FAIL,
					   "TMF task[%x]timeout.\n",
		if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
			pm8001_dbg(pm8001_ha, FAIL, "TMF task[%x]timeout.\n",
				   tmf->tmf);
			goto ex_err;
		}
		}

		if (task->task_status.resp == SAS_TASK_COMPLETE &&
			task->task_status.stat == SAS_SAM_STAT_GOOD) {
@@ -834,13 +835,10 @@ pm8001_exec_internal_task_abort(struct pm8001_hba_info *pm8001_ha,
		wait_for_completion(&task->slow_task->completion);
		res = TMF_RESP_FUNC_FAILED;
		/* Even TMF timed out, return direct. */
		if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
			if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
				pm8001_dbg(pm8001_ha, FAIL,
					   "TMF task timeout.\n");
		if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
			pm8001_dbg(pm8001_ha, FAIL, "TMF task timeout.\n");
			goto ex_err;
		}
		}

		if (task->task_status.resp == SAS_TASK_COMPLETE &&
			task->task_status.stat == SAS_SAM_STAT_GOOD) {