]>
Commit | Line | Data |
---|---|---|
c6136ec0 SG |
1 | /* |
2 | * Parent class for vhost-vsock devices | |
3 | * | |
4 | * Copyright 2015-2020 Red Hat, Inc. | |
5 | * | |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or | |
7 | * (at your option) any later version. See the COPYING file in the | |
8 | * top-level directory. | |
9 | */ | |
10 | ||
11 | #include "qemu/osdep.h" | |
12 | #include "standard-headers/linux/virtio_vsock.h" | |
13 | #include "qapi/error.h" | |
a64da64a | 14 | #include "hw/virtio/virtio-bus.h" |
c6136ec0 SG |
15 | #include "qemu/error-report.h" |
16 | #include "hw/qdev-properties.h" | |
b8f3e6a1 | 17 | #include "hw/virtio/vhost.h" |
c6136ec0 SG |
18 | #include "hw/virtio/vhost-vsock.h" |
19 | #include "qemu/iov.h" | |
20 | #include "monitor/monitor.h" | |
21 | ||
46ce0171 SG |
22 | const int feature_bits[] = { |
23 | VIRTIO_VSOCK_F_SEQPACKET, | |
562a7d23 | 24 | VIRTIO_F_RING_RESET, |
46ce0171 SG |
25 | VHOST_INVALID_FEATURE_BIT |
26 | }; | |
27 | ||
28 | uint64_t vhost_vsock_common_get_features(VirtIODevice *vdev, uint64_t features, | |
29 | Error **errp) | |
30 | { | |
31 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); | |
32 | ||
33 | if (vvc->seqpacket != ON_OFF_AUTO_OFF) { | |
34 | virtio_add_feature(&features, VIRTIO_VSOCK_F_SEQPACKET); | |
35 | } | |
36 | ||
37 | features = vhost_get_features(&vvc->vhost_dev, feature_bits, features); | |
38 | ||
39 | if (vvc->seqpacket == ON_OFF_AUTO_ON && | |
40 | !virtio_has_feature(features, VIRTIO_VSOCK_F_SEQPACKET)) { | |
41 | error_setg(errp, "vhost-vsock backend doesn't support seqpacket"); | |
42 | } | |
43 | ||
44 | return features; | |
45 | } | |
46 | ||
c6136ec0 SG |
47 | int vhost_vsock_common_start(VirtIODevice *vdev) |
48 | { | |
49 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); | |
50 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); | |
51 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
52 | int ret; | |
53 | int i; | |
54 | ||
55 | if (!k->set_guest_notifiers) { | |
56 | error_report("binding does not support guest notifiers"); | |
57 | return -ENOSYS; | |
58 | } | |
59 | ||
60 | ret = vhost_dev_enable_notifiers(&vvc->vhost_dev, vdev); | |
61 | if (ret < 0) { | |
62 | error_report("Error enabling host notifiers: %d", -ret); | |
63 | return ret; | |
64 | } | |
65 | ||
66 | ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, true); | |
67 | if (ret < 0) { | |
68 | error_report("Error binding guest notifier: %d", -ret); | |
69 | goto err_host_notifiers; | |
70 | } | |
71 | ||
72 | vvc->vhost_dev.acked_features = vdev->guest_features; | |
4daa5054 | 73 | ret = vhost_dev_start(&vvc->vhost_dev, vdev, true); |
c6136ec0 SG |
74 | if (ret < 0) { |
75 | error_report("Error starting vhost: %d", -ret); | |
76 | goto err_guest_notifiers; | |
77 | } | |
78 | ||
79 | /* | |
80 | * guest_notifier_mask/pending not used yet, so just unmask | |
81 | * everything here. virtio-pci will do the right thing by | |
82 | * enabling/disabling irqfd. | |
83 | */ | |
84 | for (i = 0; i < vvc->vhost_dev.nvqs; i++) { | |
85 | vhost_virtqueue_mask(&vvc->vhost_dev, vdev, i, false); | |
86 | } | |
87 | ||
88 | return 0; | |
89 | ||
90 | err_guest_notifiers: | |
91 | k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false); | |
92 | err_host_notifiers: | |
93 | vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev); | |
94 | return ret; | |
95 | } | |
96 | ||
97 | void vhost_vsock_common_stop(VirtIODevice *vdev) | |
98 | { | |
99 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); | |
100 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); | |
101 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
102 | int ret; | |
103 | ||
104 | if (!k->set_guest_notifiers) { | |
105 | return; | |
106 | } | |
107 | ||
4daa5054 | 108 | vhost_dev_stop(&vvc->vhost_dev, vdev, true); |
c6136ec0 SG |
109 | |
110 | ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false); | |
111 | if (ret < 0) { | |
112 | error_report("vhost guest notifier cleanup failed: %d", ret); | |
113 | return; | |
114 | } | |
115 | ||
116 | vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev); | |
117 | } | |
118 | ||
119 | ||
120 | static void vhost_vsock_common_handle_output(VirtIODevice *vdev, VirtQueue *vq) | |
121 | { | |
122 | /* Do nothing */ | |
123 | } | |
124 | ||
125 | static void vhost_vsock_common_guest_notifier_mask(VirtIODevice *vdev, int idx, | |
126 | bool mask) | |
127 | { | |
128 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); | |
129 | ||
544f0278 CL |
130 | /* |
131 | * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 | |
7e8094f0 | 132 | * as the macro of configure interrupt's IDX, If this driver does not |
544f0278 CL |
133 | * support, the function will return |
134 | */ | |
135 | ||
136 | if (idx == VIRTIO_CONFIG_IRQ_IDX) { | |
137 | return; | |
138 | } | |
c6136ec0 SG |
139 | vhost_virtqueue_mask(&vvc->vhost_dev, vdev, idx, mask); |
140 | } | |
141 | ||
142 | static bool vhost_vsock_common_guest_notifier_pending(VirtIODevice *vdev, | |
143 | int idx) | |
144 | { | |
145 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); | |
146 | ||
544f0278 CL |
147 | /* |
148 | * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1 | |
7e8094f0 | 149 | * as the macro of configure interrupt's IDX, If this driver does not |
544f0278 CL |
150 | * support, the function will return |
151 | */ | |
152 | ||
153 | if (idx == VIRTIO_CONFIG_IRQ_IDX) { | |
154 | return false; | |
155 | } | |
c6136ec0 SG |
156 | return vhost_virtqueue_pending(&vvc->vhost_dev, idx); |
157 | } | |
158 | ||
159 | static void vhost_vsock_common_send_transport_reset(VHostVSockCommon *vvc) | |
160 | { | |
161 | VirtQueueElement *elem; | |
162 | VirtQueue *vq = vvc->event_vq; | |
163 | struct virtio_vsock_event event = { | |
164 | .id = cpu_to_le32(VIRTIO_VSOCK_EVENT_TRANSPORT_RESET), | |
165 | }; | |
166 | ||
167 | elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); | |
168 | if (!elem) { | |
169 | error_report("vhost-vsock missed transport reset event"); | |
170 | return; | |
171 | } | |
172 | ||
173 | if (elem->out_num) { | |
174 | error_report("invalid vhost-vsock event virtqueue element with " | |
175 | "out buffers"); | |
8d1b247f | 176 | goto err; |
c6136ec0 SG |
177 | } |
178 | ||
179 | if (iov_from_buf(elem->in_sg, elem->in_num, 0, | |
180 | &event, sizeof(event)) != sizeof(event)) { | |
181 | error_report("vhost-vsock event virtqueue element is too short"); | |
8d1b247f | 182 | goto err; |
c6136ec0 SG |
183 | } |
184 | ||
185 | virtqueue_push(vq, elem, sizeof(event)); | |
186 | virtio_notify(VIRTIO_DEVICE(vvc), vq); | |
187 | ||
8d1b247f SG |
188 | g_free(elem); |
189 | return; | |
190 | ||
191 | err: | |
192 | virtqueue_detach_element(vq, elem, 0); | |
c6136ec0 SG |
193 | g_free(elem); |
194 | } | |
195 | ||
196 | static void vhost_vsock_common_post_load_timer_cleanup(VHostVSockCommon *vvc) | |
197 | { | |
198 | if (!vvc->post_load_timer) { | |
199 | return; | |
200 | } | |
201 | ||
c6136ec0 SG |
202 | timer_free(vvc->post_load_timer); |
203 | vvc->post_load_timer = NULL; | |
204 | } | |
205 | ||
206 | static void vhost_vsock_common_post_load_timer_cb(void *opaque) | |
207 | { | |
208 | VHostVSockCommon *vvc = opaque; | |
209 | ||
210 | vhost_vsock_common_post_load_timer_cleanup(vvc); | |
211 | vhost_vsock_common_send_transport_reset(vvc); | |
212 | } | |
213 | ||
214 | int vhost_vsock_common_pre_save(void *opaque) | |
215 | { | |
216 | VHostVSockCommon *vvc = opaque; | |
217 | ||
218 | /* | |
219 | * At this point, backend must be stopped, otherwise | |
220 | * it might keep writing to memory. | |
221 | */ | |
b8f3e6a1 | 222 | assert(!vhost_dev_is_started(&vvc->vhost_dev)); |
c6136ec0 SG |
223 | |
224 | return 0; | |
225 | } | |
226 | ||
227 | int vhost_vsock_common_post_load(void *opaque, int version_id) | |
228 | { | |
229 | VHostVSockCommon *vvc = opaque; | |
230 | VirtIODevice *vdev = VIRTIO_DEVICE(vvc); | |
231 | ||
232 | if (virtio_queue_get_addr(vdev, 2)) { | |
233 | /* | |
234 | * Defer transport reset event to a vm clock timer so that virtqueue | |
235 | * changes happen after migration has completed. | |
236 | */ | |
237 | assert(!vvc->post_load_timer); | |
238 | vvc->post_load_timer = | |
239 | timer_new_ns(QEMU_CLOCK_VIRTUAL, | |
240 | vhost_vsock_common_post_load_timer_cb, | |
241 | vvc); | |
242 | timer_mod(vvc->post_load_timer, 1); | |
243 | } | |
244 | return 0; | |
245 | } | |
246 | ||
3857cd5c | 247 | void vhost_vsock_common_realize(VirtIODevice *vdev) |
c6136ec0 SG |
248 | { |
249 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); | |
250 | ||
3857cd5c | 251 | virtio_init(vdev, VIRTIO_ID_VSOCK, sizeof(struct virtio_vsock_config)); |
c6136ec0 SG |
252 | |
253 | /* Receive and transmit queues belong to vhost */ | |
254 | vvc->recv_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, | |
255 | vhost_vsock_common_handle_output); | |
256 | vvc->trans_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, | |
257 | vhost_vsock_common_handle_output); | |
258 | ||
259 | /* The event queue belongs to QEMU */ | |
260 | vvc->event_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, | |
261 | vhost_vsock_common_handle_output); | |
262 | ||
263 | vvc->vhost_dev.nvqs = ARRAY_SIZE(vvc->vhost_vqs); | |
264 | vvc->vhost_dev.vqs = vvc->vhost_vqs; | |
265 | ||
266 | vvc->post_load_timer = NULL; | |
267 | } | |
268 | ||
269 | void vhost_vsock_common_unrealize(VirtIODevice *vdev) | |
270 | { | |
271 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); | |
272 | ||
273 | vhost_vsock_common_post_load_timer_cleanup(vvc); | |
274 | ||
275 | virtio_delete_queue(vvc->recv_vq); | |
276 | virtio_delete_queue(vvc->trans_vq); | |
277 | virtio_delete_queue(vvc->event_vq); | |
278 | virtio_cleanup(vdev); | |
279 | } | |
280 | ||
c255488d JP |
281 | static struct vhost_dev *vhost_vsock_common_get_vhost(VirtIODevice *vdev) |
282 | { | |
283 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); | |
284 | return &vvc->vhost_dev; | |
285 | } | |
286 | ||
46ce0171 SG |
287 | static Property vhost_vsock_common_properties[] = { |
288 | DEFINE_PROP_ON_OFF_AUTO("seqpacket", VHostVSockCommon, seqpacket, | |
289 | ON_OFF_AUTO_AUTO), | |
290 | DEFINE_PROP_END_OF_LIST(), | |
291 | }; | |
292 | ||
c6136ec0 SG |
293 | static void vhost_vsock_common_class_init(ObjectClass *klass, void *data) |
294 | { | |
295 | DeviceClass *dc = DEVICE_CLASS(klass); | |
296 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | |
297 | ||
46ce0171 | 298 | device_class_set_props(dc, vhost_vsock_common_properties); |
c6136ec0 SG |
299 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
300 | vdc->guest_notifier_mask = vhost_vsock_common_guest_notifier_mask; | |
301 | vdc->guest_notifier_pending = vhost_vsock_common_guest_notifier_pending; | |
c255488d | 302 | vdc->get_vhost = vhost_vsock_common_get_vhost; |
c6136ec0 SG |
303 | } |
304 | ||
305 | static const TypeInfo vhost_vsock_common_info = { | |
306 | .name = TYPE_VHOST_VSOCK_COMMON, | |
307 | .parent = TYPE_VIRTIO_DEVICE, | |
308 | .instance_size = sizeof(VHostVSockCommon), | |
309 | .class_init = vhost_vsock_common_class_init, | |
310 | .abstract = true, | |
311 | }; | |
312 | ||
313 | static void vhost_vsock_common_register_types(void) | |
314 | { | |
315 | type_register_static(&vhost_vsock_common_info); | |
316 | } | |
317 | ||
318 | type_init(vhost_vsock_common_register_types) |