xref: /openbmc/qemu/hw/scsi/virtio-scsi-dataplane.c (revision 9468484fe904ab4691de6d9c34616667f377ceac)
191cb1c9bSFam Zheng /*
291cb1c9bSFam Zheng  * Virtio SCSI dataplane
391cb1c9bSFam Zheng  *
491cb1c9bSFam Zheng  * Copyright Red Hat, Inc. 2014
591cb1c9bSFam Zheng  *
691cb1c9bSFam Zheng  * Authors:
791cb1c9bSFam Zheng  *   Fam Zheng <famz@redhat.com>
891cb1c9bSFam Zheng  *
991cb1c9bSFam Zheng  * This work is licensed under the terms of the GNU GPL, version 2 or later.
1091cb1c9bSFam Zheng  * See the COPYING file in the top-level directory.
1191cb1c9bSFam Zheng  *
1291cb1c9bSFam Zheng  */
1391cb1c9bSFam Zheng 
149b8bfe21SPeter Maydell #include "qemu/osdep.h"
15ad07cd69SPaolo Bonzini #include "qapi/error.h"
1691cb1c9bSFam Zheng #include "hw/virtio/virtio-scsi.h"
1791cb1c9bSFam Zheng #include "qemu/error-report.h"
184be74634SMarkus Armbruster #include "sysemu/block-backend.h"
19a9c94277SMarkus Armbruster #include "hw/scsi/scsi.h"
2008e2c9f1SPaolo Bonzini #include "scsi/constants.h"
21a9c94277SMarkus Armbruster #include "hw/virtio/virtio-bus.h"
2291cb1c9bSFam Zheng 
23*0b2675c4SStefan Hajnoczi /* Context: BQL held */
virtio_scsi_dataplane_setup(VirtIOSCSI * s,Error ** errp)24ad07cd69SPaolo Bonzini void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
2591cb1c9bSFam Zheng {
2691cb1c9bSFam Zheng     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
27ad07cd69SPaolo Bonzini     VirtIODevice *vdev = VIRTIO_DEVICE(s);
28ad07cd69SPaolo Bonzini     BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
29ad07cd69SPaolo Bonzini     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
3091cb1c9bSFam Zheng 
31ad07cd69SPaolo Bonzini     if (vs->conf.iothread) {
32b13d3962SPaolo Bonzini         if (!k->set_guest_notifiers || !k->ioeventfd_assign) {
33ad07cd69SPaolo Bonzini             error_setg(errp,
34ad07cd69SPaolo Bonzini                        "device is incompatible with iothread "
3591cb1c9bSFam Zheng                        "(transport does not support notifiers)");
36ad07cd69SPaolo Bonzini             return;
37ad07cd69SPaolo Bonzini         }
38ad07cd69SPaolo Bonzini         if (!virtio_device_ioeventfd_enabled(vdev)) {
39ad07cd69SPaolo Bonzini             error_setg(errp, "ioeventfd is required for iothread");
40ad07cd69SPaolo Bonzini             return;
41ad07cd69SPaolo Bonzini         }
42ad07cd69SPaolo Bonzini         s->ctx = iothread_get_aio_context(vs->conf.iothread);
43ad07cd69SPaolo Bonzini     } else {
44ad07cd69SPaolo Bonzini         if (!virtio_device_ioeventfd_enabled(vdev)) {
45ad07cd69SPaolo Bonzini             return;
46ad07cd69SPaolo Bonzini         }
47ad07cd69SPaolo Bonzini         s->ctx = qemu_get_aio_context();
4891cb1c9bSFam Zheng     }
4991cb1c9bSFam Zheng }
5091cb1c9bSFam Zheng 
virtio_scsi_set_host_notifier(VirtIOSCSI * s,VirtQueue * vq,int n)5161fc57bfSGreg Kurz static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n)
5291cb1c9bSFam Zheng {
5391cb1c9bSFam Zheng     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
546d2c8316SCornelia Huck     int rc;
5591cb1c9bSFam Zheng 
5691cb1c9bSFam Zheng     /* Set up virtqueue notify */
57b1f0a33dSCornelia Huck     rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true);
586d2c8316SCornelia Huck     if (rc != 0) {
596d2c8316SCornelia Huck         fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n",
606d2c8316SCornelia Huck                 rc);
614adea804SCornelia Huck         s->dataplane_fenced = true;
62e24a47c5SPaolo Bonzini         return rc;
6391cb1c9bSFam Zheng     }
64196d4fc5SBo Su 
65e24a47c5SPaolo Bonzini     return 0;
6691cb1c9bSFam Zheng }
6791cb1c9bSFam Zheng 
68184b9623SStefan Hajnoczi /* Context: BH in IOThread */
virtio_scsi_dataplane_stop_bh(void * opaque)69184b9623SStefan Hajnoczi static void virtio_scsi_dataplane_stop_bh(void *opaque)
70361dcc79SCornelia Huck {
71184b9623SStefan Hajnoczi     VirtIOSCSI *s = opaque;
72361dcc79SCornelia Huck     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
73bd58ab40SStefan Hajnoczi     EventNotifier *host_notifier;
74361dcc79SCornelia Huck     int i;
75361dcc79SCornelia Huck 
76db608fb7SStefan Hajnoczi     virtio_queue_aio_detach_host_notifier(vs->ctrl_vq, s->ctx);
77bd58ab40SStefan Hajnoczi     host_notifier = virtio_queue_get_host_notifier(vs->ctrl_vq);
78bd58ab40SStefan Hajnoczi 
79bd58ab40SStefan Hajnoczi     /*
80bd58ab40SStefan Hajnoczi      * Test and clear notifier after disabling event, in case poll callback
81bd58ab40SStefan Hajnoczi      * didn't have time to run.
82bd58ab40SStefan Hajnoczi      */
83bd58ab40SStefan Hajnoczi     virtio_queue_host_notifier_read(host_notifier);
84bd58ab40SStefan Hajnoczi 
85db608fb7SStefan Hajnoczi     virtio_queue_aio_detach_host_notifier(vs->event_vq, s->ctx);
86bd58ab40SStefan Hajnoczi     host_notifier = virtio_queue_get_host_notifier(vs->event_vq);
87bd58ab40SStefan Hajnoczi     virtio_queue_host_notifier_read(host_notifier);
88bd58ab40SStefan Hajnoczi 
89e24a47c5SPaolo Bonzini     for (i = 0; i < vs->conf.num_queues; i++) {
90db608fb7SStefan Hajnoczi         virtio_queue_aio_detach_host_notifier(vs->cmd_vqs[i], s->ctx);
91bd58ab40SStefan Hajnoczi         host_notifier = virtio_queue_get_host_notifier(vs->cmd_vqs[i]);
92bd58ab40SStefan Hajnoczi         virtio_queue_host_notifier_read(host_notifier);
93361dcc79SCornelia Huck     }
94361dcc79SCornelia Huck }
95361dcc79SCornelia Huck 
96*0b2675c4SStefan Hajnoczi /* Context: BQL held */
virtio_scsi_dataplane_start(VirtIODevice * vdev)97ad07cd69SPaolo Bonzini int virtio_scsi_dataplane_start(VirtIODevice *vdev)
9891cb1c9bSFam Zheng {
9991cb1c9bSFam Zheng     int i;
10091cb1c9bSFam Zheng     int rc;
101dec2bb14SMaxim Levitsky     int vq_init_count = 0;
102ad07cd69SPaolo Bonzini     BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
10391cb1c9bSFam Zheng     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
104ad07cd69SPaolo Bonzini     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
105ad07cd69SPaolo Bonzini     VirtIOSCSI *s = VIRTIO_SCSI(vdev);
10691cb1c9bSFam Zheng 
10791cb1c9bSFam Zheng     if (s->dataplane_started ||
10891cb1c9bSFam Zheng         s->dataplane_starting ||
109ad07cd69SPaolo Bonzini         s->dataplane_fenced) {
110ad07cd69SPaolo Bonzini         return 0;
11191cb1c9bSFam Zheng     }
11291cb1c9bSFam Zheng 
11391cb1c9bSFam Zheng     s->dataplane_starting = true;
11491cb1c9bSFam Zheng 
11591cb1c9bSFam Zheng     /* Set up guest notifier (irq) */
11691cb1c9bSFam Zheng     rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true);
11791cb1c9bSFam Zheng     if (rc != 0) {
118a1d30f28SThomas Huth         error_report("virtio-scsi: Failed to set guest notifiers (%d), "
119a1d30f28SThomas Huth                      "ensure -accel kvm is set.", rc);
120361dcc79SCornelia Huck         goto fail_guest_notifiers;
12191cb1c9bSFam Zheng     }
12291cb1c9bSFam Zheng 
1239cf4fd87SGreg Kurz     /*
1249cf4fd87SGreg Kurz      * Batch all the host notifiers in a single transaction to avoid
1259cf4fd87SGreg Kurz      * quadratic time complexity in address_space_update_ioeventfds().
1269cf4fd87SGreg Kurz      */
127c4f5dcc4SGreg Kurz     memory_region_transaction_begin();
128c4f5dcc4SGreg Kurz 
12961fc57bfSGreg Kurz     rc = virtio_scsi_set_host_notifier(s, vs->ctrl_vq, 0);
13061fc57bfSGreg Kurz     if (rc != 0) {
13161fc57bfSGreg Kurz         goto fail_host_notifiers;
132361dcc79SCornelia Huck     }
133dec2bb14SMaxim Levitsky 
134dec2bb14SMaxim Levitsky     vq_init_count++;
13561fc57bfSGreg Kurz     rc = virtio_scsi_set_host_notifier(s, vs->event_vq, 1);
13661fc57bfSGreg Kurz     if (rc != 0) {
13761fc57bfSGreg Kurz         goto fail_host_notifiers;
138361dcc79SCornelia Huck     }
139dec2bb14SMaxim Levitsky 
140dec2bb14SMaxim Levitsky     vq_init_count++;
14161fc57bfSGreg Kurz 
14291cb1c9bSFam Zheng     for (i = 0; i < vs->conf.num_queues; i++) {
14361fc57bfSGreg Kurz         rc = virtio_scsi_set_host_notifier(s, vs->cmd_vqs[i], i + 2);
144e24a47c5SPaolo Bonzini         if (rc) {
14561fc57bfSGreg Kurz             goto fail_host_notifiers;
146361dcc79SCornelia Huck         }
147dec2bb14SMaxim Levitsky         vq_init_count++;
14891cb1c9bSFam Zheng     }
14991cb1c9bSFam Zheng 
150c4f5dcc4SGreg Kurz     memory_region_transaction_commit();
151c4f5dcc4SGreg Kurz 
1529a4b6a63SStefan Hajnoczi     s->dataplane_starting = false;
1539a4b6a63SStefan Hajnoczi     s->dataplane_started = true;
154765ca516SStefan Hajnoczi     smp_wmb(); /* paired with aio_notify_accept() */
1559a4b6a63SStefan Hajnoczi 
156766aa2deSStefan Hajnoczi     if (s->bus.drain_count == 0) {
157db608fb7SStefan Hajnoczi         virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx);
15838738f7dSStefan Hajnoczi         virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx);
15961fc57bfSGreg Kurz 
16061fc57bfSGreg Kurz         for (i = 0; i < vs->conf.num_queues; i++) {
161db608fb7SStefan Hajnoczi             virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx);
16261fc57bfSGreg Kurz         }
163766aa2deSStefan Hajnoczi     }
164ad07cd69SPaolo Bonzini     return 0;
165361dcc79SCornelia Huck 
16661fc57bfSGreg Kurz fail_host_notifiers:
167dec2bb14SMaxim Levitsky     for (i = 0; i < vq_init_count; i++) {
16821a4d962SCornelia Huck         virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
169c4f5dcc4SGreg Kurz     }
170c4f5dcc4SGreg Kurz 
1719cf4fd87SGreg Kurz     /*
1729cf4fd87SGreg Kurz      * The transaction expects the ioeventfds to be open when it
1739cf4fd87SGreg Kurz      * commits. Do it now, before the cleanup loop.
1749cf4fd87SGreg Kurz      */
175c4f5dcc4SGreg Kurz     memory_region_transaction_commit();
176c4f5dcc4SGreg Kurz 
177c4f5dcc4SGreg Kurz     for (i = 0; i < vq_init_count; i++) {
17876143618SGal Hammer         virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
179b1f0a33dSCornelia Huck     }
180361dcc79SCornelia Huck     k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
181361dcc79SCornelia Huck fail_guest_notifiers:
182e24a47c5SPaolo Bonzini     s->dataplane_fenced = true;
183361dcc79SCornelia Huck     s->dataplane_starting = false;
184e24a47c5SPaolo Bonzini     s->dataplane_started = true;
185ad07cd69SPaolo Bonzini     return -ENOSYS;
18691cb1c9bSFam Zheng }
18791cb1c9bSFam Zheng 
188*0b2675c4SStefan Hajnoczi /* Context: BQL held */
virtio_scsi_dataplane_stop(VirtIODevice * vdev)189ad07cd69SPaolo Bonzini void virtio_scsi_dataplane_stop(VirtIODevice *vdev)
19091cb1c9bSFam Zheng {
191ad07cd69SPaolo Bonzini     BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
19291cb1c9bSFam Zheng     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
193ad07cd69SPaolo Bonzini     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
194ad07cd69SPaolo Bonzini     VirtIOSCSI *s = VIRTIO_SCSI(vdev);
19521a4d962SCornelia Huck     int i;
19691cb1c9bSFam Zheng 
197e24a47c5SPaolo Bonzini     if (!s->dataplane_started || s->dataplane_stopping) {
198e24a47c5SPaolo Bonzini         return;
199e24a47c5SPaolo Bonzini     }
200e24a47c5SPaolo Bonzini 
2014adea804SCornelia Huck     /* Better luck next time. */
2024adea804SCornelia Huck     if (s->dataplane_fenced) {
2034adea804SCornelia Huck         s->dataplane_fenced = false;
204e24a47c5SPaolo Bonzini         s->dataplane_started = false;
20591cb1c9bSFam Zheng         return;
20691cb1c9bSFam Zheng     }
20791cb1c9bSFam Zheng     s->dataplane_stopping = true;
20891cb1c9bSFam Zheng 
209766aa2deSStefan Hajnoczi     if (s->bus.drain_count == 0) {
210184b9623SStefan Hajnoczi         aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
211766aa2deSStefan Hajnoczi     }
21291cb1c9bSFam Zheng 
2134be74634SMarkus Armbruster     blk_drain_all(); /* ensure there are no in-flight requests */
21491cb1c9bSFam Zheng 
2159cf4fd87SGreg Kurz     /*
2169cf4fd87SGreg Kurz      * Batch all the host notifiers in a single transaction to avoid
2179cf4fd87SGreg Kurz      * quadratic time complexity in address_space_update_ioeventfds().
2189cf4fd87SGreg Kurz      */
219c4f5dcc4SGreg Kurz     memory_region_transaction_begin();
220c4f5dcc4SGreg Kurz 
22191cb1c9bSFam Zheng     for (i = 0; i < vs->conf.num_queues + 2; i++) {
22221a4d962SCornelia Huck         virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
223c4f5dcc4SGreg Kurz     }
224c4f5dcc4SGreg Kurz 
2259cf4fd87SGreg Kurz     /*
2269cf4fd87SGreg Kurz      * The transaction expects the ioeventfds to be open when it
2279cf4fd87SGreg Kurz      * commits. Do it now, before the cleanup loop.
2289cf4fd87SGreg Kurz      */
229c4f5dcc4SGreg Kurz     memory_region_transaction_commit();
230c4f5dcc4SGreg Kurz 
231c4f5dcc4SGreg Kurz     for (i = 0; i < vs->conf.num_queues + 2; i++) {
23276143618SGal Hammer         virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
233b1f0a33dSCornelia Huck     }
23491cb1c9bSFam Zheng 
23591cb1c9bSFam Zheng     /* Clean up guest notifier (irq) */
23691cb1c9bSFam Zheng     k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
23791cb1c9bSFam Zheng     s->dataplane_stopping = false;
23891cb1c9bSFam Zheng     s->dataplane_started = false;
23991cb1c9bSFam Zheng }
240