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