1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 2023 Red Hat, Inc. 3 */ 4 5 #include <stdint.h> 6 #include <stdio.h> 7 #include <unistd.h> 8 #include <fcntl.h> 9 10 11 #include <linux/vduse.h> 12 #include <linux/virtio_net.h> 13 14 #include <sys/ioctl.h> 15 #include <sys/mman.h> 16 #include <sys/stat.h> 17 18 #include <rte_common.h> 19 #include <rte_thread.h> 20 21 #include "fd_man.h" 22 #include "iotlb.h" 23 #include "vduse.h" 24 #include "vhost.h" 25 #include "virtio_net_ctrl.h" 26 27 #define VHOST_VDUSE_API_VERSION 0 28 #define VDUSE_CTRL_PATH "/dev/vduse/control" 29 30 struct vduse { 31 struct fdset fdset; 32 }; 33 34 static struct vduse vduse = { 35 .fdset = { 36 .fd = { [0 ... MAX_FDS - 1] = {-1, NULL, NULL, NULL, 0} }, 37 .fd_mutex = PTHREAD_MUTEX_INITIALIZER, 38 .fd_pooling_mutex = PTHREAD_MUTEX_INITIALIZER, 39 .num = 0 40 }, 41 }; 42 43 static bool vduse_events_thread; 44 45 static const char * const vduse_reqs_str[] = { 46 "VDUSE_GET_VQ_STATE", 47 "VDUSE_SET_STATUS", 48 "VDUSE_UPDATE_IOTLB", 49 }; 50 51 #define vduse_req_id_to_str(id) \ 52 (id < RTE_DIM(vduse_reqs_str) ? \ 53 vduse_reqs_str[id] : "Unknown") 54 55 static int 56 vduse_inject_irq(struct virtio_net *dev, struct vhost_virtqueue *vq) 57 { 58 return ioctl(dev->vduse_dev_fd, VDUSE_VQ_INJECT_IRQ, &vq->index); 59 } 60 61 static void 62 vduse_iotlb_remove_notify(uint64_t addr, uint64_t offset, uint64_t size) 63 { 64 munmap((void *)(uintptr_t)addr, offset + size); 65 } 66 67 static int 68 vduse_iotlb_miss(struct virtio_net *dev, uint64_t iova, uint8_t perm __rte_unused) 69 { 70 struct vduse_iotlb_entry entry; 71 uint64_t size, page_size; 72 struct stat stat; 73 void *mmap_addr; 74 int fd, ret; 75 76 entry.start = iova; 77 entry.last = iova + 1; 78 79 ret = ioctl(dev->vduse_dev_fd, VDUSE_IOTLB_GET_FD, &entry); 80 if (ret < 0) { 81 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to get IOTLB entry for 0x%" PRIx64 "\n", 82 iova); 83 return -1; 84 } 85 86 fd = ret; 87 88 VHOST_LOG_CONFIG(dev->ifname, DEBUG, "New IOTLB entry:\n"); 89 VHOST_LOG_CONFIG(dev->ifname, DEBUG, "\tIOVA: %" PRIx64 " - %" PRIx64 "\n", 90 (uint64_t)entry.start, (uint64_t)entry.last); 91 VHOST_LOG_CONFIG(dev->ifname, DEBUG, "\toffset: %" PRIx64 "\n", (uint64_t)entry.offset); 92 VHOST_LOG_CONFIG(dev->ifname, DEBUG, "\tfd: %d\n", fd); 93 VHOST_LOG_CONFIG(dev->ifname, DEBUG, "\tperm: %x\n", entry.perm); 94 95 size = entry.last - entry.start + 1; 96 mmap_addr = mmap(0, size + entry.offset, entry.perm, MAP_SHARED, fd, 0); 97 if (!mmap_addr) { 98 VHOST_LOG_CONFIG(dev->ifname, ERR, 99 "Failed to mmap IOTLB entry for 0x%" PRIx64 "\n", iova); 100 ret = -1; 101 goto close_fd; 102 } 103 104 ret = fstat(fd, &stat); 105 if (ret < 0) { 106 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to get page size.\n"); 107 munmap(mmap_addr, entry.offset + size); 108 goto close_fd; 109 } 110 page_size = (uint64_t)stat.st_blksize; 111 112 vhost_user_iotlb_cache_insert(dev, entry.start, (uint64_t)(uintptr_t)mmap_addr, 113 entry.offset, size, page_size, entry.perm); 114 115 ret = 0; 116 close_fd: 117 close(fd); 118 119 return ret; 120 } 121 122 static struct vhost_backend_ops vduse_backend_ops = { 123 .iotlb_miss = vduse_iotlb_miss, 124 .iotlb_remove_notify = vduse_iotlb_remove_notify, 125 .inject_irq = vduse_inject_irq, 126 }; 127 128 static void 129 vduse_control_queue_event(int fd, void *arg, int *remove __rte_unused) 130 { 131 struct virtio_net *dev = arg; 132 uint64_t buf; 133 int ret; 134 135 ret = read(fd, &buf, sizeof(buf)); 136 if (ret < 0) { 137 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to read control queue event: %s\n", 138 strerror(errno)); 139 return; 140 } 141 142 VHOST_LOG_CONFIG(dev->ifname, DEBUG, "Control queue kicked\n"); 143 if (virtio_net_ctrl_handle(dev)) 144 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to handle ctrl request\n"); 145 } 146 147 static void 148 vduse_vring_setup(struct virtio_net *dev, unsigned int index) 149 { 150 struct vhost_virtqueue *vq = dev->virtqueue[index]; 151 struct vhost_vring_addr *ra = &vq->ring_addrs; 152 struct vduse_vq_info vq_info; 153 struct vduse_vq_eventfd vq_efd; 154 int ret; 155 156 vq_info.index = index; 157 ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_GET_INFO, &vq_info); 158 if (ret) { 159 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to get VQ %u info: %s\n", 160 index, strerror(errno)); 161 return; 162 } 163 164 VHOST_LOG_CONFIG(dev->ifname, INFO, "VQ %u info:\n", index); 165 VHOST_LOG_CONFIG(dev->ifname, INFO, "\tnum: %u\n", vq_info.num); 166 VHOST_LOG_CONFIG(dev->ifname, INFO, "\tdesc_addr: %llx\n", vq_info.desc_addr); 167 VHOST_LOG_CONFIG(dev->ifname, INFO, "\tdriver_addr: %llx\n", vq_info.driver_addr); 168 VHOST_LOG_CONFIG(dev->ifname, INFO, "\tdevice_addr: %llx\n", vq_info.device_addr); 169 VHOST_LOG_CONFIG(dev->ifname, INFO, "\tavail_idx: %u\n", vq_info.split.avail_index); 170 VHOST_LOG_CONFIG(dev->ifname, INFO, "\tready: %u\n", vq_info.ready); 171 172 vq->last_avail_idx = vq_info.split.avail_index; 173 vq->size = vq_info.num; 174 vq->ready = true; 175 vq->enabled = vq_info.ready; 176 ra->desc_user_addr = vq_info.desc_addr; 177 ra->avail_user_addr = vq_info.driver_addr; 178 ra->used_user_addr = vq_info.device_addr; 179 180 vq->kickfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 181 if (vq->kickfd < 0) { 182 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to init kickfd for VQ %u: %s\n", 183 index, strerror(errno)); 184 vq->kickfd = VIRTIO_INVALID_EVENTFD; 185 return; 186 } 187 VHOST_LOG_CONFIG(dev->ifname, INFO, "\tkick fd: %d\n", vq->kickfd); 188 189 vq->shadow_used_split = rte_malloc_socket(NULL, 190 vq->size * sizeof(struct vring_used_elem), 191 RTE_CACHE_LINE_SIZE, 0); 192 vq->batch_copy_elems = rte_malloc_socket(NULL, 193 vq->size * sizeof(struct batch_copy_elem), 194 RTE_CACHE_LINE_SIZE, 0); 195 196 vhost_user_iotlb_rd_lock(vq); 197 if (vring_translate(dev, vq)) 198 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to translate vring %d addresses\n", 199 index); 200 201 if (vhost_enable_guest_notification(dev, vq, 0)) 202 VHOST_LOG_CONFIG(dev->ifname, ERR, 203 "Failed to disable guest notifications on vring %d\n", 204 index); 205 vhost_user_iotlb_rd_unlock(vq); 206 207 vq_efd.index = index; 208 vq_efd.fd = vq->kickfd; 209 210 ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd); 211 if (ret) { 212 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to setup kickfd for VQ %u: %s\n", 213 index, strerror(errno)); 214 close(vq->kickfd); 215 vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD; 216 return; 217 } 218 219 if (vq == dev->cvq) { 220 ret = fdset_add(&vduse.fdset, vq->kickfd, vduse_control_queue_event, NULL, dev); 221 if (ret) { 222 VHOST_LOG_CONFIG(dev->ifname, ERR, 223 "Failed to setup kickfd handler for VQ %u: %s\n", 224 index, strerror(errno)); 225 vq_efd.fd = VDUSE_EVENTFD_DEASSIGN; 226 ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd); 227 close(vq->kickfd); 228 vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD; 229 } 230 fdset_pipe_notify(&vduse.fdset); 231 vhost_enable_guest_notification(dev, vq, 1); 232 VHOST_LOG_CONFIG(dev->ifname, INFO, "Ctrl queue event handler installed\n"); 233 } 234 } 235 236 static void 237 vduse_vring_cleanup(struct virtio_net *dev, unsigned int index) 238 { 239 struct vhost_virtqueue *vq = dev->virtqueue[index]; 240 struct vduse_vq_eventfd vq_efd; 241 int ret; 242 243 if (vq == dev->cvq && vq->kickfd >= 0) { 244 fdset_del(&vduse.fdset, vq->kickfd); 245 fdset_pipe_notify(&vduse.fdset); 246 } 247 248 vq_efd.index = index; 249 vq_efd.fd = VDUSE_EVENTFD_DEASSIGN; 250 251 ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd); 252 if (ret) 253 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to cleanup kickfd for VQ %u: %s\n", 254 index, strerror(errno)); 255 256 close(vq->kickfd); 257 vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD; 258 259 vring_invalidate(dev, vq); 260 261 rte_free(vq->batch_copy_elems); 262 vq->batch_copy_elems = NULL; 263 264 rte_free(vq->shadow_used_split); 265 vq->shadow_used_split = NULL; 266 267 vq->enabled = false; 268 vq->ready = false; 269 vq->size = 0; 270 vq->last_used_idx = 0; 271 vq->last_avail_idx = 0; 272 } 273 274 static void 275 vduse_device_start(struct virtio_net *dev) 276 { 277 unsigned int i, ret; 278 279 VHOST_LOG_CONFIG(dev->ifname, INFO, "Starting device...\n"); 280 281 dev->notify_ops = vhost_driver_callback_get(dev->ifname); 282 if (!dev->notify_ops) { 283 VHOST_LOG_CONFIG(dev->ifname, ERR, 284 "Failed to get callback ops for driver\n"); 285 return; 286 } 287 288 ret = ioctl(dev->vduse_dev_fd, VDUSE_DEV_GET_FEATURES, &dev->features); 289 if (ret) { 290 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to get features: %s\n", 291 strerror(errno)); 292 return; 293 } 294 295 VHOST_LOG_CONFIG(dev->ifname, INFO, "Negotiated Virtio features: 0x%" PRIx64 "\n", 296 dev->features); 297 298 if (dev->features & 299 ((1ULL << VIRTIO_NET_F_MRG_RXBUF) | 300 (1ULL << VIRTIO_F_VERSION_1) | 301 (1ULL << VIRTIO_F_RING_PACKED))) { 302 dev->vhost_hlen = sizeof(struct virtio_net_hdr_mrg_rxbuf); 303 } else { 304 dev->vhost_hlen = sizeof(struct virtio_net_hdr); 305 } 306 307 for (i = 0; i < dev->nr_vring; i++) 308 vduse_vring_setup(dev, i); 309 310 dev->flags |= VIRTIO_DEV_READY; 311 312 if (dev->notify_ops->new_device(dev->vid) == 0) 313 dev->flags |= VIRTIO_DEV_RUNNING; 314 315 for (i = 0; i < dev->nr_vring; i++) { 316 struct vhost_virtqueue *vq = dev->virtqueue[i]; 317 318 if (vq == dev->cvq) 319 continue; 320 321 if (dev->notify_ops->vring_state_changed) 322 dev->notify_ops->vring_state_changed(dev->vid, i, vq->enabled); 323 } 324 } 325 326 static void 327 vduse_device_stop(struct virtio_net *dev) 328 { 329 unsigned int i; 330 331 VHOST_LOG_CONFIG(dev->ifname, INFO, "Stopping device...\n"); 332 333 vhost_destroy_device_notify(dev); 334 335 dev->flags &= ~VIRTIO_DEV_READY; 336 337 for (i = 0; i < dev->nr_vring; i++) 338 vduse_vring_cleanup(dev, i); 339 340 vhost_user_iotlb_flush_all(dev); 341 } 342 343 static void 344 vduse_events_handler(int fd, void *arg, int *remove __rte_unused) 345 { 346 struct virtio_net *dev = arg; 347 struct vduse_dev_request req; 348 struct vduse_dev_response resp; 349 struct vhost_virtqueue *vq; 350 uint8_t old_status = dev->status; 351 int ret; 352 353 memset(&resp, 0, sizeof(resp)); 354 355 ret = read(fd, &req, sizeof(req)); 356 if (ret < 0) { 357 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to read request: %s\n", 358 strerror(errno)); 359 return; 360 } else if (ret < (int)sizeof(req)) { 361 VHOST_LOG_CONFIG(dev->ifname, ERR, "Incomplete to read request %d\n", ret); 362 return; 363 } 364 365 VHOST_LOG_CONFIG(dev->ifname, INFO, "New request: %s (%u)\n", 366 vduse_req_id_to_str(req.type), req.type); 367 368 switch (req.type) { 369 case VDUSE_GET_VQ_STATE: 370 vq = dev->virtqueue[req.vq_state.index]; 371 VHOST_LOG_CONFIG(dev->ifname, INFO, "\tvq index: %u, avail_index: %u\n", 372 req.vq_state.index, vq->last_avail_idx); 373 resp.vq_state.split.avail_index = vq->last_avail_idx; 374 resp.result = VDUSE_REQ_RESULT_OK; 375 break; 376 case VDUSE_SET_STATUS: 377 VHOST_LOG_CONFIG(dev->ifname, INFO, "\tnew status: 0x%08x\n", 378 req.s.status); 379 old_status = dev->status; 380 dev->status = req.s.status; 381 resp.result = VDUSE_REQ_RESULT_OK; 382 break; 383 case VDUSE_UPDATE_IOTLB: 384 VHOST_LOG_CONFIG(dev->ifname, INFO, "\tIOVA range: %" PRIx64 " - %" PRIx64 "\n", 385 (uint64_t)req.iova.start, (uint64_t)req.iova.last); 386 vhost_user_iotlb_cache_remove(dev, req.iova.start, 387 req.iova.last - req.iova.start + 1); 388 resp.result = VDUSE_REQ_RESULT_OK; 389 break; 390 default: 391 resp.result = VDUSE_REQ_RESULT_FAILED; 392 break; 393 } 394 395 resp.request_id = req.request_id; 396 397 ret = write(dev->vduse_dev_fd, &resp, sizeof(resp)); 398 if (ret != sizeof(resp)) { 399 VHOST_LOG_CONFIG(dev->ifname, ERR, "Failed to write response %s\n", 400 strerror(errno)); 401 return; 402 } 403 404 if ((old_status ^ dev->status) & VIRTIO_DEVICE_STATUS_DRIVER_OK) { 405 if (dev->status & VIRTIO_DEVICE_STATUS_DRIVER_OK) 406 vduse_device_start(dev); 407 else 408 vduse_device_stop(dev); 409 } 410 411 VHOST_LOG_CONFIG(dev->ifname, INFO, "Request %s (%u) handled successfully\n", 412 vduse_req_id_to_str(req.type), req.type); 413 } 414 415 int 416 vduse_device_create(const char *path, bool compliant_ol_flags) 417 { 418 int control_fd, dev_fd, vid, ret; 419 rte_thread_t fdset_tid; 420 uint32_t i, max_queue_pairs, total_queues; 421 struct virtio_net *dev; 422 struct virtio_net_config vnet_config = {{ 0 }}; 423 uint64_t ver = VHOST_VDUSE_API_VERSION; 424 uint64_t features; 425 struct vduse_dev_config *dev_config = NULL; 426 const char *name = path + strlen("/dev/vduse/"); 427 428 /* If first device, create events dispatcher thread */ 429 if (vduse_events_thread == false) { 430 /** 431 * create a pipe which will be waited by poll and notified to 432 * rebuild the wait list of poll. 433 */ 434 if (fdset_pipe_init(&vduse.fdset) < 0) { 435 VHOST_LOG_CONFIG(path, ERR, "failed to create pipe for vduse fdset\n"); 436 return -1; 437 } 438 439 ret = rte_thread_create_internal_control(&fdset_tid, "vduse-evt", 440 fdset_event_dispatch, &vduse.fdset); 441 if (ret != 0) { 442 VHOST_LOG_CONFIG(path, ERR, "failed to create vduse fdset handling thread\n"); 443 fdset_pipe_uninit(&vduse.fdset); 444 return -1; 445 } 446 447 vduse_events_thread = true; 448 } 449 450 control_fd = open(VDUSE_CTRL_PATH, O_RDWR); 451 if (control_fd < 0) { 452 VHOST_LOG_CONFIG(name, ERR, "Failed to open %s: %s\n", 453 VDUSE_CTRL_PATH, strerror(errno)); 454 return -1; 455 } 456 457 if (ioctl(control_fd, VDUSE_SET_API_VERSION, &ver)) { 458 VHOST_LOG_CONFIG(name, ERR, "Failed to set API version: %" PRIu64 ": %s\n", 459 ver, strerror(errno)); 460 ret = -1; 461 goto out_ctrl_close; 462 } 463 464 dev_config = malloc(offsetof(struct vduse_dev_config, config) + 465 sizeof(vnet_config)); 466 if (!dev_config) { 467 VHOST_LOG_CONFIG(name, ERR, "Failed to allocate VDUSE config\n"); 468 ret = -1; 469 goto out_ctrl_close; 470 } 471 472 ret = rte_vhost_driver_get_features(path, &features); 473 if (ret < 0) { 474 VHOST_LOG_CONFIG(name, ERR, "Failed to get backend features\n"); 475 goto out_free; 476 } 477 478 ret = rte_vhost_driver_get_queue_num(path, &max_queue_pairs); 479 if (ret < 0) { 480 VHOST_LOG_CONFIG(name, ERR, "Failed to get max queue pairs\n"); 481 goto out_free; 482 } 483 484 VHOST_LOG_CONFIG(path, INFO, "VDUSE max queue pairs: %u\n", max_queue_pairs); 485 total_queues = max_queue_pairs * 2; 486 487 if (max_queue_pairs == 1) 488 features &= ~(RTE_BIT64(VIRTIO_NET_F_CTRL_VQ) | RTE_BIT64(VIRTIO_NET_F_MQ)); 489 else 490 total_queues += 1; /* Includes ctrl queue */ 491 492 vnet_config.max_virtqueue_pairs = max_queue_pairs; 493 memset(dev_config, 0, sizeof(struct vduse_dev_config)); 494 495 strncpy(dev_config->name, name, VDUSE_NAME_MAX - 1); 496 dev_config->device_id = VIRTIO_ID_NET; 497 dev_config->vendor_id = 0; 498 dev_config->features = features; 499 dev_config->vq_num = total_queues; 500 dev_config->vq_align = sysconf(_SC_PAGE_SIZE); 501 dev_config->config_size = sizeof(struct virtio_net_config); 502 memcpy(dev_config->config, &vnet_config, sizeof(vnet_config)); 503 504 ret = ioctl(control_fd, VDUSE_CREATE_DEV, dev_config); 505 if (ret < 0) { 506 VHOST_LOG_CONFIG(name, ERR, "Failed to create VDUSE device: %s\n", 507 strerror(errno)); 508 goto out_free; 509 } 510 511 dev_fd = open(path, O_RDWR); 512 if (dev_fd < 0) { 513 VHOST_LOG_CONFIG(name, ERR, "Failed to open device %s: %s\n", 514 path, strerror(errno)); 515 ret = -1; 516 goto out_dev_close; 517 } 518 519 ret = fcntl(dev_fd, F_SETFL, O_NONBLOCK); 520 if (ret < 0) { 521 VHOST_LOG_CONFIG(name, ERR, "Failed to set chardev as non-blocking: %s\n", 522 strerror(errno)); 523 goto out_dev_close; 524 } 525 526 vid = vhost_new_device(&vduse_backend_ops); 527 if (vid < 0) { 528 VHOST_LOG_CONFIG(name, ERR, "Failed to create new Vhost device\n"); 529 ret = -1; 530 goto out_dev_close; 531 } 532 533 dev = get_device(vid); 534 if (!dev) { 535 ret = -1; 536 goto out_dev_close; 537 } 538 539 strncpy(dev->ifname, path, IF_NAME_SZ - 1); 540 dev->vduse_ctrl_fd = control_fd; 541 dev->vduse_dev_fd = dev_fd; 542 vhost_setup_virtio_net(dev->vid, true, compliant_ol_flags, true, true); 543 544 for (i = 0; i < total_queues; i++) { 545 struct vduse_vq_config vq_cfg = { 0 }; 546 547 ret = alloc_vring_queue(dev, i); 548 if (ret) { 549 VHOST_LOG_CONFIG(name, ERR, "Failed to alloc vring %d metadata\n", i); 550 goto out_dev_destroy; 551 } 552 553 vq_cfg.index = i; 554 vq_cfg.max_size = 1024; 555 556 ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP, &vq_cfg); 557 if (ret) { 558 VHOST_LOG_CONFIG(name, ERR, "Failed to set-up VQ %d\n", i); 559 goto out_dev_destroy; 560 } 561 } 562 563 dev->cvq = dev->virtqueue[max_queue_pairs * 2]; 564 565 ret = fdset_add(&vduse.fdset, dev->vduse_dev_fd, vduse_events_handler, NULL, dev); 566 if (ret) { 567 VHOST_LOG_CONFIG(name, ERR, "Failed to add fd %d to vduse fdset\n", 568 dev->vduse_dev_fd); 569 goto out_dev_destroy; 570 } 571 fdset_pipe_notify(&vduse.fdset); 572 573 free(dev_config); 574 575 return 0; 576 577 out_dev_destroy: 578 vhost_destroy_device(vid); 579 out_dev_close: 580 if (dev_fd >= 0) 581 close(dev_fd); 582 ioctl(control_fd, VDUSE_DESTROY_DEV, name); 583 out_free: 584 free(dev_config); 585 out_ctrl_close: 586 close(control_fd); 587 588 return ret; 589 } 590 591 int 592 vduse_device_destroy(const char *path) 593 { 594 const char *name = path + strlen("/dev/vduse/"); 595 struct virtio_net *dev; 596 int vid, ret; 597 598 for (vid = 0; vid < RTE_MAX_VHOST_DEVICE; vid++) { 599 dev = vhost_devices[vid]; 600 601 if (dev == NULL) 602 continue; 603 604 if (!strcmp(path, dev->ifname)) 605 break; 606 } 607 608 if (vid == RTE_MAX_VHOST_DEVICE) 609 return -1; 610 611 vduse_device_stop(dev); 612 613 fdset_del(&vduse.fdset, dev->vduse_dev_fd); 614 fdset_pipe_notify(&vduse.fdset); 615 616 if (dev->vduse_dev_fd >= 0) { 617 close(dev->vduse_dev_fd); 618 dev->vduse_dev_fd = -1; 619 } 620 621 if (dev->vduse_ctrl_fd >= 0) { 622 ret = ioctl(dev->vduse_ctrl_fd, VDUSE_DESTROY_DEV, name); 623 if (ret) 624 VHOST_LOG_CONFIG(name, ERR, "Failed to destroy VDUSE device: %s\n", 625 strerror(errno)); 626 close(dev->vduse_ctrl_fd); 627 dev->vduse_ctrl_fd = -1; 628 } 629 630 vhost_destroy_device(vid); 631 632 return 0; 633 } 634