Re: [PATCH v4 18/20] virtio-scsi: implement BlockDevOps->drained_begin()

2023-05-04 Thread Kevin Wolf
Am 25.04.2023 um 19:27 hat Stefan Hajnoczi geschrieben:
> The virtio-scsi Host Bus Adapter provides access to devices on a SCSI
> bus. Those SCSI devices typically have a BlockBackend. When the
> BlockBackend enters a drained section, the SCSI device must temporarily
> stop submitting new I/O requests.
> 
> Implement this behavior by temporarily stopping virtio-scsi virtqueue
> processing when one of the SCSI devices enters a drained section. The
> new scsi_device_drained_begin() API allows scsi-disk to message the
> virtio-scsi HBA.
> 
> scsi_device_drained_begin() uses a drain counter so that multiple SCSI
> devices can have overlapping drained sections. The HBA only sees one
> pair of .drained_begin/end() calls.
> 
> After this commit, virtio-scsi no longer depends on hw/virtio's
> ioeventfd aio_set_event_notifier(is_external=true). This commit is a
> step towards removing the aio_disable_external() API.
> 
> Signed-off-by: Stefan Hajnoczi 

> @@ -206,9 +208,11 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev)
>  }
>  s->dataplane_stopping = true;
>  
> -aio_context_acquire(s->ctx);
> -aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
> -aio_context_release(s->ctx);
> +if (s->bus.drain_count == 0) {
> +aio_context_acquire(s->ctx);
> +aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
> +aio_context_release(s->ctx);
> +}

Same question as for virtio-blk: We lose processing the virtqueue one
last time during drain. Is it okay, and if so, why do we need it outside
of drain?

Kevin




[PATCH v4 18/20] virtio-scsi: implement BlockDevOps->drained_begin()

2023-04-25 Thread Stefan Hajnoczi
The virtio-scsi Host Bus Adapter provides access to devices on a SCSI
bus. Those SCSI devices typically have a BlockBackend. When the
BlockBackend enters a drained section, the SCSI device must temporarily
stop submitting new I/O requests.

Implement this behavior by temporarily stopping virtio-scsi virtqueue
processing when one of the SCSI devices enters a drained section. The
new scsi_device_drained_begin() API allows scsi-disk to message the
virtio-scsi HBA.

scsi_device_drained_begin() uses a drain counter so that multiple SCSI
devices can have overlapping drained sections. The HBA only sees one
pair of .drained_begin/end() calls.

After this commit, virtio-scsi no longer depends on hw/virtio's
ioeventfd aio_set_event_notifier(is_external=true). This commit is a
step towards removing the aio_disable_external() API.

Signed-off-by: Stefan Hajnoczi 
---
 include/hw/scsi/scsi.h  | 14 
 hw/scsi/scsi-bus.c  | 40 +
 hw/scsi/scsi-disk.c | 27 +-
 hw/scsi/virtio-scsi-dataplane.c | 22 ++
 hw/scsi/virtio-scsi.c   | 38 +++
 hw/scsi/trace-events|  2 ++
 6 files changed, 129 insertions(+), 14 deletions(-)

diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index 6f23a7a73e..e2bb1a2fbf 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -133,6 +133,16 @@ struct SCSIBusInfo {
 void (*save_request)(QEMUFile *f, SCSIRequest *req);
 void *(*load_request)(QEMUFile *f, SCSIRequest *req);
 void (*free_request)(SCSIBus *bus, void *priv);
+
+/*
+ * Temporarily stop submitting new requests between drained_begin() and
+ * drained_end(). Called from the main loop thread with the BQL held.
+ *
+ * Implement these callbacks if request processing is triggered by a file
+ * descriptor like an EventNotifier. Otherwise set them to NULL.
+ */
+void (*drained_begin)(SCSIBus *bus);
+void (*drained_end)(SCSIBus *bus);
 };
 
 #define TYPE_SCSI_BUS "SCSI"
@@ -144,6 +154,8 @@ struct SCSIBus {
 
 SCSISense unit_attention;
 const SCSIBusInfo *info;
+
+int drain_count; /* protected by BQL */
 };
 
 /**
@@ -213,6 +225,8 @@ void scsi_req_cancel_complete(SCSIRequest *req);
 void scsi_req_cancel(SCSIRequest *req);
 void scsi_req_cancel_async(SCSIRequest *req, Notifier *notifier);
 void scsi_req_retry(SCSIRequest *req);
+void scsi_device_drained_begin(SCSIDevice *sdev);
+void scsi_device_drained_end(SCSIDevice *sdev);
 void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense);
 void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense);
 void scsi_device_report_change(SCSIDevice *dev, SCSISense sense);
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 64d7311757..b571fdf895 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -1668,6 +1668,46 @@ void scsi_device_purge_requests(SCSIDevice *sdev, 
SCSISense sense)
 scsi_device_set_ua(sdev, sense);
 }
 
+void scsi_device_drained_begin(SCSIDevice *sdev)
+{
+SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, sdev->qdev.parent_bus);
+if (!bus) {
+return;
+}
+
+assert(qemu_get_current_aio_context() == qemu_get_aio_context());
+assert(bus->drain_count < INT_MAX);
+
+/*
+ * Multiple BlockBackends can be on a SCSIBus and each may begin/end
+ * draining at any time. Keep a counter so HBAs only see begin/end once.
+ */
+if (bus->drain_count++ == 0) {
+trace_scsi_bus_drained_begin(bus, sdev);
+if (bus->info->drained_begin) {
+bus->info->drained_begin(bus);
+}
+}
+}
+
+void scsi_device_drained_end(SCSIDevice *sdev)
+{
+SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, sdev->qdev.parent_bus);
+if (!bus) {
+return;
+}
+
+assert(qemu_get_current_aio_context() == qemu_get_aio_context());
+assert(bus->drain_count > 0);
+
+if (bus->drain_count-- == 1) {
+trace_scsi_bus_drained_end(bus, sdev);
+if (bus->info->drained_end) {
+bus->info->drained_end(bus);
+}
+}
+}
+
 static char *scsibus_get_dev_path(DeviceState *dev)
 {
 SCSIDevice *d = SCSI_DEVICE(dev);
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index e01bd84541..2249087d6a 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -2360,6 +2360,20 @@ static void scsi_disk_reset(DeviceState *dev)
 s->qdev.scsi_version = s->qdev.default_scsi_version;
 }
 
+static void scsi_disk_drained_begin(void *opaque)
+{
+SCSIDiskState *s = opaque;
+
+scsi_device_drained_begin(>qdev);
+}
+
+static void scsi_disk_drained_end(void *opaque)
+{
+SCSIDiskState *s = opaque;
+
+scsi_device_drained_end(>qdev);
+}
+
 static void scsi_disk_resize_cb(void *opaque)
 {
 SCSIDiskState *s = opaque;
@@ -2414,16 +2428,19 @@ static bool scsi_cd_is_medium_locked(void *opaque)
 }
 
 static const BlockDevOps scsi_disk_removable_block_ops = {
-