aboutsummaryrefslogtreecommitdiff
path: root/hw/scsi/scsi-bus.c
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2023-05-16 15:02:36 -0400
committerKevin Wolf <kwolf@redhat.com>2023-05-30 17:32:02 +0200
commit766aa2de0f29b657148e04599320d771c36fd126 (patch)
tree911117223771a1574c5a17b938f9beb840aac943 /hw/scsi/scsi-bus.c
parent1665d9326fd2dd97f1f4061decd67702956ec53c (diff)
downloadqemu-766aa2de0f29b657148e04599320d771c36fd126.zip
qemu-766aa2de0f29b657148e04599320d771c36fd126.tar.gz
qemu-766aa2de0f29b657148e04599320d771c36fd126.tar.bz2
virtio-scsi: implement BlockDevOps->drained_begin()
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> Message-Id: <20230516190238.8401-19-stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'hw/scsi/scsi-bus.c')
-rw-r--r--hw/scsi/scsi-bus.c40
1 files changed, 40 insertions, 0 deletions
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
index 64013c8..f80f4cb 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);