scsi: core: Kick the requeue list after inserting when flushing
PATCH 1/1 scsi: core: Kick the requeue list after inserting when flushing
When libata calls ata_link_abort() to abort all ata queued commands, it calls blk_abort_request() on the SCSI command representing each QC.
This causes scsi_timeout() to be called, which calls scsi_eh_scmd_add() for each SCSI command.
scsi_eh_scmd_add() sets the SCSI host to state recovery, and then adds the command to shost->eh_cmd_q.
This will wake up the SCSI EH, and eventually the libata EH strategy handler will be called, which calls scsi_eh_flush_done_q() to either flush retry or flush finish each failed command.
The commands that are flush retried by scsi_eh_flush_done_q() are done so using scsi_queue_insert().
Before commit 8b566edbdbfb ("scsi: core: Only kick the requeue list if necessary"), __scsi_queue_insert() called blk_mq_requeue_request() with the second argument set to true, indicating that it should always kick/run the requeue list after inserting.
After commit 8b566edbdbfb ("scsi: core: Only kick the requeue list if necessary"), __scsi_queue_insert() does not kick/run the requeue list after inserting, if the current SCSI host state is recovery (which is the case in the libata example above).
This optimization is probably fine in most cases, as I can only assume that most often someone will eventually kick/run the queues.
However, that is not the case for scsi_eh_flush_done_q(), where we can see that the request gets inserted to the requeue list, but the queue is never started after the request has been inserted, leading to the block layer waiting for the completion of command that never gets to run.
Since scsi_eh_flush_done_q() is called by SCSI EH context, the SCSI host state is most likely always in recovery when this function is called.
Thus, let scsi_eh_flush_done_q() explicitly kick the requeue list after inserting a flush retry command, so that scsi_eh_flush_done_q() keeps the same behavior as before commit 8b566edbdbfb ("scsi: core: Only kick the requeue list if necessary").
Simple reproducer for the libata example above:
$ hdparm -Y /dev/sda $ echo 1 > /sys/class/scsi_device/0\:0\:0\:0/device/delete
--- drivers/scsi/scsi_error.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/scsi/scsi_error.c +6 -3
@@ -2196,15 +2196,18 @@ void scsi_eh_flush_done_q(struct list_head *done_q)struct scsi_cmnd *scmd, *next;list_for_each_entry_safe (scmd, next, done_q, eh_entry) { +struct scsi_device *sdev = scmd->device; +list_del_init (&scmd->eh_entry); -if (scsi_device_online (scmd->device) && - !scsi_noretry_cmd (scmd) &&scsi_cmd_retry_allowed (scmd) && -scsi_eh_should_retry_cmd (scmd)) { +if (scsi_device_online (sdev) && !scsi_noretry_cmd (scmd) && +scsi_cmd_retry_allowed (scmd) && +scsi_eh_should_retry_cmd (scmd)) {SCSI_LOG_ERROR_RECOVERY (3 ,scmd_printk (KERN_INFO , scmd,"%s: flush retry cmd\n" , current->comm));scsi_queue_insert (scmd,SCSI_MLQUEUE_EH_RETRY ); +blk_mq_kick_requeue_list (sdev->request_queue); }else {/* * If just we got sense for the device (called
Signature · 1 line
2.43.0
Quoted 16 lines · Niklas Cassel
drivers/scsi/scsi_error.c +4
-3@@ -2196,15 +2196,18 @@ void scsi_eh_flush_done_q(struct list_head *done_q)struct scsi_cmnd *scmd, *next;list_for_each_entry_safe (scmd, next, done_q, eh_entry) { +struct scsi_device *sdev = scmd->device; +list_del_init (&scmd->eh_entry); -if (scsi_device_online (scmd->device) && - !scsi_noretry_cmd (scmd) &&scsi_cmd_retry_allowed (scmd) && -scsi_eh_should_retry_cmd (scmd)) { + if (scsi_device_online(sdev) && !scsi_noretry_cmd(scmd) && + scsi_cmd_retry_allowed(scmd) &&
drivers/scsi/scsi_error.c void scsi_eh_flush_done_q(struct list_head *done_q) · line 2213
Quoted 10 lines
drivers/scsi/scsi_error.c +2
-0+ scsi_eh_should_retry_cmd(scmd)) {SCSI_LOG_ERROR_RECOVERY (3 ,scmd_printk (KERN_INFO , scmd,"%s: flush retry cmd\n" , current->comm));scsi_queue_insert (scmd,SCSI_MLQUEUE_EH_RETRY ); +blk_mq_kick_requeue_list (sdev->request_queue); } else {/* * If just we got sense for the device (called
Quoted 70 lines · Niklas Cassel
When libata calls
ata_link_abort()to abort all ata queued commands, it callsblk_abort_request()on the SCSI command representing each QC.This causes
scsi_timeout()to be called, which callsscsi_eh_scmd_add()for each SCSI command.
scsi_eh_scmd_add()sets the SCSI host to state recovery, and then adds the command toshost->eh_cmd_q.This will wake up the SCSI EH, and eventually the libata EH strategy handler will be called, which calls
scsi_eh_flush_done_q()to either flush retry or flush finish each failed command.The commands that are flush retried by
scsi_eh_flush_done_q()are done so usingscsi_queue_insert().Before commit
8b566edbdbfb("scsi: core: Only kick the requeue list if necessary"),__scsi_queue_insert()calledblk_mq_requeue_request()with the second argument set to true, indicating that it should always kick/run the requeue list after inserting.After commit
8b566edbdbfb("scsi: core: Only kick the requeue list if necessary"),__scsi_queue_insert()does not kick/run the requeue list after inserting, if the current SCSI host state is recovery (which is the case in the libata example above).This optimization is probably fine in most cases, as I can only assume that most often someone will eventually kick/run the queues.
However, that is not the case for
scsi_eh_flush_done_q(), where we can see that the request gets inserted to the requeue list, but the queue is never started after the request has been inserted, leading to the block layer waiting for the completion of command that never gets to run.Since
scsi_eh_flush_done_q()is called by SCSI EH context, the SCSI host state is most likely always in recovery when this function is called.Thus, let
scsi_eh_flush_done_q()explicitly kick the requeue list after inserting a flush retry command, so thatscsi_eh_flush_done_q()keeps the same behavior as before commit8b566edbdbfb("scsi: core: Only kick the requeue list if necessary").Simple reproducer for the libata example above:
$ hdparm -Y /dev/sda $ echo 1 > /sys/class/scsi_device/0\:0\:0\:0/device/deleteFixes 8b566edbdbfb ("scsi: core: Only kick the requeue list if necessary")Reported-by Kevin Locke <[email protected]>Signed-off-by Niklas Cassel <[email protected]>--- drivers/scsi/scsi_error.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)drivers/scsi/scsi_error.c +4
-3@@ -2196,15 +2196,18 @@ void scsi_eh_flush_done_q(struct list_head *done_q)struct scsi_cmnd *scmd, *next;list_for_each_entry_safe (scmd, next, done_q, eh_entry) { +struct scsi_device *sdev = scmd->device; +list_del_init (&scmd->eh_entry); -if (scsi_device_online (scmd->device) && - !scsi_noretry_cmd (scmd) &&scsi_cmd_retry_allowed (scmd) && -scsi_eh_should_retry_cmd (scmd)) { + if (scsi_device_online(sdev) && !scsi_noretry_cmd(scmd) && + scsi_cmd_retry_allowed(scmd) &&
drivers/scsi/scsi_error.c void scsi_eh_flush_done_q(struct list_head *done_q) · line 2213
Quoted 10 lines
drivers/scsi/scsi_error.c +2
-0+ scsi_eh_should_retry_cmd(scmd)) {SCSI_LOG_ERROR_RECOVERY (3 ,scmd_printk (KERN_INFO , scmd,"%s: flush retry cmd\n" , current->comm));scsi_queue_insert (scmd,SCSI_MLQUEUE_EH_RETRY ); +blk_mq_kick_requeue_list (sdev->request_queue); } else {/* * If just we got sense for the device (called
Looks good to me.
Signature · 2 lines
Damien Le Moal Western Digital Research
Martin K. Petersen
AppliedNiklas,
Quoted 2 lines
When libata calls
ata_link_abort()to abort all ata queued commands, it callsblk_abort_request()on the SCSI command representing each QC.
Applied to 6.8/scsi-staging, thanks!
Signature · 1 line
Martin K. Petersen Oracle Linux Engineering