[PATCH v2 1/1] scsi: core: Restart the queue when unblocking a device set running
From: Brian Bunker
Date: Fri Oct 30 2026 - 14:03:57 EST
A device that its transport blocks has its queue stopped. If the SCSI
error handler then gives up on the device, it moves it from SDEV_BLOCK
to SDEV_OFFLINE and leaves the queue stopped. When userspace later
writes "running" to the device's state attribute, store_state_field()
moves it to SDEV_RUNNING, still with the queue stopped, and rescans it.
The rescan takes the device lock and waits for I/O that can never be
dispatched.
When the transport then unblocks the device,
scsi_internal_device_unblock_nowait() rejects a device in SDEV_RUNNING
with -EINVAL before it reaches scsi_start_queue(), so nothing ever
restarts the queue. The task that wrote "running" hangs forever holding
the device lock, and anything else that takes it hangs behind it. The
-EINVAL also makes scsi_host_unblock() stop at this device and leave the
rest of the host blocked, and keeps the device in SDEV_RUNNING when the
transport asked for SDEV_TRANSPORT_OFFLINE.
This shows up with iSCSI when a session drops again just after
recovering. iscsid onlines the session's devices after a relogin by
writing "running" to every device it finds offline:
1. The session is blocked, which blocks the device and stops its
queue.
2. The error handler cannot recover the device and moves it from
SDEV_BLOCK to SDEV_OFFLINE. The queue stays stopped.
3. iscsid writes "running": SDEV_OFFLINE -> SDEV_RUNNING with the
queue still stopped, then the rescan sends an INQUIRY that sits
unsent while holding the device lock.
4. The session recovery timeout calls scsi_target_unblock(). The
other devices go from SDEV_BLOCK to SDEV_TRANSPORT_OFFLINE and get
their queues restarted, but this one is skipped because it is
already running.
A vmcore from such a hang shows the device in SDEV_RUNNING with
queue_stopped set and the queue's quiesce_depth at 1, the rescan's
INQUIRY never started (MQ_RQ_IDLE, no start time or deadline), and
iscsid owning the device lock while other tasks wait for it.
Handle this in scsi_internal_device_unblock_nowait(), which already
restarts the queue of an SDEV_OFFLINE device: accept an SDEV_RUNNING
device whose queue is still stopped, move it to the requested state and
restart the queue. A running device whose queue is not stopped was never
blocked and is still rejected.
Do not restart the queue from store_state_field() instead. The device is
still blocked by its transport at that point, and some transports and
drivers rely on the stopped queue to keep commands away while they
reconnect or reset the adapter.
Fixes: f0f82e2476f6 ("scsi: core: Fix capacity set to zero after offlinining device")
Signed-off-by: Brian Bunker <brian@xxxxxxxxxxxxxxx>
---
drivers/scsi/scsi_lib.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index f695a6605815..b89407902430 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -3065,6 +3065,15 @@ int scsi_internal_device_unblock_nowait(struct scsi_device *sdev,
else
sdev->sdev_state = SDEV_CREATED;
break;
+ case SDEV_RUNNING:
+ /*
+ * The device was set running through sysfs while it was
+ * blocked, so its queue is still stopped. Finish the unblock.
+ */
+ if (!sdev->queue_stopped)
+ return -EINVAL;
+ sdev->sdev_state = new_state;
+ break;
case SDEV_CANCEL:
case SDEV_OFFLINE:
break;
--
2.56.0