[PATCH] scsi: libsas: Fix SMP IO deadlock during HA resume

Xingui Yang <[email protected]>
Newsgroups org.kernel.vger.linux-scsi,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
During sas_resume_ha() -> sas_drain_work(), ATA EH will trigger SMP IOs via
sas_phy_reset() -> sas_smp_phy_control() for expander-attached ATA devices.
Since ha->dev is RPM_RESUMING at that point, pm_runtime_get_sync() blocks
waiting for the resume to finish, but the resume is blocked in
sas_drain_work() waiting for the SMP IO — a deadlock.

Skip the PM get/put when SAS_HA_RESUMING is set. The hardware is already
initialized by the LLDD resume callback before sas_resume_ha() runs, so no
explicit resume is needed.

Only hisi_sas enables runtime PM among libsas LLDDs, so other drivers
(pm8001, isci, aic94xx, mvsas) are unaffected.

Fixes: 0da7ca4c4fd9 ("scsi: libsas: Resume host while sending SMP I/Os")
Signed-off-by: Xingui Yang <[email protected]>
---
 drivers/scsi/libsas/sas_expander.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 811c9eb4fef1..77ae2ae7b2c4 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -61,8 +61,17 @@ static int smp_execute_task_sg(struct domain_device *dev,
 	struct sas_internal *i =
 		to_sas_internal(dev->port->ha->shost->transportt);
 	struct sas_ha_struct *ha = dev->port->ha;
-
-	pm_runtime_get_sync(ha->dev);
+	bool skip_pm = test_bit(SAS_HA_RESUMING, &ha->state);
+
+	/*
+	 * Skip PM get/put during HA resume to avoid deadlock: the host is
+	 * RPM_RESUMING and the drain waits for this SMP IO to finish, but
+	 * pm_runtime_get_sync() would block on RPM_RESUMING. Safe because
+	 * hardware is already initialized by the LLDD before call
+	 * sas_resume_ha().
+	 */
+	if (!skip_pm)
+		pm_runtime_get_sync(ha->dev);
 	mutex_lock(&dev->ex_dev.cmd_mutex);
 	for (retry = 0; retry < 3; retry++) {
 		if (test_bit(SAS_DEV_GONE, &dev->state)) {
@@ -135,7 +144,8 @@ static int smp_execute_task_sg(struct domain_device *dev,
 		}
 	}
 	mutex_unlock(&dev->ex_dev.cmd_mutex);
-	pm_runtime_put_sync(ha->dev);
+	if (!skip_pm)
+		pm_runtime_put_sync(ha->dev);
 
 	BUG_ON(retry == 3 && task != NULL);
 	sas_free_task(task);
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.