On 11/2/26 9:06 PM, Stefan Hajnoczi wrote:
On Mon, Sep 14, 2026 at 08:31:44PM +0700, Nguyen Ngoc Thang wrote:
Once virtscsi_eh_timed_out() lets SCSI EH run on an unresponsive host,
abort and device reset both fail the same way (same dead ctrl vq), and
since virtio_scsi implements neither target nor bus reset, EH falls
through to scsi_eh_offline_sdevs(), which frees the stuck commands' DMA
buffers without any guarantee the device has actually stopped touching
them. If bus mastering (or whatever broke the transport) comes back
later, the device can still write into memory the kernel has already
reused.

Add a real eh_host_reset_handler instead. virtio_reset_device() is the
guarantee scsi_eh_offline_sdevs() was missing: once it returns, the
device will not touch guest memory again, so it is safe to hand
outstanding commands' buffers back. Reuse virtscsi_remove_vqs() +
virtscsi_init() -- the exact sequence already used across
suspend/resume -- to tear the virtqueues down and rebuild them.

Verified with a QEMU virtio-scsi repro (PCI_COMMAND_MASTER cleared
mid-write): EH now runs abort -> device reset -> host reset -> abort ->
device reset -> host reset -> offline, then dd's stuck fsync returns
EIO and D-state drains to 0, in ~244s total. No task remains
uninterruptibly blocked.

By the way, there is a known limitation in the QEMU virtio-scsi
implementation where some reset-related operations are implemented
synchronously (can block vCPUs and QEMU activity while waiting for I/O
to drain in some situations) instead of asynchronously. Recently there
has been work in QEMU to make VIRTIO device reset asynchronous, for
example, but it needs to be implemented on a per-device basis and
virtio-scsi hasn't taken advantage of it yet.

Please let me know if you'd like to discuss or contribute. It's
something that is on my radar but not until 2027.


Suggested-by: Hannes Reinecke <[email protected]>
Signed-off-by: Nguyen Ngoc Thang <[email protected]>
---
v1 -> v2: implemented eh_host_reset_handler as suggested by Hannes
Reinecke in review of the original RFC, instead of relying solely on
the EH loop falling through to offline.
---
  drivers/scsi/virtio_scsi.c | 24 ++++++++++++++++++++++++
  1 file changed, 24 insertions(+)

diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index b4f20c487718..9698f4f91cc6 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -833,6 +833,8 @@ static enum scsi_timeout_action 
virtscsi_eh_timed_out(struct scsi_cmnd *scmnd)
        return SCSI_EH_NOT_HANDLED;
  }
+static int virtscsi_host_reset(struct scsi_cmnd *sc);
+
  static const struct scsi_host_template virtscsi_host_template = {
        .module = THIS_MODULE,
        .name = "Virtio SCSI HBA",
@@ -846,6 +848,7 @@ static const struct scsi_host_template 
virtscsi_host_template = {
        .eh_abort_handler = virtscsi_abort,
        .eh_device_reset_handler = virtscsi_device_reset,
        .eh_timed_out = virtscsi_eh_timed_out,
+       .eh_host_reset_handler = virtscsi_host_reset,
        .sdev_init = virtscsi_device_alloc,
.dma_boundary = UINT_MAX,
@@ -947,6 +950,27 @@ static int virtscsi_init(struct virtio_device *vdev,
        return err;
  }
+/*
+ * No bus/target reset in virtio-scsi, so EH lands here after device

The choice to use the VIRTIO device reset in virtscsi_host_reset() looks
good to me, but I would like clarification on this comment. virtio-scsi
does have I_T_NEXUS_RESET and I believe this is the modern equivalent to
the pre-SCSI-6 TARGET RESET. Is it really the case that virtio-scsi
doesn't have target reset? And do you want to go ahead and implement
.eh_target_reset_handler() using I_T_NEXUS_RESET?

Would probably not a bad idea to use I_T_NEXUS_RESET here.
Not sure what it does internally, though; _is_ there a nexus
to reset?

+ * reset also times out. A virtio reset guarantees the device won't
+ * touch guest memory again, so stuck commands can be freed safely.
+ * Same teardown/rebuild already used for suspend/resume.
+ */
+static int virtscsi_host_reset(struct scsi_cmnd *sc)
+{
+       struct virtio_scsi *vscsi = shost_priv(sc->device->host);
+       struct virtio_device *vdev = vscsi->vdev;
+
+       scmd_printk(KERN_INFO, sc, "host reset\n");
+
+       virtscsi_remove_vqs(vdev);

This is synchronous and may take a long time. I noticed that some SCSI
drivers have a timeout that returns FAILED when the low-level host reset
operation takes too long. This can be done here too but would require
adding a timeout to virtio_reset_device() (i.e. see the virtio-pci
implementation that loops until the device register indicates reset is
complete).

Never, ever, return FAILED from a host reset.
That will induce a non-recoverable error, and you have to reset the
OS to get the virtio devices back.
Not something you'd want to do I guess.

Yes, I know that host reset can take a long time. But normally
host reset would be the last resort, so really when you have to
call it your system has exhausted all other options.

Cheers,

Hannes
--
Dr. Hannes Reinecke                  Kernel Storage Architect
[email protected]                                +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich

Reply via email to