]>
Commit | Line | Data |
---|---|---|
91cb1c9b FZ |
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 | ||
9b8bfe21 | 14 | #include "qemu/osdep.h" |
ad07cd69 | 15 | #include "qapi/error.h" |
91cb1c9b FZ |
16 | #include "hw/virtio/virtio-scsi.h" |
17 | #include "qemu/error-report.h" | |
4be74634 | 18 | #include "sysemu/block-backend.h" |
a9c94277 MA |
19 | #include "hw/scsi/scsi.h" |
20 | #include "block/scsi.h" | |
21 | #include "hw/virtio/virtio-bus.h" | |
91cb1c9b | 22 | #include "hw/virtio/virtio-access.h" |
91cb1c9b FZ |
23 | |
24 | /* Context: QEMU global mutex held */ | |
ad07cd69 | 25 | void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) |
91cb1c9b | 26 | { |
91cb1c9b | 27 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); |
ad07cd69 PB |
28 | VirtIODevice *vdev = VIRTIO_DEVICE(s); |
29 | BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); | |
30 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
91cb1c9b | 31 | |
ad07cd69 PB |
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(); | |
91cb1c9b FZ |
49 | } |
50 | } | |
51 | ||
07931698 | 52 | static bool virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev, |
a8f2e5c8 PB |
53 | VirtQueue *vq) |
54 | { | |
71407786 FZ |
55 | bool progress; |
56 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); | |
a8f2e5c8 | 57 | |
71407786 | 58 | virtio_scsi_acquire(s); |
a8f2e5c8 | 59 | assert(s->ctx && s->dataplane_started); |
71407786 FZ |
60 | progress = virtio_scsi_handle_cmd_vq(s, vq); |
61 | virtio_scsi_release(s); | |
62 | return progress; | |
a8f2e5c8 PB |
63 | } |
64 | ||
07931698 | 65 | static bool virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev, |
a8f2e5c8 PB |
66 | VirtQueue *vq) |
67 | { | |
71407786 | 68 | bool progress; |
a8f2e5c8 PB |
69 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); |
70 | ||
71407786 | 71 | virtio_scsi_acquire(s); |
a8f2e5c8 | 72 | assert(s->ctx && s->dataplane_started); |
71407786 FZ |
73 | progress = virtio_scsi_handle_ctrl_vq(s, vq); |
74 | virtio_scsi_release(s); | |
75 | return progress; | |
a8f2e5c8 PB |
76 | } |
77 | ||
07931698 | 78 | static bool virtio_scsi_data_plane_handle_event(VirtIODevice *vdev, |
a8f2e5c8 PB |
79 | VirtQueue *vq) |
80 | { | |
71407786 | 81 | bool progress; |
a8f2e5c8 PB |
82 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); |
83 | ||
71407786 | 84 | virtio_scsi_acquire(s); |
a8f2e5c8 | 85 | assert(s->ctx && s->dataplane_started); |
71407786 FZ |
86 | progress = virtio_scsi_handle_event_vq(s, vq); |
87 | virtio_scsi_release(s); | |
88 | return progress; | |
a8f2e5c8 PB |
89 | } |
90 | ||
91 | static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, | |
07931698 | 92 | VirtIOHandleAIOOutput fn) |
91cb1c9b FZ |
93 | { |
94 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); | |
6d2c8316 | 95 | int rc; |
91cb1c9b FZ |
96 | |
97 | /* Set up virtqueue notify */ | |
b1f0a33d | 98 | rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true); |
6d2c8316 CH |
99 | if (rc != 0) { |
100 | fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", | |
101 | rc); | |
4adea804 | 102 | s->dataplane_fenced = true; |
e24a47c5 | 103 | return rc; |
91cb1c9b | 104 | } |
196d4fc5 | 105 | |
a378b49a | 106 | virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn); |
e24a47c5 | 107 | return 0; |
91cb1c9b FZ |
108 | } |
109 | ||
361dcc79 CH |
110 | /* assumes s->ctx held */ |
111 | static void virtio_scsi_clear_aio(VirtIOSCSI *s) | |
112 | { | |
113 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); | |
114 | int i; | |
115 | ||
a378b49a PB |
116 | virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL); |
117 | virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL); | |
e24a47c5 | 118 | for (i = 0; i < vs->conf.num_queues; i++) { |
a378b49a | 119 | virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL); |
361dcc79 CH |
120 | } |
121 | } | |
122 | ||
91cb1c9b | 123 | /* Context: QEMU global mutex held */ |
ad07cd69 | 124 | int virtio_scsi_dataplane_start(VirtIODevice *vdev) |
91cb1c9b FZ |
125 | { |
126 | int i; | |
127 | int rc; | |
ad07cd69 | 128 | BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); |
91cb1c9b | 129 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); |
ad07cd69 PB |
130 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); |
131 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); | |
91cb1c9b FZ |
132 | |
133 | if (s->dataplane_started || | |
134 | s->dataplane_starting || | |
ad07cd69 PB |
135 | s->dataplane_fenced) { |
136 | return 0; | |
91cb1c9b FZ |
137 | } |
138 | ||
139 | s->dataplane_starting = true; | |
140 | ||
141 | /* Set up guest notifier (irq) */ | |
142 | rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); | |
143 | if (rc != 0) { | |
6d2c8316 CH |
144 | fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " |
145 | "ensure -enable-kvm is set\n", rc); | |
361dcc79 | 146 | goto fail_guest_notifiers; |
91cb1c9b FZ |
147 | } |
148 | ||
149 | aio_context_acquire(s->ctx); | |
a8f2e5c8 PB |
150 | rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0, |
151 | virtio_scsi_data_plane_handle_ctrl); | |
e24a47c5 | 152 | if (rc) { |
361dcc79 CH |
153 | goto fail_vrings; |
154 | } | |
a8f2e5c8 PB |
155 | rc = virtio_scsi_vring_init(s, vs->event_vq, 1, |
156 | virtio_scsi_data_plane_handle_event); | |
e24a47c5 | 157 | if (rc) { |
361dcc79 CH |
158 | goto fail_vrings; |
159 | } | |
91cb1c9b | 160 | for (i = 0; i < vs->conf.num_queues; i++) { |
a8f2e5c8 PB |
161 | rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2, |
162 | virtio_scsi_data_plane_handle_cmd); | |
e24a47c5 | 163 | if (rc) { |
361dcc79 CH |
164 | goto fail_vrings; |
165 | } | |
91cb1c9b FZ |
166 | } |
167 | ||
91cb1c9b FZ |
168 | s->dataplane_starting = false; |
169 | s->dataplane_started = true; | |
ace386b4 | 170 | aio_context_release(s->ctx); |
ad07cd69 | 171 | return 0; |
361dcc79 CH |
172 | |
173 | fail_vrings: | |
174 | virtio_scsi_clear_aio(s); | |
175 | aio_context_release(s->ctx); | |
361dcc79 | 176 | for (i = 0; i < vs->conf.num_queues + 2; i++) { |
21a4d962 | 177 | virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); |
361dcc79 CH |
178 | } |
179 | k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); | |
180 | fail_guest_notifiers: | |
e24a47c5 | 181 | s->dataplane_fenced = true; |
361dcc79 | 182 | s->dataplane_starting = false; |
e24a47c5 | 183 | s->dataplane_started = true; |
ad07cd69 | 184 | return -ENOSYS; |
91cb1c9b FZ |
185 | } |
186 | ||
187 | /* Context: QEMU global mutex held */ | |
ad07cd69 | 188 | void virtio_scsi_dataplane_stop(VirtIODevice *vdev) |
91cb1c9b | 189 | { |
ad07cd69 | 190 | BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); |
91cb1c9b | 191 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); |
ad07cd69 PB |
192 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); |
193 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); | |
21a4d962 | 194 | int i; |
91cb1c9b | 195 | |
e24a47c5 PB |
196 | if (!s->dataplane_started || s->dataplane_stopping) { |
197 | return; | |
198 | } | |
199 | ||
4adea804 CH |
200 | /* Better luck next time. */ |
201 | if (s->dataplane_fenced) { | |
202 | s->dataplane_fenced = false; | |
e24a47c5 | 203 | s->dataplane_started = false; |
91cb1c9b FZ |
204 | return; |
205 | } | |
206 | s->dataplane_stopping = true; | |
91cb1c9b FZ |
207 | |
208 | aio_context_acquire(s->ctx); | |
e24a47c5 | 209 | virtio_scsi_clear_aio(s); |
c9d1a561 | 210 | aio_context_release(s->ctx); |
91cb1c9b | 211 | |
4be74634 | 212 | blk_drain_all(); /* ensure there are no in-flight requests */ |
91cb1c9b | 213 | |
91cb1c9b | 214 | for (i = 0; i < vs->conf.num_queues + 2; i++) { |
21a4d962 | 215 | virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); |
91cb1c9b FZ |
216 | } |
217 | ||
218 | /* Clean up guest notifier (irq) */ | |
219 | k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); | |
220 | s->dataplane_stopping = false; | |
221 | s->dataplane_started = false; | |
222 | } |