Skip to contentSkip to conversation

scsi: ufs: core: Quiesce SCSI devices instead of host tagset during error handling

PATCH Superseded Stanley Jhu 2 participants 2 messages Sep 2 – Sep 11

Subsystems (3)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 scsi: ufs: core: Quiesce SCSI devices instead of host tagset during error handling

Commit 08b12cda6c44 ("scsi: ufs: core: Switch to scsi_get_internal_cmd()") switched UFS internal commands to allocate requests on hba->host->pseudo_sdev->request_queue, which shares the host tagset with regular LUNs.

During error recovery, ufshcd_err_handling_prepare() calls blk_mq_quiesce_tagset(&hba->host->tag_set), marking all queues in the tagset as quiesced, including pseudo_sdev->request_queue. When ufshcd_verify_dev_init() subsequently issues internal commands (e.g. NOP OUT UPIU) via blk_execute_rq(), blk_mq_run_hw_queue() skips running the quiesced queue, resulting in an unrecoverable circular wait deadlock.

Resolve this by quiescing only logical units attached to the host via shost_for_each_device() during error handling. Because shost_for_each_device() explicitly skips pseudo SCSI devices (via scsi_device_is_pseudo_dev() in __scsi_iterate_devices()), regular LUNs are quiesced to prevent retry storms, while pseudo_sdev remains unquiesced so internal commands can be dispatched and completed during error recovery.

Clock scaling and command pause (ufshcd_pause_command_processing) paths continue to use blk_mq_quiesce_tagset() as they require freezing all queues including internal commands.

---
 drivers/ufs/core/ufshcd.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)
drivers/ufs/core/ufshcd.c +19 -2
index 2ba244cf40ac..11488c5efb47 100644
@@ -6798,6 +6798,23 @@ static void ufshcd_clk_scaling_suspend(struct ufs_hba *hba, bool suspend)
 	}
 }
 
+static void ufshcd_quiesce_all_sdevs(struct ufs_hba *hba)
+{
+	struct scsi_device *sdev;
+
+	shost_for_each_device(sdev, hba->host)
+		blk_mq_quiesce_queue_nowait(sdev->request_queue);
+	blk_mq_wait_quiesce_done(&hba->host->tag_set);
+}
+
+static void ufshcd_unquiesce_all_sdevs(struct ufs_hba *hba)
+{
+	struct scsi_device *sdev;
+
+	shost_for_each_device(sdev, hba->host)
+		blk_mq_unquiesce_queue(sdev->request_queue);
+}
+
 static void ufshcd_err_handling_prepare(struct ufs_hba *hba)
 {
 	/*
@@ -6833,7 +6850,7 @@ static void ufshcd_err_handling_prepare(struct ufs_hba *hba)
 		ufshcd_clk_scaling_allow(hba, false);
 	}
 	/* Wait for ongoing ufshcd_queuecommand() calls to finish. */
-	blk_mq_quiesce_tagset(&hba->host->tag_set);
+	ufshcd_quiesce_all_sdevs(hba);
 	cancel_work_sync(&hba->eeh_work);
 }
 
@@ -6840,7 +6857,7 @@ static void ufshcd_err_handling_prepare(struct ufs_hba *hba)
 static void ufshcd_err_handling_unprepare(struct ufs_hba *hba)
 {
-	blk_mq_unquiesce_tagset(&hba->host->tag_set);
+	ufshcd_unquiesce_all_sdevs(hba);
 	ufshcd_release(hba);
 	if (ufshcd_is_clkscaling_supported(hba))
 		ufshcd_clk_scaling_suspend(hba, false);
Signature · 1 line
2.43.0
reviewer Reviewed-by on v1 1/1
#1 lore raw
Quoted 20 lines · Stanley Jhu

Commit 08b12cda6c44 ("scsi: ufs: core: Switch to scsi_get_internal_cmd()") switched UFS internal commands to allocate requests on hba->host->pseudo_sdev->request_queue, which shares the host tagset with regular LUNs.

During error recovery, ufshcd_err_handling_prepare() calls blk_mq_quiesce_tagset(&hba->host->tag_set), marking all queues in the tagset as quiesced, including pseudo_sdev->request_queue. When ufshcd_verify_dev_init() subsequently issues internal commands (e.g. NOP OUT UPIU) via blk_execute_rq(), blk_mq_run_hw_queue() skips running the quiesced queue, resulting in an unrecoverable circular wait deadlock.

Resolve this by quiescing only logical units attached to the host via shost_for_each_device() during error handling. Because shost_for_each_device() explicitly skips pseudo SCSI devices (via scsi_device_is_pseudo_dev() in __scsi_iterate_devices()), regular LUNs are quiesced to prevent retry storms, while pseudo_sdev remains unquiesced so internal commands can be dispatched and completed during error recovery.

Quoted 3 lines

Clock scaling and command pause (ufshcd_pause_command_processing) paths continue to use blk_mq_quiesce_tagset() as they require freezing all queues including internal commands.