1
2
3
4
5
6
7
8
9
10
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
25void 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
52static 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
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
70static 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
84int 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
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
112
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
141
142
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
157fail_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
164
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);
172fail_guest_notifiers:
173 s->dataplane_fenced = true;
174 s->dataplane_starting = false;
175 s->dataplane_started = true;
176 return -ENOSYS;
177}
178
179
180void 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
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_context_acquire(s->ctx);
201 aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
202 aio_context_release(s->ctx);
203
204 blk_drain_all();
205
206
207
208
209
210 memory_region_transaction_begin();
211
212 for (i = 0; i < vs->conf.num_queues + 2; i++) {
213 virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
214 }
215
216
217
218
219
220 memory_region_transaction_commit();
221
222 for (i = 0; i < vs->conf.num_queues + 2; i++) {
223 virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i);
224 }
225
226
227 k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false);
228 s->dataplane_stopping = false;
229 s->dataplane_started = false;
230}
231