diff options
| author | Brian Bunker <brian@purestorage.com> | 2026-10-30 10:57:20 -0700 |
|---|---|---|
| committer | Martin K. Petersen (Oracle) <mkp@kernel.org> | 2026-11-08 22:15:13 -0500 |
| commit | dc7ba60d45b105b91d4e2e9567b355fb4a043def (patch) | |
| tree | 829b83e3020eaa43985da73b65903e64381cf199 /scripts/Makefile.thinlto | |
| parent | 0ba0ffc7748d83c6f8792fea31b569042a12f1b2 (diff) | |
| download | linux-next-dc7ba60d45b105b91d4e2e9567b355fb4a043def.tar.gz linux-next-dc7ba60d45b105b91d4e2e9567b355fb4a043def.zip | |
scsi: core: Restart the queue when unblocking a device set running
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@purestorage.com>
Link: https://patch.msgid.link/20261030175721.24379-2-brian@purestorage.com
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
Diffstat (limited to 'scripts/Makefile.thinlto')
0 files changed, 0 insertions, 0 deletions
