xref: /dpdk/lib/vhost/vduse.c (revision 5d52418fa4b9a7f28eaedc1d88ec5cf330381c0e)
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