xref: /dpdk/drivers/net/virtio/virtio_user/virtio_user_dev.c (revision 2e4c1b50d4a3fbfb5f59fdbeed6e10c1d3761cc2)
15566a3e3SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
25566a3e3SBruce Richardson  * Copyright(c) 2010-2016 Intel Corporation
337a7eb2aSJianfeng Tan  */
437a7eb2aSJianfeng Tan 
537a7eb2aSJianfeng Tan #include <stdint.h>
637a7eb2aSJianfeng Tan #include <stdio.h>
737a7eb2aSJianfeng Tan #include <fcntl.h>
837a7eb2aSJianfeng Tan #include <string.h>
937a7eb2aSJianfeng Tan #include <errno.h>
1037a7eb2aSJianfeng Tan #include <sys/mman.h>
1137a7eb2aSJianfeng Tan #include <unistd.h>
1237a7eb2aSJianfeng Tan #include <sys/eventfd.h>
1333d24d65SJianfeng Tan #include <sys/types.h>
1433d24d65SJianfeng Tan #include <sys/stat.h>
1537a7eb2aSJianfeng Tan 
166723c0fcSBruce Richardson #include <rte_string_fns.h>
177ff26957STiwei Bie #include <rte_eal_memconfig.h>
187ff26957STiwei Bie 
1937a7eb2aSJianfeng Tan #include "vhost.h"
2037a7eb2aSJianfeng Tan #include "virtio_user_dev.h"
2137a7eb2aSJianfeng Tan #include "../virtio_ethdev.h"
2237a7eb2aSJianfeng Tan 
2312ecb2f6SMaxime Coquelin #define VIRTIO_USER_MEM_EVENT_CLB_NAME "virtio_user_mem_event_clb"
2412ecb2f6SMaxime Coquelin 
25b0395dc8SAdrian Moreno const char * const virtio_user_backend_strings[] = {
26b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_UNKNOWN] = "VIRTIO_USER_BACKEND_UNKNOWN",
27b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_VHOST_USER] = "VHOST_USER",
28b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_VHOST_KERNEL] = "VHOST_NET",
29b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_VHOST_VDPA] = "VHOST_VDPA",
30b0395dc8SAdrian Moreno };
31b0395dc8SAdrian Moreno 
3237a7eb2aSJianfeng Tan static int
3357ae79a7SJianfeng Tan virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
3457ae79a7SJianfeng Tan {
3557ae79a7SJianfeng Tan 	/* Of all per virtqueue MSGs, make sure VHOST_SET_VRING_CALL come
3657ae79a7SJianfeng Tan 	 * firstly because vhost depends on this msg to allocate virtqueue
3757ae79a7SJianfeng Tan 	 * pair.
3857ae79a7SJianfeng Tan 	 */
3957ae79a7SJianfeng Tan 	struct vhost_vring_file file;
40a3fb6b1dSMaxime Coquelin 	int ret;
4157ae79a7SJianfeng Tan 
4257ae79a7SJianfeng Tan 	file.index = queue_sel;
43e6e7ad8bSJianfeng Tan 	file.fd = dev->callfds[queue_sel];
44a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_call(dev, &file);
45a3fb6b1dSMaxime Coquelin 	if (ret < 0) {
46a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to create queue %u\n", dev->path, queue_sel);
47a3fb6b1dSMaxime Coquelin 		return -1;
48a3fb6b1dSMaxime Coquelin 	}
4957ae79a7SJianfeng Tan 
5057ae79a7SJianfeng Tan 	return 0;
5157ae79a7SJianfeng Tan }
5257ae79a7SJianfeng Tan 
5357ae79a7SJianfeng Tan static int
5437a7eb2aSJianfeng Tan virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
5537a7eb2aSJianfeng Tan {
56a3fb6b1dSMaxime Coquelin 	int ret;
5737a7eb2aSJianfeng Tan 	struct vhost_vring_file file;
5837a7eb2aSJianfeng Tan 	struct vhost_vring_state state;
5937a7eb2aSJianfeng Tan 	struct vring *vring = &dev->vrings[queue_sel];
6048a44640SJens Freimann 	struct vring_packed *pq_vring = &dev->packed_vrings[queue_sel];
6137a7eb2aSJianfeng Tan 	struct vhost_vring_addr addr = {
6237a7eb2aSJianfeng Tan 		.index = queue_sel,
6337a7eb2aSJianfeng Tan 		.log_guest_addr = 0,
6437a7eb2aSJianfeng Tan 		.flags = 0, /* disable log */
6537a7eb2aSJianfeng Tan 	};
6637a7eb2aSJianfeng Tan 
6748a44640SJens Freimann 	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
6848a44640SJens Freimann 		addr.desc_user_addr =
694cdc4d98STiwei Bie 			(uint64_t)(uintptr_t)pq_vring->desc;
7048a44640SJens Freimann 		addr.avail_user_addr =
714cdc4d98STiwei Bie 			(uint64_t)(uintptr_t)pq_vring->driver;
7248a44640SJens Freimann 		addr.used_user_addr =
734cdc4d98STiwei Bie 			(uint64_t)(uintptr_t)pq_vring->device;
7448a44640SJens Freimann 	} else {
7548a44640SJens Freimann 		addr.desc_user_addr = (uint64_t)(uintptr_t)vring->desc;
7648a44640SJens Freimann 		addr.avail_user_addr = (uint64_t)(uintptr_t)vring->avail;
7748a44640SJens Freimann 		addr.used_user_addr = (uint64_t)(uintptr_t)vring->used;
7848a44640SJens Freimann 	}
7948a44640SJens Freimann 
8037a7eb2aSJianfeng Tan 	state.index = queue_sel;
8137a7eb2aSJianfeng Tan 	state.num = vring->num;
82a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_num(dev, &state);
83a3fb6b1dSMaxime Coquelin 	if (ret < 0)
84a3fb6b1dSMaxime Coquelin 		goto err;
8537a7eb2aSJianfeng Tan 
86be7a4707SJianfeng Tan 	state.index = queue_sel;
8737a7eb2aSJianfeng Tan 	state.num = 0; /* no reservation */
8834f3966cSYuanhan Liu 	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED))
8934f3966cSYuanhan Liu 		state.num |= (1 << 15);
90a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_base(dev, &state);
91a3fb6b1dSMaxime Coquelin 	if (ret < 0)
92a3fb6b1dSMaxime Coquelin 		goto err;
9337a7eb2aSJianfeng Tan 
94a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_addr(dev, &addr);
95a3fb6b1dSMaxime Coquelin 	if (ret < 0)
96a3fb6b1dSMaxime Coquelin 		goto err;
9737a7eb2aSJianfeng Tan 
9837a7eb2aSJianfeng Tan 	/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
9937a7eb2aSJianfeng Tan 	 * lastly because vhost depends on this msg to judge if
10037a7eb2aSJianfeng Tan 	 * virtio is ready.
10137a7eb2aSJianfeng Tan 	 */
10257ae79a7SJianfeng Tan 	file.index = queue_sel;
103e6e7ad8bSJianfeng Tan 	file.fd = dev->kickfds[queue_sel];
104a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_kick(dev, &file);
105a3fb6b1dSMaxime Coquelin 	if (ret < 0)
106a3fb6b1dSMaxime Coquelin 		goto err;
10737a7eb2aSJianfeng Tan 
10837a7eb2aSJianfeng Tan 	return 0;
109a3fb6b1dSMaxime Coquelin err:
110a3fb6b1dSMaxime Coquelin 	PMD_INIT_LOG(ERR, "(%s) Failed to kick queue %u\n", dev->path, queue_sel);
111a3fb6b1dSMaxime Coquelin 
112a3fb6b1dSMaxime Coquelin 	return -1;
11337a7eb2aSJianfeng Tan }
11437a7eb2aSJianfeng Tan 
11557ae79a7SJianfeng Tan static int
11657ae79a7SJianfeng Tan virtio_user_queue_setup(struct virtio_user_dev *dev,
11757ae79a7SJianfeng Tan 			int (*fn)(struct virtio_user_dev *, uint32_t))
11857ae79a7SJianfeng Tan {
11957ae79a7SJianfeng Tan 	uint32_t i, queue_sel;
12057ae79a7SJianfeng Tan 
12157ae79a7SJianfeng Tan 	for (i = 0; i < dev->max_queue_pairs; ++i) {
12257ae79a7SJianfeng Tan 		queue_sel = 2 * i + VTNET_SQ_RQ_QUEUE_IDX;
12357ae79a7SJianfeng Tan 		if (fn(dev, queue_sel) < 0) {
124a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) setup rx vq %u failed", dev->path, i);
12557ae79a7SJianfeng Tan 			return -1;
12657ae79a7SJianfeng Tan 		}
12757ae79a7SJianfeng Tan 	}
12857ae79a7SJianfeng Tan 	for (i = 0; i < dev->max_queue_pairs; ++i) {
12957ae79a7SJianfeng Tan 		queue_sel = 2 * i + VTNET_SQ_TQ_QUEUE_IDX;
13057ae79a7SJianfeng Tan 		if (fn(dev, queue_sel) < 0) {
131a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(INFO, "(%s) setup tx vq %u failed", dev->path, i);
13257ae79a7SJianfeng Tan 			return -1;
13357ae79a7SJianfeng Tan 		}
13457ae79a7SJianfeng Tan 	}
13557ae79a7SJianfeng Tan 
13657ae79a7SJianfeng Tan 	return 0;
13757ae79a7SJianfeng Tan }
13857ae79a7SJianfeng Tan 
13937a7eb2aSJianfeng Tan int
140844e4683SMaxime Coquelin virtio_user_dev_set_features(struct virtio_user_dev *dev)
14137a7eb2aSJianfeng Tan {
14237a7eb2aSJianfeng Tan 	uint64_t features;
143844e4683SMaxime Coquelin 	int ret = -1;
144844e4683SMaxime Coquelin 
145844e4683SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
146844e4683SMaxime Coquelin 
147844e4683SMaxime Coquelin 	/* Step 0: tell vhost to create queues */
148844e4683SMaxime Coquelin 	if (virtio_user_queue_setup(dev, virtio_user_create_queue) < 0)
149844e4683SMaxime Coquelin 		goto error;
150844e4683SMaxime Coquelin 
151844e4683SMaxime Coquelin 	features = dev->features;
152844e4683SMaxime Coquelin 
153844e4683SMaxime Coquelin 	/* Strip VIRTIO_NET_F_MAC, as MAC address is handled in vdev init */
154844e4683SMaxime Coquelin 	features &= ~(1ull << VIRTIO_NET_F_MAC);
155844e4683SMaxime Coquelin 	/* Strip VIRTIO_NET_F_CTRL_VQ, as devices do not really need to know */
156844e4683SMaxime Coquelin 	features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
157844e4683SMaxime Coquelin 	features &= ~(1ull << VIRTIO_NET_F_STATUS);
158cc0151b3SMaxime Coquelin 	ret = dev->ops->set_features(dev, features);
159844e4683SMaxime Coquelin 	if (ret < 0)
160844e4683SMaxime Coquelin 		goto error;
161a3fb6b1dSMaxime Coquelin 	PMD_DRV_LOG(INFO, "(%s) set features: 0x%" PRIx64, dev->path, features);
162844e4683SMaxime Coquelin error:
163844e4683SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
164844e4683SMaxime Coquelin 
165844e4683SMaxime Coquelin 	return ret;
166844e4683SMaxime Coquelin }
167844e4683SMaxime Coquelin 
168844e4683SMaxime Coquelin int
169844e4683SMaxime Coquelin virtio_user_start_device(struct virtio_user_dev *dev)
170844e4683SMaxime Coquelin {
17137a7eb2aSJianfeng Tan 	int ret;
17237a7eb2aSJianfeng Tan 
1737ff26957STiwei Bie 	/*
1747ff26957STiwei Bie 	 * XXX workaround!
1757ff26957STiwei Bie 	 *
1767ff26957STiwei Bie 	 * We need to make sure that the locks will be
1777ff26957STiwei Bie 	 * taken in the correct order to avoid deadlocks.
1787ff26957STiwei Bie 	 *
1797ff26957STiwei Bie 	 * Before releasing this lock, this thread should
1807ff26957STiwei Bie 	 * not trigger any memory hotplug events.
1817ff26957STiwei Bie 	 *
1827ff26957STiwei Bie 	 * This is a temporary workaround, and should be
1837ff26957STiwei Bie 	 * replaced when we get proper supports from the
1847ff26957STiwei Bie 	 * memory subsystem in the future.
1857ff26957STiwei Bie 	 */
18676f80881SAnatoly Burakov 	rte_mcfg_mem_read_lock();
18712ecb2f6SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
18812ecb2f6SMaxime Coquelin 
18957ae79a7SJianfeng Tan 	/* Step 2: share memory regions */
190539d910cSMaxime Coquelin 	ret = dev->ops->set_memory_table(dev);
19157ae79a7SJianfeng Tan 	if (ret < 0)
19257ae79a7SJianfeng Tan 		goto error;
19357ae79a7SJianfeng Tan 
19457ae79a7SJianfeng Tan 	/* Step 3: kick queues */
195a3fb6b1dSMaxime Coquelin 	ret = virtio_user_queue_setup(dev, virtio_user_kick_queue);
196a3fb6b1dSMaxime Coquelin 	if (ret < 0)
19757ae79a7SJianfeng Tan 		goto error;
19857ae79a7SJianfeng Tan 
19957ae79a7SJianfeng Tan 	/* Step 4: enable queues
20057ae79a7SJianfeng Tan 	 * we enable the 1st queue pair by default.
20157ae79a7SJianfeng Tan 	 */
202a3fb6b1dSMaxime Coquelin 	ret = dev->ops->enable_qp(dev, 0, 1);
203a3fb6b1dSMaxime Coquelin 	if (ret < 0)
204a3fb6b1dSMaxime Coquelin 		goto error;
20557ae79a7SJianfeng Tan 
20612ecb2f6SMaxime Coquelin 	dev->started = true;
2079af79db2SMaxime Coquelin 
20812ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
20976f80881SAnatoly Burakov 	rte_mcfg_mem_read_unlock();
21012ecb2f6SMaxime Coquelin 
21137a7eb2aSJianfeng Tan 	return 0;
21237a7eb2aSJianfeng Tan error:
21312ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
21476f80881SAnatoly Burakov 	rte_mcfg_mem_read_unlock();
215a3fb6b1dSMaxime Coquelin 
216a3fb6b1dSMaxime Coquelin 	PMD_INIT_LOG(ERR, "(%s) Failed to start device\n", dev->path);
217a3fb6b1dSMaxime Coquelin 
21837a7eb2aSJianfeng Tan 	/* TODO: free resource here or caller to check */
21937a7eb2aSJianfeng Tan 	return -1;
22037a7eb2aSJianfeng Tan }
22137a7eb2aSJianfeng Tan 
22237a7eb2aSJianfeng Tan int virtio_user_stop_device(struct virtio_user_dev *dev)
22337a7eb2aSJianfeng Tan {
22474dc6746STiwei Bie 	struct vhost_vring_state state;
225c12a26eeSJianfeng Tan 	uint32_t i;
226a3fb6b1dSMaxime Coquelin 	int ret;
227c12a26eeSJianfeng Tan 
22812ecb2f6SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
229f457e900STiwei Bie 	if (!dev->started)
230f457e900STiwei Bie 		goto out;
231f457e900STiwei Bie 
232a3fb6b1dSMaxime Coquelin 	for (i = 0; i < dev->max_queue_pairs; ++i) {
233a3fb6b1dSMaxime Coquelin 		ret = dev->ops->enable_qp(dev, i, 0);
234a3fb6b1dSMaxime Coquelin 		if (ret < 0)
235a3fb6b1dSMaxime Coquelin 			goto err;
236a3fb6b1dSMaxime Coquelin 	}
237c12a26eeSJianfeng Tan 
23874dc6746STiwei Bie 	/* Stop the backend. */
23974dc6746STiwei Bie 	for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
24074dc6746STiwei Bie 		state.index = i;
241a3fb6b1dSMaxime Coquelin 		ret = dev->ops->get_vring_base(dev, &state);
242a3fb6b1dSMaxime Coquelin 		if (ret < 0) {
243a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) get_vring_base failed, index=%u", dev->path, i);
244a3fb6b1dSMaxime Coquelin 			goto err;
2450d6a8752SJianfeng Tan 		}
24674dc6746STiwei Bie 	}
24774dc6746STiwei Bie 
24812ecb2f6SMaxime Coquelin 	dev->started = false;
249a3fb6b1dSMaxime Coquelin 
250f457e900STiwei Bie out:
25112ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
2520d6a8752SJianfeng Tan 
253a3fb6b1dSMaxime Coquelin 	return 0;
254a3fb6b1dSMaxime Coquelin err:
255a3fb6b1dSMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
256a3fb6b1dSMaxime Coquelin 
257a3fb6b1dSMaxime Coquelin 	PMD_INIT_LOG(ERR, "(%s) Failed to stop device\n", dev->path);
258a3fb6b1dSMaxime Coquelin 
259a3fb6b1dSMaxime Coquelin 	return -1;
26037a7eb2aSJianfeng Tan }
26137a7eb2aSJianfeng Tan 
26237a7eb2aSJianfeng Tan static inline void
26337a7eb2aSJianfeng Tan parse_mac(struct virtio_user_dev *dev, const char *mac)
26437a7eb2aSJianfeng Tan {
2658eb49a18SStephen Hemminger 	struct rte_ether_addr tmp;
26637a7eb2aSJianfeng Tan 
26737a7eb2aSJianfeng Tan 	if (!mac)
26837a7eb2aSJianfeng Tan 		return;
26937a7eb2aSJianfeng Tan 
2708eb49a18SStephen Hemminger 	if (rte_ether_unformat_addr(mac, &tmp) == 0) {
2718eb49a18SStephen Hemminger 		memcpy(dev->mac_addr, &tmp, RTE_ETHER_ADDR_LEN);
27237a7eb2aSJianfeng Tan 		dev->mac_specified = 1;
27337a7eb2aSJianfeng Tan 	} else {
27437a7eb2aSJianfeng Tan 		/* ignore the wrong mac, use random mac */
27537a7eb2aSJianfeng Tan 		PMD_DRV_LOG(ERR, "wrong format of mac: %s", mac);
27637a7eb2aSJianfeng Tan 	}
27737a7eb2aSJianfeng Tan }
27837a7eb2aSJianfeng Tan 
27933d24d65SJianfeng Tan static int
280e6e7ad8bSJianfeng Tan virtio_user_dev_init_notify(struct virtio_user_dev *dev)
28133d24d65SJianfeng Tan {
282e6e7ad8bSJianfeng Tan 	uint32_t i, j;
283e6e7ad8bSJianfeng Tan 	int callfd;
284e6e7ad8bSJianfeng Tan 	int kickfd;
28533d24d65SJianfeng Tan 
286*2e4c1b50SMaxime Coquelin 	for (i = 0; i < dev->max_queue_pairs * 2; i++) {
287e6e7ad8bSJianfeng Tan 		/* May use invalid flag, but some backend uses kickfd and
288e6e7ad8bSJianfeng Tan 		 * callfd as criteria to judge if dev is alive. so finally we
289e6e7ad8bSJianfeng Tan 		 * use real event_fd.
290e6e7ad8bSJianfeng Tan 		 */
291e6e7ad8bSJianfeng Tan 		callfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
292e6e7ad8bSJianfeng Tan 		if (callfd < 0) {
293a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) callfd error, %s", dev->path, strerror(errno));
294*2e4c1b50SMaxime Coquelin 			goto err;
295e6e7ad8bSJianfeng Tan 		}
296e6e7ad8bSJianfeng Tan 		kickfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
297e6e7ad8bSJianfeng Tan 		if (kickfd < 0) {
29897ed740cSJiawei Zhu 			close(callfd);
299a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) kickfd error, %s", dev->path, strerror(errno));
300*2e4c1b50SMaxime Coquelin 			goto err;
301e6e7ad8bSJianfeng Tan 		}
302e6e7ad8bSJianfeng Tan 		dev->callfds[i] = callfd;
303e6e7ad8bSJianfeng Tan 		dev->kickfds[i] = kickfd;
304e6e7ad8bSJianfeng Tan 	}
305e6e7ad8bSJianfeng Tan 
306*2e4c1b50SMaxime Coquelin 	return 0;
307*2e4c1b50SMaxime Coquelin err:
308*2e4c1b50SMaxime Coquelin 	for (j = 0; j < i; j++) {
309*2e4c1b50SMaxime Coquelin 		if (dev->kickfds[j] >= 0) {
310e6e7ad8bSJianfeng Tan 			close(dev->kickfds[j]);
311*2e4c1b50SMaxime Coquelin 			dev->kickfds[j] = -1;
312*2e4c1b50SMaxime Coquelin 		}
313*2e4c1b50SMaxime Coquelin 		if (dev->callfds[j] >= 0) {
314*2e4c1b50SMaxime Coquelin 			close(dev->callfds[j]);
315*2e4c1b50SMaxime Coquelin 			dev->callfds[j] = -1;
316*2e4c1b50SMaxime Coquelin 		}
317e6e7ad8bSJianfeng Tan 	}
318e6e7ad8bSJianfeng Tan 
319e6e7ad8bSJianfeng Tan 	return -1;
320e6e7ad8bSJianfeng Tan }
321e6e7ad8bSJianfeng Tan 
322*2e4c1b50SMaxime Coquelin static void
323*2e4c1b50SMaxime Coquelin virtio_user_dev_uninit_notify(struct virtio_user_dev *dev)
324*2e4c1b50SMaxime Coquelin {
325*2e4c1b50SMaxime Coquelin 	uint32_t i;
326*2e4c1b50SMaxime Coquelin 
327*2e4c1b50SMaxime Coquelin 	for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
328*2e4c1b50SMaxime Coquelin 		if (dev->kickfds[i] >= 0) {
329*2e4c1b50SMaxime Coquelin 			close(dev->kickfds[i]);
330*2e4c1b50SMaxime Coquelin 			dev->kickfds[i] = -1;
331*2e4c1b50SMaxime Coquelin 		}
332*2e4c1b50SMaxime Coquelin 		if (dev->callfds[i] >= 0) {
333*2e4c1b50SMaxime Coquelin 			close(dev->callfds[i]);
334*2e4c1b50SMaxime Coquelin 			dev->callfds[i] = -1;
335*2e4c1b50SMaxime Coquelin 		}
336*2e4c1b50SMaxime Coquelin 	}
337e6e7ad8bSJianfeng Tan }
338e6e7ad8bSJianfeng Tan 
339e6e7ad8bSJianfeng Tan static int
3403d4fb6fdSJianfeng Tan virtio_user_fill_intr_handle(struct virtio_user_dev *dev)
3413d4fb6fdSJianfeng Tan {
3423d4fb6fdSJianfeng Tan 	uint32_t i;
3433d4fb6fdSJianfeng Tan 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id];
3443d4fb6fdSJianfeng Tan 
3453d4fb6fdSJianfeng Tan 	if (!eth_dev->intr_handle) {
3463d4fb6fdSJianfeng Tan 		eth_dev->intr_handle = malloc(sizeof(*eth_dev->intr_handle));
3473d4fb6fdSJianfeng Tan 		if (!eth_dev->intr_handle) {
348a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) failed to allocate intr_handle", dev->path);
3493d4fb6fdSJianfeng Tan 			return -1;
3503d4fb6fdSJianfeng Tan 		}
3513d4fb6fdSJianfeng Tan 		memset(eth_dev->intr_handle, 0, sizeof(*eth_dev->intr_handle));
3523d4fb6fdSJianfeng Tan 	}
3533d4fb6fdSJianfeng Tan 
3543d4fb6fdSJianfeng Tan 	for (i = 0; i < dev->max_queue_pairs; ++i)
3553d4fb6fdSJianfeng Tan 		eth_dev->intr_handle->efds[i] = dev->callfds[i];
3563d4fb6fdSJianfeng Tan 	eth_dev->intr_handle->nb_efd = dev->max_queue_pairs;
3573d4fb6fdSJianfeng Tan 	eth_dev->intr_handle->max_intr = dev->max_queue_pairs + 1;
3583d4fb6fdSJianfeng Tan 	eth_dev->intr_handle->type = RTE_INTR_HANDLE_VDEV;
35929906b97SJingjing Wu 	/* For virtio vdev, no need to read counter for clean */
36029906b97SJingjing Wu 	eth_dev->intr_handle->efd_counter_size = 0;
36194973531SMaxime Coquelin 	eth_dev->intr_handle->fd = dev->ops->get_intr_fd(dev);
3623d4fb6fdSJianfeng Tan 
3633d4fb6fdSJianfeng Tan 	return 0;
3643d4fb6fdSJianfeng Tan }
3653d4fb6fdSJianfeng Tan 
36612ecb2f6SMaxime Coquelin static void
36712ecb2f6SMaxime Coquelin virtio_user_mem_event_cb(enum rte_mem_event type __rte_unused,
3682286291dSTiwei Bie 			 const void *addr,
36912ecb2f6SMaxime Coquelin 			 size_t len __rte_unused,
37012ecb2f6SMaxime Coquelin 			 void *arg)
37112ecb2f6SMaxime Coquelin {
37212ecb2f6SMaxime Coquelin 	struct virtio_user_dev *dev = arg;
373f32c7c9dSAnatoly Burakov 	struct rte_memseg_list *msl;
37412ecb2f6SMaxime Coquelin 	uint16_t i;
375a3fb6b1dSMaxime Coquelin 	int ret = 0;
37612ecb2f6SMaxime Coquelin 
377f32c7c9dSAnatoly Burakov 	/* ignore externally allocated memory */
378f32c7c9dSAnatoly Burakov 	msl = rte_mem_virt2memseg_list(addr);
379f32c7c9dSAnatoly Burakov 	if (msl->external)
380f32c7c9dSAnatoly Burakov 		return;
381f32c7c9dSAnatoly Burakov 
38212ecb2f6SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
38312ecb2f6SMaxime Coquelin 
38412ecb2f6SMaxime Coquelin 	if (dev->started == false)
38512ecb2f6SMaxime Coquelin 		goto exit;
38612ecb2f6SMaxime Coquelin 
38712ecb2f6SMaxime Coquelin 	/* Step 1: pause the active queues */
388a3fb6b1dSMaxime Coquelin 	for (i = 0; i < dev->queue_pairs; i++) {
389a3fb6b1dSMaxime Coquelin 		ret = dev->ops->enable_qp(dev, i, 0);
390a3fb6b1dSMaxime Coquelin 		if (ret < 0)
391a3fb6b1dSMaxime Coquelin 			goto exit;
392a3fb6b1dSMaxime Coquelin 	}
39312ecb2f6SMaxime Coquelin 
39412ecb2f6SMaxime Coquelin 	/* Step 2: update memory regions */
395a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_memory_table(dev);
396a3fb6b1dSMaxime Coquelin 	if (ret < 0)
397a3fb6b1dSMaxime Coquelin 		goto exit;
39812ecb2f6SMaxime Coquelin 
39912ecb2f6SMaxime Coquelin 	/* Step 3: resume the active queues */
400a3fb6b1dSMaxime Coquelin 	for (i = 0; i < dev->queue_pairs; i++) {
401a3fb6b1dSMaxime Coquelin 		ret = dev->ops->enable_qp(dev, i, 1);
402a3fb6b1dSMaxime Coquelin 		if (ret < 0)
403a3fb6b1dSMaxime Coquelin 			goto exit;
404a3fb6b1dSMaxime Coquelin 	}
40512ecb2f6SMaxime Coquelin 
40612ecb2f6SMaxime Coquelin exit:
40712ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
408a3fb6b1dSMaxime Coquelin 
409a3fb6b1dSMaxime Coquelin 	if (ret < 0)
410a3fb6b1dSMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Failed to update memory table\n", dev->path);
41112ecb2f6SMaxime Coquelin }
41212ecb2f6SMaxime Coquelin 
4133d4fb6fdSJianfeng Tan static int
414e6e7ad8bSJianfeng Tan virtio_user_dev_setup(struct virtio_user_dev *dev)
415e6e7ad8bSJianfeng Tan {
416bd8f50a4SZhiyong Yang 	if (dev->is_server) {
417f908b22eSAdrian Moreno 		if (dev->backend_type != VIRTIO_USER_BACKEND_VHOST_USER) {
418f908b22eSAdrian Moreno 			PMD_DRV_LOG(ERR, "Server mode only supports vhost-user!");
419bd8f50a4SZhiyong Yang 			return -1;
420bd8f50a4SZhiyong Yang 		}
4219af79db2SMaxime Coquelin 	}
4229af79db2SMaxime Coquelin 
42386388a3aSMaxime Coquelin 	switch (dev->backend_type) {
42486388a3aSMaxime Coquelin 	case VIRTIO_USER_BACKEND_VHOST_USER:
425520dd992SFerruh Yigit 		dev->ops = &virtio_ops_user;
42686388a3aSMaxime Coquelin 		break;
42786388a3aSMaxime Coquelin 	case VIRTIO_USER_BACKEND_VHOST_KERNEL:
428520dd992SFerruh Yigit 		dev->ops = &virtio_ops_kernel;
42986388a3aSMaxime Coquelin 		break;
43086388a3aSMaxime Coquelin 	case VIRTIO_USER_BACKEND_VHOST_VDPA:
4316b901437SMaxime Coquelin 		dev->ops = &virtio_ops_vdpa;
43286388a3aSMaxime Coquelin 		break;
43386388a3aSMaxime Coquelin 	default:
434a3fb6b1dSMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Unknown backend type", dev->path);
4356b901437SMaxime Coquelin 		return -1;
436e3b43481SJianfeng Tan 	}
4379af79db2SMaxime Coquelin 
438a3fb6b1dSMaxime Coquelin 	if (dev->ops->setup(dev) < 0) {
439a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to setup backend\n", dev->path);
440cc4690e9SJianfeng Tan 		return -1;
441a3fb6b1dSMaxime Coquelin 	}
442cc4690e9SJianfeng Tan 
443a3fb6b1dSMaxime Coquelin 	if (virtio_user_dev_init_notify(dev) < 0) {
444a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to init notifiers\n", dev->path);
445*2e4c1b50SMaxime Coquelin 		goto destroy;
446a3fb6b1dSMaxime Coquelin 	}
447cc4690e9SJianfeng Tan 
448a3fb6b1dSMaxime Coquelin 	if (virtio_user_fill_intr_handle(dev) < 0) {
449a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to init interrupt handler\n", dev->path);
450*2e4c1b50SMaxime Coquelin 		goto uninit;
451a3fb6b1dSMaxime Coquelin 	}
452cc4690e9SJianfeng Tan 
453cc4690e9SJianfeng Tan 	return 0;
454*2e4c1b50SMaxime Coquelin 
455*2e4c1b50SMaxime Coquelin uninit:
456*2e4c1b50SMaxime Coquelin 	virtio_user_dev_uninit_notify(dev);
457*2e4c1b50SMaxime Coquelin destroy:
458*2e4c1b50SMaxime Coquelin 	dev->ops->destroy(dev);
459*2e4c1b50SMaxime Coquelin 
460*2e4c1b50SMaxime Coquelin 	return -1;
46133d24d65SJianfeng Tan }
46233d24d65SJianfeng Tan 
463bed3b24cSJianfeng Tan /* Use below macro to filter features from vhost backend */
464bed3b24cSJianfeng Tan #define VIRTIO_USER_SUPPORTED_FEATURES			\
465bed3b24cSJianfeng Tan 	(1ULL << VIRTIO_NET_F_MAC		|	\
466bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_STATUS		|	\
467bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_MQ		|	\
468bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_MAC_ADDR	|	\
469bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_VQ		|	\
470bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_RX		|	\
471bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_VLAN		|	\
472bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CSUM		|	\
473bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_HOST_TSO4		|	\
474bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_HOST_TSO6		|	\
475bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_MRG_RXBUF		|	\
476bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_RING_F_INDIRECT_DESC	|	\
477bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_GUEST_CSUM	|	\
478bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_GUEST_TSO4	|	\
479bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_GUEST_TSO6	|	\
48041e45c90SMarvin Liu 	 1ULL << VIRTIO_F_IN_ORDER		|	\
48134f3966cSYuanhan Liu 	 1ULL << VIRTIO_F_VERSION_1		|	\
4825b75b63cSMaxime Coquelin 	 1ULL << VIRTIO_F_RING_PACKED)
4838e756105SMaxime Coquelin 
48437a7eb2aSJianfeng Tan int
48537a7eb2aSJianfeng Tan virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
486488ed97aSMarvin Liu 		     int cq, int queue_size, const char *mac, char **ifname,
487f908b22eSAdrian Moreno 		     int server, int mrg_rxbuf, int in_order, int packed_vq,
488f908b22eSAdrian Moreno 		     enum virtio_user_backend_type backend_type)
48937a7eb2aSJianfeng Tan {
4905b75b63cSMaxime Coquelin 	uint64_t backend_features;
491*2e4c1b50SMaxime Coquelin 	int i;
4928e756105SMaxime Coquelin 
49312ecb2f6SMaxime Coquelin 	pthread_mutex_init(&dev->mutex, NULL);
4946723c0fcSBruce Richardson 	strlcpy(dev->path, path, PATH_MAX);
495*2e4c1b50SMaxime Coquelin 
496*2e4c1b50SMaxime Coquelin 	for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) {
497*2e4c1b50SMaxime Coquelin 		dev->kickfds[i] = -1;
498*2e4c1b50SMaxime Coquelin 		dev->callfds[i] = -1;
499*2e4c1b50SMaxime Coquelin 	}
500*2e4c1b50SMaxime Coquelin 
50112ecb2f6SMaxime Coquelin 	dev->started = 0;
50237a7eb2aSJianfeng Tan 	dev->max_queue_pairs = queues;
50337a7eb2aSJianfeng Tan 	dev->queue_pairs = 1; /* mq disabled by default */
50437a7eb2aSJianfeng Tan 	dev->queue_size = queue_size;
5051c8489daSTiwei Bie 	dev->is_server = server;
50637a7eb2aSJianfeng Tan 	dev->mac_specified = 0;
507bb97d2ddSTiwei Bie 	dev->frontend_features = 0;
5085b75b63cSMaxime Coquelin 	dev->unsupported_features = 0;
509f908b22eSAdrian Moreno 	dev->backend_type = backend_type;
510f908b22eSAdrian Moreno 
51137a7eb2aSJianfeng Tan 	parse_mac(dev, mac);
51237a7eb2aSJianfeng Tan 
5134214a1b4SWenfeng Liu 	if (*ifname) {
5144214a1b4SWenfeng Liu 		dev->ifname = *ifname;
5154214a1b4SWenfeng Liu 		*ifname = NULL;
5164214a1b4SWenfeng Liu 	}
5174214a1b4SWenfeng Liu 
51833d24d65SJianfeng Tan 	if (virtio_user_dev_setup(dev) < 0) {
519a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) backend set up fails", dev->path);
52037a7eb2aSJianfeng Tan 		return -1;
52137a7eb2aSJianfeng Tan 	}
522bce7e905SJianfeng Tan 
52306856cabSMaxime Coquelin 	if (dev->ops->set_owner(dev) < 0) {
524a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to set backend owner", dev->path);
52537a7eb2aSJianfeng Tan 		return -1;
52637a7eb2aSJianfeng Tan 	}
52737a7eb2aSJianfeng Tan 
5285b75b63cSMaxime Coquelin 	if (dev->ops->get_backend_features(&backend_features) < 0) {
529a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to get backend features", dev->path);
53037a7eb2aSJianfeng Tan 		return -1;
53137a7eb2aSJianfeng Tan 	}
5328e756105SMaxime Coquelin 
5335b75b63cSMaxime Coquelin 	dev->unsupported_features = ~(VIRTIO_USER_SUPPORTED_FEATURES | backend_features);
5345b75b63cSMaxime Coquelin 
5355b75b63cSMaxime Coquelin 	if (dev->ops->get_features(dev, &dev->device_features) < 0) {
5365b75b63cSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to get device features", dev->path);
5378e756105SMaxime Coquelin 		return -1;
538a3fb6b1dSMaxime Coquelin 	}
5398e756105SMaxime Coquelin 
540bd9568f3STiwei Bie 	if (!mrg_rxbuf)
541488ed97aSMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MRG_RXBUF);
542488ed97aSMarvin Liu 
543bd9568f3STiwei Bie 	if (!in_order)
544488ed97aSMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_F_IN_ORDER);
545488ed97aSMarvin Liu 
54648a44640SJens Freimann 	if (!packed_vq)
5479070f88bSTiwei Bie 		dev->unsupported_features |= (1ull << VIRTIO_F_RING_PACKED);
54834f3966cSYuanhan Liu 
5499070f88bSTiwei Bie 	if (dev->mac_specified)
5509070f88bSTiwei Bie 		dev->frontend_features |= (1ull << VIRTIO_NET_F_MAC);
5519070f88bSTiwei Bie 	else
5527c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MAC);
553f9b9d1a5SJianfeng Tan 
554142678d4SJianfeng Tan 	if (cq) {
555142678d4SJianfeng Tan 		/* device does not really need to know anything about CQ,
556142678d4SJianfeng Tan 		 * so if necessary, we just claim to support CQ
557f9b9d1a5SJianfeng Tan 		 */
558bb97d2ddSTiwei Bie 		dev->frontend_features |= (1ull << VIRTIO_NET_F_CTRL_VQ);
559142678d4SJianfeng Tan 	} else {
5607c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_VQ);
561bd9568f3STiwei Bie 		/* Also disable features that depend on VIRTIO_NET_F_CTRL_VQ */
5627c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_RX);
5637c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_VLAN);
5647c66ff61SMarvin Liu 		dev->unsupported_features |=
5657c66ff61SMarvin Liu 			(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE);
5667c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MQ);
5677c66ff61SMarvin Liu 		dev->unsupported_features |=
5687c66ff61SMarvin Liu 			(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
569f9b9d1a5SJianfeng Tan 	}
570f9b9d1a5SJianfeng Tan 
57135c4f855SJianfeng Tan 	/* The backend will not report this feature, we add it explicitly */
572f908b22eSAdrian Moreno 	if (dev->backend_type == VIRTIO_USER_BACKEND_VHOST_USER)
573bb97d2ddSTiwei Bie 		dev->frontend_features |= (1ull << VIRTIO_NET_F_STATUS);
57435c4f855SJianfeng Tan 
575bd9568f3STiwei Bie 	/*
576bd9568f3STiwei Bie 	 * Device features =
577bd9568f3STiwei Bie 	 *     (frontend_features | backend_features) & ~unsupported_features;
578bd9568f3STiwei Bie 	 */
579bb97d2ddSTiwei Bie 	dev->device_features |= dev->frontend_features;
580bd9568f3STiwei Bie 	dev->device_features &= ~dev->unsupported_features;
581bed3b24cSJianfeng Tan 
58212ecb2f6SMaxime Coquelin 	if (rte_mem_event_callback_register(VIRTIO_USER_MEM_EVENT_CLB_NAME,
58312ecb2f6SMaxime Coquelin 				virtio_user_mem_event_cb, dev)) {
58488e5469fSXiao Wang 		if (rte_errno != ENOTSUP) {
585a3fb6b1dSMaxime Coquelin 			PMD_INIT_LOG(ERR, "(%s) Failed to register mem event callback\n",
586a3fb6b1dSMaxime Coquelin 					dev->path);
58712ecb2f6SMaxime Coquelin 			return -1;
58812ecb2f6SMaxime Coquelin 		}
58988e5469fSXiao Wang 	}
59012ecb2f6SMaxime Coquelin 
59137a7eb2aSJianfeng Tan 	return 0;
59237a7eb2aSJianfeng Tan }
59337a7eb2aSJianfeng Tan 
59437a7eb2aSJianfeng Tan void
59537a7eb2aSJianfeng Tan virtio_user_dev_uninit(struct virtio_user_dev *dev)
59637a7eb2aSJianfeng Tan {
597e3b43481SJianfeng Tan 	virtio_user_stop_device(dev);
598e3b43481SJianfeng Tan 
59912ecb2f6SMaxime Coquelin 	rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev);
60012ecb2f6SMaxime Coquelin 
601*2e4c1b50SMaxime Coquelin 	virtio_user_dev_uninit_notify(dev);
6024214a1b4SWenfeng Liu 
6034214a1b4SWenfeng Liu 	free(dev->ifname);
604bd8f50a4SZhiyong Yang 
605bd8f50a4SZhiyong Yang 	if (dev->is_server)
606bd8f50a4SZhiyong Yang 		unlink(dev->path);
607748e5ea5SMaxime Coquelin 
608748e5ea5SMaxime Coquelin 	dev->ops->destroy(dev);
60937a7eb2aSJianfeng Tan }
610f9b9d1a5SJianfeng Tan 
611201a4165SZhiyong Yang uint8_t
612f9b9d1a5SJianfeng Tan virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs)
613f9b9d1a5SJianfeng Tan {
614f9b9d1a5SJianfeng Tan 	uint16_t i;
615f9b9d1a5SJianfeng Tan 	uint8_t ret = 0;
616f9b9d1a5SJianfeng Tan 
617f9b9d1a5SJianfeng Tan 	if (q_pairs > dev->max_queue_pairs) {
618a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) multi-q config %u, but only %u supported",
619a3fb6b1dSMaxime Coquelin 			     dev->path, q_pairs, dev->max_queue_pairs);
620f9b9d1a5SJianfeng Tan 		return -1;
621f9b9d1a5SJianfeng Tan 	}
622f9b9d1a5SJianfeng Tan 
623f9b9d1a5SJianfeng Tan 	for (i = 0; i < q_pairs; ++i)
62433d24d65SJianfeng Tan 		ret |= dev->ops->enable_qp(dev, i, 1);
625f9b9d1a5SJianfeng Tan 	for (i = q_pairs; i < dev->max_queue_pairs; ++i)
62633d24d65SJianfeng Tan 		ret |= dev->ops->enable_qp(dev, i, 0);
62794973531SMaxime Coquelin 
628f9b9d1a5SJianfeng Tan 	dev->queue_pairs = q_pairs;
629f9b9d1a5SJianfeng Tan 
630f9b9d1a5SJianfeng Tan 	return ret;
631f9b9d1a5SJianfeng Tan }
632f9b9d1a5SJianfeng Tan 
633f9b9d1a5SJianfeng Tan static uint32_t
634f9b9d1a5SJianfeng Tan virtio_user_handle_ctrl_msg(struct virtio_user_dev *dev, struct vring *vring,
635f9b9d1a5SJianfeng Tan 			    uint16_t idx_hdr)
636f9b9d1a5SJianfeng Tan {
637f9b9d1a5SJianfeng Tan 	struct virtio_net_ctrl_hdr *hdr;
638f9b9d1a5SJianfeng Tan 	virtio_net_ctrl_ack status = ~0;
639f9b9d1a5SJianfeng Tan 	uint16_t i, idx_data, idx_status;
640f9b9d1a5SJianfeng Tan 	uint32_t n_descs = 0;
641f9b9d1a5SJianfeng Tan 
642f9b9d1a5SJianfeng Tan 	/* locate desc for header, data, and status */
643f9b9d1a5SJianfeng Tan 	idx_data = vring->desc[idx_hdr].next;
644f9b9d1a5SJianfeng Tan 	n_descs++;
645f9b9d1a5SJianfeng Tan 
646f9b9d1a5SJianfeng Tan 	i = idx_data;
647f9b9d1a5SJianfeng Tan 	while (vring->desc[i].flags == VRING_DESC_F_NEXT) {
648f9b9d1a5SJianfeng Tan 		i = vring->desc[i].next;
649f9b9d1a5SJianfeng Tan 		n_descs++;
650f9b9d1a5SJianfeng Tan 	}
651f9b9d1a5SJianfeng Tan 
652f9b9d1a5SJianfeng Tan 	/* locate desc for status */
653f9b9d1a5SJianfeng Tan 	idx_status = i;
654f9b9d1a5SJianfeng Tan 	n_descs++;
655f9b9d1a5SJianfeng Tan 
656f9b9d1a5SJianfeng Tan 	hdr = (void *)(uintptr_t)vring->desc[idx_hdr].addr;
657f9b9d1a5SJianfeng Tan 	if (hdr->class == VIRTIO_NET_CTRL_MQ &&
658f9b9d1a5SJianfeng Tan 	    hdr->cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
659f9b9d1a5SJianfeng Tan 		uint16_t queues;
660f9b9d1a5SJianfeng Tan 
661f9b9d1a5SJianfeng Tan 		queues = *(uint16_t *)(uintptr_t)vring->desc[idx_data].addr;
662f9b9d1a5SJianfeng Tan 		status = virtio_user_handle_mq(dev, queues);
663a76552d4SMarvin Liu 	} else if (hdr->class == VIRTIO_NET_CTRL_RX  ||
664a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_MAC ||
665a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_VLAN) {
666a76552d4SMarvin Liu 		status = 0;
667f9b9d1a5SJianfeng Tan 	}
668f9b9d1a5SJianfeng Tan 
669f9b9d1a5SJianfeng Tan 	/* Update status */
670f9b9d1a5SJianfeng Tan 	*(virtio_net_ctrl_ack *)(uintptr_t)vring->desc[idx_status].addr = status;
671f9b9d1a5SJianfeng Tan 
672f9b9d1a5SJianfeng Tan 	return n_descs;
673f9b9d1a5SJianfeng Tan }
674f9b9d1a5SJianfeng Tan 
67548a44640SJens Freimann static inline int
67648a44640SJens Freimann desc_is_avail(struct vring_packed_desc *desc, bool wrap_counter)
67748a44640SJens Freimann {
6786094557dSJoyce Kong 	uint16_t flags = __atomic_load_n(&desc->flags, __ATOMIC_ACQUIRE);
67912e9e70cSTiwei Bie 
68012e9e70cSTiwei Bie 	return wrap_counter == !!(flags & VRING_PACKED_DESC_F_AVAIL) &&
68112e9e70cSTiwei Bie 		wrap_counter != !!(flags & VRING_PACKED_DESC_F_USED);
68248a44640SJens Freimann }
68348a44640SJens Freimann 
68448a44640SJens Freimann static uint32_t
68545c224e7STiwei Bie virtio_user_handle_ctrl_msg_packed(struct virtio_user_dev *dev,
68648a44640SJens Freimann 				   struct vring_packed *vring,
68748a44640SJens Freimann 				   uint16_t idx_hdr)
68848a44640SJens Freimann {
68948a44640SJens Freimann 	struct virtio_net_ctrl_hdr *hdr;
69048a44640SJens Freimann 	virtio_net_ctrl_ack status = ~0;
69148a44640SJens Freimann 	uint16_t idx_data, idx_status;
69248a44640SJens Freimann 	/* initialize to one, header is first */
69348a44640SJens Freimann 	uint32_t n_descs = 1;
69448a44640SJens Freimann 
69548a44640SJens Freimann 	/* locate desc for header, data, and status */
69648a44640SJens Freimann 	idx_data = idx_hdr + 1;
69748a44640SJens Freimann 	if (idx_data >= dev->queue_size)
69848a44640SJens Freimann 		idx_data -= dev->queue_size;
69948a44640SJens Freimann 
70048a44640SJens Freimann 	n_descs++;
70148a44640SJens Freimann 
70248a44640SJens Freimann 	idx_status = idx_data;
7034cdc4d98STiwei Bie 	while (vring->desc[idx_status].flags & VRING_DESC_F_NEXT) {
70448a44640SJens Freimann 		idx_status++;
70548a44640SJens Freimann 		if (idx_status >= dev->queue_size)
70648a44640SJens Freimann 			idx_status -= dev->queue_size;
70748a44640SJens Freimann 		n_descs++;
70848a44640SJens Freimann 	}
70948a44640SJens Freimann 
7104cdc4d98STiwei Bie 	hdr = (void *)(uintptr_t)vring->desc[idx_hdr].addr;
71148a44640SJens Freimann 	if (hdr->class == VIRTIO_NET_CTRL_MQ &&
71248a44640SJens Freimann 	    hdr->cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
71348a44640SJens Freimann 		uint16_t queues;
71448a44640SJens Freimann 
71548a44640SJens Freimann 		queues = *(uint16_t *)(uintptr_t)
7164cdc4d98STiwei Bie 				vring->desc[idx_data].addr;
71748a44640SJens Freimann 		status = virtio_user_handle_mq(dev, queues);
718a76552d4SMarvin Liu 	} else if (hdr->class == VIRTIO_NET_CTRL_RX  ||
719a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_MAC ||
720a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_VLAN) {
721a76552d4SMarvin Liu 		status = 0;
72248a44640SJens Freimann 	}
72348a44640SJens Freimann 
72448a44640SJens Freimann 	/* Update status */
72548a44640SJens Freimann 	*(virtio_net_ctrl_ack *)(uintptr_t)
7264cdc4d98STiwei Bie 		vring->desc[idx_status].addr = status;
72748a44640SJens Freimann 
72845c224e7STiwei Bie 	/* Update used descriptor */
7294cdc4d98STiwei Bie 	vring->desc[idx_hdr].id = vring->desc[idx_status].id;
7304cdc4d98STiwei Bie 	vring->desc[idx_hdr].len = sizeof(status);
73145c224e7STiwei Bie 
73248a44640SJens Freimann 	return n_descs;
73348a44640SJens Freimann }
73448a44640SJens Freimann 
73548a44640SJens Freimann void
73648a44640SJens Freimann virtio_user_handle_cq_packed(struct virtio_user_dev *dev, uint16_t queue_idx)
73748a44640SJens Freimann {
73848a44640SJens Freimann 	struct virtio_user_queue *vq = &dev->packed_queues[queue_idx];
73948a44640SJens Freimann 	struct vring_packed *vring = &dev->packed_vrings[queue_idx];
74012e9e70cSTiwei Bie 	uint16_t n_descs, flags;
74148a44640SJens Freimann 
7426094557dSJoyce Kong 	/* Perform a load-acquire barrier in desc_is_avail to
7436094557dSJoyce Kong 	 * enforce the ordering between desc flags and desc
7446094557dSJoyce Kong 	 * content.
7456094557dSJoyce Kong 	 */
7464cdc4d98STiwei Bie 	while (desc_is_avail(&vring->desc[vq->used_idx],
74748a44640SJens Freimann 			     vq->used_wrap_counter)) {
74848a44640SJens Freimann 
74945c224e7STiwei Bie 		n_descs = virtio_user_handle_ctrl_msg_packed(dev, vring,
75045c224e7STiwei Bie 				vq->used_idx);
75148a44640SJens Freimann 
75212e9e70cSTiwei Bie 		flags = VRING_DESC_F_WRITE;
75312e9e70cSTiwei Bie 		if (vq->used_wrap_counter)
75412e9e70cSTiwei Bie 			flags |= VRING_PACKED_DESC_F_AVAIL_USED;
75512e9e70cSTiwei Bie 
7562c661d41SJoyce Kong 		__atomic_store_n(&vring->desc[vq->used_idx].flags, flags,
7572c661d41SJoyce Kong 				 __ATOMIC_RELEASE);
75845c224e7STiwei Bie 
75945c224e7STiwei Bie 		vq->used_idx += n_descs;
76045c224e7STiwei Bie 		if (vq->used_idx >= dev->queue_size) {
76148a44640SJens Freimann 			vq->used_idx -= dev->queue_size;
76248a44640SJens Freimann 			vq->used_wrap_counter ^= 1;
76348a44640SJens Freimann 		}
76448a44640SJens Freimann 	}
76548a44640SJens Freimann }
76648a44640SJens Freimann 
767f9b9d1a5SJianfeng Tan void
768f9b9d1a5SJianfeng Tan virtio_user_handle_cq(struct virtio_user_dev *dev, uint16_t queue_idx)
769f9b9d1a5SJianfeng Tan {
770f9b9d1a5SJianfeng Tan 	uint16_t avail_idx, desc_idx;
771f9b9d1a5SJianfeng Tan 	struct vring_used_elem *uep;
772f9b9d1a5SJianfeng Tan 	uint32_t n_descs;
773f9b9d1a5SJianfeng Tan 	struct vring *vring = &dev->vrings[queue_idx];
774f9b9d1a5SJianfeng Tan 
775f9b9d1a5SJianfeng Tan 	/* Consume avail ring, using used ring idx as first one */
776ea5207c1SJoyce Kong 	while (__atomic_load_n(&vring->used->idx, __ATOMIC_RELAXED)
777ea5207c1SJoyce Kong 	       != vring->avail->idx) {
778ea5207c1SJoyce Kong 		avail_idx = __atomic_load_n(&vring->used->idx, __ATOMIC_RELAXED)
779ea5207c1SJoyce Kong 			    & (vring->num - 1);
780f9b9d1a5SJianfeng Tan 		desc_idx = vring->avail->ring[avail_idx];
781f9b9d1a5SJianfeng Tan 
782f9b9d1a5SJianfeng Tan 		n_descs = virtio_user_handle_ctrl_msg(dev, vring, desc_idx);
783f9b9d1a5SJianfeng Tan 
784f9b9d1a5SJianfeng Tan 		/* Update used ring */
785f9b9d1a5SJianfeng Tan 		uep = &vring->used->ring[avail_idx];
7860403e37aSTiwei Bie 		uep->id = desc_idx;
787f9b9d1a5SJianfeng Tan 		uep->len = n_descs;
788f9b9d1a5SJianfeng Tan 
789ea5207c1SJoyce Kong 		__atomic_add_fetch(&vring->used->idx, 1, __ATOMIC_RELAXED);
790f9b9d1a5SJianfeng Tan 	}
791f9b9d1a5SJianfeng Tan }
79257912824SMaxime Coquelin 
79357912824SMaxime Coquelin int
794d7e10ea9SAdrian Moreno virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status)
79557912824SMaxime Coquelin {
79657912824SMaxime Coquelin 	int ret;
79757912824SMaxime Coquelin 
798d7e10ea9SAdrian Moreno 	pthread_mutex_lock(&dev->mutex);
799d7e10ea9SAdrian Moreno 	dev->status = status;
8008723c894SMaxime Coquelin 	ret = dev->ops->set_status(dev, status);
801a3fb6b1dSMaxime Coquelin 	if (ret && ret != -ENOTSUP)
802a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to set backend status\n", dev->path);
803d7e10ea9SAdrian Moreno 
804d7e10ea9SAdrian Moreno 	pthread_mutex_unlock(&dev->mutex);
8055043a060SAdrian Moreno 	return ret;
80657912824SMaxime Coquelin }
8070b0dc66cSAdrian Moreno 
8080b0dc66cSAdrian Moreno int
809d7e10ea9SAdrian Moreno virtio_user_dev_update_status(struct virtio_user_dev *dev)
8100b0dc66cSAdrian Moreno {
8118723c894SMaxime Coquelin 	int ret;
8127784e977SMaxime Coquelin 	uint8_t status;
8130b0dc66cSAdrian Moreno 
814d7e10ea9SAdrian Moreno 	pthread_mutex_lock(&dev->mutex);
8157784e977SMaxime Coquelin 
8168723c894SMaxime Coquelin 	ret = dev->ops->get_status(dev, &status);
8178723c894SMaxime Coquelin 	if (!ret) {
8187784e977SMaxime Coquelin 		dev->status = status;
8190b0dc66cSAdrian Moreno 		PMD_INIT_LOG(DEBUG, "Updated Device Status(0x%08x):\n"
8200b0dc66cSAdrian Moreno 			"\t-RESET: %u\n"
8210b0dc66cSAdrian Moreno 			"\t-ACKNOWLEDGE: %u\n"
8220b0dc66cSAdrian Moreno 			"\t-DRIVER: %u\n"
8230b0dc66cSAdrian Moreno 			"\t-DRIVER_OK: %u\n"
8240b0dc66cSAdrian Moreno 			"\t-FEATURES_OK: %u\n"
8250b0dc66cSAdrian Moreno 			"\t-DEVICE_NEED_RESET: %u\n"
8260b0dc66cSAdrian Moreno 			"\t-FAILED: %u\n",
8270b0dc66cSAdrian Moreno 			dev->status,
8280b0dc66cSAdrian Moreno 			(dev->status == VIRTIO_CONFIG_STATUS_RESET),
8290b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_ACK),
8300b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER),
8310b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK),
8320b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_FEATURES_OK),
8330b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_DEV_NEED_RESET),
8340b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_FAILED));
8358723c894SMaxime Coquelin 	} else if (ret != -ENOTSUP) {
836a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to get backend status\n", dev->path);
8375043a060SAdrian Moreno 	}
8385043a060SAdrian Moreno 
839d7e10ea9SAdrian Moreno 	pthread_mutex_unlock(&dev->mutex);
8408723c894SMaxime Coquelin 	return ret;
8410b0dc66cSAdrian Moreno }
84294973531SMaxime Coquelin 
84394973531SMaxime Coquelin int
84494973531SMaxime Coquelin virtio_user_dev_update_link_state(struct virtio_user_dev *dev)
84594973531SMaxime Coquelin {
84694973531SMaxime Coquelin 	if (dev->ops->update_link_state)
84794973531SMaxime Coquelin 		return dev->ops->update_link_state(dev);
84894973531SMaxime Coquelin 
84994973531SMaxime Coquelin 	return 0;
85094973531SMaxime Coquelin }
85194973531SMaxime Coquelin 
85294973531SMaxime Coquelin static void
85394973531SMaxime Coquelin virtio_user_dev_reset_queues_packed(struct rte_eth_dev *eth_dev)
85494973531SMaxime Coquelin {
85594973531SMaxime Coquelin 	struct virtio_user_dev *dev = eth_dev->data->dev_private;
85694973531SMaxime Coquelin 	struct virtio_hw *hw = &dev->hw;
85794973531SMaxime Coquelin 	struct virtnet_rx *rxvq;
85894973531SMaxime Coquelin 	struct virtnet_tx *txvq;
85994973531SMaxime Coquelin 	uint16_t i;
86094973531SMaxime Coquelin 
86194973531SMaxime Coquelin 	/* Add lock to avoid queue contention. */
86294973531SMaxime Coquelin 	rte_spinlock_lock(&hw->state_lock);
86394973531SMaxime Coquelin 	hw->started = 0;
86494973531SMaxime Coquelin 
86594973531SMaxime Coquelin 	/*
86694973531SMaxime Coquelin 	 * Waiting for datapath to complete before resetting queues.
86794973531SMaxime Coquelin 	 * 1 ms should be enough for the ongoing Tx/Rx function to finish.
86894973531SMaxime Coquelin 	 */
86994973531SMaxime Coquelin 	rte_delay_ms(1);
87094973531SMaxime Coquelin 
87194973531SMaxime Coquelin 	/* Vring reset for each Tx queue and Rx queue. */
87294973531SMaxime Coquelin 	for (i = 0; i < eth_dev->data->nb_rx_queues; i++) {
87394973531SMaxime Coquelin 		rxvq = eth_dev->data->rx_queues[i];
87494973531SMaxime Coquelin 		virtqueue_rxvq_reset_packed(rxvq->vq);
87594973531SMaxime Coquelin 		virtio_dev_rx_queue_setup_finish(eth_dev, i);
87694973531SMaxime Coquelin 	}
87794973531SMaxime Coquelin 
87894973531SMaxime Coquelin 	for (i = 0; i < eth_dev->data->nb_tx_queues; i++) {
87994973531SMaxime Coquelin 		txvq = eth_dev->data->tx_queues[i];
88094973531SMaxime Coquelin 		virtqueue_txvq_reset_packed(txvq->vq);
88194973531SMaxime Coquelin 	}
88294973531SMaxime Coquelin 
88394973531SMaxime Coquelin 	hw->started = 1;
88494973531SMaxime Coquelin 	rte_spinlock_unlock(&hw->state_lock);
88594973531SMaxime Coquelin }
88694973531SMaxime Coquelin 
88794973531SMaxime Coquelin void
88894973531SMaxime Coquelin virtio_user_dev_delayed_handler(void *param)
88994973531SMaxime Coquelin {
89094973531SMaxime Coquelin 	struct virtio_user_dev *dev = param;
89194973531SMaxime Coquelin 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id];
89294973531SMaxime Coquelin 
89394973531SMaxime Coquelin 	if (rte_intr_disable(eth_dev->intr_handle) < 0) {
89494973531SMaxime Coquelin 		PMD_DRV_LOG(ERR, "interrupt disable failed");
89594973531SMaxime Coquelin 		return;
89694973531SMaxime Coquelin 	}
89794973531SMaxime Coquelin 	rte_intr_callback_unregister(eth_dev->intr_handle,
89894973531SMaxime Coquelin 				     virtio_interrupt_handler, eth_dev);
89994973531SMaxime Coquelin 	if (dev->is_server) {
90094973531SMaxime Coquelin 		if (dev->ops->server_disconnect)
90194973531SMaxime Coquelin 			dev->ops->server_disconnect(dev);
90294973531SMaxime Coquelin 		eth_dev->intr_handle->fd = dev->ops->get_intr_fd(dev);
90394973531SMaxime Coquelin 		rte_intr_callback_register(eth_dev->intr_handle,
90494973531SMaxime Coquelin 					   virtio_interrupt_handler, eth_dev);
90594973531SMaxime Coquelin 		if (rte_intr_enable(eth_dev->intr_handle) < 0) {
90694973531SMaxime Coquelin 			PMD_DRV_LOG(ERR, "interrupt enable failed");
90794973531SMaxime Coquelin 			return;
90894973531SMaxime Coquelin 		}
90994973531SMaxime Coquelin 	}
91094973531SMaxime Coquelin }
91194973531SMaxime Coquelin 
91294973531SMaxime Coquelin int
91394973531SMaxime Coquelin virtio_user_dev_server_reconnect(struct virtio_user_dev *dev)
91494973531SMaxime Coquelin {
91594973531SMaxime Coquelin 	int ret, old_status;
91694973531SMaxime Coquelin 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->port_id];
91794973531SMaxime Coquelin 	struct virtio_hw *hw = &dev->hw;
91894973531SMaxime Coquelin 
91994973531SMaxime Coquelin 	if (!dev->ops->server_reconnect) {
92094973531SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Missing server reconnect callback", dev->path);
92194973531SMaxime Coquelin 		return -1;
92294973531SMaxime Coquelin 	}
92394973531SMaxime Coquelin 
92494973531SMaxime Coquelin 	if (dev->ops->server_reconnect(dev)) {
92594973531SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Reconnect callback call failed", dev->path);
92694973531SMaxime Coquelin 		return -1;
92794973531SMaxime Coquelin 	}
92894973531SMaxime Coquelin 
92994973531SMaxime Coquelin 	old_status = dev->status;
93094973531SMaxime Coquelin 
93194973531SMaxime Coquelin 	virtio_reset(hw);
93294973531SMaxime Coquelin 
93394973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_ACK);
93494973531SMaxime Coquelin 
93594973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER);
93694973531SMaxime Coquelin 
93794973531SMaxime Coquelin 	if (dev->ops->get_features(dev, &dev->device_features) < 0) {
93894973531SMaxime Coquelin 		PMD_INIT_LOG(ERR, "get_features failed: %s",
93994973531SMaxime Coquelin 			     strerror(errno));
94094973531SMaxime Coquelin 		return -1;
94194973531SMaxime Coquelin 	}
94294973531SMaxime Coquelin 
94394973531SMaxime Coquelin 	dev->device_features |= dev->frontend_features;
94494973531SMaxime Coquelin 
94594973531SMaxime Coquelin 	/* unmask vhost-user unsupported features */
94694973531SMaxime Coquelin 	dev->device_features &= ~(dev->unsupported_features);
94794973531SMaxime Coquelin 
94894973531SMaxime Coquelin 	dev->features &= dev->device_features;
94994973531SMaxime Coquelin 
95094973531SMaxime Coquelin 	/* For packed ring, resetting queues is required in reconnection. */
95194973531SMaxime Coquelin 	if (virtio_with_packed_queue(hw) &&
95294973531SMaxime Coquelin 	   (old_status & VIRTIO_CONFIG_STATUS_DRIVER_OK)) {
95394973531SMaxime Coquelin 		PMD_INIT_LOG(NOTICE, "Packets on the fly will be dropped"
95494973531SMaxime Coquelin 				" when packed ring reconnecting.");
95594973531SMaxime Coquelin 		virtio_user_dev_reset_queues_packed(eth_dev);
95694973531SMaxime Coquelin 	}
95794973531SMaxime Coquelin 
95894973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_FEATURES_OK);
95994973531SMaxime Coquelin 
96094973531SMaxime Coquelin 	/* Start the device */
96194973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
96294973531SMaxime Coquelin 	if (!dev->started)
96394973531SMaxime Coquelin 		return -1;
96494973531SMaxime Coquelin 
96594973531SMaxime Coquelin 	if (dev->queue_pairs > 1) {
96694973531SMaxime Coquelin 		ret = virtio_user_handle_mq(dev, dev->queue_pairs);
96794973531SMaxime Coquelin 		if (ret != 0) {
96894973531SMaxime Coquelin 			PMD_INIT_LOG(ERR, "Fails to enable multi-queue pairs!");
96994973531SMaxime Coquelin 			return -1;
97094973531SMaxime Coquelin 		}
97194973531SMaxime Coquelin 	}
97294973531SMaxime Coquelin 	if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC) {
97394973531SMaxime Coquelin 		if (rte_intr_disable(eth_dev->intr_handle) < 0) {
97494973531SMaxime Coquelin 			PMD_DRV_LOG(ERR, "interrupt disable failed");
97594973531SMaxime Coquelin 			return -1;
97694973531SMaxime Coquelin 		}
97794973531SMaxime Coquelin 		rte_intr_callback_unregister(eth_dev->intr_handle,
97894973531SMaxime Coquelin 					     virtio_interrupt_handler,
97994973531SMaxime Coquelin 					     eth_dev);
98094973531SMaxime Coquelin 
98194973531SMaxime Coquelin 		eth_dev->intr_handle->fd = dev->ops->get_intr_fd(dev);
98294973531SMaxime Coquelin 		rte_intr_callback_register(eth_dev->intr_handle,
98394973531SMaxime Coquelin 					   virtio_interrupt_handler, eth_dev);
98494973531SMaxime Coquelin 
98594973531SMaxime Coquelin 		if (rte_intr_enable(eth_dev->intr_handle) < 0) {
98694973531SMaxime Coquelin 			PMD_DRV_LOG(ERR, "interrupt enable failed");
98794973531SMaxime Coquelin 			return -1;
98894973531SMaxime Coquelin 		}
98994973531SMaxime Coquelin 	}
99094973531SMaxime Coquelin 	PMD_INIT_LOG(NOTICE, "server mode virtio-user reconnection succeeds!");
99194973531SMaxime Coquelin 	return 0;
99294973531SMaxime Coquelin }
993