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 11 #include "qemu/osdep.h" 12 #include "qapi/error.h" 13 #include "hw/virtio/vhost.h" 14 #include "hw/virtio/vhost-backend.h" 15 #include "hw/virtio/virtio-net.h" 16 #include "sysemu/char.h" 17 #include "sysemu/kvm.h" 18 #include "qemu/error-report.h" 19 #include "qemu/sockets.h" 20 #include "migration/migration.h" 21 22 #include <sys/ioctl.h> 23 #include <sys/socket.h> 24 #include <sys/un.h> 25 #include <linux/vhost.h> 26 27 #define VHOST_MEMORY_MAX_NREGIONS 8 28 #define VHOST_USER_F_PROTOCOL_FEATURES 30 29 30 enum VhostUserProtocolFeature { 31 VHOST_USER_PROTOCOL_F_MQ = 0, 32 VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, 33 VHOST_USER_PROTOCOL_F_RARP = 2, 34 VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, 35 36 VHOST_USER_PROTOCOL_F_MAX 37 }; 38 39 #define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1) 40 41 typedef enum VhostUserRequest { 42 VHOST_USER_NONE = 0, 43 VHOST_USER_GET_FEATURES = 1, 44 VHOST_USER_SET_FEATURES = 2, 45 VHOST_USER_SET_OWNER = 3, 46 VHOST_USER_RESET_OWNER = 4, 47 VHOST_USER_SET_MEM_TABLE = 5, 48 VHOST_USER_SET_LOG_BASE = 6, 49 VHOST_USER_SET_LOG_FD = 7, 50 VHOST_USER_SET_VRING_NUM = 8, 51 VHOST_USER_SET_VRING_ADDR = 9, 52 VHOST_USER_SET_VRING_BASE = 10, 53 VHOST_USER_GET_VRING_BASE = 11, 54 VHOST_USER_SET_VRING_KICK = 12, 55 VHOST_USER_SET_VRING_CALL = 13, 56 VHOST_USER_SET_VRING_ERR = 14, 57 VHOST_USER_GET_PROTOCOL_FEATURES = 15, 58 VHOST_USER_SET_PROTOCOL_FEATURES = 16, 59 VHOST_USER_GET_QUEUE_NUM = 17, 60 VHOST_USER_SET_VRING_ENABLE = 18, 61 VHOST_USER_SEND_RARP = 19, 62 VHOST_USER_MAX 63 } VhostUserRequest; 64 65 typedef struct VhostUserMemoryRegion { 66 uint64_t guest_phys_addr; 67 uint64_t memory_size; 68 uint64_t userspace_addr; 69 uint64_t mmap_offset; 70 } VhostUserMemoryRegion; 71 72 typedef struct VhostUserMemory { 73 uint32_t nregions; 74 uint32_t padding; 75 VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; 76 } VhostUserMemory; 77 78 typedef struct VhostUserLog { 79 uint64_t mmap_size; 80 uint64_t mmap_offset; 81 } VhostUserLog; 82 83 typedef struct VhostUserMsg { 84 VhostUserRequest request; 85 86 #define VHOST_USER_VERSION_MASK (0x3) 87 #define VHOST_USER_REPLY_MASK (0x1<<2) 88 #define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) 89 uint32_t flags; 90 uint32_t size; /* the following payload size */ 91 union { 92 #define VHOST_USER_VRING_IDX_MASK (0xff) 93 #define VHOST_USER_VRING_NOFD_MASK (0x1<<8) 94 uint64_t u64; 95 struct vhost_vring_state state; 96 struct vhost_vring_addr addr; 97 VhostUserMemory memory; 98 VhostUserLog log; 99 } payload; 100 } QEMU_PACKED VhostUserMsg; 101 102 static VhostUserMsg m __attribute__ ((unused)); 103 #define VHOST_USER_HDR_SIZE (sizeof(m.request) \ 104 + sizeof(m.flags) \ 105 + sizeof(m.size)) 106 107 #define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) 108 109 /* The version of the protocol we support */ 110 #define VHOST_USER_VERSION (0x1) 111 112 static bool ioeventfd_enabled(void) 113 { 114 return kvm_enabled() && kvm_eventfds_enabled(); 115 } 116 117 static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) 118 { 119 CharDriverState *chr = dev->opaque; 120 uint8_t *p = (uint8_t *) msg; 121 int r, size = VHOST_USER_HDR_SIZE; 122 123 r = qemu_chr_fe_read_all(chr, p, size); 124 if (r != size) { 125 error_report("Failed to read msg header. Read %d instead of %d." 126 " Original request %d.", r, size, msg->request); 127 goto fail; 128 } 129 130 /* validate received flags */ 131 if (msg->flags != (VHOST_USER_REPLY_MASK | VHOST_USER_VERSION)) { 132 error_report("Failed to read msg header." 133 " Flags 0x%x instead of 0x%x.", msg->flags, 134 VHOST_USER_REPLY_MASK | VHOST_USER_VERSION); 135 goto fail; 136 } 137 138 /* validate message size is sane */ 139 if (msg->size > VHOST_USER_PAYLOAD_SIZE) { 140 error_report("Failed to read msg header." 141 " Size %d exceeds the maximum %zu.", msg->size, 142 VHOST_USER_PAYLOAD_SIZE); 143 goto fail; 144 } 145 146 if (msg->size) { 147 p += VHOST_USER_HDR_SIZE; 148 size = msg->size; 149 r = qemu_chr_fe_read_all(chr, p, size); 150 if (r != size) { 151 error_report("Failed to read msg payload." 152 " Read %d instead of %d.", r, msg->size); 153 goto fail; 154 } 155 } 156 157 return 0; 158 159 fail: 160 return -1; 161 } 162 163 static int process_message_reply(struct vhost_dev *dev, 164 VhostUserRequest request) 165 { 166 VhostUserMsg msg; 167 168 if (vhost_user_read(dev, &msg) < 0) { 169 return -1; 170 } 171 172 if (msg.request != request) { 173 error_report("Received unexpected msg type." 174 "Expected %d received %d", 175 request, msg.request); 176 return -1; 177 } 178 179 return msg.payload.u64 ? -1 : 0; 180 } 181 182 static bool vhost_user_one_time_request(VhostUserRequest request) 183 { 184 switch (request) { 185 case VHOST_USER_SET_OWNER: 186 case VHOST_USER_RESET_OWNER: 187 case VHOST_USER_SET_MEM_TABLE: 188 case VHOST_USER_GET_QUEUE_NUM: 189 return true; 190 default: 191 return false; 192 } 193 } 194 195 /* most non-init callers ignore the error */ 196 static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, 197 int *fds, int fd_num) 198 { 199 CharDriverState *chr = dev->opaque; 200 int ret, size = VHOST_USER_HDR_SIZE + msg->size; 201 202 /* 203 * For non-vring specific requests, like VHOST_USER_SET_MEM_TABLE, 204 * we just need send it once in the first time. For later such 205 * request, we just ignore it. 206 */ 207 if (vhost_user_one_time_request(msg->request) && dev->vq_index != 0) { 208 return 0; 209 } 210 211 if (qemu_chr_fe_set_msgfds(chr, fds, fd_num) < 0) { 212 error_report("Failed to set msg fds."); 213 return -1; 214 } 215 216 ret = qemu_chr_fe_write_all(chr, (const uint8_t *) msg, size); 217 if (ret != size) { 218 error_report("Failed to write msg." 219 " Wrote %d instead of %d.", ret, size); 220 return -1; 221 } 222 223 return 0; 224 } 225 226 static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, 227 struct vhost_log *log) 228 { 229 int fds[VHOST_MEMORY_MAX_NREGIONS]; 230 size_t fd_num = 0; 231 bool shmfd = virtio_has_feature(dev->protocol_features, 232 VHOST_USER_PROTOCOL_F_LOG_SHMFD); 233 VhostUserMsg msg = { 234 .request = VHOST_USER_SET_LOG_BASE, 235 .flags = VHOST_USER_VERSION, 236 .payload.log.mmap_size = log->size * sizeof(*(log->log)), 237 .payload.log.mmap_offset = 0, 238 .size = sizeof(msg.payload.log), 239 }; 240 241 if (shmfd && log->fd != -1) { 242 fds[fd_num++] = log->fd; 243 } 244 245 if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { 246 return -1; 247 } 248 249 if (shmfd) { 250 msg.size = 0; 251 if (vhost_user_read(dev, &msg) < 0) { 252 return -1; 253 } 254 255 if (msg.request != VHOST_USER_SET_LOG_BASE) { 256 error_report("Received unexpected msg type. " 257 "Expected %d received %d", 258 VHOST_USER_SET_LOG_BASE, msg.request); 259 return -1; 260 } 261 } 262 263 return 0; 264 } 265 266 static int vhost_user_set_mem_table(struct vhost_dev *dev, 267 struct vhost_memory *mem) 268 { 269 int fds[VHOST_MEMORY_MAX_NREGIONS]; 270 int i, fd; 271 size_t fd_num = 0; 272 bool reply_supported = virtio_has_feature(dev->protocol_features, 273 VHOST_USER_PROTOCOL_F_REPLY_ACK); 274 275 VhostUserMsg msg = { 276 .request = VHOST_USER_SET_MEM_TABLE, 277 .flags = VHOST_USER_VERSION, 278 }; 279 280 if (reply_supported) { 281 msg.flags |= VHOST_USER_NEED_REPLY_MASK; 282 } 283 284 for (i = 0; i < dev->mem->nregions; ++i) { 285 struct vhost_memory_region *reg = dev->mem->regions + i; 286 ram_addr_t offset; 287 MemoryRegion *mr; 288 289 assert((uintptr_t)reg->userspace_addr == reg->userspace_addr); 290 mr = memory_region_from_host((void *)(uintptr_t)reg->userspace_addr, 291 &offset); 292 fd = memory_region_get_fd(mr); 293 if (fd > 0) { 294 msg.payload.memory.regions[fd_num].userspace_addr = reg->userspace_addr; 295 msg.payload.memory.regions[fd_num].memory_size = reg->memory_size; 296 msg.payload.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr; 297 msg.payload.memory.regions[fd_num].mmap_offset = offset; 298 assert(fd_num < VHOST_MEMORY_MAX_NREGIONS); 299 fds[fd_num++] = fd; 300 } 301 } 302 303 msg.payload.memory.nregions = fd_num; 304 305 if (!fd_num) { 306 error_report("Failed initializing vhost-user memory map, " 307 "consider using -object memory-backend-file share=on"); 308 return -1; 309 } 310 311 msg.size = sizeof(msg.payload.memory.nregions); 312 msg.size += sizeof(msg.payload.memory.padding); 313 msg.size += fd_num * sizeof(VhostUserMemoryRegion); 314 315 if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { 316 return -1; 317 } 318 319 if (reply_supported) { 320 return process_message_reply(dev, msg.request); 321 } 322 323 return 0; 324 } 325 326 static int vhost_user_set_vring_addr(struct vhost_dev *dev, 327 struct vhost_vring_addr *addr) 328 { 329 VhostUserMsg msg = { 330 .request = VHOST_USER_SET_VRING_ADDR, 331 .flags = VHOST_USER_VERSION, 332 .payload.addr = *addr, 333 .size = sizeof(msg.payload.addr), 334 }; 335 336 if (vhost_user_write(dev, &msg, NULL, 0) < 0) { 337 return -1; 338 } 339 340 return 0; 341 } 342 343 static int vhost_user_set_vring_endian(struct vhost_dev *dev, 344 struct vhost_vring_state *ring) 345 { 346 error_report("vhost-user trying to send unhandled ioctl"); 347 return -1; 348 } 349 350 static int vhost_set_vring(struct vhost_dev *dev, 351 unsigned long int request, 352 struct vhost_vring_state *ring) 353 { 354 VhostUserMsg msg = { 355 .request = request, 356 .flags = VHOST_USER_VERSION, 357 .payload.state = *ring, 358 .size = sizeof(msg.payload.state), 359 }; 360 361 if (vhost_user_write(dev, &msg, NULL, 0) < 0) { 362 return -1; 363 } 364 365 return 0; 366 } 367 368 static int vhost_user_set_vring_num(struct vhost_dev *dev, 369 struct vhost_vring_state *ring) 370 { 371 return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring); 372 } 373 374 static int vhost_user_set_vring_base(struct vhost_dev *dev, 375 struct vhost_vring_state *ring) 376 { 377 return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring); 378 } 379 380 static int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable) 381 { 382 int i; 383 384 if (!virtio_has_feature(dev->features, VHOST_USER_F_PROTOCOL_FEATURES)) { 385 return -1; 386 } 387 388 for (i = 0; i < dev->nvqs; ++i) { 389 struct vhost_vring_state state = { 390 .index = dev->vq_index + i, 391 .num = enable, 392 }; 393 394 vhost_set_vring(dev, VHOST_USER_SET_VRING_ENABLE, &state); 395 } 396 397 return 0; 398 } 399 400 static int vhost_user_get_vring_base(struct vhost_dev *dev, 401 struct vhost_vring_state *ring) 402 { 403 VhostUserMsg msg = { 404 .request = VHOST_USER_GET_VRING_BASE, 405 .flags = VHOST_USER_VERSION, 406 .payload.state = *ring, 407 .size = sizeof(msg.payload.state), 408 }; 409 410 if (vhost_user_write(dev, &msg, NULL, 0) < 0) { 411 return -1; 412 } 413 414 if (vhost_user_read(dev, &msg) < 0) { 415 return -1; 416 } 417 418 if (msg.request != VHOST_USER_GET_VRING_BASE) { 419 error_report("Received unexpected msg type. Expected %d received %d", 420 VHOST_USER_GET_VRING_BASE, msg.request); 421 return -1; 422 } 423 424 if (msg.size != sizeof(msg.payload.state)) { 425 error_report("Received bad msg size."); 426 return -1; 427 } 428 429 *ring = msg.payload.state; 430 431 return 0; 432 } 433 434 static int vhost_set_vring_file(struct vhost_dev *dev, 435 VhostUserRequest request, 436 struct vhost_vring_file *file) 437 { 438 int fds[VHOST_MEMORY_MAX_NREGIONS]; 439 size_t fd_num = 0; 440 VhostUserMsg msg = { 441 .request = request, 442 .flags = VHOST_USER_VERSION, 443 .payload.u64 = file->index & VHOST_USER_VRING_IDX_MASK, 444 .size = sizeof(msg.payload.u64), 445 }; 446 447 if (ioeventfd_enabled() && file->fd > 0) { 448 fds[fd_num++] = file->fd; 449 } else { 450 msg.payload.u64 |= VHOST_USER_VRING_NOFD_MASK; 451 } 452 453 if (vhost_user_write(dev, &msg, fds, fd_num) < 0) { 454 return -1; 455 } 456 457 return 0; 458 } 459 460 static int vhost_user_set_vring_kick(struct vhost_dev *dev, 461 struct vhost_vring_file *file) 462 { 463 return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_KICK, file); 464 } 465 466 static int vhost_user_set_vring_call(struct vhost_dev *dev, 467 struct vhost_vring_file *file) 468 { 469 return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_CALL, file); 470 } 471 472 static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64) 473 { 474 VhostUserMsg msg = { 475 .request = request, 476 .flags = VHOST_USER_VERSION, 477 .payload.u64 = u64, 478 .size = sizeof(msg.payload.u64), 479 }; 480 481 if (vhost_user_write(dev, &msg, NULL, 0) < 0) { 482 return -1; 483 } 484 485 return 0; 486 } 487 488 static int vhost_user_set_features(struct vhost_dev *dev, 489 uint64_t features) 490 { 491 return vhost_user_set_u64(dev, VHOST_USER_SET_FEATURES, features); 492 } 493 494 static int vhost_user_set_protocol_features(struct vhost_dev *dev, 495 uint64_t features) 496 { 497 return vhost_user_set_u64(dev, VHOST_USER_SET_PROTOCOL_FEATURES, features); 498 } 499 500 static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64) 501 { 502 VhostUserMsg msg = { 503 .request = request, 504 .flags = VHOST_USER_VERSION, 505 }; 506 507 if (vhost_user_one_time_request(request) && dev->vq_index != 0) { 508 return 0; 509 } 510 511 if (vhost_user_write(dev, &msg, NULL, 0) < 0) { 512 return -1; 513 } 514 515 if (vhost_user_read(dev, &msg) < 0) { 516 return -1; 517 } 518 519 if (msg.request != request) { 520 error_report("Received unexpected msg type. Expected %d received %d", 521 request, msg.request); 522 return -1; 523 } 524 525 if (msg.size != sizeof(msg.payload.u64)) { 526 error_report("Received bad msg size."); 527 return -1; 528 } 529 530 *u64 = msg.payload.u64; 531 532 return 0; 533 } 534 535 static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features) 536 { 537 return vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features); 538 } 539 540 static int vhost_user_set_owner(struct vhost_dev *dev) 541 { 542 VhostUserMsg msg = { 543 .request = VHOST_USER_SET_OWNER, 544 .flags = VHOST_USER_VERSION, 545 }; 546 547 if (vhost_user_write(dev, &msg, NULL, 0) < 0) { 548 return -1; 549 } 550 551 return 0; 552 } 553 554 static int vhost_user_reset_device(struct vhost_dev *dev) 555 { 556 VhostUserMsg msg = { 557 .request = VHOST_USER_RESET_OWNER, 558 .flags = VHOST_USER_VERSION, 559 }; 560 561 if (vhost_user_write(dev, &msg, NULL, 0) < 0) { 562 return -1; 563 } 564 565 return 0; 566 } 567 568 static int vhost_user_init(struct vhost_dev *dev, void *opaque) 569 { 570 uint64_t features; 571 int err; 572 573 assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); 574 575 dev->opaque = opaque; 576 577 err = vhost_user_get_features(dev, &features); 578 if (err < 0) { 579 return err; 580 } 581 582 if (virtio_has_feature(features, VHOST_USER_F_PROTOCOL_FEATURES)) { 583 dev->backend_features |= 1ULL << VHOST_USER_F_PROTOCOL_FEATURES; 584 585 err = vhost_user_get_u64(dev, VHOST_USER_GET_PROTOCOL_FEATURES, 586 &features); 587 if (err < 0) { 588 return err; 589 } 590 591 dev->protocol_features = features & VHOST_USER_PROTOCOL_FEATURE_MASK; 592 err = vhost_user_set_protocol_features(dev, dev->protocol_features); 593 if (err < 0) { 594 return err; 595 } 596 597 /* query the max queues we support if backend supports Multiple Queue */ 598 if (dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_MQ)) { 599 err = vhost_user_get_u64(dev, VHOST_USER_GET_QUEUE_NUM, 600 &dev->max_queues); 601 if (err < 0) { 602 return err; 603 } 604 } 605 } 606 607 if (dev->migration_blocker == NULL && 608 !virtio_has_feature(dev->protocol_features, 609 VHOST_USER_PROTOCOL_F_LOG_SHMFD)) { 610 error_setg(&dev->migration_blocker, 611 "Migration disabled: vhost-user backend lacks " 612 "VHOST_USER_PROTOCOL_F_LOG_SHMFD feature."); 613 } 614 615 return 0; 616 } 617 618 static int vhost_user_cleanup(struct vhost_dev *dev) 619 { 620 assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); 621 622 dev->opaque = 0; 623 624 return 0; 625 } 626 627 static int vhost_user_get_vq_index(struct vhost_dev *dev, int idx) 628 { 629 assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); 630 631 return idx; 632 } 633 634 static int vhost_user_memslots_limit(struct vhost_dev *dev) 635 { 636 return VHOST_MEMORY_MAX_NREGIONS; 637 } 638 639 static bool vhost_user_requires_shm_log(struct vhost_dev *dev) 640 { 641 assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); 642 643 return virtio_has_feature(dev->protocol_features, 644 VHOST_USER_PROTOCOL_F_LOG_SHMFD); 645 } 646 647 static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) 648 { 649 VhostUserMsg msg = { 0 }; 650 651 assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); 652 653 /* If guest supports GUEST_ANNOUNCE do nothing */ 654 if (virtio_has_feature(dev->acked_features, VIRTIO_NET_F_GUEST_ANNOUNCE)) { 655 return 0; 656 } 657 658 /* if backend supports VHOST_USER_PROTOCOL_F_RARP ask it to send the RARP */ 659 if (virtio_has_feature(dev->protocol_features, 660 VHOST_USER_PROTOCOL_F_RARP)) { 661 msg.request = VHOST_USER_SEND_RARP; 662 msg.flags = VHOST_USER_VERSION; 663 memcpy((char *)&msg.payload.u64, mac_addr, 6); 664 msg.size = sizeof(msg.payload.u64); 665 666 return vhost_user_write(dev, &msg, NULL, 0); 667 } 668 return -1; 669 } 670 671 static bool vhost_user_can_merge(struct vhost_dev *dev, 672 uint64_t start1, uint64_t size1, 673 uint64_t start2, uint64_t size2) 674 { 675 ram_addr_t offset; 676 int mfd, rfd; 677 MemoryRegion *mr; 678 679 mr = memory_region_from_host((void *)(uintptr_t)start1, &offset); 680 mfd = memory_region_get_fd(mr); 681 682 mr = memory_region_from_host((void *)(uintptr_t)start2, &offset); 683 rfd = memory_region_get_fd(mr); 684 685 return mfd == rfd; 686 } 687 688 const VhostOps user_ops = { 689 .backend_type = VHOST_BACKEND_TYPE_USER, 690 .vhost_backend_init = vhost_user_init, 691 .vhost_backend_cleanup = vhost_user_cleanup, 692 .vhost_backend_memslots_limit = vhost_user_memslots_limit, 693 .vhost_set_log_base = vhost_user_set_log_base, 694 .vhost_set_mem_table = vhost_user_set_mem_table, 695 .vhost_set_vring_addr = vhost_user_set_vring_addr, 696 .vhost_set_vring_endian = vhost_user_set_vring_endian, 697 .vhost_set_vring_num = vhost_user_set_vring_num, 698 .vhost_set_vring_base = vhost_user_set_vring_base, 699 .vhost_get_vring_base = vhost_user_get_vring_base, 700 .vhost_set_vring_kick = vhost_user_set_vring_kick, 701 .vhost_set_vring_call = vhost_user_set_vring_call, 702 .vhost_set_features = vhost_user_set_features, 703 .vhost_get_features = vhost_user_get_features, 704 .vhost_set_owner = vhost_user_set_owner, 705 .vhost_reset_device = vhost_user_reset_device, 706 .vhost_get_vq_index = vhost_user_get_vq_index, 707 .vhost_set_vring_enable = vhost_user_set_vring_enable, 708 .vhost_requires_shm_log = vhost_user_requires_shm_log, 709 .vhost_migration_done = vhost_user_migration_done, 710 .vhost_backend_can_merge = vhost_user_can_merge, 711 }; 712