1 /*
2  * Virtio SCSI dataplane
3  *
4  * Copyright Red Hat, Inc. 2014
5  *
6  * Authors:
7  *   Fam Zheng <famz@redhat.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  *
12  */
13 
14 #include "qemu/osdep.h"
15 #include "qapi/error.h"
16 #include "hw/virtio/virtio-scsi.h"
17 #include "qemu/error-report.h"
18 #include "sysemu/block-backend.h"
19 #include "hw/scsi/scsi.h"
20 #include "scsi/constants.h"
21 #include "hw/virtio/virtio-bus.h"
22 #include "hw/virtio/virtio-access.h"
23 
24 /* Context: QEMU global mutex held */
25 void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp)
26 {
27     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
28     VirtIODevice *vdev = VIRTIO_DEVICE(s);
29     BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
30     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
31 
32     if (vs->conf.iothread) {
33         if (!k->set_guest_notifiers || !k->ioeventfd_assign) {
34             error_setg(errp,
35                        "device is incompatible with iothread "
36                        "(transport does not support notifiers)");
37             return;
38         }
39         if (!virtio_device_ioeventfd_enabled(vdev)) {
40             error_setg(errp, "ioeventfd is required for iothread");
41             return;
42         }
43         s->ctx = iothread_get_aio_context(vs->conf.iothread);
44     } else {
45         if (!virtio_device_ioeventfd_enabled(vdev)) {
46             return;
47         }
48         s->ctx = qemu_get_aio_context();
49     }
50 }
51 
52 static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n)
53 {
54     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
55     int rc;
56 
57     /* Set up virtqueue notify */
58     rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true);
59     if (rc != 0) {
60         fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n",
61                 rc);
62         s->dataplane_fenced = true;
63         return rc;
64     }
65 
66     return 0;
67 }
68 
69 /* Context: BH in IOThread */
70 static void virtio_scsi_dataplane_stop_bh(void *opaque)
71 {
72     VirtIOSCSI *s = opaque;
73     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
74     int i;
75 
76     virtio_queue_aio_detach_host_notifier(vs->ctrl_vq, s->ctx);
77     virtio_queue_aio_detach_host_notifier(vs->event_vq, s->ctx);
78     for (i = 0; i < vs->conf.num_queues; i++) {
79         virtio_queue_aio_detach_host_notifier(vs->cmd_vqs[i], s->ctx);
80     }
81 }
82 
83 /* Context: QEMU global mutex held */
84 int virtio_scsi_dataplane_start(VirtIODevice *vdev)
85 {
86     int i;
87     int rc;
88     int vq_init_count = 0;
89     BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
90     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
91     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
92     VirtIOSCSI *s = VIRTIO_SCSI(vdev);
93 
94     if (s->dataplane_started ||
95         s->dataplane_starting ||
96         s->dataplane_fenced) {
97         return 0;
98     }
99 
100     s->dataplane_starting = true;
101 
102     /* Set up guest notifier (irq) */
103     rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true);
104     if (rc != 0) {
105         error_report("virtio-scsi: Failed to set guest notifiers (%d), "
106                      "ensure -accel kvm is set.", rc);
107         goto fail_guest_notifiers;
108     }
109 
110     /*
111      * Batch all the host notifiers in a single transaction to avoid
112      * quadratic time complexity in address_space_update_ioeventfds().
113      */
114     memory_region_transaction_begin();
115 
116     rc = virtio_scsi_set_host_notifier(s, vs->ctrl_vq, 0);
117     if (rc != 0) {
118         goto fail_host_notifiers;
119     }
120 
121     vq_init_count++;
122     rc = virtio_scsi_set_host_notifier(s, vs->event_vq, 1);
123     if (rc != 0) {
124         goto fail_host_notifiers;
125     }
126 
127     vq_init_count++;
128 
129     for (i = 0; i < vs->conf.num_queues; i++) {
130         rc = virtio_scsi_set_host_notifier(s, vs->cmd_vqs[i], i + 2);
131         if (rc) {
132             goto fail_host_notifiers;
133         }
134         vq_init_count++;
135     }
136 
137     memory_region_transaction_commit();
138 
139     /*
140      * These fields are visible to the IOThread so we rely on implicit barriers
141      * in aio_context_acquire() on the write side and aio_notify_accept() on
142      * the read side.
143      */
144     s->dataplane_starting = false;
145     s->dataplane_started = true;
146 
147     aio_context_acquire(s->ctx);
148     virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx);
149     virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, s->ctx);
150 
151     for (i = 0; i < vs->conf.num_queues; i++) {
152         virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx);
153     }
154     aio_context_release(s->ctx);
155     return 0;
156 
157 fail_host_notifiers:
158     for (i = 0; i < vq_init_count; i++) {
159         virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
160     }
161 
162     /*
163      * The transaction expects the ioeventfds to be open when it
164      * commits. Do it now, before the cleanup loop.
165      */
166     memory_region_transaction_commit();
167 
168     for (i = 0; i < vq_init_count; i++) {
169         virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
170     }
171     k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
172 fail_guest_notifiers:
173     s->dataplane_fenced = true;
174     s->dataplane_starting = false;
175     s->dataplane_started = true;
176     return -ENOSYS;
177 }
178 
179 /* Context: QEMU global mutex held */
180 void virtio_scsi_dataplane_stop(VirtIODevice *vdev)
181 {
182     BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
183     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
184     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
185     VirtIOSCSI *s = VIRTIO_SCSI(vdev);
186     int i;
187 
188     if (!s->dataplane_started || s->dataplane_stopping) {
189         return;
190     }
191 
192     /* Better luck next time. */
193     if (s->dataplane_fenced) {
194         s->dataplane_fenced = false;
195         s->dataplane_started = false;
196         return;
197     }
198     s->dataplane_stopping = true;
199 
200     aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
201 
202     blk_drain_all(); /* ensure there are no in-flight requests */
203 
204     /*
205      * Batch all the host notifiers in a single transaction to avoid
206      * quadratic time complexity in address_space_update_ioeventfds().
207      */
208     memory_region_transaction_begin();
209 
210     for (i = 0; i < vs->conf.num_queues + 2; i++) {
211         virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
212     }
213 
214     /*
215      * The transaction expects the ioeventfds to be open when it
216      * commits. Do it now, before the cleanup loop.
217      */
218     memory_region_transaction_commit();
219 
220     for (i = 0; i < vs->conf.num_queues + 2; i++) {
221         virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
222     }
223 
224     /* Clean up guest notifier (irq) */
225     k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
226     s->dataplane_stopping = false;
227     s->dataplane_started = false;
228 }
229