xref: /dpdk/lib/vhost/vduse.c (revision 4025e36fa5b7e44df07d53f8e7ddfeab5f1512a2)
10adb8eccSMaxime Coquelin /* SPDX-License-Identifier: BSD-3-Clause
20adb8eccSMaxime Coquelin  * Copyright (c) 2023 Red Hat, Inc.
30adb8eccSMaxime Coquelin  */
40adb8eccSMaxime Coquelin 
50adb8eccSMaxime Coquelin #include <stdint.h>
60adb8eccSMaxime Coquelin #include <stdio.h>
70adb8eccSMaxime Coquelin #include <unistd.h>
80adb8eccSMaxime Coquelin #include <fcntl.h>
90adb8eccSMaxime Coquelin 
100adb8eccSMaxime Coquelin 
11*4025e36fSStephen Hemminger #include <linux/vduse.h>
120adb8eccSMaxime Coquelin #include <linux/virtio_net.h>
130adb8eccSMaxime Coquelin 
140adb8eccSMaxime Coquelin #include <sys/ioctl.h>
150adb8eccSMaxime Coquelin #include <sys/mman.h>
16f27d5206SMaxime Coquelin #include <sys/stat.h>
170adb8eccSMaxime Coquelin 
180adb8eccSMaxime Coquelin #include <rte_common.h>
191c1abf17SThomas Monjalon #include <rte_thread.h>
200adb8eccSMaxime Coquelin 
2151d018fdSMaxime Coquelin #include "fd_man.h"
22f27d5206SMaxime Coquelin #include "iotlb.h"
230adb8eccSMaxime Coquelin #include "vduse.h"
240adb8eccSMaxime Coquelin #include "vhost.h"
25653327e1SMaxime Coquelin #include "virtio_net_ctrl.h"
260adb8eccSMaxime Coquelin 
270adb8eccSMaxime Coquelin #define VHOST_VDUSE_API_VERSION 0
280adb8eccSMaxime Coquelin #define VDUSE_CTRL_PATH "/dev/vduse/control"
290adb8eccSMaxime Coquelin 
3051d018fdSMaxime Coquelin struct vduse {
31e68a6feaSMaxime Coquelin 	struct fdset *fdset;
3251d018fdSMaxime Coquelin };
3351d018fdSMaxime Coquelin 
347945769cSMaxime Coquelin static struct vduse vduse;
3551d018fdSMaxime Coquelin 
3651d018fdSMaxime Coquelin static const char * const vduse_reqs_str[] = {
3751d018fdSMaxime Coquelin 	"VDUSE_GET_VQ_STATE",
3851d018fdSMaxime Coquelin 	"VDUSE_SET_STATUS",
3951d018fdSMaxime Coquelin 	"VDUSE_UPDATE_IOTLB",
4051d018fdSMaxime Coquelin };
4151d018fdSMaxime Coquelin 
4251d018fdSMaxime Coquelin #define vduse_req_id_to_str(id) \
4351d018fdSMaxime Coquelin 	(id < RTE_DIM(vduse_reqs_str) ? \
4451d018fdSMaxime Coquelin 	vduse_reqs_str[id] : "Unknown")
4551d018fdSMaxime Coquelin 
46ab28f62cSMaxime Coquelin static int
47ab28f62cSMaxime Coquelin vduse_inject_irq(struct virtio_net *dev, struct vhost_virtqueue *vq)
48ab28f62cSMaxime Coquelin {
49ab28f62cSMaxime Coquelin 	return ioctl(dev->vduse_dev_fd, VDUSE_VQ_INJECT_IRQ, &vq->index);
50ab28f62cSMaxime Coquelin }
51ab28f62cSMaxime Coquelin 
5273d8bbaaSMaxime Coquelin static void
5373d8bbaaSMaxime Coquelin vduse_iotlb_remove_notify(uint64_t addr, uint64_t offset, uint64_t size)
5473d8bbaaSMaxime Coquelin {
5573d8bbaaSMaxime Coquelin 	munmap((void *)(uintptr_t)addr, offset + size);
5673d8bbaaSMaxime Coquelin }
5773d8bbaaSMaxime Coquelin 
58f27d5206SMaxime Coquelin static int
59f27d5206SMaxime Coquelin vduse_iotlb_miss(struct virtio_net *dev, uint64_t iova, uint8_t perm __rte_unused)
60f27d5206SMaxime Coquelin {
61f27d5206SMaxime Coquelin 	struct vduse_iotlb_entry entry;
62f27d5206SMaxime Coquelin 	uint64_t size, page_size;
63f27d5206SMaxime Coquelin 	struct stat stat;
64f27d5206SMaxime Coquelin 	void *mmap_addr;
65f27d5206SMaxime Coquelin 	int fd, ret;
66f27d5206SMaxime Coquelin 
67f27d5206SMaxime Coquelin 	entry.start = iova;
68f27d5206SMaxime Coquelin 	entry.last = iova + 1;
69f27d5206SMaxime Coquelin 
70f27d5206SMaxime Coquelin 	ret = ioctl(dev->vduse_dev_fd, VDUSE_IOTLB_GET_FD, &entry);
71f27d5206SMaxime Coquelin 	if (ret < 0) {
720e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get IOTLB entry for 0x%" PRIx64,
73f27d5206SMaxime Coquelin 				iova);
74f27d5206SMaxime Coquelin 		return -1;
75f27d5206SMaxime Coquelin 	}
76f27d5206SMaxime Coquelin 
77f27d5206SMaxime Coquelin 	fd = ret;
78f27d5206SMaxime Coquelin 
790e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, DEBUG, "New IOTLB entry:");
800e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\tIOVA: %" PRIx64 " - %" PRIx64,
81f27d5206SMaxime Coquelin 			(uint64_t)entry.start, (uint64_t)entry.last);
820e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\toffset: %" PRIx64, (uint64_t)entry.offset);
830e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\tfd: %d", fd);
840e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\tperm: %x", entry.perm);
85f27d5206SMaxime Coquelin 
86f27d5206SMaxime Coquelin 	size = entry.last - entry.start + 1;
87f27d5206SMaxime Coquelin 	mmap_addr = mmap(0, size + entry.offset, entry.perm, MAP_SHARED, fd, 0);
88f27d5206SMaxime Coquelin 	if (!mmap_addr) {
890e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR,
900e21c7c0SDavid Marchand 				"Failed to mmap IOTLB entry for 0x%" PRIx64, iova);
91f27d5206SMaxime Coquelin 		ret = -1;
92f27d5206SMaxime Coquelin 		goto close_fd;
93f27d5206SMaxime Coquelin 	}
94f27d5206SMaxime Coquelin 
95f27d5206SMaxime Coquelin 	ret = fstat(fd, &stat);
96f27d5206SMaxime Coquelin 	if (ret < 0) {
970e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get page size.");
98f27d5206SMaxime Coquelin 		munmap(mmap_addr, entry.offset + size);
99f27d5206SMaxime Coquelin 		goto close_fd;
100f27d5206SMaxime Coquelin 	}
101f27d5206SMaxime Coquelin 	page_size = (uint64_t)stat.st_blksize;
102f27d5206SMaxime Coquelin 
103f27d5206SMaxime Coquelin 	vhost_user_iotlb_cache_insert(dev, entry.start, (uint64_t)(uintptr_t)mmap_addr,
104f27d5206SMaxime Coquelin 		entry.offset, size, page_size, entry.perm);
105f27d5206SMaxime Coquelin 
106f27d5206SMaxime Coquelin 	ret = 0;
107f27d5206SMaxime Coquelin close_fd:
108f27d5206SMaxime Coquelin 	close(fd);
109f27d5206SMaxime Coquelin 
110f27d5206SMaxime Coquelin 	return ret;
111f27d5206SMaxime Coquelin }
112f27d5206SMaxime Coquelin 
1130adb8eccSMaxime Coquelin static struct vhost_backend_ops vduse_backend_ops = {
114f27d5206SMaxime Coquelin 	.iotlb_miss = vduse_iotlb_miss,
11573d8bbaaSMaxime Coquelin 	.iotlb_remove_notify = vduse_iotlb_remove_notify,
116ab28f62cSMaxime Coquelin 	.inject_irq = vduse_inject_irq,
1170adb8eccSMaxime Coquelin };
1180adb8eccSMaxime Coquelin 
11951d018fdSMaxime Coquelin static void
120653327e1SMaxime Coquelin vduse_control_queue_event(int fd, void *arg, int *remove __rte_unused)
121653327e1SMaxime Coquelin {
122653327e1SMaxime Coquelin 	struct virtio_net *dev = arg;
123653327e1SMaxime Coquelin 	uint64_t buf;
124653327e1SMaxime Coquelin 	int ret;
125653327e1SMaxime Coquelin 
126653327e1SMaxime Coquelin 	ret = read(fd, &buf, sizeof(buf));
127653327e1SMaxime Coquelin 	if (ret < 0) {
1280e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to read control queue event: %s",
129653327e1SMaxime Coquelin 				strerror(errno));
130653327e1SMaxime Coquelin 		return;
131653327e1SMaxime Coquelin 	}
132653327e1SMaxime Coquelin 
1330e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, DEBUG, "Control queue kicked");
134653327e1SMaxime Coquelin 	if (virtio_net_ctrl_handle(dev))
1350e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to handle ctrl request");
136653327e1SMaxime Coquelin }
137653327e1SMaxime Coquelin 
138653327e1SMaxime Coquelin static void
139da79cc7fSMaxime Coquelin vduse_vring_setup(struct virtio_net *dev, unsigned int index, bool reconnect)
140a9120db8SMaxime Coquelin {
141a9120db8SMaxime Coquelin 	struct vhost_virtqueue *vq = dev->virtqueue[index];
142a9120db8SMaxime Coquelin 	struct vhost_vring_addr *ra = &vq->ring_addrs;
143a9120db8SMaxime Coquelin 	struct vduse_vq_info vq_info;
144a9120db8SMaxime Coquelin 	struct vduse_vq_eventfd vq_efd;
145a9120db8SMaxime Coquelin 	int ret;
146a9120db8SMaxime Coquelin 
147a9120db8SMaxime Coquelin 	vq_info.index = index;
148a9120db8SMaxime Coquelin 	ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_GET_INFO, &vq_info);
149a9120db8SMaxime Coquelin 	if (ret) {
1500e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get VQ %u info: %s",
151a9120db8SMaxime Coquelin 				index, strerror(errno));
152a9120db8SMaxime Coquelin 		return;
153a9120db8SMaxime Coquelin 	}
154a9120db8SMaxime Coquelin 
155da79cc7fSMaxime Coquelin 	if (reconnect) {
156da79cc7fSMaxime Coquelin 		vq->last_avail_idx = vq->reconnect_log->last_avail_idx;
157da79cc7fSMaxime Coquelin 		vq->last_used_idx = vq->reconnect_log->last_avail_idx;
158da79cc7fSMaxime Coquelin 	} else {
159da79cc7fSMaxime Coquelin 		vq->last_avail_idx = vq_info.split.avail_index;
160da79cc7fSMaxime Coquelin 		vq->last_used_idx = vq_info.split.avail_index;
161da79cc7fSMaxime Coquelin 	}
162da79cc7fSMaxime Coquelin 	vq->size = vq_info.num;
163da79cc7fSMaxime Coquelin 	vq->ready = true;
164da79cc7fSMaxime Coquelin 	vq->enabled = vq_info.ready;
165da79cc7fSMaxime Coquelin 	ra->desc_user_addr = vq_info.desc_addr;
166da79cc7fSMaxime Coquelin 	ra->avail_user_addr = vq_info.driver_addr;
167da79cc7fSMaxime Coquelin 	ra->used_user_addr = vq_info.device_addr;
1680e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "VQ %u info:", index);
1690e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "\tnum: %u", vq_info.num);
1700e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "\tdesc_addr: %llx",
171b037cefbSBruce Richardson 			(unsigned long long)vq_info.desc_addr);
1720e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "\tdriver_addr: %llx",
173b037cefbSBruce Richardson 			(unsigned long long)vq_info.driver_addr);
1740e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "\tdevice_addr: %llx",
175b037cefbSBruce Richardson 			(unsigned long long)vq_info.device_addr);
176da79cc7fSMaxime Coquelin 	VHOST_CONFIG_LOG(dev->ifname, INFO, "\tavail_idx: %u", vq->last_avail_idx);
177da79cc7fSMaxime Coquelin 	VHOST_CONFIG_LOG(dev->ifname, INFO, "\tused_idx: %u", vq->last_used_idx);
1780e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "\tready: %u", vq_info.ready);
179a9120db8SMaxime Coquelin 	vq->kickfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
180a9120db8SMaxime Coquelin 	if (vq->kickfd < 0) {
1810e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to init kickfd for VQ %u: %s",
182a9120db8SMaxime Coquelin 				index, strerror(errno));
183a9120db8SMaxime Coquelin 		vq->kickfd = VIRTIO_INVALID_EVENTFD;
184a9120db8SMaxime Coquelin 		return;
185a9120db8SMaxime Coquelin 	}
1860e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "\tkick fd: %d", vq->kickfd);
187a9120db8SMaxime Coquelin 
188a9120db8SMaxime Coquelin 	vq->shadow_used_split = rte_malloc_socket(NULL,
189a9120db8SMaxime Coquelin 				vq->size * sizeof(struct vring_used_elem),
190a9120db8SMaxime Coquelin 				RTE_CACHE_LINE_SIZE, 0);
191a9120db8SMaxime Coquelin 	vq->batch_copy_elems = rte_malloc_socket(NULL,
192a9120db8SMaxime Coquelin 				vq->size * sizeof(struct batch_copy_elem),
193a9120db8SMaxime Coquelin 				RTE_CACHE_LINE_SIZE, 0);
194a9120db8SMaxime Coquelin 
195af532865SDavid Marchand 	rte_rwlock_write_lock(&vq->access_lock);
196a9120db8SMaxime Coquelin 	vhost_user_iotlb_rd_lock(vq);
197a9120db8SMaxime Coquelin 	if (vring_translate(dev, vq))
1980e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to translate vring %d addresses",
199a9120db8SMaxime Coquelin 				index);
200a9120db8SMaxime Coquelin 
201a9120db8SMaxime Coquelin 	if (vhost_enable_guest_notification(dev, vq, 0))
2020e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR,
2030e21c7c0SDavid Marchand 				"Failed to disable guest notifications on vring %d",
204a9120db8SMaxime Coquelin 				index);
205a9120db8SMaxime Coquelin 	vhost_user_iotlb_rd_unlock(vq);
206af532865SDavid Marchand 	rte_rwlock_write_unlock(&vq->access_lock);
207a9120db8SMaxime Coquelin 
208a9120db8SMaxime Coquelin 	vq_efd.index = index;
209a9120db8SMaxime Coquelin 	vq_efd.fd = vq->kickfd;
210a9120db8SMaxime Coquelin 
211a9120db8SMaxime Coquelin 	ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd);
212a9120db8SMaxime Coquelin 	if (ret) {
2130e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to setup kickfd for VQ %u: %s",
214a9120db8SMaxime Coquelin 				index, strerror(errno));
215a9120db8SMaxime Coquelin 		close(vq->kickfd);
216a9120db8SMaxime Coquelin 		vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD;
217a9120db8SMaxime Coquelin 		return;
218a9120db8SMaxime Coquelin 	}
219653327e1SMaxime Coquelin 
220653327e1SMaxime Coquelin 	if (vq == dev->cvq) {
221e68a6feaSMaxime Coquelin 		ret = fdset_add(vduse.fdset, vq->kickfd, vduse_control_queue_event, NULL, dev);
222653327e1SMaxime Coquelin 		if (ret) {
2230e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(dev->ifname, ERR,
2240e21c7c0SDavid Marchand 					"Failed to setup kickfd handler for VQ %u: %s",
225653327e1SMaxime Coquelin 					index, strerror(errno));
226653327e1SMaxime Coquelin 			vq_efd.fd = VDUSE_EVENTFD_DEASSIGN;
227653327e1SMaxime Coquelin 			ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd);
228653327e1SMaxime Coquelin 			close(vq->kickfd);
229653327e1SMaxime Coquelin 			vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD;
230653327e1SMaxime Coquelin 		}
231653327e1SMaxime Coquelin 		vhost_enable_guest_notification(dev, vq, 1);
2320e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl queue event handler installed");
233653327e1SMaxime Coquelin 	}
234a9120db8SMaxime Coquelin }
235a9120db8SMaxime Coquelin 
236a9120db8SMaxime Coquelin static void
237ad67c65eSMaxime Coquelin vduse_vring_cleanup(struct virtio_net *dev, unsigned int index)
238ad67c65eSMaxime Coquelin {
239ad67c65eSMaxime Coquelin 	struct vhost_virtqueue *vq = dev->virtqueue[index];
240ad67c65eSMaxime Coquelin 	struct vduse_vq_eventfd vq_efd;
241ad67c65eSMaxime Coquelin 	int ret;
242ad67c65eSMaxime Coquelin 
2430c18e4fbSMaxime Coquelin 	if (vq == dev->cvq && vq->kickfd >= 0)
244e68a6feaSMaxime Coquelin 		fdset_del(vduse.fdset, vq->kickfd);
245ad67c65eSMaxime Coquelin 
246ad67c65eSMaxime Coquelin 	vq_efd.index = index;
247ad67c65eSMaxime Coquelin 	vq_efd.fd = VDUSE_EVENTFD_DEASSIGN;
248ad67c65eSMaxime Coquelin 
249ad67c65eSMaxime Coquelin 	ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd);
250ad67c65eSMaxime Coquelin 	if (ret)
2510e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to cleanup kickfd for VQ %u: %s",
252ad67c65eSMaxime Coquelin 				index, strerror(errno));
253ad67c65eSMaxime Coquelin 
254ad67c65eSMaxime Coquelin 	close(vq->kickfd);
255ad67c65eSMaxime Coquelin 	vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD;
256ad67c65eSMaxime Coquelin 
257af532865SDavid Marchand 	rte_rwlock_write_lock(&vq->access_lock);
258ad67c65eSMaxime Coquelin 	vring_invalidate(dev, vq);
259af532865SDavid Marchand 	rte_rwlock_write_unlock(&vq->access_lock);
260ad67c65eSMaxime Coquelin 
261ad67c65eSMaxime Coquelin 	rte_free(vq->batch_copy_elems);
262ad67c65eSMaxime Coquelin 	vq->batch_copy_elems = NULL;
263ad67c65eSMaxime Coquelin 
264ad67c65eSMaxime Coquelin 	rte_free(vq->shadow_used_split);
265ad67c65eSMaxime Coquelin 	vq->shadow_used_split = NULL;
266ad67c65eSMaxime Coquelin 
267ad67c65eSMaxime Coquelin 	vq->enabled = false;
268ad67c65eSMaxime Coquelin 	vq->ready = false;
269ad67c65eSMaxime Coquelin 	vq->size = 0;
270ad67c65eSMaxime Coquelin 	vq->last_used_idx = 0;
271ad67c65eSMaxime Coquelin 	vq->last_avail_idx = 0;
272ad67c65eSMaxime Coquelin }
273ad67c65eSMaxime Coquelin 
274ad67c65eSMaxime Coquelin static void
275da79cc7fSMaxime Coquelin vduse_device_start(struct virtio_net *dev, bool reconnect)
276a9120db8SMaxime Coquelin {
277a9120db8SMaxime Coquelin 	unsigned int i, ret;
278a9120db8SMaxime Coquelin 
2790e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "Starting device...");
280a9120db8SMaxime Coquelin 
281a9120db8SMaxime Coquelin 	dev->notify_ops = vhost_driver_callback_get(dev->ifname);
282a9120db8SMaxime Coquelin 	if (!dev->notify_ops) {
2830e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR,
2840e21c7c0SDavid Marchand 				"Failed to get callback ops for driver");
285a9120db8SMaxime Coquelin 		return;
286a9120db8SMaxime Coquelin 	}
287a9120db8SMaxime Coquelin 
288a9120db8SMaxime Coquelin 	ret = ioctl(dev->vduse_dev_fd, VDUSE_DEV_GET_FEATURES, &dev->features);
289a9120db8SMaxime Coquelin 	if (ret) {
2900e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get features: %s",
291a9120db8SMaxime Coquelin 				strerror(errno));
292a9120db8SMaxime Coquelin 		return;
293a9120db8SMaxime Coquelin 	}
294a9120db8SMaxime Coquelin 
295da79cc7fSMaxime Coquelin 	if (reconnect && dev->features != dev->reconnect_log->features) {
296da79cc7fSMaxime Coquelin 		VHOST_CONFIG_LOG(dev->ifname, ERR,
297da79cc7fSMaxime Coquelin 				"Mismatch between reconnect file features 0x%" PRIx64 " & device features 0x%" PRIx64,
298da79cc7fSMaxime Coquelin 				dev->reconnect_log->features, dev->features);
299da79cc7fSMaxime Coquelin 		return;
300da79cc7fSMaxime Coquelin 	}
301da79cc7fSMaxime Coquelin 
302da79cc7fSMaxime Coquelin 	dev->reconnect_log->features = dev->features;
303da79cc7fSMaxime Coquelin 
3040e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "Negotiated Virtio features: 0x%" PRIx64,
305a9120db8SMaxime Coquelin 		dev->features);
306a9120db8SMaxime Coquelin 
307a9120db8SMaxime Coquelin 	if (dev->features &
308a9120db8SMaxime Coquelin 		((1ULL << VIRTIO_NET_F_MRG_RXBUF) |
309a9120db8SMaxime Coquelin 		 (1ULL << VIRTIO_F_VERSION_1) |
310a9120db8SMaxime Coquelin 		 (1ULL << VIRTIO_F_RING_PACKED))) {
311a9120db8SMaxime Coquelin 		dev->vhost_hlen = sizeof(struct virtio_net_hdr_mrg_rxbuf);
312a9120db8SMaxime Coquelin 	} else {
313a9120db8SMaxime Coquelin 		dev->vhost_hlen = sizeof(struct virtio_net_hdr);
314a9120db8SMaxime Coquelin 	}
315a9120db8SMaxime Coquelin 
316a9120db8SMaxime Coquelin 	for (i = 0; i < dev->nr_vring; i++)
317da79cc7fSMaxime Coquelin 		vduse_vring_setup(dev, i, reconnect);
318a9120db8SMaxime Coquelin 
319a9120db8SMaxime Coquelin 	dev->flags |= VIRTIO_DEV_READY;
320a9120db8SMaxime Coquelin 
321a9120db8SMaxime Coquelin 	if (dev->notify_ops->new_device(dev->vid) == 0)
322a9120db8SMaxime Coquelin 		dev->flags |= VIRTIO_DEV_RUNNING;
323a9120db8SMaxime Coquelin 
324a9120db8SMaxime Coquelin 	for (i = 0; i < dev->nr_vring; i++) {
325a9120db8SMaxime Coquelin 		struct vhost_virtqueue *vq = dev->virtqueue[i];
326a9120db8SMaxime Coquelin 
327653327e1SMaxime Coquelin 		if (vq == dev->cvq)
328653327e1SMaxime Coquelin 			continue;
329653327e1SMaxime Coquelin 
330a9120db8SMaxime Coquelin 		if (dev->notify_ops->vring_state_changed)
331a9120db8SMaxime Coquelin 			dev->notify_ops->vring_state_changed(dev->vid, i, vq->enabled);
332a9120db8SMaxime Coquelin 	}
333a9120db8SMaxime Coquelin }
334a9120db8SMaxime Coquelin 
335a9120db8SMaxime Coquelin static void
336ad67c65eSMaxime Coquelin vduse_device_stop(struct virtio_net *dev)
337ad67c65eSMaxime Coquelin {
338ad67c65eSMaxime Coquelin 	unsigned int i;
339ad67c65eSMaxime Coquelin 
3400e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "Stopping device...");
341ad67c65eSMaxime Coquelin 
342ad67c65eSMaxime Coquelin 	vhost_destroy_device_notify(dev);
343ad67c65eSMaxime Coquelin 
344ad67c65eSMaxime Coquelin 	dev->flags &= ~VIRTIO_DEV_READY;
345ad67c65eSMaxime Coquelin 
346ad67c65eSMaxime Coquelin 	for (i = 0; i < dev->nr_vring; i++)
347ad67c65eSMaxime Coquelin 		vduse_vring_cleanup(dev, i);
348ad67c65eSMaxime Coquelin 
349ad67c65eSMaxime Coquelin 	vhost_user_iotlb_flush_all(dev);
350ad67c65eSMaxime Coquelin }
351ad67c65eSMaxime Coquelin 
352ad67c65eSMaxime Coquelin static void
35351d018fdSMaxime Coquelin vduse_events_handler(int fd, void *arg, int *remove __rte_unused)
35451d018fdSMaxime Coquelin {
35551d018fdSMaxime Coquelin 	struct virtio_net *dev = arg;
35651d018fdSMaxime Coquelin 	struct vduse_dev_request req;
35751d018fdSMaxime Coquelin 	struct vduse_dev_response resp;
35810f18cb1SMaxime Coquelin 	struct vhost_virtqueue *vq;
359ad67c65eSMaxime Coquelin 	uint8_t old_status = dev->status;
36051d018fdSMaxime Coquelin 	int ret;
36151d018fdSMaxime Coquelin 
36251d018fdSMaxime Coquelin 	memset(&resp, 0, sizeof(resp));
36351d018fdSMaxime Coquelin 
36451d018fdSMaxime Coquelin 	ret = read(fd, &req, sizeof(req));
36551d018fdSMaxime Coquelin 	if (ret < 0) {
3660e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to read request: %s",
36751d018fdSMaxime Coquelin 				strerror(errno));
36851d018fdSMaxime Coquelin 		return;
36951d018fdSMaxime Coquelin 	} else if (ret < (int)sizeof(req)) {
3700e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Incomplete to read request %d", ret);
37151d018fdSMaxime Coquelin 		return;
37251d018fdSMaxime Coquelin 	}
37351d018fdSMaxime Coquelin 
3740e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "New request: %s (%u)",
37551d018fdSMaxime Coquelin 			vduse_req_id_to_str(req.type), req.type);
37651d018fdSMaxime Coquelin 
37751d018fdSMaxime Coquelin 	switch (req.type) {
37810f18cb1SMaxime Coquelin 	case VDUSE_GET_VQ_STATE:
37910f18cb1SMaxime Coquelin 		vq = dev->virtqueue[req.vq_state.index];
3800e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, INFO, "\tvq index: %u, avail_index: %u",
38110f18cb1SMaxime Coquelin 				req.vq_state.index, vq->last_avail_idx);
38210f18cb1SMaxime Coquelin 		resp.vq_state.split.avail_index = vq->last_avail_idx;
38310f18cb1SMaxime Coquelin 		resp.result = VDUSE_REQ_RESULT_OK;
38410f18cb1SMaxime Coquelin 		break;
38577c78c17SMaxime Coquelin 	case VDUSE_SET_STATUS:
3860e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, INFO, "\tnew status: 0x%08x",
38777c78c17SMaxime Coquelin 				req.s.status);
388ad67c65eSMaxime Coquelin 		old_status = dev->status;
38977c78c17SMaxime Coquelin 		dev->status = req.s.status;
390da79cc7fSMaxime Coquelin 		dev->reconnect_log->status = dev->status;
39177c78c17SMaxime Coquelin 		resp.result = VDUSE_REQ_RESULT_OK;
39277c78c17SMaxime Coquelin 		break;
393400f40a3SMaxime Coquelin 	case VDUSE_UPDATE_IOTLB:
3940e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, INFO, "\tIOVA range: %" PRIx64 " - %" PRIx64,
395400f40a3SMaxime Coquelin 				(uint64_t)req.iova.start, (uint64_t)req.iova.last);
396400f40a3SMaxime Coquelin 		vhost_user_iotlb_cache_remove(dev, req.iova.start,
397400f40a3SMaxime Coquelin 				req.iova.last - req.iova.start + 1);
398400f40a3SMaxime Coquelin 		resp.result = VDUSE_REQ_RESULT_OK;
399400f40a3SMaxime Coquelin 		break;
40051d018fdSMaxime Coquelin 	default:
40151d018fdSMaxime Coquelin 		resp.result = VDUSE_REQ_RESULT_FAILED;
40251d018fdSMaxime Coquelin 		break;
40351d018fdSMaxime Coquelin 	}
40451d018fdSMaxime Coquelin 
40551d018fdSMaxime Coquelin 	resp.request_id = req.request_id;
40651d018fdSMaxime Coquelin 
40751d018fdSMaxime Coquelin 	ret = write(dev->vduse_dev_fd, &resp, sizeof(resp));
40851d018fdSMaxime Coquelin 	if (ret != sizeof(resp)) {
4090e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to write response %s",
41051d018fdSMaxime Coquelin 				strerror(errno));
41151d018fdSMaxime Coquelin 		return;
41251d018fdSMaxime Coquelin 	}
413a9120db8SMaxime Coquelin 
414ad67c65eSMaxime Coquelin 	if ((old_status ^ dev->status) & VIRTIO_DEVICE_STATUS_DRIVER_OK) {
415a9120db8SMaxime Coquelin 		if (dev->status & VIRTIO_DEVICE_STATUS_DRIVER_OK)
416da79cc7fSMaxime Coquelin 			vduse_device_start(dev, false);
417ad67c65eSMaxime Coquelin 		else
418ad67c65eSMaxime Coquelin 			vduse_device_stop(dev);
419ad67c65eSMaxime Coquelin 	}
420a9120db8SMaxime Coquelin 
4210e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(dev->ifname, INFO, "Request %s (%u) handled successfully",
42251d018fdSMaxime Coquelin 			vduse_req_id_to_str(req.type), req.type);
42351d018fdSMaxime Coquelin }
42451d018fdSMaxime Coquelin 
425da79cc7fSMaxime Coquelin static char vduse_reconnect_dir[PATH_MAX];
426da79cc7fSMaxime Coquelin static bool vduse_reconnect_path_set;
427da79cc7fSMaxime Coquelin 
428da79cc7fSMaxime Coquelin static int
429da79cc7fSMaxime Coquelin vduse_reconnect_path_init(void)
430da79cc7fSMaxime Coquelin {
431da79cc7fSMaxime Coquelin 	const char *directory;
432da79cc7fSMaxime Coquelin 	int ret;
433da79cc7fSMaxime Coquelin 
43455977694SMaxime Coquelin 	if (vduse_reconnect_path_set == true)
43555977694SMaxime Coquelin 		return 0;
43655977694SMaxime Coquelin 
437da79cc7fSMaxime Coquelin 	/* from RuntimeDirectory= see systemd.exec */
438da79cc7fSMaxime Coquelin 	directory = getenv("RUNTIME_DIRECTORY");
439da79cc7fSMaxime Coquelin 	if (directory == NULL) {
440da79cc7fSMaxime Coquelin 		/*
441da79cc7fSMaxime Coquelin 		 * Used standard convention defined in
442da79cc7fSMaxime Coquelin 		 * XDG Base Directory Specification and
443da79cc7fSMaxime Coquelin 		 * Filesystem Hierarchy Standard.
444da79cc7fSMaxime Coquelin 		 */
445da79cc7fSMaxime Coquelin 		if (getuid() == 0)
446da79cc7fSMaxime Coquelin 			directory = "/var/run";
447da79cc7fSMaxime Coquelin 		else
448da79cc7fSMaxime Coquelin 			directory = getenv("XDG_RUNTIME_DIR") ? : "/tmp";
449da79cc7fSMaxime Coquelin 	}
450da79cc7fSMaxime Coquelin 
451da79cc7fSMaxime Coquelin 	ret = snprintf(vduse_reconnect_dir, sizeof(vduse_reconnect_dir), "%s/vduse",
452da79cc7fSMaxime Coquelin 			directory);
453da79cc7fSMaxime Coquelin 	if (ret < 0 || ret == sizeof(vduse_reconnect_dir)) {
454da79cc7fSMaxime Coquelin 		VHOST_CONFIG_LOG("vduse", ERR, "Error creating VDUSE reconnect path name");
455da79cc7fSMaxime Coquelin 		return -1;
456da79cc7fSMaxime Coquelin 	}
457da79cc7fSMaxime Coquelin 
458da79cc7fSMaxime Coquelin 	ret = mkdir(vduse_reconnect_dir, 0700);
459da79cc7fSMaxime Coquelin 	if (ret < 0 && errno != EEXIST) {
460da79cc7fSMaxime Coquelin 		VHOST_CONFIG_LOG("vduse", ERR, "Error creating '%s': %s",
461da79cc7fSMaxime Coquelin 				vduse_reconnect_dir, strerror(errno));
462da79cc7fSMaxime Coquelin 		return -1;
463da79cc7fSMaxime Coquelin 	}
464da79cc7fSMaxime Coquelin 
465da79cc7fSMaxime Coquelin 	VHOST_CONFIG_LOG("vduse", INFO, "Created VDUSE reconnect directory in %s",
466da79cc7fSMaxime Coquelin 			vduse_reconnect_dir);
467da79cc7fSMaxime Coquelin 
46855977694SMaxime Coquelin 	vduse_reconnect_path_set = true;
46955977694SMaxime Coquelin 
470da79cc7fSMaxime Coquelin 	return 0;
471da79cc7fSMaxime Coquelin }
472da79cc7fSMaxime Coquelin 
47355977694SMaxime Coquelin static int
474a2a05e55SMaxime Coquelin vduse_reconnect_log_map(struct virtio_net *dev, bool create)
47555977694SMaxime Coquelin {
47655977694SMaxime Coquelin 	char reco_file[PATH_MAX];
47755977694SMaxime Coquelin 	int fd, ret;
478a2a05e55SMaxime Coquelin 	const char *name = dev->ifname + strlen("/dev/vduse/");
47955977694SMaxime Coquelin 
48055977694SMaxime Coquelin 	if (vduse_reconnect_path_init() < 0) {
481a2a05e55SMaxime Coquelin 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to initialize reconnect path");
48255977694SMaxime Coquelin 		return -1;
48355977694SMaxime Coquelin 	}
48455977694SMaxime Coquelin 
485a2a05e55SMaxime Coquelin 	ret = snprintf(reco_file, sizeof(reco_file), "%s/%s", vduse_reconnect_dir, name);
48655977694SMaxime Coquelin 	if (ret < 0 || ret == sizeof(reco_file)) {
487a2a05e55SMaxime Coquelin 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to create vduse reconnect path name");
48855977694SMaxime Coquelin 		return -1;
48955977694SMaxime Coquelin 	}
49055977694SMaxime Coquelin 
49155977694SMaxime Coquelin 	if (create) {
49255977694SMaxime Coquelin 		fd = open(reco_file, O_CREAT | O_EXCL | O_RDWR, 0600);
49355977694SMaxime Coquelin 		if (fd < 0) {
49455977694SMaxime Coquelin 			if (errno == EEXIST) {
495a2a05e55SMaxime Coquelin 				VHOST_CONFIG_LOG(dev->ifname, ERR, "Reconnect file %s exists but not the device",
49655977694SMaxime Coquelin 						reco_file);
49755977694SMaxime Coquelin 			} else {
498a2a05e55SMaxime Coquelin 				VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to open reconnect file %s (%s)",
49955977694SMaxime Coquelin 						reco_file, strerror(errno));
50055977694SMaxime Coquelin 			}
50155977694SMaxime Coquelin 			return -1;
50255977694SMaxime Coquelin 		}
50355977694SMaxime Coquelin 
504a2a05e55SMaxime Coquelin 		ret = ftruncate(fd, sizeof(*dev->reconnect_log));
50555977694SMaxime Coquelin 		if (ret < 0) {
506a2a05e55SMaxime Coquelin 			VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to truncate reconnect file %s (%s)",
50755977694SMaxime Coquelin 					reco_file, strerror(errno));
50855977694SMaxime Coquelin 			goto out_close;
50955977694SMaxime Coquelin 		}
51055977694SMaxime Coquelin 	} else {
51155977694SMaxime Coquelin 		fd = open(reco_file, O_RDWR, 0600);
51255977694SMaxime Coquelin 		if (fd < 0) {
51355977694SMaxime Coquelin 			if (errno == ENOENT)
514a2a05e55SMaxime Coquelin 				VHOST_CONFIG_LOG(dev->ifname, ERR, "Missing reconnect file (%s)", reco_file);
51555977694SMaxime Coquelin 			else
516a2a05e55SMaxime Coquelin 				VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to open reconnect file %s (%s)",
51755977694SMaxime Coquelin 						reco_file, strerror(errno));
51855977694SMaxime Coquelin 			return -1;
51955977694SMaxime Coquelin 		}
52055977694SMaxime Coquelin 	}
52155977694SMaxime Coquelin 
522a2a05e55SMaxime Coquelin 	dev->reconnect_log = mmap(NULL, sizeof(*dev->reconnect_log), PROT_READ | PROT_WRITE,
523a2a05e55SMaxime Coquelin 				MAP_SHARED, fd, 0);
524a2a05e55SMaxime Coquelin 	if (dev->reconnect_log == MAP_FAILED) {
525a2a05e55SMaxime Coquelin 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to mmap reconnect file %s (%s)",
52655977694SMaxime Coquelin 				reco_file, strerror(errno));
52755977694SMaxime Coquelin 		ret = -1;
52855977694SMaxime Coquelin 		goto out_close;
52955977694SMaxime Coquelin 	}
53055977694SMaxime Coquelin 	ret = 0;
53155977694SMaxime Coquelin 
53255977694SMaxime Coquelin out_close:
53355977694SMaxime Coquelin 	close(fd);
53455977694SMaxime Coquelin 
53555977694SMaxime Coquelin 	return ret;
53655977694SMaxime Coquelin }
53755977694SMaxime Coquelin 
53869d2e256SMaxime Coquelin static int
539a2a05e55SMaxime Coquelin vduse_reconnect_log_check(struct virtio_net *dev, uint64_t features, uint32_t total_queues)
54069d2e256SMaxime Coquelin {
541a2a05e55SMaxime Coquelin 	if (dev->reconnect_log->version != VHOST_RECONNECT_VERSION) {
542a2a05e55SMaxime Coquelin 		VHOST_CONFIG_LOG(dev->ifname, ERR,
54369d2e256SMaxime Coquelin 				"Version mismatch between backend (0x%x) & reconnection file (0x%x)",
544a2a05e55SMaxime Coquelin 				VHOST_RECONNECT_VERSION, dev->reconnect_log->version);
54569d2e256SMaxime Coquelin 		return -1;
54669d2e256SMaxime Coquelin 	}
54769d2e256SMaxime Coquelin 
548a2a05e55SMaxime Coquelin 	if ((dev->reconnect_log->features & features) != dev->reconnect_log->features) {
549a2a05e55SMaxime Coquelin 		VHOST_CONFIG_LOG(dev->ifname, ERR,
55069d2e256SMaxime Coquelin 				"Features mismatch between backend (0x%" PRIx64 ") & reconnection file (0x%" PRIx64 ")",
551a2a05e55SMaxime Coquelin 				features, dev->reconnect_log->features);
55269d2e256SMaxime Coquelin 		return -1;
55369d2e256SMaxime Coquelin 	}
55469d2e256SMaxime Coquelin 
555a2a05e55SMaxime Coquelin 	if (dev->reconnect_log->nr_vrings != total_queues) {
556a2a05e55SMaxime Coquelin 		VHOST_CONFIG_LOG(dev->ifname, ERR,
55769d2e256SMaxime Coquelin 				"Queues number mismatch between backend (%u) and reconnection file (%u)",
558a2a05e55SMaxime Coquelin 				total_queues, dev->reconnect_log->nr_vrings);
55969d2e256SMaxime Coquelin 		return -1;
56069d2e256SMaxime Coquelin 	}
56169d2e256SMaxime Coquelin 
56269d2e256SMaxime Coquelin 	return 0;
56369d2e256SMaxime Coquelin }
56469d2e256SMaxime Coquelin 
565da79cc7fSMaxime Coquelin static void
566da79cc7fSMaxime Coquelin vduse_reconnect_handler(int fd, void *arg, int *remove)
567da79cc7fSMaxime Coquelin {
568da79cc7fSMaxime Coquelin 	struct virtio_net *dev = arg;
569da79cc7fSMaxime Coquelin 
570da79cc7fSMaxime Coquelin 	vduse_device_start(dev, true);
571da79cc7fSMaxime Coquelin 
572da79cc7fSMaxime Coquelin 	close(fd);
573da79cc7fSMaxime Coquelin 	*remove = 1;
574da79cc7fSMaxime Coquelin }
575da79cc7fSMaxime Coquelin 
576952e4945SMaxime Coquelin static int
577952e4945SMaxime Coquelin vduse_reconnect_start_device(struct virtio_net *dev)
578952e4945SMaxime Coquelin {
579952e4945SMaxime Coquelin 	int fd, ret;
580952e4945SMaxime Coquelin 
581952e4945SMaxime Coquelin 	/*
582952e4945SMaxime Coquelin 	 * Make vduse_device_start() being executed in the same
583952e4945SMaxime Coquelin 	 * context for both reconnection and fresh startup.
584952e4945SMaxime Coquelin 	 */
585952e4945SMaxime Coquelin 	fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
586952e4945SMaxime Coquelin 	if (fd < 0) {
587952e4945SMaxime Coquelin 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to create reconnect efd: %s",
588952e4945SMaxime Coquelin 				strerror(errno));
589952e4945SMaxime Coquelin 		ret = -1;
590952e4945SMaxime Coquelin 		goto out_err;
591952e4945SMaxime Coquelin 	}
592952e4945SMaxime Coquelin 
593952e4945SMaxime Coquelin 	ret = fdset_add(vduse.fdset, fd, vduse_reconnect_handler, NULL, dev);
594952e4945SMaxime Coquelin 	if (ret) {
595952e4945SMaxime Coquelin 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to add reconnect efd %d to vduse fdset",
596952e4945SMaxime Coquelin 				fd);
597952e4945SMaxime Coquelin 		goto out_err_close;
598952e4945SMaxime Coquelin 	}
599952e4945SMaxime Coquelin 
600952e4945SMaxime Coquelin 	ret = eventfd_write(fd, (eventfd_t)1);
601952e4945SMaxime Coquelin 	if (ret < 0) {
602952e4945SMaxime Coquelin 		VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to write to reconnect eventfd");
603952e4945SMaxime Coquelin 		goto out_err_fdset;
604952e4945SMaxime Coquelin 	}
605952e4945SMaxime Coquelin 
606952e4945SMaxime Coquelin 	return 0;
607952e4945SMaxime Coquelin 
608952e4945SMaxime Coquelin out_err_fdset:
609952e4945SMaxime Coquelin 	fdset_del(vduse.fdset, fd);
610952e4945SMaxime Coquelin out_err_close:
611952e4945SMaxime Coquelin 	close(fd);
612952e4945SMaxime Coquelin out_err:
613952e4945SMaxime Coquelin 	return ret;
614952e4945SMaxime Coquelin }
615952e4945SMaxime Coquelin 
6160adb8eccSMaxime Coquelin int
617927d2aefSMaxime Coquelin vduse_device_create(const char *path, bool compliant_ol_flags)
6180adb8eccSMaxime Coquelin {
61955977694SMaxime Coquelin 	int control_fd, dev_fd, vid, ret;
620653327e1SMaxime Coquelin 	uint32_t i, max_queue_pairs, total_queues;
6210adb8eccSMaxime Coquelin 	struct virtio_net *dev;
62225bdd1c3SAli Alnubani 	struct virtio_net_config vnet_config = {{ 0 }};
6230adb8eccSMaxime Coquelin 	uint64_t ver = VHOST_VDUSE_API_VERSION;
6244789eb43SMaxime Coquelin 	uint64_t features;
6250adb8eccSMaxime Coquelin 	const char *name = path + strlen("/dev/vduse/");
626da79cc7fSMaxime Coquelin 	bool reconnect = false;
6270adb8eccSMaxime Coquelin 
628e68a6feaSMaxime Coquelin 	if (vduse.fdset == NULL) {
629e68a6feaSMaxime Coquelin 		vduse.fdset = fdset_init("vduse-evt");
630e68a6feaSMaxime Coquelin 		if (vduse.fdset == NULL) {
6317945769cSMaxime Coquelin 			VHOST_CONFIG_LOG(path, ERR, "failed to init VDUSE fdset");
6327945769cSMaxime Coquelin 			return -1;
6337945769cSMaxime Coquelin 		}
63451d018fdSMaxime Coquelin 	}
63551d018fdSMaxime Coquelin 
6360adb8eccSMaxime Coquelin 	control_fd = open(VDUSE_CTRL_PATH, O_RDWR);
6370adb8eccSMaxime Coquelin 	if (control_fd < 0) {
6380e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(name, ERR, "Failed to open %s: %s",
6390adb8eccSMaxime Coquelin 				VDUSE_CTRL_PATH, strerror(errno));
6400adb8eccSMaxime Coquelin 		return -1;
6410adb8eccSMaxime Coquelin 	}
6420adb8eccSMaxime Coquelin 
6430adb8eccSMaxime Coquelin 	if (ioctl(control_fd, VDUSE_SET_API_VERSION, &ver)) {
6440e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(name, ERR, "Failed to set API version: %" PRIu64 ": %s",
6450adb8eccSMaxime Coquelin 				ver, strerror(errno));
6460adb8eccSMaxime Coquelin 		ret = -1;
6470adb8eccSMaxime Coquelin 		goto out_ctrl_close;
6480adb8eccSMaxime Coquelin 	}
6490adb8eccSMaxime Coquelin 
6504789eb43SMaxime Coquelin 	ret = rte_vhost_driver_get_features(path, &features);
6514789eb43SMaxime Coquelin 	if (ret < 0) {
6520e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(name, ERR, "Failed to get backend features");
65347458d13SMaxime Coquelin 		goto out_ctrl_close;
6544789eb43SMaxime Coquelin 	}
6554789eb43SMaxime Coquelin 
656653327e1SMaxime Coquelin 	ret = rte_vhost_driver_get_queue_num(path, &max_queue_pairs);
657653327e1SMaxime Coquelin 	if (ret < 0) {
6580e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(name, ERR, "Failed to get max queue pairs");
65947458d13SMaxime Coquelin 		goto out_ctrl_close;
660653327e1SMaxime Coquelin 	}
661653327e1SMaxime Coquelin 
6620e21c7c0SDavid Marchand 	VHOST_CONFIG_LOG(path, INFO, "VDUSE max queue pairs: %u", max_queue_pairs);
663653327e1SMaxime Coquelin 	total_queues = max_queue_pairs * 2;
664653327e1SMaxime Coquelin 
665653327e1SMaxime Coquelin 	if (max_queue_pairs == 1)
666653327e1SMaxime Coquelin 		features &= ~(RTE_BIT64(VIRTIO_NET_F_CTRL_VQ) | RTE_BIT64(VIRTIO_NET_F_MQ));
667653327e1SMaxime Coquelin 	else
668653327e1SMaxime Coquelin 		total_queues += 1; /* Includes ctrl queue */
669653327e1SMaxime Coquelin 
67029ab97ddSMaxime Coquelin 	dev_fd = open(path, O_RDWR);
67129ab97ddSMaxime Coquelin 	if (dev_fd >= 0) {
672da79cc7fSMaxime Coquelin 		VHOST_CONFIG_LOG(name, INFO, "Device already exists, reconnecting...");
673da79cc7fSMaxime Coquelin 		reconnect = true;
67429ab97ddSMaxime Coquelin 	} else if (errno == ENOENT) {
67547458d13SMaxime Coquelin 		struct vduse_dev_config *dev_config;
67647458d13SMaxime Coquelin 
677da79cc7fSMaxime Coquelin 		dev_config = malloc(offsetof(struct vduse_dev_config, config) +
678da79cc7fSMaxime Coquelin 				sizeof(vnet_config));
679da79cc7fSMaxime Coquelin 		if (!dev_config) {
680da79cc7fSMaxime Coquelin 			VHOST_CONFIG_LOG(name, ERR, "Failed to allocate VDUSE config");
681da79cc7fSMaxime Coquelin 			ret = -1;
682a2a05e55SMaxime Coquelin 			goto out_ctrl_close;
683da79cc7fSMaxime Coquelin 		}
684da79cc7fSMaxime Coquelin 
685653327e1SMaxime Coquelin 		vnet_config.max_virtqueue_pairs = max_queue_pairs;
6860adb8eccSMaxime Coquelin 		memset(dev_config, 0, sizeof(struct vduse_dev_config));
6870adb8eccSMaxime Coquelin 
688da79cc7fSMaxime Coquelin 		rte_strscpy(dev_config->name, name, VDUSE_NAME_MAX - 1);
6890adb8eccSMaxime Coquelin 		dev_config->device_id = VIRTIO_ID_NET;
6900adb8eccSMaxime Coquelin 		dev_config->vendor_id = 0;
691653327e1SMaxime Coquelin 		dev_config->features = features;
692653327e1SMaxime Coquelin 		dev_config->vq_num = total_queues;
6930adb8eccSMaxime Coquelin 		dev_config->vq_align = sysconf(_SC_PAGE_SIZE);
694653327e1SMaxime Coquelin 		dev_config->config_size = sizeof(struct virtio_net_config);
695653327e1SMaxime Coquelin 		memcpy(dev_config->config, &vnet_config, sizeof(vnet_config));
6960adb8eccSMaxime Coquelin 
6970adb8eccSMaxime Coquelin 		ret = ioctl(control_fd, VDUSE_CREATE_DEV, dev_config);
69847458d13SMaxime Coquelin 		free(dev_config);
69947458d13SMaxime Coquelin 		dev_config = NULL;
7000adb8eccSMaxime Coquelin 		if (ret < 0) {
7010e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(name, ERR, "Failed to create VDUSE device: %s",
7020adb8eccSMaxime Coquelin 					strerror(errno));
703a2a05e55SMaxime Coquelin 			goto out_ctrl_close;
7040adb8eccSMaxime Coquelin 		}
7050adb8eccSMaxime Coquelin 
7060adb8eccSMaxime Coquelin 		dev_fd = open(path, O_RDWR);
7070adb8eccSMaxime Coquelin 		if (dev_fd < 0) {
70829ab97ddSMaxime Coquelin 			VHOST_CONFIG_LOG(name, ERR, "Failed to open newly created device %s: %s",
70929ab97ddSMaxime Coquelin 					path, strerror(errno));
71029ab97ddSMaxime Coquelin 			ret = -1;
711a2a05e55SMaxime Coquelin 			goto out_ctrl_close;
71229ab97ddSMaxime Coquelin 		}
71329ab97ddSMaxime Coquelin 	} else {
7140e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(name, ERR, "Failed to open device %s: %s",
7150adb8eccSMaxime Coquelin 				path, strerror(errno));
7160adb8eccSMaxime Coquelin 		ret = -1;
71729ab97ddSMaxime Coquelin 		goto out_ctrl_close;
7180adb8eccSMaxime Coquelin 	}
7190adb8eccSMaxime Coquelin 
7200adb8eccSMaxime Coquelin 	ret = fcntl(dev_fd, F_SETFL, O_NONBLOCK);
7210adb8eccSMaxime Coquelin 	if (ret < 0) {
7220e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(name, ERR, "Failed to set chardev as non-blocking: %s",
7230adb8eccSMaxime Coquelin 				strerror(errno));
724a2a05e55SMaxime Coquelin 		goto out_dev_close;
7250adb8eccSMaxime Coquelin 	}
7260adb8eccSMaxime Coquelin 
7270adb8eccSMaxime Coquelin 	vid = vhost_new_device(&vduse_backend_ops);
7280adb8eccSMaxime Coquelin 	if (vid < 0) {
7290e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(name, ERR, "Failed to create new Vhost device");
7300adb8eccSMaxime Coquelin 		ret = -1;
731a2a05e55SMaxime Coquelin 		goto out_dev_close;
7320adb8eccSMaxime Coquelin 	}
7330adb8eccSMaxime Coquelin 
7340adb8eccSMaxime Coquelin 	dev = get_device(vid);
7350adb8eccSMaxime Coquelin 	if (!dev) {
7360adb8eccSMaxime Coquelin 		ret = -1;
73729ab97ddSMaxime Coquelin 		goto out_dev_destroy;
7380adb8eccSMaxime Coquelin 	}
7390adb8eccSMaxime Coquelin 
7400adb8eccSMaxime Coquelin 	strncpy(dev->ifname, path, IF_NAME_SZ - 1);
7410adb8eccSMaxime Coquelin 	dev->vduse_ctrl_fd = control_fd;
7420adb8eccSMaxime Coquelin 	dev->vduse_dev_fd = dev_fd;
743a2a05e55SMaxime Coquelin 
744a2a05e55SMaxime Coquelin 	ret = vduse_reconnect_log_map(dev, !reconnect);
745a2a05e55SMaxime Coquelin 	if (ret < 0)
746a2a05e55SMaxime Coquelin 		goto out_dev_destroy;
747a2a05e55SMaxime Coquelin 
748a2a05e55SMaxime Coquelin 	if (reconnect) {
749a2a05e55SMaxime Coquelin 		ret = vduse_reconnect_log_check(dev, features, total_queues);
750a2a05e55SMaxime Coquelin 		if (ret < 0)
751a2a05e55SMaxime Coquelin 			goto out_log_unmap;
752a2a05e55SMaxime Coquelin 
753da79cc7fSMaxime Coquelin 		dev->status = dev->reconnect_log->status;
754a2a05e55SMaxime Coquelin 	} else {
755a2a05e55SMaxime Coquelin 		dev->reconnect_log->version = VHOST_RECONNECT_VERSION;
756a2a05e55SMaxime Coquelin 		dev->reconnect_log->nr_vrings = total_queues;
757a2a05e55SMaxime Coquelin 		memcpy(&dev->reconnect_log->config, &vnet_config, sizeof(vnet_config));
758a2a05e55SMaxime Coquelin 	}
759da79cc7fSMaxime Coquelin 
760927d2aefSMaxime Coquelin 	vhost_setup_virtio_net(dev->vid, true, compliant_ol_flags, true, true);
7610adb8eccSMaxime Coquelin 
762653327e1SMaxime Coquelin 	for (i = 0; i < total_queues; i++) {
7630adb8eccSMaxime Coquelin 		struct vduse_vq_config vq_cfg = { 0 };
764da79cc7fSMaxime Coquelin 		struct vhost_virtqueue *vq;
7650adb8eccSMaxime Coquelin 
7660adb8eccSMaxime Coquelin 		ret = alloc_vring_queue(dev, i);
7670adb8eccSMaxime Coquelin 		if (ret) {
7680e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(name, ERR, "Failed to alloc vring %d metadata", i);
769a2a05e55SMaxime Coquelin 			goto out_log_unmap;
7700adb8eccSMaxime Coquelin 		}
7710adb8eccSMaxime Coquelin 
772da79cc7fSMaxime Coquelin 		vq = dev->virtqueue[i];
773a2a05e55SMaxime Coquelin 		vq->reconnect_log = &dev->reconnect_log->vring[i];
774da79cc7fSMaxime Coquelin 
775da79cc7fSMaxime Coquelin 		if (reconnect)
776da79cc7fSMaxime Coquelin 			continue;
777da79cc7fSMaxime Coquelin 
7780adb8eccSMaxime Coquelin 		vq_cfg.index = i;
7790adb8eccSMaxime Coquelin 		vq_cfg.max_size = 1024;
7800adb8eccSMaxime Coquelin 
7810adb8eccSMaxime Coquelin 		ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP, &vq_cfg);
7820adb8eccSMaxime Coquelin 		if (ret) {
7830e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(name, ERR, "Failed to set-up VQ %d", i);
784a2a05e55SMaxime Coquelin 			goto out_log_unmap;
7850adb8eccSMaxime Coquelin 		}
7860adb8eccSMaxime Coquelin 	}
7870adb8eccSMaxime Coquelin 
788653327e1SMaxime Coquelin 	dev->cvq = dev->virtqueue[max_queue_pairs * 2];
789653327e1SMaxime Coquelin 
790e68a6feaSMaxime Coquelin 	ret = fdset_add(vduse.fdset, dev->vduse_dev_fd, vduse_events_handler, NULL, dev);
79151d018fdSMaxime Coquelin 	if (ret) {
7920e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(name, ERR, "Failed to add fd %d to vduse fdset",
79351d018fdSMaxime Coquelin 				dev->vduse_dev_fd);
794a2a05e55SMaxime Coquelin 		goto out_log_unmap;
79551d018fdSMaxime Coquelin 	}
79651d018fdSMaxime Coquelin 
797da79cc7fSMaxime Coquelin 	if (reconnect && dev->status & VIRTIO_DEVICE_STATUS_DRIVER_OK)  {
798952e4945SMaxime Coquelin 		ret = vduse_reconnect_start_device(dev);
799952e4945SMaxime Coquelin 		if (ret)
800a2a05e55SMaxime Coquelin 			goto out_log_unmap;
801da79cc7fSMaxime Coquelin 	}
802da79cc7fSMaxime Coquelin 
8030adb8eccSMaxime Coquelin 	return 0;
8040adb8eccSMaxime Coquelin 
805a2a05e55SMaxime Coquelin out_log_unmap:
806a2a05e55SMaxime Coquelin 	munmap(dev->reconnect_log, sizeof(*dev->reconnect_log));
8070adb8eccSMaxime Coquelin out_dev_destroy:
8080adb8eccSMaxime Coquelin 	vhost_destroy_device(vid);
8090adb8eccSMaxime Coquelin out_dev_close:
8100adb8eccSMaxime Coquelin 	if (dev_fd >= 0)
8110adb8eccSMaxime Coquelin 		close(dev_fd);
8120adb8eccSMaxime Coquelin 	ioctl(control_fd, VDUSE_DESTROY_DEV, name);
8130adb8eccSMaxime Coquelin out_ctrl_close:
8140adb8eccSMaxime Coquelin 	close(control_fd);
8150adb8eccSMaxime Coquelin 
8160adb8eccSMaxime Coquelin 	return ret;
8170adb8eccSMaxime Coquelin }
8180adb8eccSMaxime Coquelin 
8190adb8eccSMaxime Coquelin int
8200adb8eccSMaxime Coquelin vduse_device_destroy(const char *path)
8210adb8eccSMaxime Coquelin {
8220adb8eccSMaxime Coquelin 	const char *name = path + strlen("/dev/vduse/");
8230adb8eccSMaxime Coquelin 	struct virtio_net *dev;
8240adb8eccSMaxime Coquelin 	int vid, ret;
8250adb8eccSMaxime Coquelin 
8260adb8eccSMaxime Coquelin 	for (vid = 0; vid < RTE_MAX_VHOST_DEVICE; vid++) {
8270adb8eccSMaxime Coquelin 		dev = vhost_devices[vid];
8280adb8eccSMaxime Coquelin 
8290adb8eccSMaxime Coquelin 		if (dev == NULL)
8300adb8eccSMaxime Coquelin 			continue;
8310adb8eccSMaxime Coquelin 
8320adb8eccSMaxime Coquelin 		if (!strcmp(path, dev->ifname))
8330adb8eccSMaxime Coquelin 			break;
8340adb8eccSMaxime Coquelin 	}
8350adb8eccSMaxime Coquelin 
8360adb8eccSMaxime Coquelin 	if (vid == RTE_MAX_VHOST_DEVICE)
8370adb8eccSMaxime Coquelin 		return -1;
8380adb8eccSMaxime Coquelin 
839da79cc7fSMaxime Coquelin 	if (dev->reconnect_log)
840da79cc7fSMaxime Coquelin 		munmap(dev->reconnect_log, sizeof(*dev->reconnect_log));
841da79cc7fSMaxime Coquelin 
842ad67c65eSMaxime Coquelin 	vduse_device_stop(dev);
843653327e1SMaxime Coquelin 
844e68a6feaSMaxime Coquelin 	fdset_del(vduse.fdset, dev->vduse_dev_fd);
84551d018fdSMaxime Coquelin 
8460adb8eccSMaxime Coquelin 	if (dev->vduse_dev_fd >= 0) {
8470adb8eccSMaxime Coquelin 		close(dev->vduse_dev_fd);
8480adb8eccSMaxime Coquelin 		dev->vduse_dev_fd = -1;
8490adb8eccSMaxime Coquelin 	}
8500adb8eccSMaxime Coquelin 
8510adb8eccSMaxime Coquelin 	if (dev->vduse_ctrl_fd >= 0) {
852da79cc7fSMaxime Coquelin 		char reconnect_file[PATH_MAX];
853da79cc7fSMaxime Coquelin 
8540adb8eccSMaxime Coquelin 		ret = ioctl(dev->vduse_ctrl_fd, VDUSE_DESTROY_DEV, name);
855da79cc7fSMaxime Coquelin 		if (ret) {
8560e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(name, ERR, "Failed to destroy VDUSE device: %s",
8570adb8eccSMaxime Coquelin 					strerror(errno));
858da79cc7fSMaxime Coquelin 		} else {
859da79cc7fSMaxime Coquelin 			/*
860da79cc7fSMaxime Coquelin 			 * VDUSE device was no more attached to the vDPA bus,
861da79cc7fSMaxime Coquelin 			 * so we can remove the reconnect file.
862da79cc7fSMaxime Coquelin 			 */
863da79cc7fSMaxime Coquelin 			ret = snprintf(reconnect_file, sizeof(reconnect_file), "%s/%s",
864da79cc7fSMaxime Coquelin 					vduse_reconnect_dir, name);
865da79cc7fSMaxime Coquelin 			if (ret < 0 || ret == sizeof(reconnect_file))
866da79cc7fSMaxime Coquelin 				VHOST_CONFIG_LOG(name, ERR,
867da79cc7fSMaxime Coquelin 						"Failed to create vduse reconnect path name");
868da79cc7fSMaxime Coquelin 			else
869da79cc7fSMaxime Coquelin 				unlink(reconnect_file);
870da79cc7fSMaxime Coquelin 		}
871da79cc7fSMaxime Coquelin 
8720adb8eccSMaxime Coquelin 		close(dev->vduse_ctrl_fd);
8730adb8eccSMaxime Coquelin 		dev->vduse_ctrl_fd = -1;
8740adb8eccSMaxime Coquelin 	}
8750adb8eccSMaxime Coquelin 
8760adb8eccSMaxime Coquelin 	vhost_destroy_device(vid);
8770adb8eccSMaxime Coquelin 
8780adb8eccSMaxime Coquelin 	return 0;
8790adb8eccSMaxime Coquelin }
880