[RFC] virtio_scsi: to poll and kick the virtqueue in timeout handler

From: Dongli Zhang
Date: Sun May 23 2021 - 02:38:26 EST


This RFC is to trigger the discussion about to poll and kick the
virtqueue on purpose in virtio-scsi timeout handler.

The virtio-scsi relies on the virtio vring shared between VM and host.
The VM side produces requests to vring and kicks the virtqueue, while the
host side produces responses to vring and interrupts the VM side.

By default the virtio-scsi handler depends on the host timeout handler
by BLK_EH_RESET_TIMER to give host a chance to perform EH.

However, this is not helpful for the case that the responses are available
on vring but the notification from host to VM is lost.


Would you mind share your feedback on this idea to poll the vring on
purpose in timeout handler to give VM a chance to recover from stalled
IO? In addition, how about to kick for an extra time, in case the
stalled IO is due to the loss of VM-to-host notification?


I have tested the IO can be recovered after interrupt is lost on purpose
with below debug patch.

https://github.com/finallyjustice/patchset/blob/master/scsi-virtio_scsi-to-lose-an-interrupt-on-purpose.patch


In addition, the virtio-blk may implement the timeout handler as well,
with the helper function in below patchset from Stefan.

https://lore.kernel.org/linux-block/20210520141305.355961-1-stefanha@xxxxxxxxxx/T/#t


Thank you very much!

Dongli Zhang

--------

Considering there might be loss of interrupt or kick issue, the timeout
handler may poll or kick the virtqueue on purpose, in order to recover the
IO.

If the response is already available on vring, it indicates the host side
has already processed the request and it is not helpful by giving host a
chance to perform EH.

There will be syslog like below by the timeout handler:

[ 135.830746] virtio_scsi: Virtio SCSI HBA 0: I/O 196 QID 3 timeout, completion polled

Signed-off-by: Dongli Zhang <dongli.zhang@xxxxxxxxxx>
---
drivers/scsi/virtio_scsi.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)

diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index b9c86a7e3b97..633950b6336a 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -36,6 +36,9 @@
#define VIRTIO_SCSI_EVENT_LEN 8
#define VIRTIO_SCSI_VQ_BASE 2

+static bool __read_mostly timed_out_enabled;
+module_param(timed_out_enabled, bool, 0644);
+
/* Command queue element */
struct virtio_scsi_cmd {
struct scsi_cmnd *sc;
@@ -732,9 +735,38 @@ static void virtscsi_commit_rqs(struct Scsi_Host *shost, u16 hwq)
* The host guarantees to respond to each command, although I/O
* latencies might be higher than on bare metal. Reset the timer
* unconditionally to give the host a chance to perform EH.
+ *
+ * If 'timed_out_enabled' is set, the timeout handler polls or kicks the
+ * virtqueue on purpose, in order to recover the IO, in case there is loss
+ * of interrupt or kick issue with virtio.
*/
static enum blk_eh_timer_return virtscsi_eh_timed_out(struct scsi_cmnd *scmnd)
{
+ struct Scsi_Host *shost;
+ struct virtio_scsi *vscsi;
+ struct virtio_scsi_vq *req_vq;
+
+ if (!timed_out_enabled)
+ return BLK_EH_RESET_TIMER;
+
+ shost = scmnd->device->host;
+ vscsi = shost_priv(shost);
+ req_vq = virtscsi_pick_vq_mq(vscsi, scmnd);
+
+ virtscsi_vq_done(vscsi, req_vq, virtscsi_complete_cmd);
+
+ if (test_bit(SCMD_STATE_COMPLETE, &scmnd->state)) {
+ pr_warn("Virtio SCSI HBA %u: I/O %u QID %d timeout, completion polled\n",
+ shost->host_no, scmnd->tag, req_vq->vq->index);
+ return BLK_EH_DONE;
+ }
+
+ /*
+ * To kick the virtqueue in case the timeout is due to the loss of
+ * one prior notification.
+ */
+ virtqueue_notify(req_vq->vq);
+
return BLK_EH_RESET_TIMER;
}

--
2.17.1