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