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