X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/041ccc922ee474693a2869d4e3b59e920c739bc0..947231ad3b479de82d8f5ec185e2d00f3c96edcd:/hw/scsi/virtio-scsi-dataplane.c diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 418d73b1b4..944ea4eb53 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -11,146 +11,100 @@ * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/virtio/virtio-scsi.h" #include "qemu/error-report.h" #include "sysemu/block-backend.h" -#include -#include -#include +#include "hw/scsi/scsi.h" +#include "block/scsi.h" +#include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" -#include "stdio.h" /* Context: QEMU global mutex held */ -void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread) +void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) { - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - - assert(!s->ctx); - s->ctx = iothread_get_aio_context(vs->conf.iothread); - - /* Don't try if transport does not support notifiers. */ - if (!k->set_guest_notifiers || !k->set_host_notifier) { - fprintf(stderr, "virtio-scsi: Failed to set iothread " - "(transport does not support notifiers)"); - exit(1); - } -} - -static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, - VirtQueue *vq, - EventNotifierHandler *handler, - int n) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); + VirtIODevice *vdev = VIRTIO_DEVICE(s); + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOSCSIVring *r = g_slice_new(VirtIOSCSIVring); - int rc; - /* Set up virtqueue notify */ - rc = k->set_host_notifier(qbus->parent, n, true); - if (rc != 0) { - fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", - rc); - s->dataplane_fenced = true; - return NULL; - } - r->host_notifier = *virtio_queue_get_host_notifier(vq); - r->guest_notifier = *virtio_queue_get_guest_notifier(vq); - aio_set_event_notifier(s->ctx, &r->host_notifier, handler); - - r->parent = s; - - if (!vring_setup(&r->vring, VIRTIO_DEVICE(s), n)) { - fprintf(stderr, "virtio-scsi: VRing setup failed\n"); - goto fail_vring; - } - return r; - -fail_vring: - aio_set_event_notifier(s->ctx, &r->host_notifier, NULL); - k->set_host_notifier(qbus->parent, n, false); - g_slice_free(VirtIOSCSIVring, r); - return NULL; -} - -VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s, - VirtIOSCSIVring *vring) -{ - VirtIOSCSIReq *req = virtio_scsi_init_req(s, NULL); - int r; - - req->vring = vring; - r = vring_pop((VirtIODevice *)s, &vring->vring, &req->elem); - if (r < 0) { - virtio_scsi_free_req(req); - req = NULL; + if (vs->conf.iothread) { + if (!k->set_guest_notifiers || !k->ioeventfd_assign) { + error_setg(errp, + "device is incompatible with iothread " + "(transport does not support notifiers)"); + return; + } + if (!virtio_device_ioeventfd_enabled(vdev)) { + error_setg(errp, "ioeventfd is required for iothread"); + return; + } + s->ctx = iothread_get_aio_context(vs->conf.iothread); + } else { + if (!virtio_device_ioeventfd_enabled(vdev)) { + return; + } + s->ctx = qemu_get_aio_context(); } - return req; } -void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req) +static bool virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev, + VirtQueue *vq) { - VirtIODevice *vdev = VIRTIO_DEVICE(req->vring->parent); - - vring_push(vdev, &req->vring->vring, &req->elem, - req->qsgl.size + req->resp_iov.size); - - if (vring_should_notify(vdev, &req->vring->vring)) { - event_notifier_set(&req->vring->guest_notifier); - } + bool progress; + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + + virtio_scsi_acquire(s); + assert(s->ctx && s->dataplane_started); + progress = virtio_scsi_handle_cmd_vq(s, vq); + virtio_scsi_release(s); + return progress; } -static void virtio_scsi_iothread_handle_ctrl(EventNotifier *notifier) +static bool virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev, + VirtQueue *vq) { - VirtIOSCSIVring *vring = container_of(notifier, - VirtIOSCSIVring, host_notifier); - VirtIOSCSI *s = VIRTIO_SCSI(vring->parent); - VirtIOSCSIReq *req; - - event_notifier_test_and_clear(notifier); - while ((req = virtio_scsi_pop_req_vring(s, vring))) { - virtio_scsi_handle_ctrl_req(s, req); - } + bool progress; + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + + virtio_scsi_acquire(s); + assert(s->ctx && s->dataplane_started); + progress = virtio_scsi_handle_ctrl_vq(s, vq); + virtio_scsi_release(s); + return progress; } -static void virtio_scsi_iothread_handle_event(EventNotifier *notifier) +static bool virtio_scsi_data_plane_handle_event(VirtIODevice *vdev, + VirtQueue *vq) { - VirtIOSCSIVring *vring = container_of(notifier, - VirtIOSCSIVring, host_notifier); - VirtIOSCSI *s = vring->parent; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - - event_notifier_test_and_clear(notifier); - - if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return; - } - - if (s->events_dropped) { - virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); - } + bool progress; + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + + virtio_scsi_acquire(s); + assert(s->ctx && s->dataplane_started); + progress = virtio_scsi_handle_event_vq(s, vq); + virtio_scsi_release(s); + return progress; } -static void virtio_scsi_iothread_handle_cmd(EventNotifier *notifier) +static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, + VirtIOHandleAIOOutput fn) { - VirtIOSCSIVring *vring = container_of(notifier, - VirtIOSCSIVring, host_notifier); - VirtIOSCSI *s = (VirtIOSCSI *)vring->parent; - VirtIOSCSIReq *req, *next; - QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); + int rc; - event_notifier_test_and_clear(notifier); - while ((req = virtio_scsi_pop_req_vring(s, vring))) { - if (virtio_scsi_handle_cmd_req_prepare(s, req)) { - QTAILQ_INSERT_TAIL(&reqs, req, next); - } + /* Set up virtqueue notify */ + rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true); + if (rc != 0) { + fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", + rc); + s->dataplane_fenced = true; + return rc; } - QTAILQ_FOREACH_SAFE(req, &reqs, next, next) { - virtio_scsi_handle_cmd_req_submit(s, req); - } + virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn); + return 0; } /* assumes s->ctx held */ @@ -159,89 +113,54 @@ static void virtio_scsi_clear_aio(VirtIOSCSI *s) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); int i; - if (s->ctrl_vring) { - aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL); - } - if (s->event_vring) { - aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL); - } - if (s->cmd_vrings) { - for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { - aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL); - } - } -} - -static void virtio_scsi_vring_teardown(VirtIOSCSI *s) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(s); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - int i; - - if (s->ctrl_vring) { - vring_teardown(&s->ctrl_vring->vring, vdev, 0); - } - if (s->event_vring) { - vring_teardown(&s->event_vring->vring, vdev, 1); - } - if (s->cmd_vrings) { - for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { - vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i); - } - free(s->cmd_vrings); - s->cmd_vrings = NULL; + virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL); + virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL); + for (i = 0; i < vs->conf.num_queues; i++) { + virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL); } } /* Context: QEMU global mutex held */ -void virtio_scsi_dataplane_start(VirtIOSCSI *s) +int virtio_scsi_dataplane_start(VirtIODevice *vdev) { int i; int rc; - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); + VirtIOSCSI *s = VIRTIO_SCSI(vdev); if (s->dataplane_started || s->dataplane_starting || - s->dataplane_fenced || - s->ctx != iothread_get_aio_context(vs->conf.iothread)) { - return; + s->dataplane_fenced) { + return 0; } s->dataplane_starting = true; - assert(!s->blocker); - error_setg(&s->blocker, "block device is in use by data plane"); /* Set up guest notifier (irq) */ rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); if (rc != 0) { fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " "ensure -enable-kvm is set\n", rc); - s->dataplane_fenced = true; goto fail_guest_notifiers; } aio_context_acquire(s->ctx); - s->ctrl_vring = virtio_scsi_vring_init(s, vs->ctrl_vq, - virtio_scsi_iothread_handle_ctrl, - 0); - if (!s->ctrl_vring) { + rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0, + virtio_scsi_data_plane_handle_ctrl); + if (rc) { goto fail_vrings; } - s->event_vring = virtio_scsi_vring_init(s, vs->event_vq, - virtio_scsi_iothread_handle_event, - 1); - if (!s->event_vring) { + rc = virtio_scsi_vring_init(s, vs->event_vq, 1, + virtio_scsi_data_plane_handle_event); + if (rc) { goto fail_vrings; } - s->cmd_vrings = g_new(VirtIOSCSIVring *, vs->conf.num_queues); for (i = 0; i < vs->conf.num_queues; i++) { - s->cmd_vrings[i] = - virtio_scsi_vring_init(s, vs->cmd_vqs[i], - virtio_scsi_iothread_handle_cmd, - i + 2); - if (!s->cmd_vrings[i]) { + rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2, + virtio_scsi_data_plane_handle_cmd); + if (rc) { goto fail_vrings; } } @@ -249,60 +168,51 @@ void virtio_scsi_dataplane_start(VirtIOSCSI *s) s->dataplane_starting = false; s->dataplane_started = true; aio_context_release(s->ctx); - return; + return 0; fail_vrings: virtio_scsi_clear_aio(s); aio_context_release(s->ctx); - virtio_scsi_vring_teardown(s); for (i = 0; i < vs->conf.num_queues + 2; i++) { - k->set_host_notifier(qbus->parent, i, false); + virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); fail_guest_notifiers: + s->dataplane_fenced = true; s->dataplane_starting = false; + s->dataplane_started = true; + return -ENOSYS; } /* Context: QEMU global mutex held */ -void virtio_scsi_dataplane_stop(VirtIOSCSI *s) +void virtio_scsi_dataplane_stop(VirtIODevice *vdev) { - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); + VirtIOSCSI *s = VIRTIO_SCSI(vdev); int i; + if (!s->dataplane_started || s->dataplane_stopping) { + return; + } + /* Better luck next time. */ if (s->dataplane_fenced) { s->dataplane_fenced = false; + s->dataplane_started = false; return; } - if (!s->dataplane_started || s->dataplane_stopping) { - return; - } - error_free(s->blocker); - s->blocker = NULL; s->dataplane_stopping = true; - assert(s->ctx == iothread_get_aio_context(vs->conf.iothread)); aio_context_acquire(s->ctx); - - aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL); - aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL); - for (i = 0; i < vs->conf.num_queues; i++) { - aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL); - } - - blk_drain_all(); /* ensure there are no in-flight requests */ - + virtio_scsi_clear_aio(s); aio_context_release(s->ctx); - /* Sync vring state back to virtqueue so that non-dataplane request - * processing can continue when we disable the host notifier below. - */ - virtio_scsi_vring_teardown(s); + blk_drain_all(); /* ensure there are no in-flight requests */ for (i = 0; i < vs->conf.num_queues + 2; i++) { - k->set_host_notifier(qbus->parent, i, false); + virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } /* Clean up guest notifier (irq) */