]>
Commit | Line | Data |
---|---|---|
5f6f6664 NN |
1 | /* |
2 | * vhost-user | |
3 | * | |
4 | * Copyright (c) 2013 Virtual Open Systems Sarl. | |
5 | * | |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
7 | * See the COPYING file in the top-level directory. | |
8 | * | |
9 | */ | |
10 | ||
9b8bfe21 | 11 | #include "qemu/osdep.h" |
da34e65c | 12 | #include "qapi/error.h" |
5f6f6664 NN |
13 | #include "hw/virtio/vhost.h" |
14 | #include "hw/virtio/vhost-backend.h" | |
3e866365 | 15 | #include "hw/virtio/virtio-net.h" |
4d43a603 | 16 | #include "chardev/char-fe.h" |
5f6f6664 NN |
17 | #include "sysemu/kvm.h" |
18 | #include "qemu/error-report.h" | |
19 | #include "qemu/sockets.h" | |
20 | ||
5f6f6664 NN |
21 | #include <sys/ioctl.h> |
22 | #include <sys/socket.h> | |
23 | #include <sys/un.h> | |
24 | #include <linux/vhost.h> | |
25 | ||
26 | #define VHOST_MEMORY_MAX_NREGIONS 8 | |
dcb10c00 | 27 | #define VHOST_USER_F_PROTOCOL_FEATURES 30 |
e2051e9e | 28 | |
de1372d4 TC |
29 | enum VhostUserProtocolFeature { |
30 | VHOST_USER_PROTOCOL_F_MQ = 0, | |
31 | VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, | |
32 | VHOST_USER_PROTOCOL_F_RARP = 2, | |
ca525ce5 | 33 | VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, |
c5f048d8 | 34 | VHOST_USER_PROTOCOL_F_NET_MTU = 4, |
4bbeeba0 | 35 | VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5, |
5df04f17 | 36 | VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6, |
de1372d4 TC |
37 | |
38 | VHOST_USER_PROTOCOL_F_MAX | |
39 | }; | |
40 | ||
41 | #define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1) | |
5f6f6664 NN |
42 | |
43 | typedef enum VhostUserRequest { | |
44 | VHOST_USER_NONE = 0, | |
45 | VHOST_USER_GET_FEATURES = 1, | |
46 | VHOST_USER_SET_FEATURES = 2, | |
47 | VHOST_USER_SET_OWNER = 3, | |
60915dc4 | 48 | VHOST_USER_RESET_OWNER = 4, |
5f6f6664 NN |
49 | VHOST_USER_SET_MEM_TABLE = 5, |
50 | VHOST_USER_SET_LOG_BASE = 6, | |
51 | VHOST_USER_SET_LOG_FD = 7, | |
52 | VHOST_USER_SET_VRING_NUM = 8, | |
53 | VHOST_USER_SET_VRING_ADDR = 9, | |
54 | VHOST_USER_SET_VRING_BASE = 10, | |
55 | VHOST_USER_GET_VRING_BASE = 11, | |
56 | VHOST_USER_SET_VRING_KICK = 12, | |
57 | VHOST_USER_SET_VRING_CALL = 13, | |
58 | VHOST_USER_SET_VRING_ERR = 14, | |
dcb10c00 MT |
59 | VHOST_USER_GET_PROTOCOL_FEATURES = 15, |
60 | VHOST_USER_SET_PROTOCOL_FEATURES = 16, | |
e2051e9e | 61 | VHOST_USER_GET_QUEUE_NUM = 17, |
7263a0ad | 62 | VHOST_USER_SET_VRING_ENABLE = 18, |
3e866365 | 63 | VHOST_USER_SEND_RARP = 19, |
c5f048d8 | 64 | VHOST_USER_NET_SET_MTU = 20, |
4bbeeba0 | 65 | VHOST_USER_SET_SLAVE_REQ_FD = 21, |
6dcdd06e | 66 | VHOST_USER_IOTLB_MSG = 22, |
5df04f17 | 67 | VHOST_USER_SET_VRING_ENDIAN = 23, |
5f6f6664 NN |
68 | VHOST_USER_MAX |
69 | } VhostUserRequest; | |
70 | ||
4bbeeba0 MAL |
71 | typedef enum VhostUserSlaveRequest { |
72 | VHOST_USER_SLAVE_NONE = 0, | |
6dcdd06e | 73 | VHOST_USER_SLAVE_IOTLB_MSG = 1, |
4bbeeba0 MAL |
74 | VHOST_USER_SLAVE_MAX |
75 | } VhostUserSlaveRequest; | |
76 | ||
5f6f6664 NN |
77 | typedef struct VhostUserMemoryRegion { |
78 | uint64_t guest_phys_addr; | |
79 | uint64_t memory_size; | |
80 | uint64_t userspace_addr; | |
3fd74b84 | 81 | uint64_t mmap_offset; |
5f6f6664 NN |
82 | } VhostUserMemoryRegion; |
83 | ||
84 | typedef struct VhostUserMemory { | |
85 | uint32_t nregions; | |
86 | uint32_t padding; | |
87 | VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; | |
88 | } VhostUserMemory; | |
89 | ||
2b8819c6 VK |
90 | typedef struct VhostUserLog { |
91 | uint64_t mmap_size; | |
92 | uint64_t mmap_offset; | |
93 | } VhostUserLog; | |
94 | ||
5f6f6664 NN |
95 | typedef struct VhostUserMsg { |
96 | VhostUserRequest request; | |
97 | ||
98 | #define VHOST_USER_VERSION_MASK (0x3) | |
99 | #define VHOST_USER_REPLY_MASK (0x1<<2) | |
ca525ce5 | 100 | #define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) |
5f6f6664 NN |
101 | uint32_t flags; |
102 | uint32_t size; /* the following payload size */ | |
103 | union { | |
104 | #define VHOST_USER_VRING_IDX_MASK (0xff) | |
105 | #define VHOST_USER_VRING_NOFD_MASK (0x1<<8) | |
106 | uint64_t u64; | |
107 | struct vhost_vring_state state; | |
108 | struct vhost_vring_addr addr; | |
109 | VhostUserMemory memory; | |
2b8819c6 | 110 | VhostUserLog log; |
6dcdd06e | 111 | struct vhost_iotlb_msg iotlb; |
7f4a930e | 112 | } payload; |
5f6f6664 NN |
113 | } QEMU_PACKED VhostUserMsg; |
114 | ||
115 | static VhostUserMsg m __attribute__ ((unused)); | |
116 | #define VHOST_USER_HDR_SIZE (sizeof(m.request) \ | |
117 | + sizeof(m.flags) \ | |
118 | + sizeof(m.size)) | |
119 | ||
120 | #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) | |
121 | ||
122 | /* The version of the protocol we support */ | |
123 | #define VHOST_USER_VERSION (0x1) | |
124 | ||
2152f3fe MAL |
125 | struct vhost_user { |
126 | CharBackend *chr; | |
4bbeeba0 | 127 | int slave_fd; |
2152f3fe MAL |
128 | }; |
129 | ||
5f6f6664 NN |
130 | static bool ioeventfd_enabled(void) |
131 | { | |
132 | return kvm_enabled() && kvm_eventfds_enabled(); | |
133 | } | |
134 | ||
5f6f6664 NN |
135 | static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) |
136 | { | |
2152f3fe MAL |
137 | struct vhost_user *u = dev->opaque; |
138 | CharBackend *chr = u->chr; | |
5f6f6664 NN |
139 | uint8_t *p = (uint8_t *) msg; |
140 | int r, size = VHOST_USER_HDR_SIZE; | |
141 | ||
142 | r = qemu_chr_fe_read_all(chr, p, size); | |
143 | if (r != size) { | |
5421f318 MT |
144 | error_report("Failed to read msg header. Read %d instead of %d." |
145 | " Original request %d.", r, size, msg->request); | |
5f6f6664 NN |
146 | goto fail; |
147 | } | |
148 | ||
149 | /* validate received flags */ | |
150 | if (msg->flags != (VHOST_USER_REPLY_MASK | VHOST_USER_VERSION)) { | |
151 | error_report("Failed to read msg header." | |
ab7c5aaf | 152 | " Flags 0x%x instead of 0x%x.", msg->flags, |
5f6f6664 NN |
153 | VHOST_USER_REPLY_MASK | VHOST_USER_VERSION); |
154 | goto fail; | |
155 | } | |
156 | ||
157 | /* validate message size is sane */ | |
158 | if (msg->size > VHOST_USER_PAYLOAD_SIZE) { | |
159 | error_report("Failed to read msg header." | |
ab7c5aaf | 160 | " Size %d exceeds the maximum %zu.", msg->size, |
5f6f6664 NN |
161 | VHOST_USER_PAYLOAD_SIZE); |
162 | goto fail; | |
163 | } | |
164 | ||
165 | if (msg->size) { | |
166 | p += VHOST_USER_HDR_SIZE; | |
167 | size = msg->size; | |
168 | r = qemu_chr_fe_read_all(chr, p, size); | |
169 | if (r != size) { | |
170 | error_report("Failed to read msg payload." | |
ab7c5aaf | 171 | " Read %d instead of %d.", r, msg->size); |
5f6f6664 NN |
172 | goto fail; |
173 | } | |
174 | } | |
175 | ||
176 | return 0; | |
177 | ||
178 | fail: | |
179 | return -1; | |
180 | } | |
181 | ||
ca525ce5 | 182 | static int process_message_reply(struct vhost_dev *dev, |
3cf7daf8 | 183 | const VhostUserMsg *msg) |
ca525ce5 | 184 | { |
60cd1102 | 185 | VhostUserMsg msg_reply; |
ca525ce5 | 186 | |
3cf7daf8 | 187 | if ((msg->flags & VHOST_USER_NEED_REPLY_MASK) == 0) { |
60cd1102 ZY |
188 | return 0; |
189 | } | |
190 | ||
191 | if (vhost_user_read(dev, &msg_reply) < 0) { | |
ca525ce5 PS |
192 | return -1; |
193 | } | |
194 | ||
3cf7daf8 | 195 | if (msg_reply.request != msg->request) { |
ca525ce5 PS |
196 | error_report("Received unexpected msg type." |
197 | "Expected %d received %d", | |
3cf7daf8 | 198 | msg->request, msg_reply.request); |
ca525ce5 PS |
199 | return -1; |
200 | } | |
201 | ||
60cd1102 | 202 | return msg_reply.payload.u64 ? -1 : 0; |
ca525ce5 PS |
203 | } |
204 | ||
21e70425 MAL |
205 | static bool vhost_user_one_time_request(VhostUserRequest request) |
206 | { | |
207 | switch (request) { | |
208 | case VHOST_USER_SET_OWNER: | |
60915dc4 | 209 | case VHOST_USER_RESET_OWNER: |
21e70425 MAL |
210 | case VHOST_USER_SET_MEM_TABLE: |
211 | case VHOST_USER_GET_QUEUE_NUM: | |
c5f048d8 | 212 | case VHOST_USER_NET_SET_MTU: |
21e70425 MAL |
213 | return true; |
214 | default: | |
215 | return false; | |
216 | } | |
217 | } | |
218 | ||
219 | /* most non-init callers ignore the error */ | |
5f6f6664 NN |
220 | static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, |
221 | int *fds, int fd_num) | |
222 | { | |
2152f3fe MAL |
223 | struct vhost_user *u = dev->opaque; |
224 | CharBackend *chr = u->chr; | |
f6b85710 | 225 | int ret, size = VHOST_USER_HDR_SIZE + msg->size; |
5f6f6664 | 226 | |
21e70425 MAL |
227 | /* |
228 | * For non-vring specific requests, like VHOST_USER_SET_MEM_TABLE, | |
229 | * we just need send it once in the first time. For later such | |
230 | * request, we just ignore it. | |
231 | */ | |
232 | if (vhost_user_one_time_request(msg->request) && dev->vq_index != 0) { | |
60cd1102 | 233 | msg->flags &= ~VHOST_USER_NEED_REPLY_MASK; |
21e70425 MAL |
234 | return 0; |
235 | } | |
236 | ||
6fab2f3f | 237 | if (qemu_chr_fe_set_msgfds(chr, fds, fd_num) < 0) { |
f6b85710 | 238 | error_report("Failed to set msg fds."); |
6fab2f3f MAL |
239 | return -1; |
240 | } | |
5f6f6664 | 241 | |
f6b85710 MAL |
242 | ret = qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size); |
243 | if (ret != size) { | |
244 | error_report("Failed to write msg." | |
245 | " Wrote %d instead of %d.", ret, size); | |
246 | return -1; | |
247 | } | |
248 | ||
249 | return 0; | |
5f6f6664 NN |
250 | } |
251 | ||
21e70425 MAL |
252 | static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, |
253 | struct vhost_log *log) | |
b931bfbf | 254 | { |
21e70425 MAL |
255 | int fds[VHOST_MEMORY_MAX_NREGIONS]; |
256 | size_t fd_num = 0; | |
257 | bool shmfd = virtio_has_feature(dev->protocol_features, | |
258 | VHOST_USER_PROTOCOL_F_LOG_SHMFD); | |
259 | VhostUserMsg msg = { | |
260 | .request = VHOST_USER_SET_LOG_BASE, | |
261 | .flags = VHOST_USER_VERSION, | |
48854f57 | 262 | .payload.log.mmap_size = log->size * sizeof(*(log->log)), |
2b8819c6 VK |
263 | .payload.log.mmap_offset = 0, |
264 | .size = sizeof(msg.payload.log), | |
21e70425 MAL |
265 | }; |
266 | ||
267 | if (shmfd && log->fd != -1) { | |
268 | fds[fd_num++] = log->fd; | |
269 | } | |
270 | ||
c4843a45 MAL |
271 | if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { |
272 | return -1; | |
273 | } | |
21e70425 MAL |
274 | |
275 | if (shmfd) { | |
276 | msg.size = 0; | |
277 | if (vhost_user_read(dev, &msg) < 0) { | |
c4843a45 | 278 | return -1; |
21e70425 MAL |
279 | } |
280 | ||
281 | if (msg.request != VHOST_USER_SET_LOG_BASE) { | |
282 | error_report("Received unexpected msg type. " | |
283 | "Expected %d received %d", | |
284 | VHOST_USER_SET_LOG_BASE, msg.request); | |
285 | return -1; | |
286 | } | |
b931bfbf | 287 | } |
21e70425 MAL |
288 | |
289 | return 0; | |
b931bfbf CO |
290 | } |
291 | ||
94c9cb31 MT |
292 | static int vhost_user_set_mem_table(struct vhost_dev *dev, |
293 | struct vhost_memory *mem) | |
294 | { | |
295 | int fds[VHOST_MEMORY_MAX_NREGIONS]; | |
296 | int i, fd; | |
297 | size_t fd_num = 0; | |
298 | bool reply_supported = virtio_has_feature(dev->protocol_features, | |
299 | VHOST_USER_PROTOCOL_F_REPLY_ACK); | |
300 | ||
301 | VhostUserMsg msg = { | |
302 | .request = VHOST_USER_SET_MEM_TABLE, | |
303 | .flags = VHOST_USER_VERSION, | |
304 | }; | |
305 | ||
306 | if (reply_supported) { | |
307 | msg.flags |= VHOST_USER_NEED_REPLY_MASK; | |
308 | } | |
309 | ||
310 | for (i = 0; i < dev->mem->nregions; ++i) { | |
311 | struct vhost_memory_region *reg = dev->mem->regions + i; | |
312 | ram_addr_t offset; | |
313 | MemoryRegion *mr; | |
314 | ||
315 | assert((uintptr_t)reg->userspace_addr == reg->userspace_addr); | |
316 | mr = memory_region_from_host((void *)(uintptr_t)reg->userspace_addr, | |
317 | &offset); | |
318 | fd = memory_region_get_fd(mr); | |
319 | if (fd > 0) { | |
88bf4a70 JZ |
320 | if (fd_num == VHOST_MEMORY_MAX_NREGIONS) { |
321 | error_report("Failed preparing vhost-user memory table msg"); | |
322 | return -1; | |
323 | } | |
94c9cb31 MT |
324 | msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr; |
325 | msg.payload.memory.regions[fd_num].memory_size = reg->memory_size; | |
326 | msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr; | |
327 | msg.payload.memory.regions[fd_num].mmap_offset = offset; | |
94c9cb31 MT |
328 | fds[fd_num++] = fd; |
329 | } | |
330 | } | |
331 | ||
332 | msg.payload.memory.nregions = fd_num; | |
333 | ||
334 | if (!fd_num) { | |
335 | error_report("Failed initializing vhost-user memory map, " | |
336 | "consider using -object memory-backend-file share=on"); | |
337 | return -1; | |
338 | } | |
339 | ||
340 | msg.size = sizeof(msg.payload.memory.nregions); | |
341 | msg.size += sizeof(msg.payload.memory.padding); | |
342 | msg.size += fd_num * sizeof(VhostUserMemoryRegion); | |
343 | ||
344 | if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { | |
345 | return -1; | |
346 | } | |
347 | ||
348 | if (reply_supported) { | |
3cf7daf8 | 349 | return process_message_reply(dev, &msg); |
94c9cb31 MT |
350 | } |
351 | ||
352 | return 0; | |
353 | } | |
354 | ||
21e70425 MAL |
355 | static int vhost_user_set_vring_addr(struct vhost_dev *dev, |
356 | struct vhost_vring_addr *addr) | |
357 | { | |
358 | VhostUserMsg msg = { | |
359 | .request = VHOST_USER_SET_VRING_ADDR, | |
360 | .flags = VHOST_USER_VERSION, | |
7f4a930e | 361 | .payload.addr = *addr, |
7fc0246c | 362 | .size = sizeof(msg.payload.addr), |
21e70425 | 363 | }; |
5f6f6664 | 364 | |
c4843a45 MAL |
365 | if (vhost_user_write(dev, &msg, NULL, 0) < 0) { |
366 | return -1; | |
367 | } | |
5f6f6664 | 368 | |
21e70425 MAL |
369 | return 0; |
370 | } | |
5f6f6664 | 371 | |
21e70425 MAL |
372 | static int vhost_user_set_vring_endian(struct vhost_dev *dev, |
373 | struct vhost_vring_state *ring) | |
374 | { | |
5df04f17 FF |
375 | bool cross_endian = virtio_has_feature(dev->protocol_features, |
376 | VHOST_USER_PROTOCOL_F_CROSS_ENDIAN); | |
377 | VhostUserMsg msg = { | |
378 | .request = VHOST_USER_SET_VRING_ENDIAN, | |
379 | .flags = VHOST_USER_VERSION, | |
380 | .payload.state = *ring, | |
381 | .size = sizeof(msg.payload.state), | |
382 | }; | |
383 | ||
384 | if (!cross_endian) { | |
385 | error_report("vhost-user trying to send unhandled ioctl"); | |
386 | return -1; | |
387 | } | |
388 | ||
389 | if (vhost_user_write(dev, &msg, NULL, 0) < 0) { | |
390 | return -1; | |
391 | } | |
392 | ||
393 | return 0; | |
21e70425 | 394 | } |
5f6f6664 | 395 | |
21e70425 MAL |
396 | static int vhost_set_vring(struct vhost_dev *dev, |
397 | unsigned long int request, | |
398 | struct vhost_vring_state *ring) | |
399 | { | |
400 | VhostUserMsg msg = { | |
401 | .request = request, | |
402 | .flags = VHOST_USER_VERSION, | |
7f4a930e | 403 | .payload.state = *ring, |
7fc0246c | 404 | .size = sizeof(msg.payload.state), |
21e70425 MAL |
405 | }; |
406 | ||
c4843a45 MAL |
407 | if (vhost_user_write(dev, &msg, NULL, 0) < 0) { |
408 | return -1; | |
409 | } | |
21e70425 MAL |
410 | |
411 | return 0; | |
412 | } | |
413 | ||
414 | static int vhost_user_set_vring_num(struct vhost_dev *dev, | |
415 | struct vhost_vring_state *ring) | |
416 | { | |
417 | return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring); | |
418 | } | |
419 | ||
420 | static int vhost_user_set_vring_base(struct vhost_dev *dev, | |
421 | struct vhost_vring_state *ring) | |
422 | { | |
423 | return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring); | |
424 | } | |
425 | ||
426 | static int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable) | |
427 | { | |
dc3db6ad | 428 | int i; |
21e70425 | 429 | |
923e2d98 | 430 | if (!virtio_has_feature(dev->features, VHOST_USER_F_PROTOCOL_FEATURES)) { |
5f6f6664 | 431 | return -1; |
5f6f6664 NN |
432 | } |
433 | ||
dc3db6ad MT |
434 | for (i = 0; i < dev->nvqs; ++i) { |
435 | struct vhost_vring_state state = { | |
436 | .index = dev->vq_index + i, | |
437 | .num = enable, | |
438 | }; | |
439 | ||
440 | vhost_set_vring(dev, VHOST_USER_SET_VRING_ENABLE, &state); | |
441 | } | |
21e70425 | 442 | |
dc3db6ad MT |
443 | return 0; |
444 | } | |
21e70425 MAL |
445 | |
446 | static int vhost_user_get_vring_base(struct vhost_dev *dev, | |
447 | struct vhost_vring_state *ring) | |
448 | { | |
449 | VhostUserMsg msg = { | |
450 | .request = VHOST_USER_GET_VRING_BASE, | |
451 | .flags = VHOST_USER_VERSION, | |
7f4a930e | 452 | .payload.state = *ring, |
7fc0246c | 453 | .size = sizeof(msg.payload.state), |
21e70425 MAL |
454 | }; |
455 | ||
c4843a45 MAL |
456 | if (vhost_user_write(dev, &msg, NULL, 0) < 0) { |
457 | return -1; | |
458 | } | |
21e70425 MAL |
459 | |
460 | if (vhost_user_read(dev, &msg) < 0) { | |
c4843a45 | 461 | return -1; |
5f6f6664 NN |
462 | } |
463 | ||
21e70425 MAL |
464 | if (msg.request != VHOST_USER_GET_VRING_BASE) { |
465 | error_report("Received unexpected msg type. Expected %d received %d", | |
466 | VHOST_USER_GET_VRING_BASE, msg.request); | |
467 | return -1; | |
468 | } | |
5f6f6664 | 469 | |
86abad0f | 470 | if (msg.size != sizeof(msg.payload.state)) { |
21e70425 MAL |
471 | error_report("Received bad msg size."); |
472 | return -1; | |
5f6f6664 NN |
473 | } |
474 | ||
7f4a930e | 475 | *ring = msg.payload.state; |
21e70425 | 476 | |
5f6f6664 NN |
477 | return 0; |
478 | } | |
479 | ||
21e70425 MAL |
480 | static int vhost_set_vring_file(struct vhost_dev *dev, |
481 | VhostUserRequest request, | |
482 | struct vhost_vring_file *file) | |
c2bea314 | 483 | { |
9a78a5dd MAL |
484 | int fds[VHOST_MEMORY_MAX_NREGIONS]; |
485 | size_t fd_num = 0; | |
c2bea314 | 486 | VhostUserMsg msg = { |
21e70425 | 487 | .request = request, |
c2bea314 | 488 | .flags = VHOST_USER_VERSION, |
7f4a930e | 489 | .payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK, |
86abad0f | 490 | .size = sizeof(msg.payload.u64), |
c2bea314 MAL |
491 | }; |
492 | ||
21e70425 MAL |
493 | if (ioeventfd_enabled() && file->fd > 0) { |
494 | fds[fd_num++] = file->fd; | |
495 | } else { | |
7f4a930e | 496 | msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK; |
9a78a5dd MAL |
497 | } |
498 | ||
c4843a45 MAL |
499 | if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { |
500 | return -1; | |
501 | } | |
9a78a5dd | 502 | |
21e70425 MAL |
503 | return 0; |
504 | } | |
9a78a5dd | 505 | |
21e70425 MAL |
506 | static int vhost_user_set_vring_kick(struct vhost_dev *dev, |
507 | struct vhost_vring_file *file) | |
508 | { | |
509 | return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, file); | |
510 | } | |
511 | ||
512 | static int vhost_user_set_vring_call(struct vhost_dev *dev, | |
513 | struct vhost_vring_file *file) | |
514 | { | |
515 | return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, file); | |
516 | } | |
517 | ||
518 | static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64) | |
519 | { | |
520 | VhostUserMsg msg = { | |
521 | .request = request, | |
522 | .flags = VHOST_USER_VERSION, | |
7f4a930e | 523 | .payload.u64 = u64, |
86abad0f | 524 | .size = sizeof(msg.payload.u64), |
21e70425 MAL |
525 | }; |
526 | ||
c4843a45 MAL |
527 | if (vhost_user_write(dev, &msg, NULL, 0) < 0) { |
528 | return -1; | |
529 | } | |
21e70425 MAL |
530 | |
531 | return 0; | |
532 | } | |
533 | ||
534 | static int vhost_user_set_features(struct vhost_dev *dev, | |
535 | uint64_t features) | |
536 | { | |
537 | return vhost_user_set_u64(dev, VHOST_USER_SET_FEATURES, features); | |
538 | } | |
539 | ||
540 | static int vhost_user_set_protocol_features(struct vhost_dev *dev, | |
541 | uint64_t features) | |
542 | { | |
543 | return vhost_user_set_u64(dev, VHOST_USER_SET_PROTOCOL_FEATURES, features); | |
544 | } | |
545 | ||
546 | static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64) | |
547 | { | |
548 | VhostUserMsg msg = { | |
549 | .request = request, | |
550 | .flags = VHOST_USER_VERSION, | |
551 | }; | |
552 | ||
553 | if (vhost_user_one_time_request(request) && dev->vq_index != 0) { | |
554 | return 0; | |
9a78a5dd | 555 | } |
c2bea314 | 556 | |
c4843a45 MAL |
557 | if (vhost_user_write(dev, &msg, NULL, 0) < 0) { |
558 | return -1; | |
559 | } | |
21e70425 MAL |
560 | |
561 | if (vhost_user_read(dev, &msg) < 0) { | |
c4843a45 | 562 | return -1; |
21e70425 MAL |
563 | } |
564 | ||
565 | if (msg.request != request) { | |
566 | error_report("Received unexpected msg type. Expected %d received %d", | |
567 | request, msg.request); | |
568 | return -1; | |
569 | } | |
570 | ||
86abad0f | 571 | if (msg.size != sizeof(msg.payload.u64)) { |
21e70425 MAL |
572 | error_report("Received bad msg size."); |
573 | return -1; | |
574 | } | |
575 | ||
7f4a930e | 576 | *u64 = msg.payload.u64; |
21e70425 MAL |
577 | |
578 | return 0; | |
579 | } | |
580 | ||
581 | static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features) | |
582 | { | |
583 | return vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features); | |
584 | } | |
585 | ||
586 | static int vhost_user_set_owner(struct vhost_dev *dev) | |
587 | { | |
588 | VhostUserMsg msg = { | |
589 | .request = VHOST_USER_SET_OWNER, | |
590 | .flags = VHOST_USER_VERSION, | |
591 | }; | |
592 | ||
c4843a45 MAL |
593 | if (vhost_user_write(dev, &msg, NULL, 0) < 0) { |
594 | return -1; | |
595 | } | |
21e70425 MAL |
596 | |
597 | return 0; | |
598 | } | |
599 | ||
600 | static int vhost_user_reset_device(struct vhost_dev *dev) | |
601 | { | |
602 | VhostUserMsg msg = { | |
60915dc4 | 603 | .request = VHOST_USER_RESET_OWNER, |
21e70425 MAL |
604 | .flags = VHOST_USER_VERSION, |
605 | }; | |
606 | ||
c4843a45 MAL |
607 | if (vhost_user_write(dev, &msg, NULL, 0) < 0) { |
608 | return -1; | |
609 | } | |
21e70425 | 610 | |
c2bea314 MAL |
611 | return 0; |
612 | } | |
613 | ||
4bbeeba0 MAL |
614 | static void slave_read(void *opaque) |
615 | { | |
616 | struct vhost_dev *dev = opaque; | |
617 | struct vhost_user *u = dev->opaque; | |
618 | VhostUserMsg msg = { 0, }; | |
619 | int size, ret = 0; | |
620 | ||
621 | /* Read header */ | |
622 | size = read(u->slave_fd, &msg, VHOST_USER_HDR_SIZE); | |
623 | if (size != VHOST_USER_HDR_SIZE) { | |
624 | error_report("Failed to read from slave."); | |
625 | goto err; | |
626 | } | |
627 | ||
628 | if (msg.size > VHOST_USER_PAYLOAD_SIZE) { | |
629 | error_report("Failed to read msg header." | |
630 | " Size %d exceeds the maximum %zu.", msg.size, | |
631 | VHOST_USER_PAYLOAD_SIZE); | |
632 | goto err; | |
633 | } | |
634 | ||
635 | /* Read payload */ | |
636 | size = read(u->slave_fd, &msg.payload, msg.size); | |
637 | if (size != msg.size) { | |
638 | error_report("Failed to read payload from slave."); | |
639 | goto err; | |
640 | } | |
641 | ||
642 | switch (msg.request) { | |
6dcdd06e MC |
643 | case VHOST_USER_SLAVE_IOTLB_MSG: |
644 | ret = vhost_backend_handle_iotlb_msg(dev, &msg.payload.iotlb); | |
645 | break; | |
4bbeeba0 MAL |
646 | default: |
647 | error_report("Received unexpected msg type."); | |
648 | ret = -EINVAL; | |
649 | } | |
650 | ||
651 | /* | |
652 | * REPLY_ACK feature handling. Other reply types has to be managed | |
653 | * directly in their request handlers. | |
654 | */ | |
655 | if (msg.flags & VHOST_USER_NEED_REPLY_MASK) { | |
656 | msg.flags &= ~VHOST_USER_NEED_REPLY_MASK; | |
657 | msg.flags |= VHOST_USER_REPLY_MASK; | |
658 | ||
659 | msg.payload.u64 = !!ret; | |
660 | msg.size = sizeof(msg.payload.u64); | |
661 | ||
662 | size = write(u->slave_fd, &msg, VHOST_USER_HDR_SIZE + msg.size); | |
663 | if (size != VHOST_USER_HDR_SIZE + msg.size) { | |
664 | error_report("Failed to send msg reply to slave."); | |
665 | goto err; | |
666 | } | |
667 | } | |
668 | ||
669 | return; | |
670 | ||
671 | err: | |
672 | qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); | |
673 | close(u->slave_fd); | |
674 | u->slave_fd = -1; | |
675 | return; | |
676 | } | |
677 | ||
678 | static int vhost_setup_slave_channel(struct vhost_dev *dev) | |
679 | { | |
680 | VhostUserMsg msg = { | |
681 | .request = VHOST_USER_SET_SLAVE_REQ_FD, | |
682 | .flags = VHOST_USER_VERSION, | |
683 | }; | |
684 | struct vhost_user *u = dev->opaque; | |
685 | int sv[2], ret = 0; | |
686 | bool reply_supported = virtio_has_feature(dev->protocol_features, | |
687 | VHOST_USER_PROTOCOL_F_REPLY_ACK); | |
688 | ||
689 | if (!virtio_has_feature(dev->protocol_features, | |
690 | VHOST_USER_PROTOCOL_F_SLAVE_REQ)) { | |
691 | return 0; | |
692 | } | |
693 | ||
694 | if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { | |
695 | error_report("socketpair() failed"); | |
696 | return -1; | |
697 | } | |
698 | ||
699 | u->slave_fd = sv[0]; | |
700 | qemu_set_fd_handler(u->slave_fd, slave_read, NULL, dev); | |
701 | ||
702 | if (reply_supported) { | |
703 | msg.flags |= VHOST_USER_NEED_REPLY_MASK; | |
704 | } | |
705 | ||
706 | ret = vhost_user_write(dev, &msg, &sv[1], 1); | |
707 | if (ret) { | |
708 | goto out; | |
709 | } | |
710 | ||
711 | if (reply_supported) { | |
712 | ret = process_message_reply(dev, &msg); | |
713 | } | |
714 | ||
715 | out: | |
716 | close(sv[1]); | |
717 | if (ret) { | |
718 | qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); | |
719 | close(u->slave_fd); | |
720 | u->slave_fd = -1; | |
721 | } | |
722 | ||
723 | return ret; | |
724 | } | |
725 | ||
5f6f6664 NN |
726 | static int vhost_user_init(struct vhost_dev *dev, void *opaque) |
727 | { | |
6dcdd06e | 728 | uint64_t features, protocol_features; |
2152f3fe | 729 | struct vhost_user *u; |
dcb10c00 MT |
730 | int err; |
731 | ||
5f6f6664 NN |
732 | assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); |
733 | ||
2152f3fe MAL |
734 | u = g_new0(struct vhost_user, 1); |
735 | u->chr = opaque; | |
4bbeeba0 | 736 | u->slave_fd = -1; |
2152f3fe | 737 | dev->opaque = u; |
5f6f6664 | 738 | |
21e70425 | 739 | err = vhost_user_get_features(dev, &features); |
dcb10c00 MT |
740 | if (err < 0) { |
741 | return err; | |
742 | } | |
743 | ||
744 | if (virtio_has_feature(features, VHOST_USER_F_PROTOCOL_FEATURES)) { | |
745 | dev->backend_features |= 1ULL << VHOST_USER_F_PROTOCOL_FEATURES; | |
746 | ||
21e70425 | 747 | err = vhost_user_get_u64(dev, VHOST_USER_GET_PROTOCOL_FEATURES, |
6dcdd06e | 748 | &protocol_features); |
dcb10c00 MT |
749 | if (err < 0) { |
750 | return err; | |
751 | } | |
752 | ||
6dcdd06e MC |
753 | dev->protocol_features = |
754 | protocol_features & VHOST_USER_PROTOCOL_FEATURE_MASK; | |
21e70425 | 755 | err = vhost_user_set_protocol_features(dev, dev->protocol_features); |
dcb10c00 MT |
756 | if (err < 0) { |
757 | return err; | |
758 | } | |
e2051e9e YL |
759 | |
760 | /* query the max queues we support if backend supports Multiple Queue */ | |
761 | if (dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_MQ)) { | |
21e70425 MAL |
762 | err = vhost_user_get_u64(dev, VHOST_USER_GET_QUEUE_NUM, |
763 | &dev->max_queues); | |
e2051e9e YL |
764 | if (err < 0) { |
765 | return err; | |
766 | } | |
767 | } | |
6dcdd06e MC |
768 | |
769 | if (virtio_has_feature(features, VIRTIO_F_IOMMU_PLATFORM) && | |
770 | !(virtio_has_feature(dev->protocol_features, | |
771 | VHOST_USER_PROTOCOL_F_SLAVE_REQ) && | |
772 | virtio_has_feature(dev->protocol_features, | |
773 | VHOST_USER_PROTOCOL_F_REPLY_ACK))) { | |
774 | error_report("IOMMU support requires reply-ack and " | |
775 | "slave-req protocol features."); | |
776 | return -1; | |
777 | } | |
dcb10c00 MT |
778 | } |
779 | ||
d2fc4402 MAL |
780 | if (dev->migration_blocker == NULL && |
781 | !virtio_has_feature(dev->protocol_features, | |
782 | VHOST_USER_PROTOCOL_F_LOG_SHMFD)) { | |
783 | error_setg(&dev->migration_blocker, | |
784 | "Migration disabled: vhost-user backend lacks " | |
785 | "VHOST_USER_PROTOCOL_F_LOG_SHMFD feature."); | |
786 | } | |
787 | ||
4bbeeba0 MAL |
788 | err = vhost_setup_slave_channel(dev); |
789 | if (err < 0) { | |
790 | return err; | |
791 | } | |
792 | ||
5f6f6664 NN |
793 | return 0; |
794 | } | |
795 | ||
796 | static int vhost_user_cleanup(struct vhost_dev *dev) | |
797 | { | |
2152f3fe MAL |
798 | struct vhost_user *u; |
799 | ||
5f6f6664 NN |
800 | assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); |
801 | ||
2152f3fe | 802 | u = dev->opaque; |
4bbeeba0 | 803 | if (u->slave_fd >= 0) { |
b9ec9bd4 | 804 | qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); |
4bbeeba0 MAL |
805 | close(u->slave_fd); |
806 | u->slave_fd = -1; | |
807 | } | |
2152f3fe | 808 | g_free(u); |
5f6f6664 NN |
809 | dev->opaque = 0; |
810 | ||
811 | return 0; | |
812 | } | |
813 | ||
fc57fd99 YL |
814 | static int vhost_user_get_vq_index(struct vhost_dev *dev, int idx) |
815 | { | |
816 | assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); | |
817 | ||
818 | return idx; | |
819 | } | |
820 | ||
2ce68e4c IM |
821 | static int vhost_user_memslots_limit(struct vhost_dev *dev) |
822 | { | |
823 | return VHOST_MEMORY_MAX_NREGIONS; | |
824 | } | |
825 | ||
1be0ac21 MAL |
826 | static bool vhost_user_requires_shm_log(struct vhost_dev *dev) |
827 | { | |
828 | assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); | |
829 | ||
830 | return virtio_has_feature(dev->protocol_features, | |
831 | VHOST_USER_PROTOCOL_F_LOG_SHMFD); | |
832 | } | |
833 | ||
3e866365 TC |
834 | static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) |
835 | { | |
836 | VhostUserMsg msg = { 0 }; | |
3e866365 TC |
837 | |
838 | assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); | |
839 | ||
840 | /* If guest supports GUEST_ANNOUNCE do nothing */ | |
841 | if (virtio_has_feature(dev->acked_features, VIRTIO_NET_F_GUEST_ANNOUNCE)) { | |
842 | return 0; | |
843 | } | |
844 | ||
845 | /* if backend supports VHOST_USER_PROTOCOL_F_RARP ask it to send the RARP */ | |
846 | if (virtio_has_feature(dev->protocol_features, | |
847 | VHOST_USER_PROTOCOL_F_RARP)) { | |
848 | msg.request = VHOST_USER_SEND_RARP; | |
849 | msg.flags = VHOST_USER_VERSION; | |
7f4a930e | 850 | memcpy((char *)&msg.payload.u64, mac_addr, 6); |
86abad0f | 851 | msg.size = sizeof(msg.payload.u64); |
3e866365 | 852 | |
c4843a45 | 853 | return vhost_user_write(dev, &msg, NULL, 0); |
3e866365 TC |
854 | } |
855 | return -1; | |
856 | } | |
857 | ||
ffe42cc1 MT |
858 | static bool vhost_user_can_merge(struct vhost_dev *dev, |
859 | uint64_t start1, uint64_t size1, | |
860 | uint64_t start2, uint64_t size2) | |
861 | { | |
07bdaa41 | 862 | ram_addr_t offset; |
ffe42cc1 MT |
863 | int mfd, rfd; |
864 | MemoryRegion *mr; | |
865 | ||
07bdaa41 | 866 | mr = memory_region_from_host((void *)(uintptr_t)start1, &offset); |
4ff87573 | 867 | mfd = memory_region_get_fd(mr); |
ffe42cc1 | 868 | |
07bdaa41 | 869 | mr = memory_region_from_host((void *)(uintptr_t)start2, &offset); |
4ff87573 | 870 | rfd = memory_region_get_fd(mr); |
ffe42cc1 MT |
871 | |
872 | return mfd == rfd; | |
873 | } | |
874 | ||
c5f048d8 MC |
875 | static int vhost_user_net_set_mtu(struct vhost_dev *dev, uint16_t mtu) |
876 | { | |
877 | VhostUserMsg msg; | |
878 | bool reply_supported = virtio_has_feature(dev->protocol_features, | |
879 | VHOST_USER_PROTOCOL_F_REPLY_ACK); | |
880 | ||
881 | if (!(dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_NET_MTU))) { | |
882 | return 0; | |
883 | } | |
884 | ||
885 | msg.request = VHOST_USER_NET_SET_MTU; | |
886 | msg.payload.u64 = mtu; | |
887 | msg.size = sizeof(msg.payload.u64); | |
888 | msg.flags = VHOST_USER_VERSION; | |
889 | if (reply_supported) { | |
890 | msg.flags |= VHOST_USER_NEED_REPLY_MASK; | |
891 | } | |
892 | ||
893 | if (vhost_user_write(dev, &msg, NULL, 0) < 0) { | |
894 | return -1; | |
895 | } | |
896 | ||
897 | /* If reply_ack supported, slave has to ack specified MTU is valid */ | |
898 | if (reply_supported) { | |
3cf7daf8 | 899 | return process_message_reply(dev, &msg); |
c5f048d8 MC |
900 | } |
901 | ||
902 | return 0; | |
903 | } | |
904 | ||
6dcdd06e MC |
905 | static int vhost_user_send_device_iotlb_msg(struct vhost_dev *dev, |
906 | struct vhost_iotlb_msg *imsg) | |
907 | { | |
908 | VhostUserMsg msg = { | |
909 | .request = VHOST_USER_IOTLB_MSG, | |
910 | .size = sizeof(msg.payload.iotlb), | |
911 | .flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK, | |
912 | .payload.iotlb = *imsg, | |
913 | }; | |
914 | ||
915 | if (vhost_user_write(dev, &msg, NULL, 0) < 0) { | |
916 | return -EFAULT; | |
917 | } | |
918 | ||
919 | return process_message_reply(dev, &msg); | |
920 | } | |
921 | ||
922 | ||
923 | static void vhost_user_set_iotlb_callback(struct vhost_dev *dev, int enabled) | |
924 | { | |
925 | /* No-op as the receive channel is not dedicated to IOTLB messages. */ | |
926 | } | |
927 | ||
5f6f6664 NN |
928 | const VhostOps user_ops = { |
929 | .backend_type = VHOST_BACKEND_TYPE_USER, | |
5f6f6664 | 930 | .vhost_backend_init = vhost_user_init, |
fc57fd99 | 931 | .vhost_backend_cleanup = vhost_user_cleanup, |
2ce68e4c | 932 | .vhost_backend_memslots_limit = vhost_user_memslots_limit, |
21e70425 MAL |
933 | .vhost_set_log_base = vhost_user_set_log_base, |
934 | .vhost_set_mem_table = vhost_user_set_mem_table, | |
935 | .vhost_set_vring_addr = vhost_user_set_vring_addr, | |
936 | .vhost_set_vring_endian = vhost_user_set_vring_endian, | |
937 | .vhost_set_vring_num = vhost_user_set_vring_num, | |
938 | .vhost_set_vring_base = vhost_user_set_vring_base, | |
939 | .vhost_get_vring_base = vhost_user_get_vring_base, | |
940 | .vhost_set_vring_kick = vhost_user_set_vring_kick, | |
941 | .vhost_set_vring_call = vhost_user_set_vring_call, | |
942 | .vhost_set_features = vhost_user_set_features, | |
943 | .vhost_get_features = vhost_user_get_features, | |
944 | .vhost_set_owner = vhost_user_set_owner, | |
945 | .vhost_reset_device = vhost_user_reset_device, | |
946 | .vhost_get_vq_index = vhost_user_get_vq_index, | |
947 | .vhost_set_vring_enable = vhost_user_set_vring_enable, | |
1be0ac21 | 948 | .vhost_requires_shm_log = vhost_user_requires_shm_log, |
3e866365 | 949 | .vhost_migration_done = vhost_user_migration_done, |
ffe42cc1 | 950 | .vhost_backend_can_merge = vhost_user_can_merge, |
c5f048d8 | 951 | .vhost_net_set_mtu = vhost_user_net_set_mtu, |
6dcdd06e MC |
952 | .vhost_set_iotlb_callback = vhost_user_set_iotlb_callback, |
953 | .vhost_send_device_iotlb_msg = vhost_user_send_device_iotlb_msg, | |
fc57fd99 | 954 | }; |