15566a3e3SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
25566a3e3SBruce Richardson  * Copyright(c) 2016 Intel Corporation
3e3b43481SJianfeng Tan  */
4e3b43481SJianfeng Tan 
5e3b43481SJianfeng Tan #include <sys/types.h>
6e3b43481SJianfeng Tan #include <sys/stat.h>
7e3b43481SJianfeng Tan #include <fcntl.h>
8e3b43481SJianfeng Tan #include <unistd.h>
906856cabSMaxime Coquelin #include <errno.h>
10e3b43481SJianfeng Tan 
11e3b43481SJianfeng Tan #include <rte_memory.h>
12e3b43481SJianfeng Tan 
13e3b43481SJianfeng Tan #include "vhost.h"
14e3b43481SJianfeng Tan #include "virtio_user_dev.h"
15e3b43481SJianfeng Tan #include "vhost_kernel_tap.h"
16e3b43481SJianfeng Tan 
17*86388a3aSMaxime Coquelin struct vhost_kernel_data {
18*86388a3aSMaxime Coquelin 	int *vhostfds;
19*86388a3aSMaxime Coquelin 	int *tapfds;
20*86388a3aSMaxime Coquelin };
21*86388a3aSMaxime Coquelin 
22e3b43481SJianfeng Tan struct vhost_memory_kernel {
23e3b43481SJianfeng Tan 	uint32_t nregions;
24e3b43481SJianfeng Tan 	uint32_t padding;
25e3b43481SJianfeng Tan 	struct vhost_memory_region regions[0];
26e3b43481SJianfeng Tan };
27e3b43481SJianfeng Tan 
28e3b43481SJianfeng Tan /* vhost kernel ioctls */
29e3b43481SJianfeng Tan #define VHOST_VIRTIO 0xAF
30e3b43481SJianfeng Tan #define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
31e3b43481SJianfeng Tan #define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
32e3b43481SJianfeng Tan #define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
33e3b43481SJianfeng Tan #define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
34e3b43481SJianfeng Tan #define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory_kernel)
35e3b43481SJianfeng Tan #define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
36e3b43481SJianfeng Tan #define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
37e3b43481SJianfeng Tan #define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
38e3b43481SJianfeng Tan #define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
39e3b43481SJianfeng Tan #define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
40e3b43481SJianfeng Tan #define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
41e3b43481SJianfeng Tan #define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
42e3b43481SJianfeng Tan #define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
43e3b43481SJianfeng Tan #define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file)
44e3b43481SJianfeng Tan #define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file)
45e3b43481SJianfeng Tan 
46cc0151b3SMaxime Coquelin /* with below features, vhost kernel does not need to do the checksum and TSO,
47cc0151b3SMaxime Coquelin  * these info will be passed to virtio_user through virtio net header.
48cc0151b3SMaxime Coquelin  */
49cc0151b3SMaxime Coquelin #define VHOST_KERNEL_GUEST_OFFLOADS_MASK	\
50cc0151b3SMaxime Coquelin 	((1ULL << VIRTIO_NET_F_GUEST_CSUM) |	\
51cc0151b3SMaxime Coquelin 	 (1ULL << VIRTIO_NET_F_GUEST_TSO4) |	\
52cc0151b3SMaxime Coquelin 	 (1ULL << VIRTIO_NET_F_GUEST_TSO6) |	\
53cc0151b3SMaxime Coquelin 	 (1ULL << VIRTIO_NET_F_GUEST_ECN)  |	\
54cc0151b3SMaxime Coquelin 	 (1ULL << VIRTIO_NET_F_GUEST_UFO))
55cc0151b3SMaxime Coquelin 
56cc0151b3SMaxime Coquelin /* with below features, when flows from virtio_user to vhost kernel
57cc0151b3SMaxime Coquelin  * (1) if flows goes up through the kernel networking stack, it does not need
58cc0151b3SMaxime Coquelin  * to verify checksum, which can save CPU cycles;
59cc0151b3SMaxime Coquelin  * (2) if flows goes through a Linux bridge and outside from an interface
60cc0151b3SMaxime Coquelin  * (kernel driver), checksum and TSO will be done by GSO in kernel or even
61cc0151b3SMaxime Coquelin  * offloaded into real physical device.
62cc0151b3SMaxime Coquelin  */
63cc0151b3SMaxime Coquelin #define VHOST_KERNEL_HOST_OFFLOADS_MASK		\
64cc0151b3SMaxime Coquelin 	((1ULL << VIRTIO_NET_F_HOST_TSO4) |	\
65cc0151b3SMaxime Coquelin 	 (1ULL << VIRTIO_NET_F_HOST_TSO6) |	\
66cc0151b3SMaxime Coquelin 	 (1ULL << VIRTIO_NET_F_CSUM))
67cc0151b3SMaxime Coquelin 
68e3b43481SJianfeng Tan static uint64_t max_regions = 64;
69e3b43481SJianfeng Tan 
70e3b43481SJianfeng Tan static void
71e3b43481SJianfeng Tan get_vhost_kernel_max_regions(void)
72e3b43481SJianfeng Tan {
73e3b43481SJianfeng Tan 	int fd;
74e3b43481SJianfeng Tan 	char buf[20] = {'\0'};
75e3b43481SJianfeng Tan 
76e3b43481SJianfeng Tan 	fd = open("/sys/module/vhost/parameters/max_mem_regions", O_RDONLY);
77e3b43481SJianfeng Tan 	if (fd < 0)
78e3b43481SJianfeng Tan 		return;
79e3b43481SJianfeng Tan 
80e3b43481SJianfeng Tan 	if (read(fd, buf, sizeof(buf) - 1) > 0)
81e3b43481SJianfeng Tan 		max_regions = strtoull(buf, NULL, 10);
82e3b43481SJianfeng Tan 
83e3b43481SJianfeng Tan 	close(fd);
84e3b43481SJianfeng Tan }
85e3b43481SJianfeng Tan 
8606856cabSMaxime Coquelin static int
8706856cabSMaxime Coquelin vhost_kernel_ioctl(int fd, uint64_t request, void *arg)
8806856cabSMaxime Coquelin {
8906856cabSMaxime Coquelin 	int ret;
9006856cabSMaxime Coquelin 
9106856cabSMaxime Coquelin 	ret = ioctl(fd, request, arg);
9206856cabSMaxime Coquelin 	if (ret) {
9306856cabSMaxime Coquelin 		PMD_DRV_LOG(ERR, "Vhost-kernel ioctl %"PRIu64" failed (%s)",
9406856cabSMaxime Coquelin 				request, strerror(errno));
9506856cabSMaxime Coquelin 		return -1;
9606856cabSMaxime Coquelin 	}
9706856cabSMaxime Coquelin 
9806856cabSMaxime Coquelin 	return 0;
9906856cabSMaxime Coquelin }
10006856cabSMaxime Coquelin 
10106856cabSMaxime Coquelin static int
10206856cabSMaxime Coquelin vhost_kernel_set_owner(struct virtio_user_dev *dev)
10306856cabSMaxime Coquelin {
104*86388a3aSMaxime Coquelin 	struct vhost_kernel_data *data = dev->backend_data;
105*86388a3aSMaxime Coquelin 
106*86388a3aSMaxime Coquelin 	return vhost_kernel_ioctl(data->vhostfds[0], VHOST_SET_OWNER, NULL);
10706856cabSMaxime Coquelin }
10806856cabSMaxime Coquelin 
109cc0151b3SMaxime Coquelin static int
110cc0151b3SMaxime Coquelin vhost_kernel_get_features(struct virtio_user_dev *dev, uint64_t *features)
111cc0151b3SMaxime Coquelin {
112cc0151b3SMaxime Coquelin 	int ret;
113cc0151b3SMaxime Coquelin 	unsigned int tap_features;
114*86388a3aSMaxime Coquelin 	struct vhost_kernel_data *data = dev->backend_data;
115cc0151b3SMaxime Coquelin 
116*86388a3aSMaxime Coquelin 	ret = vhost_kernel_ioctl(data->vhostfds[0], VHOST_GET_FEATURES, features);
117cc0151b3SMaxime Coquelin 	if (ret < 0) {
118cc0151b3SMaxime Coquelin 		PMD_DRV_LOG(ERR, "Failed to get features");
119cc0151b3SMaxime Coquelin 		return -1;
120cc0151b3SMaxime Coquelin 	}
121cc0151b3SMaxime Coquelin 
122cc0151b3SMaxime Coquelin 	ret = tap_support_features(&tap_features);
123cc0151b3SMaxime Coquelin 	if (ret < 0) {
124cc0151b3SMaxime Coquelin 		PMD_DRV_LOG(ERR, "Failed to get TAP features");
125cc0151b3SMaxime Coquelin 		return -1;
126cc0151b3SMaxime Coquelin 	}
127cc0151b3SMaxime Coquelin 
128cc0151b3SMaxime Coquelin 	/* with tap as the backend, all these features are supported
129cc0151b3SMaxime Coquelin 	 * but not claimed by vhost-net, so we add them back when
130cc0151b3SMaxime Coquelin 	 * reporting to upper layer.
131cc0151b3SMaxime Coquelin 	 */
132cc0151b3SMaxime Coquelin 	if (tap_features & IFF_VNET_HDR) {
133cc0151b3SMaxime Coquelin 		*features |= VHOST_KERNEL_GUEST_OFFLOADS_MASK;
134cc0151b3SMaxime Coquelin 		*features |= VHOST_KERNEL_HOST_OFFLOADS_MASK;
135cc0151b3SMaxime Coquelin 	}
136cc0151b3SMaxime Coquelin 
137cc0151b3SMaxime Coquelin 	/* vhost_kernel will not declare this feature, but it does
138cc0151b3SMaxime Coquelin 	 * support multi-queue.
139cc0151b3SMaxime Coquelin 	 */
140cc0151b3SMaxime Coquelin 	if (tap_features & IFF_MULTI_QUEUE)
141cc0151b3SMaxime Coquelin 		*features |= (1ull << VIRTIO_NET_F_MQ);
142cc0151b3SMaxime Coquelin 
143cc0151b3SMaxime Coquelin 	return 0;
144cc0151b3SMaxime Coquelin }
145cc0151b3SMaxime Coquelin 
146cc0151b3SMaxime Coquelin static int
147cc0151b3SMaxime Coquelin vhost_kernel_set_features(struct virtio_user_dev *dev, uint64_t features)
148cc0151b3SMaxime Coquelin {
149*86388a3aSMaxime Coquelin 	struct vhost_kernel_data *data = dev->backend_data;
150*86388a3aSMaxime Coquelin 
151cc0151b3SMaxime Coquelin 	/* We don't need memory protection here */
152cc0151b3SMaxime Coquelin 	features &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
153cc0151b3SMaxime Coquelin 	/* VHOST kernel does not know about below flags */
154cc0151b3SMaxime Coquelin 	features &= ~VHOST_KERNEL_GUEST_OFFLOADS_MASK;
155cc0151b3SMaxime Coquelin 	features &= ~VHOST_KERNEL_HOST_OFFLOADS_MASK;
156cc0151b3SMaxime Coquelin 	features &= ~(1ULL << VIRTIO_NET_F_MQ);
157cc0151b3SMaxime Coquelin 
158*86388a3aSMaxime Coquelin 	return vhost_kernel_ioctl(data->vhostfds[0], VHOST_SET_FEATURES, &features);
159cc0151b3SMaxime Coquelin }
160cc0151b3SMaxime Coquelin 
161746c346dSAnatoly Burakov static int
16275728654STiwei Bie add_memseg_list(const struct rte_memseg_list *msl, void *arg)
163746c346dSAnatoly Burakov {
16475728654STiwei Bie 	struct vhost_memory_kernel *vm = arg;
165746c346dSAnatoly Burakov 	struct vhost_memory_region *mr;
166746c346dSAnatoly Burakov 	void *start_addr;
16775728654STiwei Bie 	uint64_t len;
168746c346dSAnatoly Burakov 
1695282bb1cSAnatoly Burakov 	if (msl->external)
1705282bb1cSAnatoly Burakov 		return 0;
1715282bb1cSAnatoly Burakov 
17275728654STiwei Bie 	if (vm->nregions >= max_regions)
173746c346dSAnatoly Burakov 		return -1;
174746c346dSAnatoly Burakov 
17575728654STiwei Bie 	start_addr = msl->base_va;
17675728654STiwei Bie 	len = msl->page_sz * msl->memseg_arr.len;
17775728654STiwei Bie 
17875728654STiwei Bie 	mr = &vm->regions[vm->nregions++];
179746c346dSAnatoly Burakov 
180746c346dSAnatoly Burakov 	mr->guest_phys_addr = (uint64_t)(uintptr_t)start_addr;
181746c346dSAnatoly Burakov 	mr->userspace_addr = (uint64_t)(uintptr_t)start_addr;
182746c346dSAnatoly Burakov 	mr->memory_size = len;
18375728654STiwei Bie 	mr->mmap_offset = 0; /* flags_padding */
18475728654STiwei Bie 
18575728654STiwei Bie 	PMD_DRV_LOG(DEBUG, "index=%u addr=%p len=%" PRIu64,
18675728654STiwei Bie 			vm->nregions - 1, start_addr, len);
187746c346dSAnatoly Burakov 
188746c346dSAnatoly Burakov 	return 0;
189746c346dSAnatoly Burakov }
190746c346dSAnatoly Burakov 
19175728654STiwei Bie /* By default, vhost kernel module allows 64 regions, but DPDK may
19275728654STiwei Bie  * have much more memory regions. Below function will treat each
19375728654STiwei Bie  * contiguous memory space reserved by DPDK as one region.
194e3b43481SJianfeng Tan  */
195539d910cSMaxime Coquelin static int
196539d910cSMaxime Coquelin vhost_kernel_set_memory_table(struct virtio_user_dev *dev)
197e3b43481SJianfeng Tan {
198*86388a3aSMaxime Coquelin 	struct vhost_kernel_data *data = dev->backend_data;
199e3b43481SJianfeng Tan 	struct vhost_memory_kernel *vm;
200539d910cSMaxime Coquelin 	int ret;
201e3b43481SJianfeng Tan 
202e3b43481SJianfeng Tan 	vm = malloc(sizeof(struct vhost_memory_kernel) +
203e3b43481SJianfeng Tan 			max_regions *
204e3b43481SJianfeng Tan 			sizeof(struct vhost_memory_region));
2051e9057a9SJianfeng Tan 	if (!vm)
206539d910cSMaxime Coquelin 		goto err;
207e3b43481SJianfeng Tan 
20875728654STiwei Bie 	vm->nregions = 0;
20975728654STiwei Bie 	vm->padding = 0;
210e3b43481SJianfeng Tan 
2117ff26957STiwei Bie 	/*
2127ff26957STiwei Bie 	 * The memory lock has already been taken by memory subsystem
2137ff26957STiwei Bie 	 * or virtio_user_start_device().
2147ff26957STiwei Bie 	 */
215539d910cSMaxime Coquelin 	ret = rte_memseg_list_walk_thread_unsafe(add_memseg_list, vm);
216539d910cSMaxime Coquelin 	if (ret < 0)
217539d910cSMaxime Coquelin 		goto err_free;
218539d910cSMaxime Coquelin 
219*86388a3aSMaxime Coquelin 	ret = vhost_kernel_ioctl(data->vhostfds[0], VHOST_SET_MEM_TABLE, vm);
220539d910cSMaxime Coquelin 	if (ret < 0)
221539d910cSMaxime Coquelin 		goto err_free;
222539d910cSMaxime Coquelin 
223e3b43481SJianfeng Tan 	free(vm);
224539d910cSMaxime Coquelin 
225539d910cSMaxime Coquelin 	return 0;
226539d910cSMaxime Coquelin err_free:
227539d910cSMaxime Coquelin 	free(vm);
228539d910cSMaxime Coquelin err:
229539d910cSMaxime Coquelin 	PMD_DRV_LOG(ERR, "Failed to set memory table");
230539d910cSMaxime Coquelin 	return -1;
231e3b43481SJianfeng Tan }
232e3b43481SJianfeng Tan 
233ab9098d2SMaxime Coquelin static int
234ab9098d2SMaxime Coquelin vhost_kernel_set_vring(struct virtio_user_dev *dev, uint64_t req, struct vhost_vring_state *state)
235ab9098d2SMaxime Coquelin {
236ab9098d2SMaxime Coquelin 	int ret, fd;
237ab9098d2SMaxime Coquelin 	unsigned int index = state->index;
238*86388a3aSMaxime Coquelin 	struct vhost_kernel_data *data = dev->backend_data;
239ab9098d2SMaxime Coquelin 
240ab9098d2SMaxime Coquelin 	/* Convert from queue index to queue-pair & offset */
241*86388a3aSMaxime Coquelin 	fd = data->vhostfds[state->index / 2];
242ab9098d2SMaxime Coquelin 	state->index %= 2;
243ab9098d2SMaxime Coquelin 
244ab9098d2SMaxime Coquelin 	ret = vhost_kernel_ioctl(fd, req, state);
245ab9098d2SMaxime Coquelin 	if (ret < 0) {
246ab9098d2SMaxime Coquelin 		PMD_DRV_LOG(ERR, "Failed to set vring (request %" PRIu64 ")", req);
247ab9098d2SMaxime Coquelin 		return -1;
248ab9098d2SMaxime Coquelin 	}
249ab9098d2SMaxime Coquelin 
250ab9098d2SMaxime Coquelin 	/* restore index back to queue index */
251ab9098d2SMaxime Coquelin 	state->index = index;
252ab9098d2SMaxime Coquelin 
253ab9098d2SMaxime Coquelin 	return 0;
254ab9098d2SMaxime Coquelin }
255ab9098d2SMaxime Coquelin 
256ab9098d2SMaxime Coquelin static int
257ab9098d2SMaxime Coquelin vhost_kernel_set_vring_num(struct virtio_user_dev *dev, struct vhost_vring_state *state)
258ab9098d2SMaxime Coquelin {
259ab9098d2SMaxime Coquelin 	return vhost_kernel_set_vring(dev, VHOST_SET_VRING_NUM, state);
260ab9098d2SMaxime Coquelin }
261ab9098d2SMaxime Coquelin 
262ab9098d2SMaxime Coquelin static int
263ab9098d2SMaxime Coquelin vhost_kernel_set_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state)
264ab9098d2SMaxime Coquelin {
265ab9098d2SMaxime Coquelin 	return vhost_kernel_set_vring(dev, VHOST_SET_VRING_BASE, state);
266ab9098d2SMaxime Coquelin }
267ab9098d2SMaxime Coquelin 
268ab9098d2SMaxime Coquelin static int
269ab9098d2SMaxime Coquelin vhost_kernel_get_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state)
270ab9098d2SMaxime Coquelin {
271ab9098d2SMaxime Coquelin 	return vhost_kernel_set_vring(dev, VHOST_GET_VRING_BASE, state);
272ab9098d2SMaxime Coquelin }
273ab9098d2SMaxime Coquelin 
274ce399c36SMaxime Coquelin static int
275ce399c36SMaxime Coquelin vhost_kernel_set_vring_file(struct virtio_user_dev *dev, uint64_t req,
276ce399c36SMaxime Coquelin 		struct vhost_vring_file *file)
277ce399c36SMaxime Coquelin {
278ce399c36SMaxime Coquelin 	int ret, fd;
279ce399c36SMaxime Coquelin 	unsigned int index = file->index;
280*86388a3aSMaxime Coquelin 	struct vhost_kernel_data *data = dev->backend_data;
281ce399c36SMaxime Coquelin 
282ce399c36SMaxime Coquelin 	/* Convert from queue index to queue-pair & offset */
283*86388a3aSMaxime Coquelin 	fd = data->vhostfds[file->index / 2];
284ce399c36SMaxime Coquelin 	file->index %= 2;
285ce399c36SMaxime Coquelin 
286ce399c36SMaxime Coquelin 	ret = vhost_kernel_ioctl(fd, req, file);
287ce399c36SMaxime Coquelin 	if (ret < 0) {
288ce399c36SMaxime Coquelin 		PMD_DRV_LOG(ERR, "Failed to set vring file (request %" PRIu64 ")", req);
289ce399c36SMaxime Coquelin 		return -1;
290ce399c36SMaxime Coquelin 	}
291ce399c36SMaxime Coquelin 
292ce399c36SMaxime Coquelin 	/* restore index back to queue index */
293ce399c36SMaxime Coquelin 	file->index = index;
294ce399c36SMaxime Coquelin 
295ce399c36SMaxime Coquelin 	return 0;
296ce399c36SMaxime Coquelin }
297ce399c36SMaxime Coquelin 
298ce399c36SMaxime Coquelin static int
299ce399c36SMaxime Coquelin vhost_kernel_set_vring_kick(struct virtio_user_dev *dev, struct vhost_vring_file *file)
300ce399c36SMaxime Coquelin {
301ce399c36SMaxime Coquelin 	return vhost_kernel_set_vring_file(dev, VHOST_SET_VRING_KICK, file);
302ce399c36SMaxime Coquelin }
303ce399c36SMaxime Coquelin 
304ce399c36SMaxime Coquelin static int
305ce399c36SMaxime Coquelin vhost_kernel_set_vring_call(struct virtio_user_dev *dev, struct vhost_vring_file *file)
306ce399c36SMaxime Coquelin {
307ce399c36SMaxime Coquelin 	return vhost_kernel_set_vring_file(dev, VHOST_SET_VRING_CALL, file);
308ce399c36SMaxime Coquelin }
309ce399c36SMaxime Coquelin 
310dc65db73SMaxime Coquelin static int
311dc65db73SMaxime Coquelin vhost_kernel_set_vring_addr(struct virtio_user_dev *dev, struct vhost_vring_addr *addr)
312dc65db73SMaxime Coquelin {
313dc65db73SMaxime Coquelin 	int ret, fd;
314dc65db73SMaxime Coquelin 	unsigned int index = addr->index;
315*86388a3aSMaxime Coquelin 	struct vhost_kernel_data *data = dev->backend_data;
316dc65db73SMaxime Coquelin 
317dc65db73SMaxime Coquelin 	/* Convert from queue index to queue-pair & offset */
318*86388a3aSMaxime Coquelin 	fd = data->vhostfds[addr->index / 2];
319dc65db73SMaxime Coquelin 	addr->index %= 2;
320dc65db73SMaxime Coquelin 
321dc65db73SMaxime Coquelin 	ret = vhost_kernel_ioctl(fd, VHOST_SET_VRING_ADDR, addr);
322dc65db73SMaxime Coquelin 	if (ret < 0) {
323dc65db73SMaxime Coquelin 		PMD_DRV_LOG(ERR, "Failed to set vring address");
324dc65db73SMaxime Coquelin 		return -1;
325dc65db73SMaxime Coquelin 	}
326dc65db73SMaxime Coquelin 
327dc65db73SMaxime Coquelin 	/* restore index back to queue index */
328dc65db73SMaxime Coquelin 	addr->index = index;
329dc65db73SMaxime Coquelin 
330dc65db73SMaxime Coquelin 	return 0;
331dc65db73SMaxime Coquelin }
332dc65db73SMaxime Coquelin 
3338723c894SMaxime Coquelin static int
3348723c894SMaxime Coquelin vhost_kernel_get_status(struct virtio_user_dev *dev __rte_unused, uint8_t *status __rte_unused)
3358723c894SMaxime Coquelin {
3368723c894SMaxime Coquelin 	return -ENOTSUP;
3378723c894SMaxime Coquelin }
3388723c894SMaxime Coquelin 
3398723c894SMaxime Coquelin static int
3408723c894SMaxime Coquelin vhost_kernel_set_status(struct virtio_user_dev *dev __rte_unused, uint8_t status __rte_unused)
3418723c894SMaxime Coquelin {
3428723c894SMaxime Coquelin 	return -ENOTSUP;
3438723c894SMaxime Coquelin }
3448723c894SMaxime Coquelin 
345e3b43481SJianfeng Tan /**
346e3b43481SJianfeng Tan  * Set up environment to talk with a vhost kernel backend.
347e3b43481SJianfeng Tan  *
348e3b43481SJianfeng Tan  * @return
349e3b43481SJianfeng Tan  *   - (-1) if fail to set up;
350e3b43481SJianfeng Tan  *   - (>=0) if successful.
351e3b43481SJianfeng Tan  */
352e3b43481SJianfeng Tan static int
353e3b43481SJianfeng Tan vhost_kernel_setup(struct virtio_user_dev *dev)
354e3b43481SJianfeng Tan {
355e3b43481SJianfeng Tan 	int vhostfd;
356*86388a3aSMaxime Coquelin 	uint32_t q, i;
357*86388a3aSMaxime Coquelin 	struct vhost_kernel_data *data;
358*86388a3aSMaxime Coquelin 
359*86388a3aSMaxime Coquelin 	data = malloc(sizeof(*data));
360*86388a3aSMaxime Coquelin 	if (!data) {
361*86388a3aSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to allocate Vhost-kernel data", dev->path);
362*86388a3aSMaxime Coquelin 		return -1;
363*86388a3aSMaxime Coquelin 	}
364*86388a3aSMaxime Coquelin 
365*86388a3aSMaxime Coquelin 	data->vhostfds = malloc(dev->max_queue_pairs * sizeof(int));
366*86388a3aSMaxime Coquelin 	if (!data->vhostfds) {
367*86388a3aSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to allocate Vhost FDs", dev->path);
368*86388a3aSMaxime Coquelin 		goto err_data;
369*86388a3aSMaxime Coquelin 	}
370*86388a3aSMaxime Coquelin 	data->tapfds = malloc(dev->max_queue_pairs * sizeof(int));
371*86388a3aSMaxime Coquelin 	if (!data->tapfds) {
372*86388a3aSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to allocate TAP FDs", dev->path);
373*86388a3aSMaxime Coquelin 		goto err_vhostfds;
374*86388a3aSMaxime Coquelin 	}
375*86388a3aSMaxime Coquelin 
376*86388a3aSMaxime Coquelin 	for (q = 0; q < dev->max_queue_pairs; ++q) {
377*86388a3aSMaxime Coquelin 		data->vhostfds[q] = -1;
378*86388a3aSMaxime Coquelin 		data->tapfds[q] = -1;
379*86388a3aSMaxime Coquelin 	}
380e3b43481SJianfeng Tan 
381e3b43481SJianfeng Tan 	get_vhost_kernel_max_regions();
382e3b43481SJianfeng Tan 
383e3b43481SJianfeng Tan 	for (i = 0; i < dev->max_queue_pairs; ++i) {
384e3b43481SJianfeng Tan 		vhostfd = open(dev->path, O_RDWR);
385e3b43481SJianfeng Tan 		if (vhostfd < 0) {
386*86388a3aSMaxime Coquelin 			PMD_DRV_LOG(ERR, "fail to open %s, %s", dev->path, strerror(errno));
387*86388a3aSMaxime Coquelin 			goto err_tapfds;
388*86388a3aSMaxime Coquelin 		}
389*86388a3aSMaxime Coquelin 
390*86388a3aSMaxime Coquelin 		data->vhostfds[i] = vhostfd;
391*86388a3aSMaxime Coquelin 	}
392*86388a3aSMaxime Coquelin 
393*86388a3aSMaxime Coquelin 	dev->backend_data = data;
394*86388a3aSMaxime Coquelin 
395*86388a3aSMaxime Coquelin 	return 0;
396*86388a3aSMaxime Coquelin 
397*86388a3aSMaxime Coquelin err_tapfds:
398*86388a3aSMaxime Coquelin 	for (i = 0; i < dev->max_queue_pairs; i++)
399*86388a3aSMaxime Coquelin 		if (data->vhostfds[i] >= 0)
400*86388a3aSMaxime Coquelin 			close(data->vhostfds[i]);
401*86388a3aSMaxime Coquelin 
402*86388a3aSMaxime Coquelin 	free(data->tapfds);
403*86388a3aSMaxime Coquelin err_vhostfds:
404*86388a3aSMaxime Coquelin 	free(data->vhostfds);
405*86388a3aSMaxime Coquelin err_data:
406*86388a3aSMaxime Coquelin 	free(data);
407*86388a3aSMaxime Coquelin 
408e3b43481SJianfeng Tan 	return -1;
409e3b43481SJianfeng Tan }
410e3b43481SJianfeng Tan 
411e3b43481SJianfeng Tan static int
412*86388a3aSMaxime Coquelin vhost_kernel_destroy(struct virtio_user_dev *dev)
413748e5ea5SMaxime Coquelin {
414*86388a3aSMaxime Coquelin 	struct vhost_kernel_data *data = dev->backend_data;
415*86388a3aSMaxime Coquelin 	uint32_t i;
416*86388a3aSMaxime Coquelin 
417*86388a3aSMaxime Coquelin 	if (!data)
418*86388a3aSMaxime Coquelin 		return 0;
419*86388a3aSMaxime Coquelin 
420*86388a3aSMaxime Coquelin 	for (i = 0; i < dev->max_queue_pairs; ++i) {
421*86388a3aSMaxime Coquelin 		if (data->vhostfds[i] >= 0)
422*86388a3aSMaxime Coquelin 			close(data->vhostfds[i]);
423*86388a3aSMaxime Coquelin 		if (data->tapfds[i] >= 0)
424*86388a3aSMaxime Coquelin 			close(data->tapfds[i]);
425*86388a3aSMaxime Coquelin 	}
426*86388a3aSMaxime Coquelin 
427*86388a3aSMaxime Coquelin 	free(data->vhostfds);
428*86388a3aSMaxime Coquelin 	free(data->tapfds);
429*86388a3aSMaxime Coquelin 	free(data);
430*86388a3aSMaxime Coquelin 	dev->backend_data = NULL;
431*86388a3aSMaxime Coquelin 
432748e5ea5SMaxime Coquelin 	return 0;
433748e5ea5SMaxime Coquelin }
434748e5ea5SMaxime Coquelin 
435748e5ea5SMaxime Coquelin static int
436e3b43481SJianfeng Tan vhost_kernel_set_backend(int vhostfd, int tapfd)
437e3b43481SJianfeng Tan {
438e3b43481SJianfeng Tan 	struct vhost_vring_file f;
439e3b43481SJianfeng Tan 
440e3b43481SJianfeng Tan 	f.fd = tapfd;
441e3b43481SJianfeng Tan 	f.index = 0;
442e3b43481SJianfeng Tan 	if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
443e3b43481SJianfeng Tan 		PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
444e3b43481SJianfeng Tan 				strerror(errno));
445e3b43481SJianfeng Tan 		return -1;
446e3b43481SJianfeng Tan 	}
447e3b43481SJianfeng Tan 
448e3b43481SJianfeng Tan 	f.index = 1;
449e3b43481SJianfeng Tan 	if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
450e3b43481SJianfeng Tan 		PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
451e3b43481SJianfeng Tan 				strerror(errno));
452e3b43481SJianfeng Tan 		return -1;
453e3b43481SJianfeng Tan 	}
454e3b43481SJianfeng Tan 
455e3b43481SJianfeng Tan 	return 0;
456e3b43481SJianfeng Tan }
457e3b43481SJianfeng Tan 
458e3b43481SJianfeng Tan static int
459e3b43481SJianfeng Tan vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
460e3b43481SJianfeng Tan 			       uint16_t pair_idx,
461e3b43481SJianfeng Tan 			       int enable)
462e3b43481SJianfeng Tan {
463e3b43481SJianfeng Tan 	int hdr_size;
464e3b43481SJianfeng Tan 	int vhostfd;
465e3b43481SJianfeng Tan 	int tapfd;
466be7a4707SJianfeng Tan 	int req_mq = (dev->max_queue_pairs > 1);
467*86388a3aSMaxime Coquelin 	struct vhost_kernel_data *data = dev->backend_data;
468e3b43481SJianfeng Tan 
469*86388a3aSMaxime Coquelin 	vhostfd = data->vhostfds[pair_idx];
470e3b43481SJianfeng Tan 
47147ac9661STiwei Bie 	if (dev->qp_enabled[pair_idx] == enable)
472e3b43481SJianfeng Tan 		return 0;
47347ac9661STiwei Bie 
47447ac9661STiwei Bie 	if (!enable) {
475*86388a3aSMaxime Coquelin 		tapfd = data->tapfds[pair_idx];
47647ac9661STiwei Bie 		if (vhost_kernel_set_backend(vhostfd, -1) < 0) {
47747ac9661STiwei Bie 			PMD_DRV_LOG(ERR, "fail to set backend for vhost kernel");
47847ac9661STiwei Bie 			return -1;
47947ac9661STiwei Bie 		}
48047ac9661STiwei Bie 		if (req_mq && vhost_kernel_tap_set_queue(tapfd, false) < 0) {
48147ac9661STiwei Bie 			PMD_DRV_LOG(ERR, "fail to disable tap for vhost kernel");
48247ac9661STiwei Bie 			return -1;
48347ac9661STiwei Bie 		}
48447ac9661STiwei Bie 		dev->qp_enabled[pair_idx] = false;
48547ac9661STiwei Bie 		return 0;
48647ac9661STiwei Bie 	}
48747ac9661STiwei Bie 
488*86388a3aSMaxime Coquelin 	if (data->tapfds[pair_idx] >= 0) {
489*86388a3aSMaxime Coquelin 		tapfd = data->tapfds[pair_idx];
49047ac9661STiwei Bie 		if (vhost_kernel_tap_set_offload(tapfd, dev->features) == -1)
49147ac9661STiwei Bie 			return -1;
49247ac9661STiwei Bie 		if (req_mq && vhost_kernel_tap_set_queue(tapfd, true) < 0) {
49347ac9661STiwei Bie 			PMD_DRV_LOG(ERR, "fail to enable tap for vhost kernel");
49447ac9661STiwei Bie 			return -1;
49547ac9661STiwei Bie 		}
49647ac9661STiwei Bie 		goto set_backend;
497e3b43481SJianfeng Tan 	}
498e3b43481SJianfeng Tan 
499e3b43481SJianfeng Tan 	if ((dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) ||
500e3b43481SJianfeng Tan 	    (dev->features & (1ULL << VIRTIO_F_VERSION_1)))
501e3b43481SJianfeng Tan 		hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf);
502e3b43481SJianfeng Tan 	else
503e3b43481SJianfeng Tan 		hdr_size = sizeof(struct virtio_net_hdr);
504e3b43481SJianfeng Tan 
505791b43e0SNing Li 	tapfd = vhost_kernel_open_tap(&dev->ifname, hdr_size, req_mq,
5061db4d233SEric Zhang 			 (char *)dev->mac_addr, dev->features);
507e3b43481SJianfeng Tan 	if (tapfd < 0) {
508e3b43481SJianfeng Tan 		PMD_DRV_LOG(ERR, "fail to open tap for vhost kernel");
509e3b43481SJianfeng Tan 		return -1;
510e3b43481SJianfeng Tan 	}
511e3b43481SJianfeng Tan 
512*86388a3aSMaxime Coquelin 	data->tapfds[pair_idx] = tapfd;
51347ac9661STiwei Bie 
51447ac9661STiwei Bie set_backend:
515e3b43481SJianfeng Tan 	if (vhost_kernel_set_backend(vhostfd, tapfd) < 0) {
516e3b43481SJianfeng Tan 		PMD_DRV_LOG(ERR, "fail to set backend for vhost kernel");
517e3b43481SJianfeng Tan 		return -1;
518e3b43481SJianfeng Tan 	}
519e3b43481SJianfeng Tan 
52047ac9661STiwei Bie 	dev->qp_enabled[pair_idx] = true;
521e3b43481SJianfeng Tan 	return 0;
522e3b43481SJianfeng Tan }
523e3b43481SJianfeng Tan 
5245b75b63cSMaxime Coquelin static int
5255b75b63cSMaxime Coquelin vhost_kernel_get_backend_features(uint64_t *features)
5265b75b63cSMaxime Coquelin {
5275b75b63cSMaxime Coquelin 	*features = 0;
5285b75b63cSMaxime Coquelin 
5295b75b63cSMaxime Coquelin 	return 0;
5305b75b63cSMaxime Coquelin }
5315b75b63cSMaxime Coquelin 
53294973531SMaxime Coquelin static int
53394973531SMaxime Coquelin vhost_kernel_update_link_state(struct virtio_user_dev *dev __rte_unused)
53494973531SMaxime Coquelin {
53594973531SMaxime Coquelin 	/* Nothing to update (Maybe get TAP interface link state?) */
53694973531SMaxime Coquelin 	return 0;
53794973531SMaxime Coquelin }
53894973531SMaxime Coquelin 
53994973531SMaxime Coquelin static int
54094973531SMaxime Coquelin vhost_kernel_get_intr_fd(struct virtio_user_dev *dev __rte_unused)
54194973531SMaxime Coquelin {
54294973531SMaxime Coquelin 	/* No link state interrupt with Vhost-kernel */
54394973531SMaxime Coquelin 	return -1;
54494973531SMaxime Coquelin }
54594973531SMaxime Coquelin 
546520dd992SFerruh Yigit struct virtio_user_backend_ops virtio_ops_kernel = {
547e3b43481SJianfeng Tan 	.setup = vhost_kernel_setup,
548748e5ea5SMaxime Coquelin 	.destroy = vhost_kernel_destroy,
5495b75b63cSMaxime Coquelin 	.get_backend_features = vhost_kernel_get_backend_features,
55006856cabSMaxime Coquelin 	.set_owner = vhost_kernel_set_owner,
551cc0151b3SMaxime Coquelin 	.get_features = vhost_kernel_get_features,
552cc0151b3SMaxime Coquelin 	.set_features = vhost_kernel_set_features,
553539d910cSMaxime Coquelin 	.set_memory_table = vhost_kernel_set_memory_table,
554ab9098d2SMaxime Coquelin 	.set_vring_num = vhost_kernel_set_vring_num,
555ab9098d2SMaxime Coquelin 	.set_vring_base = vhost_kernel_set_vring_base,
556ab9098d2SMaxime Coquelin 	.get_vring_base = vhost_kernel_get_vring_base,
557ce399c36SMaxime Coquelin 	.set_vring_call = vhost_kernel_set_vring_call,
558ce399c36SMaxime Coquelin 	.set_vring_kick = vhost_kernel_set_vring_kick,
559dc65db73SMaxime Coquelin 	.set_vring_addr = vhost_kernel_set_vring_addr,
5608723c894SMaxime Coquelin 	.get_status = vhost_kernel_get_status,
5618723c894SMaxime Coquelin 	.set_status = vhost_kernel_set_status,
56294973531SMaxime Coquelin 	.enable_qp = vhost_kernel_enable_queue_pair,
56394973531SMaxime Coquelin 	.update_link_state = vhost_kernel_update_link_state,
56494973531SMaxime Coquelin 	.get_intr_fd = vhost_kernel_get_intr_fd,
565e3b43481SJianfeng Tan };
566