scsi: ufs: core: Quiesce SCSI devices instead of host tagset during error handling
Subsystems (3)
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
@@ -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
Quoted 20 lines · Stanley Jhu
Commit
08b12cda6c44("scsi: ufs: core: Switch toscsi_get_internal_cmd()") switched UFS internal commands to allocate requests onhba->host->pseudo_sdev->request_queue, which shares the host tagset with regular LUNs.During error recovery,
ufshcd_err_handling_prepare()callsblk_mq_quiesce_tagset(&hba->host->tag_set), marking all queues in the tagset as quiesced, includingpseudo_sdev->request_queue. Whenufshcd_verify_dev_init()subsequently issues internal commands (e.g. NOP OUT UPIU) viablk_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. Becauseshost_for_each_device()explicitly skips pseudo SCSI devices (viascsi_device_is_pseudo_dev()in__scsi_iterate_devices()), regular LUNs are quiesced to prevent retry storms, whilepseudo_sdevremains 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 useblk_mq_quiesce_tagset()as they require freezing all queues including internal commands.