[PATCH 2/3] ufs: core: Fix a race condition triggered by ufshcd_eh_timed_out()
Bart Van Assche <[email protected]>
| Newsgroups | org.kernel.vger.linux-scsi |
|---|---|
| Message-ID | <fc388a5ff11decf35246780f19ccfce2fda35f16.1786558726.git.bvanassche@acm.org> |
If a START STOP UNIT command times out, the ufshcd_link_recovery() call
in ufshcd_eh_timed_out() may call scsi_done() for that command via the
force-completion mechanism. This may cause ufshcd_set_dev_pwr_mode() to
return before link recovery has finished. Fix this race condition by
moving the ufshcd_link_recovery() call into ufshcd_set_dev_pwr_mode().
Fixes: 7029e2151a7c ("scsi: ufs: Fix a deadlock between PM and the SCSI error handler")
Signed-off-by: Bart Van Assche <[email protected]>
---
drivers/ufs/core/ufshcd.c | 36 ++++++++++++------------------------
1 file changed, 12 insertions(+), 24 deletions(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 73f1e8817f44..0618712be32c 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -9582,34 +9582,18 @@ static enum scsi_timeout_action ufshcd_eh_timed_out(struct scsi_cmnd *scmd)
return SCSI_EH_NOT_HANDLED;
}
- /*
- * Handle the timeout directly to prevent a deadlock between
- * ufshcd_set_dev_pwr_mode() and ufshcd_err_handler().
- */
- ufshcd_link_recovery(hba);
- dev_info(hba->dev, "%s() finished; outstanding_tasks = %#lx.\n",
- __func__, hba->outstanding_tasks);
-
- /*
- * ufshcd_link_recovery() may already have completed @scmd, e.g. via
- * the existing MCQ force-completion path.
- */
- if (!test_bit(SCMD_STATE_COMPLETE, &scmd->state)) {
- if (!hba->mcq_enabled) {
- unsigned long flags;
- struct request *rq = scsi_cmd_to_rq(scmd);
+ if (!hba->mcq_enabled) {
+ struct request *rq = scsi_cmd_to_rq(scmd);
- spin_lock_irqsave(&hba->outstanding_lock, flags);
+ scoped_guard(spinlock_irqsave, &hba->outstanding_lock)
__clear_bit(rq->tag, &hba->outstanding_reqs);
- spin_unlock_irqrestore(&hba->outstanding_lock, flags);
- }
-
- set_host_byte(scmd, DID_TIME_OUT);
- if (ufshcd_is_scsi_cmd(scmd))
- ufshcd_release_scsi_cmd(hba, scmd);
- scsi_done(scmd);
}
+ set_host_byte(scmd, DID_TIME_OUT);
+ if (ufshcd_is_scsi_cmd(scmd))
+ ufshcd_release_scsi_cmd(hba, scmd);
+ scsi_done(scmd);
+
return SCSI_EH_DONE;
}
@@ -10123,9 +10107,13 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
sdev_printk(KERN_WARNING, sdp,
"START_STOP failed for power mode: %d, result %x\n",
pwr_mode, ret);
+
+ ufshcd_link_recovery(hba);
+
if (ret > 0) {
if (scsi_sense_valid(&sshdr))
scsi_print_sense_hdr(sdp, NULL, &sshdr);
+
ret = -EIO;
}
} else {