From: Stefan Hajnoczi <stefanha@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Coiby Xu" <Coiby.Xu@gmail.com>,
"Marcel Apfelbaum" <marcel.apfelbaum@gmail.com>,
"Michael S. Tsirkin" <mst@redhat.com>,
"Richard W.M. Jones" <rjones@redhat.com>,
"Peter Xu" <peterx@redhat.com>,
xen-devel@lists.xenproject.org, "Kevin Wolf" <kwolf@redhat.com>,
"Ronnie Sahlberg" <ronniesahlberg@gmail.com>,
"Stefano Stabellini" <sstabellini@kernel.org>,
"Richard Henderson" <richard.henderson@linaro.org>,
"Stefan Hajnoczi" <stefanha@redhat.com>,
"Julia Suvorova" <jusual@redhat.com>,
"Hanna Reitz" <hreitz@redhat.com>,
"Leonardo Bras" <leobras@redhat.com>,
eesposit@redhat.com, "Fam Zheng" <fam@euphon.net>,
"Aarushi Mehta" <mehta.aaru20@gmail.com>,
"David Woodhouse" <dwmw2@infradead.org>,
"Xie Yongji" <xieyongji@bytedance.com>,
"Stefano Garzarella" <sgarzare@redhat.com>,
qemu-block@nongnu.org, "Eduardo Habkost" <eduardo@habkost.net>,
"Paul Durrant" <paul@xen.org>, "Stefan Weil" <sw@weilnetz.de>,
"Anthony Perard" <anthony.perard@citrix.com>,
"Daniel P. Berrangé" <berrange@redhat.com>,
"Peter Lieven" <pl@kamp.de>,
"Paolo Bonzini" <pbonzini@redhat.com>,
"Philippe Mathieu-Daudé" <philmd@linaro.org>,
"Juan Quintela" <quintela@redhat.com>
Subject: [PATCH v6 18/20] virtio-scsi: implement BlockDevOps->drained_begin()
Date: Tue, 16 May 2023 15:02:36 -0400 [thread overview]
Message-ID: <20230516190238.8401-19-stefanha@redhat.com> (raw)
In-Reply-To: <20230516190238.8401-1-stefanha@redhat.com>
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 <stefanha@redhat.com>
---
include/hw/scsi/scsi.h | 14 ++++++++++++
hw/scsi/scsi-bus.c | 40 +++++++++++++++++++++++++++++++++
hw/scsi/scsi-disk.c | 27 +++++++++++++++++-----
hw/scsi/virtio-scsi-dataplane.c | 18 +++++++++------
hw/scsi/virtio-scsi.c | 38 +++++++++++++++++++++++++++++++
hw/scsi/trace-events | 2 ++
6 files changed, 127 insertions(+), 12 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 64013c8a24..f80f4cb4fc 100644
--- a/hw/scsi/scsi-bus.c
+++ b/hw/scsi/scsi-bus.c
@@ -1669,6 +1669,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 97c9b1c8cd..e0d79c7966 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(&s->qdev);
+}
+
+static void scsi_disk_drained_end(void *opaque)
+{
+ SCSIDiskState *s = opaque;
+
+ scsi_device_drained_end(&s->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 = {
- .change_media_cb = scsi_cd_change_media_cb,
+ .change_media_cb = scsi_cd_change_media_cb,
+ .drained_begin = scsi_disk_drained_begin,
+ .drained_end = scsi_disk_drained_end,
.eject_request_cb = scsi_cd_eject_request_cb,
- .is_tray_open = scsi_cd_is_tray_open,
.is_medium_locked = scsi_cd_is_medium_locked,
-
- .resize_cb = scsi_disk_resize_cb,
+ .is_tray_open = scsi_cd_is_tray_open,
+ .resize_cb = scsi_disk_resize_cb,
};
static const BlockDevOps scsi_disk_block_ops = {
- .resize_cb = scsi_disk_resize_cb,
+ .drained_begin = scsi_disk_drained_begin,
+ .drained_end = scsi_disk_drained_end,
+ .resize_cb = scsi_disk_resize_cb,
};
static void scsi_disk_unit_attention_reported(SCSIDevice *dev)
diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c
index b3a1ed21f7..d55de4c8ca 100644
--- a/hw/scsi/virtio-scsi-dataplane.c
+++ b/hw/scsi/virtio-scsi-dataplane.c
@@ -158,14 +158,16 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev)
s->dataplane_starting = false;
s->dataplane_started = true;
- aio_context_acquire(s->ctx);
- virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx);
- virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx);
+ if (s->bus.drain_count == 0) {
+ aio_context_acquire(s->ctx);
+ virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx);
+ virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx);
- for (i = 0; i < vs->conf.num_queues; i++) {
- virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx);
+ for (i = 0; i < vs->conf.num_queues; i++) {
+ virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx);
+ }
+ aio_context_release(s->ctx);
}
- aio_context_release(s->ctx);
return 0;
fail_host_notifiers:
@@ -211,7 +213,9 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev)
}
s->dataplane_stopping = true;
- aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
+ if (s->bus.drain_count == 0) {
+ aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
+ }
blk_drain_all(); /* ensure there are no in-flight requests */
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
index c1a7ea9ae2..4a8849cc7e 100644
--- a/hw/scsi/virtio-scsi.c
+++ b/hw/scsi/virtio-scsi.c
@@ -1117,6 +1117,42 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev,
}
}
+/* Suspend virtqueue ioeventfd processing during drain */
+static void virtio_scsi_drained_begin(SCSIBus *bus)
+{
+ VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
+ VirtIODevice *vdev = VIRTIO_DEVICE(s);
+ uint32_t total_queues = VIRTIO_SCSI_VQ_NUM_FIXED +
+ s->parent_obj.conf.num_queues;
+
+ if (!s->dataplane_started) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < total_queues; i++) {
+ VirtQueue *vq = virtio_get_queue(vdev, i);
+ virtio_queue_aio_detach_host_notifier(vq, s->ctx);
+ }
+}
+
+/* Resume virtqueue ioeventfd processing after drain */
+static void virtio_scsi_drained_end(SCSIBus *bus)
+{
+ VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
+ VirtIODevice *vdev = VIRTIO_DEVICE(s);
+ uint32_t total_queues = VIRTIO_SCSI_VQ_NUM_FIXED +
+ s->parent_obj.conf.num_queues;
+
+ if (!s->dataplane_started) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < total_queues; i++) {
+ VirtQueue *vq = virtio_get_queue(vdev, i);
+ virtio_queue_aio_attach_host_notifier(vq, s->ctx);
+ }
+}
+
static struct SCSIBusInfo virtio_scsi_scsi_info = {
.tcq = true,
.max_channel = VIRTIO_SCSI_MAX_CHANNEL,
@@ -1131,6 +1167,8 @@ static struct SCSIBusInfo virtio_scsi_scsi_info = {
.get_sg_list = virtio_scsi_get_sg_list,
.save_request = virtio_scsi_save_request,
.load_request = virtio_scsi_load_request,
+ .drained_begin = virtio_scsi_drained_begin,
+ .drained_end = virtio_scsi_drained_end,
};
void virtio_scsi_common_realize(DeviceState *dev,
diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events
index ab238293f0..bdd4e2c7c7 100644
--- a/hw/scsi/trace-events
+++ b/hw/scsi/trace-events
@@ -6,6 +6,8 @@ scsi_req_cancel(int target, int lun, int tag) "target %d lun %d tag %d"
scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
scsi_req_data_canceled(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d"
+scsi_bus_drained_begin(void *bus, void *sdev) "bus %p sdev %p"
+scsi_bus_drained_end(void *bus, void *sdev) "bus %p sdev %p"
scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d"
scsi_req_continue_canceled(int target, int lun, int tag) "target %d lun %d tag %d"
scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "target %d lun %d tag %d command %d dir %d length %d"
--
2.40.1
next prev parent reply other threads:[~2023-05-16 19:10 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-05-16 19:02 [PATCH v6 00/20] block: remove aio_disable_external() API Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 01/20] block-backend: split blk_do_set_aio_context() Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 02/20] hw/qdev: introduce qdev_is_realized() helper Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 03/20] virtio-scsi: avoid race between unplug and transport event Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 04/20] virtio-scsi: stop using aio_disable_external() during unplug Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 05/20] util/vhost-user-server: rename refcount to in_flight counter Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 06/20] block/export: wait for vhost-user-blk requests when draining Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 07/20] block/export: stop using is_external in vhost-user-blk server Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 08/20] hw/xen: do not use aio_set_fd_handler(is_external=true) in xen_xenstore Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 09/20] block: add blk_in_drain() API Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 10/20] block: drain from main loop thread in bdrv_co_yield_to_drain() Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 11/20] xen-block: implement BlockDevOps->drained_begin() Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 12/20] hw/xen: do not set is_external=true on evtchn fds Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 13/20] block/export: rewrite vduse-blk drain code Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 14/20] block/export: don't require AioContext lock around blk_exp_ref/unref() Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 15/20] block/fuse: do not set is_external=true on FUSE fd Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 16/20] virtio: make it possible to detach host notifier from any thread Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 17/20] virtio-blk: implement BlockDevOps->drained_begin() Stefan Hajnoczi
2023-05-16 19:02 ` Stefan Hajnoczi [this message]
2023-05-16 19:02 ` [PATCH v6 19/20] virtio: do not set is_external=true on host notifiers Stefan Hajnoczi
2023-05-16 19:02 ` [PATCH v6 20/20] aio: remove aio_disable_external() API Stefan Hajnoczi
2023-05-30 16:24 ` [PATCH v6 00/20] block: " Kevin Wolf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230516190238.8401-19-stefanha@redhat.com \
--to=stefanha@redhat.com \
--cc=Coiby.Xu@gmail.com \
--cc=anthony.perard@citrix.com \
--cc=berrange@redhat.com \
--cc=dwmw2@infradead.org \
--cc=eduardo@habkost.net \
--cc=eesposit@redhat.com \
--cc=fam@euphon.net \
--cc=hreitz@redhat.com \
--cc=jusual@redhat.com \
--cc=kwolf@redhat.com \
--cc=leobras@redhat.com \
--cc=marcel.apfelbaum@gmail.com \
--cc=mehta.aaru20@gmail.com \
--cc=mst@redhat.com \
--cc=paul@xen.org \
--cc=pbonzini@redhat.com \
--cc=peterx@redhat.com \
--cc=philmd@linaro.org \
--cc=pl@kamp.de \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.com \
--cc=richard.henderson@linaro.org \
--cc=rjones@redhat.com \
--cc=ronniesahlberg@gmail.com \
--cc=sgarzare@redhat.com \
--cc=sstabellini@kernel.org \
--cc=sw@weilnetz.de \
--cc=xen-devel@lists.xenproject.org \
--cc=xieyongji@bytedance.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).