xref: /dpdk/drivers/net/virtio/virtio_user/virtio_user_dev.c (revision 9eb56fb2dbd30110c25b929e234b1d78bdbdb931)
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>
772b452c5SDmitry Kozlyuk #include <stdlib.h>
837a7eb2aSJianfeng Tan #include <fcntl.h>
937a7eb2aSJianfeng Tan #include <string.h>
1037a7eb2aSJianfeng Tan #include <errno.h>
1137a7eb2aSJianfeng Tan #include <sys/mman.h>
1237a7eb2aSJianfeng Tan #include <unistd.h>
1337a7eb2aSJianfeng Tan #include <sys/eventfd.h>
1433d24d65SJianfeng Tan #include <sys/types.h>
1533d24d65SJianfeng Tan #include <sys/stat.h>
1637a7eb2aSJianfeng Tan 
1723abee9dSIlya Maximets #include <rte_alarm.h>
186723c0fcSBruce Richardson #include <rte_string_fns.h>
197ff26957STiwei Bie #include <rte_eal_memconfig.h>
207ff26957STiwei Bie 
2137a7eb2aSJianfeng Tan #include "vhost.h"
2237a7eb2aSJianfeng Tan #include "virtio_user_dev.h"
2337a7eb2aSJianfeng Tan #include "../virtio_ethdev.h"
2437a7eb2aSJianfeng Tan 
2512ecb2f6SMaxime Coquelin #define VIRTIO_USER_MEM_EVENT_CLB_NAME "virtio_user_mem_event_clb"
2612ecb2f6SMaxime Coquelin 
27b0395dc8SAdrian Moreno const char * const virtio_user_backend_strings[] = {
28b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_UNKNOWN] = "VIRTIO_USER_BACKEND_UNKNOWN",
29b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_VHOST_USER] = "VHOST_USER",
30b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_VHOST_KERNEL] = "VHOST_NET",
31b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_VHOST_VDPA] = "VHOST_VDPA",
32b0395dc8SAdrian Moreno };
33b0395dc8SAdrian Moreno 
3437a7eb2aSJianfeng Tan static int
3557ae79a7SJianfeng Tan virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
3657ae79a7SJianfeng Tan {
3757ae79a7SJianfeng Tan 	/* Of all per virtqueue MSGs, make sure VHOST_SET_VRING_CALL come
3857ae79a7SJianfeng Tan 	 * firstly because vhost depends on this msg to allocate virtqueue
3957ae79a7SJianfeng Tan 	 * pair.
4057ae79a7SJianfeng Tan 	 */
4157ae79a7SJianfeng Tan 	struct vhost_vring_file file;
42a3fb6b1dSMaxime Coquelin 	int ret;
4357ae79a7SJianfeng Tan 
4457ae79a7SJianfeng Tan 	file.index = queue_sel;
45e6e7ad8bSJianfeng Tan 	file.fd = dev->callfds[queue_sel];
46a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_call(dev, &file);
47a3fb6b1dSMaxime Coquelin 	if (ret < 0) {
48f3854ebaSThomas Monjalon 		PMD_INIT_LOG(ERR, "(%s) Failed to create queue %u", dev->path, queue_sel);
49a3fb6b1dSMaxime Coquelin 		return -1;
50a3fb6b1dSMaxime Coquelin 	}
5157ae79a7SJianfeng Tan 
5257ae79a7SJianfeng Tan 	return 0;
5357ae79a7SJianfeng Tan }
5457ae79a7SJianfeng Tan 
5557ae79a7SJianfeng Tan static int
5637a7eb2aSJianfeng Tan virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
5737a7eb2aSJianfeng Tan {
58a3fb6b1dSMaxime Coquelin 	int ret;
5937a7eb2aSJianfeng Tan 	struct vhost_vring_file file;
6037a7eb2aSJianfeng Tan 	struct vhost_vring_state state;
6137a7eb2aSJianfeng Tan 	struct vring *vring = &dev->vrings[queue_sel];
6248a44640SJens Freimann 	struct vring_packed *pq_vring = &dev->packed_vrings[queue_sel];
6337a7eb2aSJianfeng Tan 	struct vhost_vring_addr addr = {
6437a7eb2aSJianfeng Tan 		.index = queue_sel,
6537a7eb2aSJianfeng Tan 		.log_guest_addr = 0,
6637a7eb2aSJianfeng Tan 		.flags = 0, /* disable log */
6737a7eb2aSJianfeng Tan 	};
6837a7eb2aSJianfeng Tan 
6990966e8eSMaxime Coquelin 	if (queue_sel == dev->max_queue_pairs * 2) {
7090966e8eSMaxime Coquelin 		if (!dev->scvq) {
7190966e8eSMaxime Coquelin 			PMD_INIT_LOG(ERR, "(%s) Shadow control queue expected but missing",
7290966e8eSMaxime Coquelin 					dev->path);
7390966e8eSMaxime Coquelin 			goto err;
7490966e8eSMaxime Coquelin 		}
7590966e8eSMaxime Coquelin 
7690966e8eSMaxime Coquelin 		/* Use shadow control queue information */
7790966e8eSMaxime Coquelin 		vring = &dev->scvq->vq_split.ring;
7890966e8eSMaxime Coquelin 		pq_vring = &dev->scvq->vq_packed.ring;
7990966e8eSMaxime Coquelin 	}
8090966e8eSMaxime Coquelin 
8148a44640SJens Freimann 	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
8248a44640SJens Freimann 		addr.desc_user_addr =
834cdc4d98STiwei Bie 			(uint64_t)(uintptr_t)pq_vring->desc;
8448a44640SJens Freimann 		addr.avail_user_addr =
854cdc4d98STiwei Bie 			(uint64_t)(uintptr_t)pq_vring->driver;
8648a44640SJens Freimann 		addr.used_user_addr =
874cdc4d98STiwei Bie 			(uint64_t)(uintptr_t)pq_vring->device;
8848a44640SJens Freimann 	} else {
8948a44640SJens Freimann 		addr.desc_user_addr = (uint64_t)(uintptr_t)vring->desc;
9048a44640SJens Freimann 		addr.avail_user_addr = (uint64_t)(uintptr_t)vring->avail;
9148a44640SJens Freimann 		addr.used_user_addr = (uint64_t)(uintptr_t)vring->used;
9248a44640SJens Freimann 	}
9348a44640SJens Freimann 
9437a7eb2aSJianfeng Tan 	state.index = queue_sel;
9537a7eb2aSJianfeng Tan 	state.num = vring->num;
96a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_num(dev, &state);
97a3fb6b1dSMaxime Coquelin 	if (ret < 0)
98a3fb6b1dSMaxime Coquelin 		goto err;
9937a7eb2aSJianfeng Tan 
100be7a4707SJianfeng Tan 	state.index = queue_sel;
10137a7eb2aSJianfeng Tan 	state.num = 0; /* no reservation */
10234f3966cSYuanhan Liu 	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED))
10334f3966cSYuanhan Liu 		state.num |= (1 << 15);
104a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_base(dev, &state);
105a3fb6b1dSMaxime Coquelin 	if (ret < 0)
106a3fb6b1dSMaxime Coquelin 		goto err;
10737a7eb2aSJianfeng Tan 
108a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_addr(dev, &addr);
109a3fb6b1dSMaxime Coquelin 	if (ret < 0)
110a3fb6b1dSMaxime Coquelin 		goto err;
11137a7eb2aSJianfeng Tan 
11237a7eb2aSJianfeng Tan 	/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
11337a7eb2aSJianfeng Tan 	 * lastly because vhost depends on this msg to judge if
11437a7eb2aSJianfeng Tan 	 * virtio is ready.
11537a7eb2aSJianfeng Tan 	 */
11657ae79a7SJianfeng Tan 	file.index = queue_sel;
117e6e7ad8bSJianfeng Tan 	file.fd = dev->kickfds[queue_sel];
118a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_kick(dev, &file);
119a3fb6b1dSMaxime Coquelin 	if (ret < 0)
120a3fb6b1dSMaxime Coquelin 		goto err;
12137a7eb2aSJianfeng Tan 
12237a7eb2aSJianfeng Tan 	return 0;
123a3fb6b1dSMaxime Coquelin err:
124f3854ebaSThomas Monjalon 	PMD_INIT_LOG(ERR, "(%s) Failed to kick queue %u", dev->path, queue_sel);
125a3fb6b1dSMaxime Coquelin 
126a3fb6b1dSMaxime Coquelin 	return -1;
12737a7eb2aSJianfeng Tan }
12837a7eb2aSJianfeng Tan 
12957ae79a7SJianfeng Tan static int
13057ae79a7SJianfeng Tan virtio_user_queue_setup(struct virtio_user_dev *dev,
13157ae79a7SJianfeng Tan 			int (*fn)(struct virtio_user_dev *, uint32_t))
13257ae79a7SJianfeng Tan {
13390966e8eSMaxime Coquelin 	uint32_t i, nr_vq;
13457ae79a7SJianfeng Tan 
13590966e8eSMaxime Coquelin 	nr_vq = dev->max_queue_pairs * 2;
13690966e8eSMaxime Coquelin 	if (dev->hw_cvq)
13790966e8eSMaxime Coquelin 		nr_vq++;
13890966e8eSMaxime Coquelin 
13990966e8eSMaxime Coquelin 	for (i = 0; i < nr_vq; i++) {
140da508066SMaxime Coquelin 		if (fn(dev, i) < 0) {
141da508066SMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) setup VQ %u failed", dev->path, i);
14257ae79a7SJianfeng Tan 			return -1;
14357ae79a7SJianfeng Tan 		}
14457ae79a7SJianfeng Tan 	}
14557ae79a7SJianfeng Tan 
14657ae79a7SJianfeng Tan 	return 0;
14757ae79a7SJianfeng Tan }
14857ae79a7SJianfeng Tan 
14937a7eb2aSJianfeng Tan int
150844e4683SMaxime Coquelin virtio_user_dev_set_features(struct virtio_user_dev *dev)
15137a7eb2aSJianfeng Tan {
15237a7eb2aSJianfeng Tan 	uint64_t features;
153844e4683SMaxime Coquelin 	int ret = -1;
154844e4683SMaxime Coquelin 
155844e4683SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
156844e4683SMaxime Coquelin 
157844e4683SMaxime Coquelin 	/* Step 0: tell vhost to create queues */
158844e4683SMaxime Coquelin 	if (virtio_user_queue_setup(dev, virtio_user_create_queue) < 0)
159844e4683SMaxime Coquelin 		goto error;
160844e4683SMaxime Coquelin 
161844e4683SMaxime Coquelin 	features = dev->features;
162844e4683SMaxime Coquelin 
163844e4683SMaxime Coquelin 	/* Strip VIRTIO_NET_F_MAC, as MAC address is handled in vdev init */
164844e4683SMaxime Coquelin 	features &= ~(1ull << VIRTIO_NET_F_MAC);
16566b45ceaSMaxime Coquelin 	/* Strip VIRTIO_NET_F_CTRL_VQ if the devices does not really support control VQ */
16666b45ceaSMaxime Coquelin 	if (!dev->hw_cvq)
167844e4683SMaxime Coquelin 		features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
168844e4683SMaxime Coquelin 	features &= ~(1ull << VIRTIO_NET_F_STATUS);
169cc0151b3SMaxime Coquelin 	ret = dev->ops->set_features(dev, features);
170844e4683SMaxime Coquelin 	if (ret < 0)
171844e4683SMaxime Coquelin 		goto error;
172a3fb6b1dSMaxime Coquelin 	PMD_DRV_LOG(INFO, "(%s) set features: 0x%" PRIx64, dev->path, features);
173844e4683SMaxime Coquelin error:
174844e4683SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
175844e4683SMaxime Coquelin 
176844e4683SMaxime Coquelin 	return ret;
177844e4683SMaxime Coquelin }
178844e4683SMaxime Coquelin 
179844e4683SMaxime Coquelin int
180844e4683SMaxime Coquelin virtio_user_start_device(struct virtio_user_dev *dev)
181844e4683SMaxime Coquelin {
18237a7eb2aSJianfeng Tan 	int ret;
18337a7eb2aSJianfeng Tan 
1847ff26957STiwei Bie 	/*
1857ff26957STiwei Bie 	 * XXX workaround!
1867ff26957STiwei Bie 	 *
1877ff26957STiwei Bie 	 * We need to make sure that the locks will be
1887ff26957STiwei Bie 	 * taken in the correct order to avoid deadlocks.
1897ff26957STiwei Bie 	 *
1907ff26957STiwei Bie 	 * Before releasing this lock, this thread should
1917ff26957STiwei Bie 	 * not trigger any memory hotplug events.
1927ff26957STiwei Bie 	 *
1937ff26957STiwei Bie 	 * This is a temporary workaround, and should be
1947ff26957STiwei Bie 	 * replaced when we get proper supports from the
1957ff26957STiwei Bie 	 * memory subsystem in the future.
1967ff26957STiwei Bie 	 */
19776f80881SAnatoly Burakov 	rte_mcfg_mem_read_lock();
19812ecb2f6SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
19912ecb2f6SMaxime Coquelin 
20057ae79a7SJianfeng Tan 	/* Step 2: share memory regions */
201539d910cSMaxime Coquelin 	ret = dev->ops->set_memory_table(dev);
20257ae79a7SJianfeng Tan 	if (ret < 0)
20357ae79a7SJianfeng Tan 		goto error;
20457ae79a7SJianfeng Tan 
20557ae79a7SJianfeng Tan 	/* Step 3: kick queues */
206a3fb6b1dSMaxime Coquelin 	ret = virtio_user_queue_setup(dev, virtio_user_kick_queue);
207a3fb6b1dSMaxime Coquelin 	if (ret < 0)
20857ae79a7SJianfeng Tan 		goto error;
20957ae79a7SJianfeng Tan 
21057ae79a7SJianfeng Tan 	/* Step 4: enable queues
21157ae79a7SJianfeng Tan 	 * we enable the 1st queue pair by default.
21257ae79a7SJianfeng Tan 	 */
213a3fb6b1dSMaxime Coquelin 	ret = dev->ops->enable_qp(dev, 0, 1);
214a3fb6b1dSMaxime Coquelin 	if (ret < 0)
215a3fb6b1dSMaxime Coquelin 		goto error;
21657ae79a7SJianfeng Tan 
21712ecb2f6SMaxime Coquelin 	dev->started = true;
2189af79db2SMaxime Coquelin 
21912ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
22076f80881SAnatoly Burakov 	rte_mcfg_mem_read_unlock();
22112ecb2f6SMaxime Coquelin 
22237a7eb2aSJianfeng Tan 	return 0;
22337a7eb2aSJianfeng Tan error:
22412ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
22576f80881SAnatoly Burakov 	rte_mcfg_mem_read_unlock();
226a3fb6b1dSMaxime Coquelin 
227f3854ebaSThomas Monjalon 	PMD_INIT_LOG(ERR, "(%s) Failed to start device", dev->path);
228a3fb6b1dSMaxime Coquelin 
22937a7eb2aSJianfeng Tan 	/* TODO: free resource here or caller to check */
23037a7eb2aSJianfeng Tan 	return -1;
23137a7eb2aSJianfeng Tan }
23237a7eb2aSJianfeng Tan 
23337a7eb2aSJianfeng Tan int virtio_user_stop_device(struct virtio_user_dev *dev)
23437a7eb2aSJianfeng Tan {
23574dc6746STiwei Bie 	struct vhost_vring_state state;
236c12a26eeSJianfeng Tan 	uint32_t i;
237a3fb6b1dSMaxime Coquelin 	int ret;
238c12a26eeSJianfeng Tan 
23912ecb2f6SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
240f457e900STiwei Bie 	if (!dev->started)
241f457e900STiwei Bie 		goto out;
242f457e900STiwei Bie 
243a3fb6b1dSMaxime Coquelin 	for (i = 0; i < dev->max_queue_pairs; ++i) {
244a3fb6b1dSMaxime Coquelin 		ret = dev->ops->enable_qp(dev, i, 0);
245a3fb6b1dSMaxime Coquelin 		if (ret < 0)
246a3fb6b1dSMaxime Coquelin 			goto err;
247a3fb6b1dSMaxime Coquelin 	}
248c12a26eeSJianfeng Tan 
24974dc6746STiwei Bie 	/* Stop the backend. */
25074dc6746STiwei Bie 	for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
25174dc6746STiwei Bie 		state.index = i;
252a3fb6b1dSMaxime Coquelin 		ret = dev->ops->get_vring_base(dev, &state);
253a3fb6b1dSMaxime Coquelin 		if (ret < 0) {
254a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) get_vring_base failed, index=%u", dev->path, i);
255a3fb6b1dSMaxime Coquelin 			goto err;
2560d6a8752SJianfeng Tan 		}
25774dc6746STiwei Bie 	}
25874dc6746STiwei Bie 
25912ecb2f6SMaxime Coquelin 	dev->started = false;
260a3fb6b1dSMaxime Coquelin 
261f457e900STiwei Bie out:
26212ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
2630d6a8752SJianfeng Tan 
264a3fb6b1dSMaxime Coquelin 	return 0;
265a3fb6b1dSMaxime Coquelin err:
266a3fb6b1dSMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
267a3fb6b1dSMaxime Coquelin 
268f3854ebaSThomas Monjalon 	PMD_INIT_LOG(ERR, "(%s) Failed to stop device", dev->path);
269a3fb6b1dSMaxime Coquelin 
270a3fb6b1dSMaxime Coquelin 	return -1;
27137a7eb2aSJianfeng Tan }
27237a7eb2aSJianfeng Tan 
2737be72485SMaxime Coquelin static int
2747be72485SMaxime Coquelin virtio_user_dev_init_max_queue_pairs(struct virtio_user_dev *dev, uint32_t user_max_qp)
2757be72485SMaxime Coquelin {
2767be72485SMaxime Coquelin 	int ret;
2777be72485SMaxime Coquelin 
2787be72485SMaxime Coquelin 	if (!(dev->device_features & (1ULL << VIRTIO_NET_F_MQ))) {
2797be72485SMaxime Coquelin 		dev->max_queue_pairs = 1;
2807be72485SMaxime Coquelin 		return 0;
2817be72485SMaxime Coquelin 	}
2827be72485SMaxime Coquelin 
2837be72485SMaxime Coquelin 	if (!dev->ops->get_config) {
2847be72485SMaxime Coquelin 		dev->max_queue_pairs = user_max_qp;
2857be72485SMaxime Coquelin 		return 0;
2867be72485SMaxime Coquelin 	}
2877be72485SMaxime Coquelin 
2887be72485SMaxime Coquelin 	ret = dev->ops->get_config(dev, (uint8_t *)&dev->max_queue_pairs,
2897be72485SMaxime Coquelin 			offsetof(struct virtio_net_config, max_virtqueue_pairs),
2907be72485SMaxime Coquelin 			sizeof(uint16_t));
2917be72485SMaxime Coquelin 	if (ret) {
2927be72485SMaxime Coquelin 		/*
2937be72485SMaxime Coquelin 		 * We need to know the max queue pair from the device so that
2947be72485SMaxime Coquelin 		 * the control queue gets the right index.
2957be72485SMaxime Coquelin 		 */
2967be72485SMaxime Coquelin 		dev->max_queue_pairs = 1;
2977be72485SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Failed to get max queue pairs from device", dev->path);
2987be72485SMaxime Coquelin 
2997be72485SMaxime Coquelin 		return ret;
3007be72485SMaxime Coquelin 	}
3017be72485SMaxime Coquelin 
3027be72485SMaxime Coquelin 	if (dev->max_queue_pairs > VIRTIO_MAX_VIRTQUEUE_PAIRS) {
3037be72485SMaxime Coquelin 		/*
3047be72485SMaxime Coquelin 		 * If the device supports control queue, the control queue
3057be72485SMaxime Coquelin 		 * index is max_virtqueue_pairs * 2. Disable MQ if it happens.
3067be72485SMaxime Coquelin 		 */
3077be72485SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Device advertises too many queues (%u, max supported %u)",
3087be72485SMaxime Coquelin 				dev->path, dev->max_queue_pairs, VIRTIO_MAX_VIRTQUEUE_PAIRS);
3097be72485SMaxime Coquelin 		dev->max_queue_pairs = 1;
3107be72485SMaxime Coquelin 
3117be72485SMaxime Coquelin 		return -1;
3127be72485SMaxime Coquelin 	}
3137be72485SMaxime Coquelin 
3147be72485SMaxime Coquelin 	return 0;
3157be72485SMaxime Coquelin }
3167be72485SMaxime Coquelin 
317c995b005SMaxime Coquelin int
318c995b005SMaxime Coquelin virtio_user_dev_set_mac(struct virtio_user_dev *dev)
31937a7eb2aSJianfeng Tan {
320c995b005SMaxime Coquelin 	int ret = 0;
32137a7eb2aSJianfeng Tan 
322c995b005SMaxime Coquelin 	if (!(dev->device_features & (1ULL << VIRTIO_NET_F_MAC)))
323c995b005SMaxime Coquelin 		return -ENOTSUP;
32437a7eb2aSJianfeng Tan 
325c995b005SMaxime Coquelin 	if (!dev->ops->set_config)
326c995b005SMaxime Coquelin 		return -ENOTSUP;
327c995b005SMaxime Coquelin 
328c995b005SMaxime Coquelin 	ret = dev->ops->set_config(dev, dev->mac_addr,
329c995b005SMaxime Coquelin 			offsetof(struct virtio_net_config, mac),
330c995b005SMaxime Coquelin 			RTE_ETHER_ADDR_LEN);
331c995b005SMaxime Coquelin 	if (ret)
332c995b005SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Failed to set MAC address in device", dev->path);
333c995b005SMaxime Coquelin 
334c995b005SMaxime Coquelin 	return ret;
33537a7eb2aSJianfeng Tan }
336c995b005SMaxime Coquelin 
337c995b005SMaxime Coquelin int
338c995b005SMaxime Coquelin virtio_user_dev_get_mac(struct virtio_user_dev *dev)
339c995b005SMaxime Coquelin {
340c995b005SMaxime Coquelin 	int ret = 0;
341c995b005SMaxime Coquelin 
342c995b005SMaxime Coquelin 	if (!(dev->device_features & (1ULL << VIRTIO_NET_F_MAC)))
343c995b005SMaxime Coquelin 		return -ENOTSUP;
344c995b005SMaxime Coquelin 
345c995b005SMaxime Coquelin 	if (!dev->ops->get_config)
346c995b005SMaxime Coquelin 		return -ENOTSUP;
347c995b005SMaxime Coquelin 
348c995b005SMaxime Coquelin 	ret = dev->ops->get_config(dev, dev->mac_addr,
349c995b005SMaxime Coquelin 			offsetof(struct virtio_net_config, mac),
350c995b005SMaxime Coquelin 			RTE_ETHER_ADDR_LEN);
351c995b005SMaxime Coquelin 	if (ret)
352c995b005SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Failed to get MAC address from device", dev->path);
353c995b005SMaxime Coquelin 
354c995b005SMaxime Coquelin 	return ret;
355c995b005SMaxime Coquelin }
356c995b005SMaxime Coquelin 
357c995b005SMaxime Coquelin static void
358c995b005SMaxime Coquelin virtio_user_dev_init_mac(struct virtio_user_dev *dev, const char *mac)
359c995b005SMaxime Coquelin {
360c995b005SMaxime Coquelin 	struct rte_ether_addr cmdline_mac;
361c995b005SMaxime Coquelin 	char buf[RTE_ETHER_ADDR_FMT_SIZE];
362c995b005SMaxime Coquelin 	int ret;
363c995b005SMaxime Coquelin 
364c995b005SMaxime Coquelin 	if (mac && rte_ether_unformat_addr(mac, &cmdline_mac) == 0) {
365c995b005SMaxime Coquelin 		/*
366c995b005SMaxime Coquelin 		 * MAC address was passed from command-line, try to store
367c995b005SMaxime Coquelin 		 * it in the device if it supports it. Otherwise try to use
368c995b005SMaxime Coquelin 		 * the device one.
369c995b005SMaxime Coquelin 		 */
370c995b005SMaxime Coquelin 		memcpy(dev->mac_addr, &cmdline_mac, RTE_ETHER_ADDR_LEN);
371c995b005SMaxime Coquelin 		dev->mac_specified = 1;
372c995b005SMaxime Coquelin 
373c995b005SMaxime Coquelin 		/* Setting MAC may fail, continue to get the device one in this case */
374c995b005SMaxime Coquelin 		virtio_user_dev_set_mac(dev);
375c995b005SMaxime Coquelin 		ret = virtio_user_dev_get_mac(dev);
376c995b005SMaxime Coquelin 		if (ret == -ENOTSUP)
377c995b005SMaxime Coquelin 			goto out;
378c995b005SMaxime Coquelin 
379c995b005SMaxime Coquelin 		if (memcmp(&cmdline_mac, dev->mac_addr, RTE_ETHER_ADDR_LEN))
380c995b005SMaxime Coquelin 			PMD_DRV_LOG(INFO, "(%s) Device MAC update failed", dev->path);
381c995b005SMaxime Coquelin 	} else {
382c995b005SMaxime Coquelin 		ret = virtio_user_dev_get_mac(dev);
383c995b005SMaxime Coquelin 		if (ret) {
384c995b005SMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) No valid MAC in devargs or device, use random",
385c995b005SMaxime Coquelin 					dev->path);
386c995b005SMaxime Coquelin 			return;
387c995b005SMaxime Coquelin 		}
388c995b005SMaxime Coquelin 
389c995b005SMaxime Coquelin 		dev->mac_specified = 1;
390c995b005SMaxime Coquelin 	}
391c995b005SMaxime Coquelin out:
392c995b005SMaxime Coquelin 	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE,
393c995b005SMaxime Coquelin 			(struct rte_ether_addr *)dev->mac_addr);
394c995b005SMaxime Coquelin 	PMD_DRV_LOG(INFO, "(%s) MAC %s specified", dev->path, buf);
39537a7eb2aSJianfeng Tan }
39637a7eb2aSJianfeng Tan 
39733d24d65SJianfeng Tan static int
398e6e7ad8bSJianfeng Tan virtio_user_dev_init_notify(struct virtio_user_dev *dev)
39933d24d65SJianfeng Tan {
40090966e8eSMaxime Coquelin 	uint32_t i, j, nr_vq;
401e6e7ad8bSJianfeng Tan 	int callfd;
402e6e7ad8bSJianfeng Tan 	int kickfd;
40333d24d65SJianfeng Tan 
40490966e8eSMaxime Coquelin 	nr_vq = dev->max_queue_pairs * 2;
40590966e8eSMaxime Coquelin 	if (dev->hw_cvq)
40690966e8eSMaxime Coquelin 		nr_vq++;
40790966e8eSMaxime Coquelin 
40890966e8eSMaxime Coquelin 	for (i = 0; i < nr_vq; i++) {
409e6e7ad8bSJianfeng Tan 		/* May use invalid flag, but some backend uses kickfd and
410e6e7ad8bSJianfeng Tan 		 * callfd as criteria to judge if dev is alive. so finally we
411e6e7ad8bSJianfeng Tan 		 * use real event_fd.
412e6e7ad8bSJianfeng Tan 		 */
413e6e7ad8bSJianfeng Tan 		callfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
414e6e7ad8bSJianfeng Tan 		if (callfd < 0) {
415a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) callfd error, %s", dev->path, strerror(errno));
4162e4c1b50SMaxime Coquelin 			goto err;
417e6e7ad8bSJianfeng Tan 		}
418e6e7ad8bSJianfeng Tan 		kickfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
419e6e7ad8bSJianfeng Tan 		if (kickfd < 0) {
42097ed740cSJiawei Zhu 			close(callfd);
421a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) kickfd error, %s", dev->path, strerror(errno));
4222e4c1b50SMaxime Coquelin 			goto err;
423e6e7ad8bSJianfeng Tan 		}
424e6e7ad8bSJianfeng Tan 		dev->callfds[i] = callfd;
425e6e7ad8bSJianfeng Tan 		dev->kickfds[i] = kickfd;
426e6e7ad8bSJianfeng Tan 	}
427e6e7ad8bSJianfeng Tan 
4282e4c1b50SMaxime Coquelin 	return 0;
4292e4c1b50SMaxime Coquelin err:
4302e4c1b50SMaxime Coquelin 	for (j = 0; j < i; j++) {
4312e4c1b50SMaxime Coquelin 		if (dev->kickfds[j] >= 0) {
432e6e7ad8bSJianfeng Tan 			close(dev->kickfds[j]);
4332e4c1b50SMaxime Coquelin 			dev->kickfds[j] = -1;
4342e4c1b50SMaxime Coquelin 		}
4352e4c1b50SMaxime Coquelin 		if (dev->callfds[j] >= 0) {
4362e4c1b50SMaxime Coquelin 			close(dev->callfds[j]);
4372e4c1b50SMaxime Coquelin 			dev->callfds[j] = -1;
4382e4c1b50SMaxime Coquelin 		}
439e6e7ad8bSJianfeng Tan 	}
440e6e7ad8bSJianfeng Tan 
441e6e7ad8bSJianfeng Tan 	return -1;
442e6e7ad8bSJianfeng Tan }
443e6e7ad8bSJianfeng Tan 
4442e4c1b50SMaxime Coquelin static void
4452e4c1b50SMaxime Coquelin virtio_user_dev_uninit_notify(struct virtio_user_dev *dev)
4462e4c1b50SMaxime Coquelin {
4472e4c1b50SMaxime Coquelin 	uint32_t i;
4482e4c1b50SMaxime Coquelin 
4492e4c1b50SMaxime Coquelin 	for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
4502e4c1b50SMaxime Coquelin 		if (dev->kickfds[i] >= 0) {
4512e4c1b50SMaxime Coquelin 			close(dev->kickfds[i]);
4522e4c1b50SMaxime Coquelin 			dev->kickfds[i] = -1;
4532e4c1b50SMaxime Coquelin 		}
4542e4c1b50SMaxime Coquelin 		if (dev->callfds[i] >= 0) {
4552e4c1b50SMaxime Coquelin 			close(dev->callfds[i]);
4562e4c1b50SMaxime Coquelin 			dev->callfds[i] = -1;
4572e4c1b50SMaxime Coquelin 		}
4582e4c1b50SMaxime Coquelin 	}
459e6e7ad8bSJianfeng Tan }
460e6e7ad8bSJianfeng Tan 
461e6e7ad8bSJianfeng Tan static int
4623d4fb6fdSJianfeng Tan virtio_user_fill_intr_handle(struct virtio_user_dev *dev)
4633d4fb6fdSJianfeng Tan {
4643d4fb6fdSJianfeng Tan 	uint32_t i;
4656564ddcdSDavid Marchand 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
4663d4fb6fdSJianfeng Tan 
467d61138d4SHarman Kalra 	if (eth_dev->intr_handle == NULL) {
468d61138d4SHarman Kalra 		eth_dev->intr_handle =
469d61138d4SHarman Kalra 			rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
470d61138d4SHarman Kalra 		if (eth_dev->intr_handle == NULL) {
471a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) failed to allocate intr_handle", dev->path);
4723d4fb6fdSJianfeng Tan 			return -1;
4733d4fb6fdSJianfeng Tan 		}
4743d4fb6fdSJianfeng Tan 	}
4753d4fb6fdSJianfeng Tan 
476d61138d4SHarman Kalra 	for (i = 0; i < dev->max_queue_pairs; ++i) {
477d61138d4SHarman Kalra 		if (rte_intr_efds_index_set(eth_dev->intr_handle, i,
47823ab0c59SYuan Wang 				dev->callfds[2 * i + VTNET_SQ_RQ_QUEUE_IDX]))
479d61138d4SHarman Kalra 			return -rte_errno;
480d61138d4SHarman Kalra 	}
481d61138d4SHarman Kalra 
482d61138d4SHarman Kalra 	if (rte_intr_nb_efd_set(eth_dev->intr_handle, dev->max_queue_pairs))
483d61138d4SHarman Kalra 		return -rte_errno;
484d61138d4SHarman Kalra 
485d61138d4SHarman Kalra 	if (rte_intr_max_intr_set(eth_dev->intr_handle,
486d61138d4SHarman Kalra 			dev->max_queue_pairs + 1))
487d61138d4SHarman Kalra 		return -rte_errno;
488d61138d4SHarman Kalra 
489d61138d4SHarman Kalra 	if (rte_intr_type_set(eth_dev->intr_handle, RTE_INTR_HANDLE_VDEV))
490d61138d4SHarman Kalra 		return -rte_errno;
491d61138d4SHarman Kalra 
49229906b97SJingjing Wu 	/* For virtio vdev, no need to read counter for clean */
493d61138d4SHarman Kalra 	if (rte_intr_efd_counter_size_set(eth_dev->intr_handle, 0))
494d61138d4SHarman Kalra 		return -rte_errno;
495d61138d4SHarman Kalra 
496d61138d4SHarman Kalra 	if (rte_intr_fd_set(eth_dev->intr_handle, dev->ops->get_intr_fd(dev)))
497d61138d4SHarman Kalra 		return -rte_errno;
4983d4fb6fdSJianfeng Tan 
4993d4fb6fdSJianfeng Tan 	return 0;
5003d4fb6fdSJianfeng Tan }
5013d4fb6fdSJianfeng Tan 
50212ecb2f6SMaxime Coquelin static void
50312ecb2f6SMaxime Coquelin virtio_user_mem_event_cb(enum rte_mem_event type __rte_unused,
5042286291dSTiwei Bie 			 const void *addr,
50512ecb2f6SMaxime Coquelin 			 size_t len __rte_unused,
50612ecb2f6SMaxime Coquelin 			 void *arg)
50712ecb2f6SMaxime Coquelin {
50812ecb2f6SMaxime Coquelin 	struct virtio_user_dev *dev = arg;
509f32c7c9dSAnatoly Burakov 	struct rte_memseg_list *msl;
51012ecb2f6SMaxime Coquelin 	uint16_t i;
511a3fb6b1dSMaxime Coquelin 	int ret = 0;
51212ecb2f6SMaxime Coquelin 
513f32c7c9dSAnatoly Burakov 	/* ignore externally allocated memory */
514f32c7c9dSAnatoly Burakov 	msl = rte_mem_virt2memseg_list(addr);
515f32c7c9dSAnatoly Burakov 	if (msl->external)
516f32c7c9dSAnatoly Burakov 		return;
517f32c7c9dSAnatoly Burakov 
51812ecb2f6SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
51912ecb2f6SMaxime Coquelin 
52012ecb2f6SMaxime Coquelin 	if (dev->started == false)
52112ecb2f6SMaxime Coquelin 		goto exit;
52212ecb2f6SMaxime Coquelin 
52312ecb2f6SMaxime Coquelin 	/* Step 1: pause the active queues */
524a3fb6b1dSMaxime Coquelin 	for (i = 0; i < dev->queue_pairs; i++) {
525a3fb6b1dSMaxime Coquelin 		ret = dev->ops->enable_qp(dev, i, 0);
526a3fb6b1dSMaxime Coquelin 		if (ret < 0)
527a3fb6b1dSMaxime Coquelin 			goto exit;
528a3fb6b1dSMaxime Coquelin 	}
52912ecb2f6SMaxime Coquelin 
53012ecb2f6SMaxime Coquelin 	/* Step 2: update memory regions */
531a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_memory_table(dev);
532a3fb6b1dSMaxime Coquelin 	if (ret < 0)
533a3fb6b1dSMaxime Coquelin 		goto exit;
53412ecb2f6SMaxime Coquelin 
53512ecb2f6SMaxime Coquelin 	/* Step 3: resume the active queues */
536a3fb6b1dSMaxime Coquelin 	for (i = 0; i < dev->queue_pairs; i++) {
537a3fb6b1dSMaxime Coquelin 		ret = dev->ops->enable_qp(dev, i, 1);
538a3fb6b1dSMaxime Coquelin 		if (ret < 0)
539a3fb6b1dSMaxime Coquelin 			goto exit;
540a3fb6b1dSMaxime Coquelin 	}
54112ecb2f6SMaxime Coquelin 
54212ecb2f6SMaxime Coquelin exit:
54312ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
544a3fb6b1dSMaxime Coquelin 
545a3fb6b1dSMaxime Coquelin 	if (ret < 0)
546f3854ebaSThomas Monjalon 		PMD_DRV_LOG(ERR, "(%s) Failed to update memory table", dev->path);
54712ecb2f6SMaxime Coquelin }
54812ecb2f6SMaxime Coquelin 
5493d4fb6fdSJianfeng Tan static int
550e6e7ad8bSJianfeng Tan virtio_user_dev_setup(struct virtio_user_dev *dev)
551e6e7ad8bSJianfeng Tan {
552bd8f50a4SZhiyong Yang 	if (dev->is_server) {
553f908b22eSAdrian Moreno 		if (dev->backend_type != VIRTIO_USER_BACKEND_VHOST_USER) {
554f908b22eSAdrian Moreno 			PMD_DRV_LOG(ERR, "Server mode only supports vhost-user!");
555bd8f50a4SZhiyong Yang 			return -1;
556bd8f50a4SZhiyong Yang 		}
5579af79db2SMaxime Coquelin 	}
5589af79db2SMaxime Coquelin 
55986388a3aSMaxime Coquelin 	switch (dev->backend_type) {
56086388a3aSMaxime Coquelin 	case VIRTIO_USER_BACKEND_VHOST_USER:
561520dd992SFerruh Yigit 		dev->ops = &virtio_ops_user;
56286388a3aSMaxime Coquelin 		break;
56386388a3aSMaxime Coquelin 	case VIRTIO_USER_BACKEND_VHOST_KERNEL:
564520dd992SFerruh Yigit 		dev->ops = &virtio_ops_kernel;
56586388a3aSMaxime Coquelin 		break;
56686388a3aSMaxime Coquelin 	case VIRTIO_USER_BACKEND_VHOST_VDPA:
5676b901437SMaxime Coquelin 		dev->ops = &virtio_ops_vdpa;
56886388a3aSMaxime Coquelin 		break;
56986388a3aSMaxime Coquelin 	default:
570a3fb6b1dSMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Unknown backend type", dev->path);
5716b901437SMaxime Coquelin 		return -1;
572e3b43481SJianfeng Tan 	}
5739af79db2SMaxime Coquelin 
574a3fb6b1dSMaxime Coquelin 	if (dev->ops->setup(dev) < 0) {
575f3854ebaSThomas Monjalon 		PMD_INIT_LOG(ERR, "(%s) Failed to setup backend", dev->path);
576cc4690e9SJianfeng Tan 		return -1;
577a3fb6b1dSMaxime Coquelin 	}
578cc4690e9SJianfeng Tan 
579cc4690e9SJianfeng Tan 	return 0;
58033d24d65SJianfeng Tan }
58133d24d65SJianfeng Tan 
582bed3b24cSJianfeng Tan /* Use below macro to filter features from vhost backend */
583bed3b24cSJianfeng Tan #define VIRTIO_USER_SUPPORTED_FEATURES			\
584bed3b24cSJianfeng Tan 	(1ULL << VIRTIO_NET_F_MAC		|	\
585bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_STATUS		|	\
586bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_MQ		|	\
587bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_MAC_ADDR	|	\
588bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_VQ		|	\
589bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_RX		|	\
590bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_VLAN		|	\
591bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CSUM		|	\
592bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_HOST_TSO4		|	\
593bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_HOST_TSO6		|	\
594bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_MRG_RXBUF		|	\
595bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_RING_F_INDIRECT_DESC	|	\
596bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_GUEST_CSUM	|	\
597bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_GUEST_TSO4	|	\
598bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_GUEST_TSO6	|	\
59941e45c90SMarvin Liu 	 1ULL << VIRTIO_F_IN_ORDER		|	\
60034f3966cSYuanhan Liu 	 1ULL << VIRTIO_F_VERSION_1		|	\
6015b75b63cSMaxime Coquelin 	 1ULL << VIRTIO_F_RING_PACKED)
6028e756105SMaxime Coquelin 
60337a7eb2aSJianfeng Tan int
60452901852SMaxime Coquelin virtio_user_dev_init(struct virtio_user_dev *dev, char *path, uint16_t queues,
605488ed97aSMarvin Liu 		     int cq, int queue_size, const char *mac, char **ifname,
606f908b22eSAdrian Moreno 		     int server, int mrg_rxbuf, int in_order, int packed_vq,
607f908b22eSAdrian Moreno 		     enum virtio_user_backend_type backend_type)
60837a7eb2aSJianfeng Tan {
6095b75b63cSMaxime Coquelin 	uint64_t backend_features;
6102e4c1b50SMaxime Coquelin 	int i;
6118e756105SMaxime Coquelin 
61212ecb2f6SMaxime Coquelin 	pthread_mutex_init(&dev->mutex, NULL);
6136723c0fcSBruce Richardson 	strlcpy(dev->path, path, PATH_MAX);
6142e4c1b50SMaxime Coquelin 
6152e4c1b50SMaxime Coquelin 	for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) {
6162e4c1b50SMaxime Coquelin 		dev->kickfds[i] = -1;
6172e4c1b50SMaxime Coquelin 		dev->callfds[i] = -1;
6182e4c1b50SMaxime Coquelin 	}
6192e4c1b50SMaxime Coquelin 
62012ecb2f6SMaxime Coquelin 	dev->started = 0;
62137a7eb2aSJianfeng Tan 	dev->queue_pairs = 1; /* mq disabled by default */
62237a7eb2aSJianfeng Tan 	dev->queue_size = queue_size;
6231c8489daSTiwei Bie 	dev->is_server = server;
62437a7eb2aSJianfeng Tan 	dev->mac_specified = 0;
625bb97d2ddSTiwei Bie 	dev->frontend_features = 0;
6265b75b63cSMaxime Coquelin 	dev->unsupported_features = 0;
627f908b22eSAdrian Moreno 	dev->backend_type = backend_type;
628f908b22eSAdrian Moreno 
6294214a1b4SWenfeng Liu 	if (*ifname) {
6304214a1b4SWenfeng Liu 		dev->ifname = *ifname;
6314214a1b4SWenfeng Liu 		*ifname = NULL;
6324214a1b4SWenfeng Liu 	}
6334214a1b4SWenfeng Liu 
63433d24d65SJianfeng Tan 	if (virtio_user_dev_setup(dev) < 0) {
635a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) backend set up fails", dev->path);
63637a7eb2aSJianfeng Tan 		return -1;
63737a7eb2aSJianfeng Tan 	}
638bce7e905SJianfeng Tan 
63906856cabSMaxime Coquelin 	if (dev->ops->set_owner(dev) < 0) {
640a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to set backend owner", dev->path);
6417be72485SMaxime Coquelin 		goto destroy;
64237a7eb2aSJianfeng Tan 	}
64337a7eb2aSJianfeng Tan 
6445b75b63cSMaxime Coquelin 	if (dev->ops->get_backend_features(&backend_features) < 0) {
645a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to get backend features", dev->path);
6467be72485SMaxime Coquelin 		goto destroy;
64737a7eb2aSJianfeng Tan 	}
6488e756105SMaxime Coquelin 
6495b75b63cSMaxime Coquelin 	dev->unsupported_features = ~(VIRTIO_USER_SUPPORTED_FEATURES | backend_features);
6505b75b63cSMaxime Coquelin 
6515b75b63cSMaxime Coquelin 	if (dev->ops->get_features(dev, &dev->device_features) < 0) {
6525b75b63cSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to get device features", dev->path);
6537be72485SMaxime Coquelin 		goto destroy;
654a3fb6b1dSMaxime Coquelin 	}
6558e756105SMaxime Coquelin 
656c995b005SMaxime Coquelin 	virtio_user_dev_init_mac(dev, mac);
657c995b005SMaxime Coquelin 
6587be72485SMaxime Coquelin 	if (virtio_user_dev_init_max_queue_pairs(dev, queues))
6597be72485SMaxime Coquelin 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MQ);
6607be72485SMaxime Coquelin 
6617be72485SMaxime Coquelin 	if (dev->max_queue_pairs > 1)
6627be72485SMaxime Coquelin 		cq = 1;
6637be72485SMaxime Coquelin 
6647be72485SMaxime Coquelin 	if (virtio_user_dev_init_notify(dev) < 0) {
6657be72485SMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to init notifiers", dev->path);
6667be72485SMaxime Coquelin 		goto destroy;
6677be72485SMaxime Coquelin 	}
6687be72485SMaxime Coquelin 
6697be72485SMaxime Coquelin 	if (virtio_user_fill_intr_handle(dev) < 0) {
6707be72485SMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to init interrupt handler", dev->path);
6717be72485SMaxime Coquelin 		goto notify_uninit;
6727be72485SMaxime Coquelin 	}
6737be72485SMaxime Coquelin 
674bd9568f3STiwei Bie 	if (!mrg_rxbuf)
675488ed97aSMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MRG_RXBUF);
676488ed97aSMarvin Liu 
677bd9568f3STiwei Bie 	if (!in_order)
678488ed97aSMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_F_IN_ORDER);
679488ed97aSMarvin Liu 
68048a44640SJens Freimann 	if (!packed_vq)
6819070f88bSTiwei Bie 		dev->unsupported_features |= (1ull << VIRTIO_F_RING_PACKED);
68234f3966cSYuanhan Liu 
6839070f88bSTiwei Bie 	if (dev->mac_specified)
6849070f88bSTiwei Bie 		dev->frontend_features |= (1ull << VIRTIO_NET_F_MAC);
6859070f88bSTiwei Bie 	else
6867c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MAC);
687f9b9d1a5SJianfeng Tan 
688142678d4SJianfeng Tan 	if (cq) {
689142678d4SJianfeng Tan 		/* device does not really need to know anything about CQ,
690142678d4SJianfeng Tan 		 * so if necessary, we just claim to support CQ
691f9b9d1a5SJianfeng Tan 		 */
692bb97d2ddSTiwei Bie 		dev->frontend_features |= (1ull << VIRTIO_NET_F_CTRL_VQ);
693142678d4SJianfeng Tan 	} else {
6947c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_VQ);
695bd9568f3STiwei Bie 		/* Also disable features that depend on VIRTIO_NET_F_CTRL_VQ */
6967c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_RX);
6977c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_VLAN);
6987c66ff61SMarvin Liu 		dev->unsupported_features |=
6997c66ff61SMarvin Liu 			(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE);
7007c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MQ);
7017c66ff61SMarvin Liu 		dev->unsupported_features |=
7027c66ff61SMarvin Liu 			(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
703f9b9d1a5SJianfeng Tan 	}
704f9b9d1a5SJianfeng Tan 
70535c4f855SJianfeng Tan 	/* The backend will not report this feature, we add it explicitly */
706f908b22eSAdrian Moreno 	if (dev->backend_type == VIRTIO_USER_BACKEND_VHOST_USER)
707bb97d2ddSTiwei Bie 		dev->frontend_features |= (1ull << VIRTIO_NET_F_STATUS);
70835c4f855SJianfeng Tan 
709f078c2f0SMaxime Coquelin 	dev->frontend_features &= ~dev->unsupported_features;
710bd9568f3STiwei Bie 	dev->device_features &= ~dev->unsupported_features;
711bed3b24cSJianfeng Tan 
71212ecb2f6SMaxime Coquelin 	if (rte_mem_event_callback_register(VIRTIO_USER_MEM_EVENT_CLB_NAME,
71312ecb2f6SMaxime Coquelin 				virtio_user_mem_event_cb, dev)) {
71488e5469fSXiao Wang 		if (rte_errno != ENOTSUP) {
715f3854ebaSThomas Monjalon 			PMD_INIT_LOG(ERR, "(%s) Failed to register mem event callback",
716a3fb6b1dSMaxime Coquelin 					dev->path);
7177be72485SMaxime Coquelin 			goto notify_uninit;
71812ecb2f6SMaxime Coquelin 		}
71988e5469fSXiao Wang 	}
72012ecb2f6SMaxime Coquelin 
72137a7eb2aSJianfeng Tan 	return 0;
7227be72485SMaxime Coquelin 
7237be72485SMaxime Coquelin notify_uninit:
7247be72485SMaxime Coquelin 	virtio_user_dev_uninit_notify(dev);
7257be72485SMaxime Coquelin destroy:
7267be72485SMaxime Coquelin 	dev->ops->destroy(dev);
7277be72485SMaxime Coquelin 
7287be72485SMaxime Coquelin 	return -1;
72937a7eb2aSJianfeng Tan }
73037a7eb2aSJianfeng Tan 
73137a7eb2aSJianfeng Tan void
73237a7eb2aSJianfeng Tan virtio_user_dev_uninit(struct virtio_user_dev *dev)
73337a7eb2aSJianfeng Tan {
7347b919515SGaoxiang Liu 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
7357b919515SGaoxiang Liu 
736d61138d4SHarman Kalra 	rte_intr_instance_free(eth_dev->intr_handle);
7377b919515SGaoxiang Liu 	eth_dev->intr_handle = NULL;
7387b919515SGaoxiang Liu 
739e3b43481SJianfeng Tan 	virtio_user_stop_device(dev);
740e3b43481SJianfeng Tan 
74112ecb2f6SMaxime Coquelin 	rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev);
74212ecb2f6SMaxime Coquelin 
7432e4c1b50SMaxime Coquelin 	virtio_user_dev_uninit_notify(dev);
7444214a1b4SWenfeng Liu 
7454214a1b4SWenfeng Liu 	free(dev->ifname);
746bd8f50a4SZhiyong Yang 
747bd8f50a4SZhiyong Yang 	if (dev->is_server)
748bd8f50a4SZhiyong Yang 		unlink(dev->path);
749748e5ea5SMaxime Coquelin 
750748e5ea5SMaxime Coquelin 	dev->ops->destroy(dev);
75137a7eb2aSJianfeng Tan }
752f9b9d1a5SJianfeng Tan 
753201a4165SZhiyong Yang uint8_t
754f9b9d1a5SJianfeng Tan virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs)
755f9b9d1a5SJianfeng Tan {
756f9b9d1a5SJianfeng Tan 	uint16_t i;
757f9b9d1a5SJianfeng Tan 	uint8_t ret = 0;
758f9b9d1a5SJianfeng Tan 
759f9b9d1a5SJianfeng Tan 	if (q_pairs > dev->max_queue_pairs) {
760a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) multi-q config %u, but only %u supported",
761a3fb6b1dSMaxime Coquelin 			     dev->path, q_pairs, dev->max_queue_pairs);
762f9b9d1a5SJianfeng Tan 		return -1;
763f9b9d1a5SJianfeng Tan 	}
764f9b9d1a5SJianfeng Tan 
765f9b9d1a5SJianfeng Tan 	for (i = 0; i < q_pairs; ++i)
76633d24d65SJianfeng Tan 		ret |= dev->ops->enable_qp(dev, i, 1);
767f9b9d1a5SJianfeng Tan 	for (i = q_pairs; i < dev->max_queue_pairs; ++i)
76833d24d65SJianfeng Tan 		ret |= dev->ops->enable_qp(dev, i, 0);
76994973531SMaxime Coquelin 
770*9eb56fb2SMaxime Coquelin 	if (dev->scvq)
771*9eb56fb2SMaxime Coquelin 		ret |= dev->ops->cvq_enable(dev, 1);
772*9eb56fb2SMaxime Coquelin 
773f9b9d1a5SJianfeng Tan 	dev->queue_pairs = q_pairs;
774f9b9d1a5SJianfeng Tan 
775f9b9d1a5SJianfeng Tan 	return ret;
776f9b9d1a5SJianfeng Tan }
777f9b9d1a5SJianfeng Tan 
778f9b9d1a5SJianfeng Tan static uint32_t
779f9b9d1a5SJianfeng Tan virtio_user_handle_ctrl_msg(struct virtio_user_dev *dev, struct vring *vring,
780f9b9d1a5SJianfeng Tan 			    uint16_t idx_hdr)
781f9b9d1a5SJianfeng Tan {
782f9b9d1a5SJianfeng Tan 	struct virtio_net_ctrl_hdr *hdr;
783f9b9d1a5SJianfeng Tan 	virtio_net_ctrl_ack status = ~0;
784f9b9d1a5SJianfeng Tan 	uint16_t i, idx_data, idx_status;
785f9b9d1a5SJianfeng Tan 	uint32_t n_descs = 0;
786f9b9d1a5SJianfeng Tan 
787f9b9d1a5SJianfeng Tan 	/* locate desc for header, data, and status */
788f9b9d1a5SJianfeng Tan 	idx_data = vring->desc[idx_hdr].next;
789f9b9d1a5SJianfeng Tan 	n_descs++;
790f9b9d1a5SJianfeng Tan 
791f9b9d1a5SJianfeng Tan 	i = idx_data;
792f9b9d1a5SJianfeng Tan 	while (vring->desc[i].flags == VRING_DESC_F_NEXT) {
793f9b9d1a5SJianfeng Tan 		i = vring->desc[i].next;
794f9b9d1a5SJianfeng Tan 		n_descs++;
795f9b9d1a5SJianfeng Tan 	}
796f9b9d1a5SJianfeng Tan 
797f9b9d1a5SJianfeng Tan 	/* locate desc for status */
798f9b9d1a5SJianfeng Tan 	idx_status = i;
799f9b9d1a5SJianfeng Tan 	n_descs++;
800f9b9d1a5SJianfeng Tan 
801f9b9d1a5SJianfeng Tan 	hdr = (void *)(uintptr_t)vring->desc[idx_hdr].addr;
802f9b9d1a5SJianfeng Tan 	if (hdr->class == VIRTIO_NET_CTRL_MQ &&
803f9b9d1a5SJianfeng Tan 	    hdr->cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
804f9b9d1a5SJianfeng Tan 		uint16_t queues;
805f9b9d1a5SJianfeng Tan 
806f9b9d1a5SJianfeng Tan 		queues = *(uint16_t *)(uintptr_t)vring->desc[idx_data].addr;
807f9b9d1a5SJianfeng Tan 		status = virtio_user_handle_mq(dev, queues);
808a76552d4SMarvin Liu 	} else if (hdr->class == VIRTIO_NET_CTRL_RX  ||
809a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_MAC ||
810a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_VLAN) {
811a76552d4SMarvin Liu 		status = 0;
812f9b9d1a5SJianfeng Tan 	}
813f9b9d1a5SJianfeng Tan 
814f9b9d1a5SJianfeng Tan 	/* Update status */
815f9b9d1a5SJianfeng Tan 	*(virtio_net_ctrl_ack *)(uintptr_t)vring->desc[idx_status].addr = status;
816f9b9d1a5SJianfeng Tan 
817f9b9d1a5SJianfeng Tan 	return n_descs;
818f9b9d1a5SJianfeng Tan }
819f9b9d1a5SJianfeng Tan 
82048a44640SJens Freimann static inline int
82148a44640SJens Freimann desc_is_avail(struct vring_packed_desc *desc, bool wrap_counter)
82248a44640SJens Freimann {
8236094557dSJoyce Kong 	uint16_t flags = __atomic_load_n(&desc->flags, __ATOMIC_ACQUIRE);
82412e9e70cSTiwei Bie 
82512e9e70cSTiwei Bie 	return wrap_counter == !!(flags & VRING_PACKED_DESC_F_AVAIL) &&
82612e9e70cSTiwei Bie 		wrap_counter != !!(flags & VRING_PACKED_DESC_F_USED);
82748a44640SJens Freimann }
82848a44640SJens Freimann 
82948a44640SJens Freimann static uint32_t
83045c224e7STiwei Bie virtio_user_handle_ctrl_msg_packed(struct virtio_user_dev *dev,
83148a44640SJens Freimann 				   struct vring_packed *vring,
83248a44640SJens Freimann 				   uint16_t idx_hdr)
83348a44640SJens Freimann {
83448a44640SJens Freimann 	struct virtio_net_ctrl_hdr *hdr;
83548a44640SJens Freimann 	virtio_net_ctrl_ack status = ~0;
83648a44640SJens Freimann 	uint16_t idx_data, idx_status;
83748a44640SJens Freimann 	/* initialize to one, header is first */
83848a44640SJens Freimann 	uint32_t n_descs = 1;
83948a44640SJens Freimann 
84048a44640SJens Freimann 	/* locate desc for header, data, and status */
84148a44640SJens Freimann 	idx_data = idx_hdr + 1;
84248a44640SJens Freimann 	if (idx_data >= dev->queue_size)
84348a44640SJens Freimann 		idx_data -= dev->queue_size;
84448a44640SJens Freimann 
84548a44640SJens Freimann 	n_descs++;
84648a44640SJens Freimann 
84748a44640SJens Freimann 	idx_status = idx_data;
8484cdc4d98STiwei Bie 	while (vring->desc[idx_status].flags & VRING_DESC_F_NEXT) {
84948a44640SJens Freimann 		idx_status++;
85048a44640SJens Freimann 		if (idx_status >= dev->queue_size)
85148a44640SJens Freimann 			idx_status -= dev->queue_size;
85248a44640SJens Freimann 		n_descs++;
85348a44640SJens Freimann 	}
85448a44640SJens Freimann 
8554cdc4d98STiwei Bie 	hdr = (void *)(uintptr_t)vring->desc[idx_hdr].addr;
85648a44640SJens Freimann 	if (hdr->class == VIRTIO_NET_CTRL_MQ &&
85748a44640SJens Freimann 	    hdr->cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
85848a44640SJens Freimann 		uint16_t queues;
85948a44640SJens Freimann 
86048a44640SJens Freimann 		queues = *(uint16_t *)(uintptr_t)
8614cdc4d98STiwei Bie 				vring->desc[idx_data].addr;
86248a44640SJens Freimann 		status = virtio_user_handle_mq(dev, queues);
863a76552d4SMarvin Liu 	} else if (hdr->class == VIRTIO_NET_CTRL_RX  ||
864a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_MAC ||
865a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_VLAN) {
866a76552d4SMarvin Liu 		status = 0;
86748a44640SJens Freimann 	}
86848a44640SJens Freimann 
86948a44640SJens Freimann 	/* Update status */
87048a44640SJens Freimann 	*(virtio_net_ctrl_ack *)(uintptr_t)
8714cdc4d98STiwei Bie 		vring->desc[idx_status].addr = status;
87248a44640SJens Freimann 
87345c224e7STiwei Bie 	/* Update used descriptor */
8744cdc4d98STiwei Bie 	vring->desc[idx_hdr].id = vring->desc[idx_status].id;
8754cdc4d98STiwei Bie 	vring->desc[idx_hdr].len = sizeof(status);
87645c224e7STiwei Bie 
87748a44640SJens Freimann 	return n_descs;
87848a44640SJens Freimann }
87948a44640SJens Freimann 
88048a44640SJens Freimann void
88148a44640SJens Freimann virtio_user_handle_cq_packed(struct virtio_user_dev *dev, uint16_t queue_idx)
88248a44640SJens Freimann {
88348a44640SJens Freimann 	struct virtio_user_queue *vq = &dev->packed_queues[queue_idx];
88448a44640SJens Freimann 	struct vring_packed *vring = &dev->packed_vrings[queue_idx];
88512e9e70cSTiwei Bie 	uint16_t n_descs, flags;
88648a44640SJens Freimann 
8876094557dSJoyce Kong 	/* Perform a load-acquire barrier in desc_is_avail to
8886094557dSJoyce Kong 	 * enforce the ordering between desc flags and desc
8896094557dSJoyce Kong 	 * content.
8906094557dSJoyce Kong 	 */
8914cdc4d98STiwei Bie 	while (desc_is_avail(&vring->desc[vq->used_idx],
89248a44640SJens Freimann 			     vq->used_wrap_counter)) {
89348a44640SJens Freimann 
89445c224e7STiwei Bie 		n_descs = virtio_user_handle_ctrl_msg_packed(dev, vring,
89545c224e7STiwei Bie 				vq->used_idx);
89648a44640SJens Freimann 
89712e9e70cSTiwei Bie 		flags = VRING_DESC_F_WRITE;
89812e9e70cSTiwei Bie 		if (vq->used_wrap_counter)
89912e9e70cSTiwei Bie 			flags |= VRING_PACKED_DESC_F_AVAIL_USED;
90012e9e70cSTiwei Bie 
9012c661d41SJoyce Kong 		__atomic_store_n(&vring->desc[vq->used_idx].flags, flags,
9022c661d41SJoyce Kong 				 __ATOMIC_RELEASE);
90345c224e7STiwei Bie 
90445c224e7STiwei Bie 		vq->used_idx += n_descs;
90545c224e7STiwei Bie 		if (vq->used_idx >= dev->queue_size) {
90648a44640SJens Freimann 			vq->used_idx -= dev->queue_size;
90748a44640SJens Freimann 			vq->used_wrap_counter ^= 1;
90848a44640SJens Freimann 		}
90948a44640SJens Freimann 	}
91048a44640SJens Freimann }
91148a44640SJens Freimann 
912f9b9d1a5SJianfeng Tan void
913f9b9d1a5SJianfeng Tan virtio_user_handle_cq(struct virtio_user_dev *dev, uint16_t queue_idx)
914f9b9d1a5SJianfeng Tan {
915f9b9d1a5SJianfeng Tan 	uint16_t avail_idx, desc_idx;
916f9b9d1a5SJianfeng Tan 	struct vring_used_elem *uep;
917f9b9d1a5SJianfeng Tan 	uint32_t n_descs;
918f9b9d1a5SJianfeng Tan 	struct vring *vring = &dev->vrings[queue_idx];
919f9b9d1a5SJianfeng Tan 
920f9b9d1a5SJianfeng Tan 	/* Consume avail ring, using used ring idx as first one */
921ea5207c1SJoyce Kong 	while (__atomic_load_n(&vring->used->idx, __ATOMIC_RELAXED)
922ea5207c1SJoyce Kong 	       != vring->avail->idx) {
923ea5207c1SJoyce Kong 		avail_idx = __atomic_load_n(&vring->used->idx, __ATOMIC_RELAXED)
924ea5207c1SJoyce Kong 			    & (vring->num - 1);
925f9b9d1a5SJianfeng Tan 		desc_idx = vring->avail->ring[avail_idx];
926f9b9d1a5SJianfeng Tan 
927f9b9d1a5SJianfeng Tan 		n_descs = virtio_user_handle_ctrl_msg(dev, vring, desc_idx);
928f9b9d1a5SJianfeng Tan 
929f9b9d1a5SJianfeng Tan 		/* Update used ring */
930f9b9d1a5SJianfeng Tan 		uep = &vring->used->ring[avail_idx];
9310403e37aSTiwei Bie 		uep->id = desc_idx;
932f9b9d1a5SJianfeng Tan 		uep->len = n_descs;
933f9b9d1a5SJianfeng Tan 
934ea5207c1SJoyce Kong 		__atomic_add_fetch(&vring->used->idx, 1, __ATOMIC_RELAXED);
935f9b9d1a5SJianfeng Tan 	}
936f9b9d1a5SJianfeng Tan }
93757912824SMaxime Coquelin 
93866b45ceaSMaxime Coquelin static void
93966b45ceaSMaxime Coquelin virtio_user_control_queue_notify(struct virtqueue *vq, void *cookie)
94066b45ceaSMaxime Coquelin {
94166b45ceaSMaxime Coquelin 	struct virtio_user_dev *dev = cookie;
94266b45ceaSMaxime Coquelin 	uint64_t buf = 1;
94366b45ceaSMaxime Coquelin 
94466b45ceaSMaxime Coquelin 	if (write(dev->kickfds[vq->vq_queue_index], &buf, sizeof(buf)) < 0)
94566b45ceaSMaxime Coquelin 		PMD_DRV_LOG(ERR, "failed to kick backend: %s",
94666b45ceaSMaxime Coquelin 			    strerror(errno));
94766b45ceaSMaxime Coquelin }
94866b45ceaSMaxime Coquelin 
94966b45ceaSMaxime Coquelin int
95066b45ceaSMaxime Coquelin virtio_user_dev_create_shadow_cvq(struct virtio_user_dev *dev, struct virtqueue *vq)
95166b45ceaSMaxime Coquelin {
95266b45ceaSMaxime Coquelin 	char name[VIRTQUEUE_MAX_NAME_SZ];
95366b45ceaSMaxime Coquelin 	struct virtqueue *scvq;
95466b45ceaSMaxime Coquelin 
95566b45ceaSMaxime Coquelin 	snprintf(name, sizeof(name), "port%d_shadow_cvq", vq->hw->port_id);
95666b45ceaSMaxime Coquelin 	scvq = virtqueue_alloc(&dev->hw, vq->vq_queue_index, vq->vq_nentries,
95766b45ceaSMaxime Coquelin 			VTNET_CQ, SOCKET_ID_ANY, name);
95866b45ceaSMaxime Coquelin 	if (!scvq) {
95966b45ceaSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to alloc shadow control vq\n", dev->path);
96066b45ceaSMaxime Coquelin 		return -ENOMEM;
96166b45ceaSMaxime Coquelin 	}
96266b45ceaSMaxime Coquelin 
96366b45ceaSMaxime Coquelin 	scvq->cq.notify_queue = &virtio_user_control_queue_notify;
96466b45ceaSMaxime Coquelin 	scvq->cq.notify_cookie = dev;
96566b45ceaSMaxime Coquelin 	dev->scvq = scvq;
96666b45ceaSMaxime Coquelin 
96766b45ceaSMaxime Coquelin 	return 0;
96866b45ceaSMaxime Coquelin }
96966b45ceaSMaxime Coquelin 
97066b45ceaSMaxime Coquelin void
97166b45ceaSMaxime Coquelin virtio_user_dev_destroy_shadow_cvq(struct virtio_user_dev *dev)
97266b45ceaSMaxime Coquelin {
97366b45ceaSMaxime Coquelin 	if (!dev->scvq)
97466b45ceaSMaxime Coquelin 		return;
97566b45ceaSMaxime Coquelin 
97666b45ceaSMaxime Coquelin 	virtqueue_free(dev->scvq);
97766b45ceaSMaxime Coquelin 	dev->scvq = NULL;
97866b45ceaSMaxime Coquelin }
97966b45ceaSMaxime Coquelin 
98057912824SMaxime Coquelin int
981d7e10ea9SAdrian Moreno virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status)
98257912824SMaxime Coquelin {
98357912824SMaxime Coquelin 	int ret;
98457912824SMaxime Coquelin 
985d7e10ea9SAdrian Moreno 	pthread_mutex_lock(&dev->mutex);
986d7e10ea9SAdrian Moreno 	dev->status = status;
9878723c894SMaxime Coquelin 	ret = dev->ops->set_status(dev, status);
988a3fb6b1dSMaxime Coquelin 	if (ret && ret != -ENOTSUP)
989f3854ebaSThomas Monjalon 		PMD_INIT_LOG(ERR, "(%s) Failed to set backend status", dev->path);
990d7e10ea9SAdrian Moreno 
991d7e10ea9SAdrian Moreno 	pthread_mutex_unlock(&dev->mutex);
9925043a060SAdrian Moreno 	return ret;
99357912824SMaxime Coquelin }
9940b0dc66cSAdrian Moreno 
9950b0dc66cSAdrian Moreno int
996d7e10ea9SAdrian Moreno virtio_user_dev_update_status(struct virtio_user_dev *dev)
9970b0dc66cSAdrian Moreno {
9988723c894SMaxime Coquelin 	int ret;
9997784e977SMaxime Coquelin 	uint8_t status;
10000b0dc66cSAdrian Moreno 
1001d7e10ea9SAdrian Moreno 	pthread_mutex_lock(&dev->mutex);
10027784e977SMaxime Coquelin 
10038723c894SMaxime Coquelin 	ret = dev->ops->get_status(dev, &status);
10048723c894SMaxime Coquelin 	if (!ret) {
10057784e977SMaxime Coquelin 		dev->status = status;
10060b0dc66cSAdrian Moreno 		PMD_INIT_LOG(DEBUG, "Updated Device Status(0x%08x):\n"
10070b0dc66cSAdrian Moreno 			"\t-RESET: %u\n"
10080b0dc66cSAdrian Moreno 			"\t-ACKNOWLEDGE: %u\n"
10090b0dc66cSAdrian Moreno 			"\t-DRIVER: %u\n"
10100b0dc66cSAdrian Moreno 			"\t-DRIVER_OK: %u\n"
10110b0dc66cSAdrian Moreno 			"\t-FEATURES_OK: %u\n"
10120b0dc66cSAdrian Moreno 			"\t-DEVICE_NEED_RESET: %u\n"
1013f3854ebaSThomas Monjalon 			"\t-FAILED: %u",
10140b0dc66cSAdrian Moreno 			dev->status,
10150b0dc66cSAdrian Moreno 			(dev->status == VIRTIO_CONFIG_STATUS_RESET),
10160b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_ACK),
10170b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER),
10180b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK),
10190b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_FEATURES_OK),
10200b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_DEV_NEED_RESET),
10210b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_FAILED));
10228723c894SMaxime Coquelin 	} else if (ret != -ENOTSUP) {
1023f3854ebaSThomas Monjalon 		PMD_INIT_LOG(ERR, "(%s) Failed to get backend status", dev->path);
10245043a060SAdrian Moreno 	}
10255043a060SAdrian Moreno 
1026d7e10ea9SAdrian Moreno 	pthread_mutex_unlock(&dev->mutex);
10278723c894SMaxime Coquelin 	return ret;
10280b0dc66cSAdrian Moreno }
102994973531SMaxime Coquelin 
103094973531SMaxime Coquelin int
103194973531SMaxime Coquelin virtio_user_dev_update_link_state(struct virtio_user_dev *dev)
103294973531SMaxime Coquelin {
103394973531SMaxime Coquelin 	if (dev->ops->update_link_state)
103494973531SMaxime Coquelin 		return dev->ops->update_link_state(dev);
103594973531SMaxime Coquelin 
103694973531SMaxime Coquelin 	return 0;
103794973531SMaxime Coquelin }
103894973531SMaxime Coquelin 
103994973531SMaxime Coquelin static void
104094973531SMaxime Coquelin virtio_user_dev_reset_queues_packed(struct rte_eth_dev *eth_dev)
104194973531SMaxime Coquelin {
104294973531SMaxime Coquelin 	struct virtio_user_dev *dev = eth_dev->data->dev_private;
104394973531SMaxime Coquelin 	struct virtio_hw *hw = &dev->hw;
104494973531SMaxime Coquelin 	struct virtnet_rx *rxvq;
104594973531SMaxime Coquelin 	struct virtnet_tx *txvq;
104694973531SMaxime Coquelin 	uint16_t i;
104794973531SMaxime Coquelin 
104894973531SMaxime Coquelin 	/* Add lock to avoid queue contention. */
104994973531SMaxime Coquelin 	rte_spinlock_lock(&hw->state_lock);
105094973531SMaxime Coquelin 	hw->started = 0;
105194973531SMaxime Coquelin 
105294973531SMaxime Coquelin 	/*
105394973531SMaxime Coquelin 	 * Waiting for datapath to complete before resetting queues.
105494973531SMaxime Coquelin 	 * 1 ms should be enough for the ongoing Tx/Rx function to finish.
105594973531SMaxime Coquelin 	 */
105694973531SMaxime Coquelin 	rte_delay_ms(1);
105794973531SMaxime Coquelin 
105894973531SMaxime Coquelin 	/* Vring reset for each Tx queue and Rx queue. */
105994973531SMaxime Coquelin 	for (i = 0; i < eth_dev->data->nb_rx_queues; i++) {
106094973531SMaxime Coquelin 		rxvq = eth_dev->data->rx_queues[i];
10613169550fSMaxime Coquelin 		virtqueue_rxvq_reset_packed(virtnet_rxq_to_vq(rxvq));
106294973531SMaxime Coquelin 		virtio_dev_rx_queue_setup_finish(eth_dev, i);
106394973531SMaxime Coquelin 	}
106494973531SMaxime Coquelin 
106594973531SMaxime Coquelin 	for (i = 0; i < eth_dev->data->nb_tx_queues; i++) {
106694973531SMaxime Coquelin 		txvq = eth_dev->data->tx_queues[i];
10673169550fSMaxime Coquelin 		virtqueue_txvq_reset_packed(virtnet_txq_to_vq(txvq));
106894973531SMaxime Coquelin 	}
106994973531SMaxime Coquelin 
107094973531SMaxime Coquelin 	hw->started = 1;
107194973531SMaxime Coquelin 	rte_spinlock_unlock(&hw->state_lock);
107294973531SMaxime Coquelin }
107394973531SMaxime Coquelin 
107494973531SMaxime Coquelin void
107523abee9dSIlya Maximets virtio_user_dev_delayed_disconnect_handler(void *param)
107694973531SMaxime Coquelin {
107794973531SMaxime Coquelin 	struct virtio_user_dev *dev = param;
10786564ddcdSDavid Marchand 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
107994973531SMaxime Coquelin 
108094973531SMaxime Coquelin 	if (rte_intr_disable(eth_dev->intr_handle) < 0) {
108194973531SMaxime Coquelin 		PMD_DRV_LOG(ERR, "interrupt disable failed");
108294973531SMaxime Coquelin 		return;
108394973531SMaxime Coquelin 	}
108423abee9dSIlya Maximets 	PMD_DRV_LOG(DEBUG, "Unregistering intr fd: %d",
1085d61138d4SHarman Kalra 		    rte_intr_fd_get(eth_dev->intr_handle));
108623abee9dSIlya Maximets 	if (rte_intr_callback_unregister(eth_dev->intr_handle,
108723abee9dSIlya Maximets 					 virtio_interrupt_handler,
108823abee9dSIlya Maximets 					 eth_dev) != 1)
108923abee9dSIlya Maximets 		PMD_DRV_LOG(ERR, "interrupt unregister failed");
109023abee9dSIlya Maximets 
109194973531SMaxime Coquelin 	if (dev->is_server) {
109294973531SMaxime Coquelin 		if (dev->ops->server_disconnect)
109394973531SMaxime Coquelin 			dev->ops->server_disconnect(dev);
109423abee9dSIlya Maximets 
1095d61138d4SHarman Kalra 		rte_intr_fd_set(eth_dev->intr_handle,
1096d61138d4SHarman Kalra 			dev->ops->get_intr_fd(dev));
109723abee9dSIlya Maximets 
109823abee9dSIlya Maximets 		PMD_DRV_LOG(DEBUG, "Registering intr fd: %d",
1099d61138d4SHarman Kalra 			    rte_intr_fd_get(eth_dev->intr_handle));
110023abee9dSIlya Maximets 
110123abee9dSIlya Maximets 		if (rte_intr_callback_register(eth_dev->intr_handle,
110223abee9dSIlya Maximets 					       virtio_interrupt_handler,
110323abee9dSIlya Maximets 					       eth_dev))
110423abee9dSIlya Maximets 			PMD_DRV_LOG(ERR, "interrupt register failed");
110523abee9dSIlya Maximets 
110694973531SMaxime Coquelin 		if (rte_intr_enable(eth_dev->intr_handle) < 0) {
110794973531SMaxime Coquelin 			PMD_DRV_LOG(ERR, "interrupt enable failed");
110894973531SMaxime Coquelin 			return;
110994973531SMaxime Coquelin 		}
111094973531SMaxime Coquelin 	}
111194973531SMaxime Coquelin }
111294973531SMaxime Coquelin 
111323abee9dSIlya Maximets static void
111423abee9dSIlya Maximets virtio_user_dev_delayed_intr_reconfig_handler(void *param)
111523abee9dSIlya Maximets {
111623abee9dSIlya Maximets 	struct virtio_user_dev *dev = param;
111723abee9dSIlya Maximets 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
111823abee9dSIlya Maximets 
111923abee9dSIlya Maximets 	PMD_DRV_LOG(DEBUG, "Unregistering intr fd: %d",
1120d61138d4SHarman Kalra 		    rte_intr_fd_get(eth_dev->intr_handle));
112123abee9dSIlya Maximets 
112223abee9dSIlya Maximets 	if (rte_intr_callback_unregister(eth_dev->intr_handle,
112323abee9dSIlya Maximets 					 virtio_interrupt_handler,
112423abee9dSIlya Maximets 					 eth_dev) != 1)
112523abee9dSIlya Maximets 		PMD_DRV_LOG(ERR, "interrupt unregister failed");
112623abee9dSIlya Maximets 
1127d61138d4SHarman Kalra 	rte_intr_fd_set(eth_dev->intr_handle, dev->ops->get_intr_fd(dev));
112823abee9dSIlya Maximets 
1129d61138d4SHarman Kalra 	PMD_DRV_LOG(DEBUG, "Registering intr fd: %d",
1130d61138d4SHarman Kalra 		    rte_intr_fd_get(eth_dev->intr_handle));
113123abee9dSIlya Maximets 
113223abee9dSIlya Maximets 	if (rte_intr_callback_register(eth_dev->intr_handle,
113323abee9dSIlya Maximets 				       virtio_interrupt_handler, eth_dev))
113423abee9dSIlya Maximets 		PMD_DRV_LOG(ERR, "interrupt register failed");
113523abee9dSIlya Maximets 
113623abee9dSIlya Maximets 	if (rte_intr_enable(eth_dev->intr_handle) < 0)
113723abee9dSIlya Maximets 		PMD_DRV_LOG(ERR, "interrupt enable failed");
113823abee9dSIlya Maximets }
113923abee9dSIlya Maximets 
114094973531SMaxime Coquelin int
114194973531SMaxime Coquelin virtio_user_dev_server_reconnect(struct virtio_user_dev *dev)
114294973531SMaxime Coquelin {
114394973531SMaxime Coquelin 	int ret, old_status;
11446564ddcdSDavid Marchand 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
114594973531SMaxime Coquelin 	struct virtio_hw *hw = &dev->hw;
114694973531SMaxime Coquelin 
114794973531SMaxime Coquelin 	if (!dev->ops->server_reconnect) {
114894973531SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Missing server reconnect callback", dev->path);
114994973531SMaxime Coquelin 		return -1;
115094973531SMaxime Coquelin 	}
115194973531SMaxime Coquelin 
115294973531SMaxime Coquelin 	if (dev->ops->server_reconnect(dev)) {
115394973531SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Reconnect callback call failed", dev->path);
115494973531SMaxime Coquelin 		return -1;
115594973531SMaxime Coquelin 	}
115694973531SMaxime Coquelin 
115794973531SMaxime Coquelin 	old_status = dev->status;
115894973531SMaxime Coquelin 
115994973531SMaxime Coquelin 	virtio_reset(hw);
116094973531SMaxime Coquelin 
116194973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_ACK);
116294973531SMaxime Coquelin 
116394973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER);
116494973531SMaxime Coquelin 
116594973531SMaxime Coquelin 	if (dev->ops->get_features(dev, &dev->device_features) < 0) {
116694973531SMaxime Coquelin 		PMD_INIT_LOG(ERR, "get_features failed: %s",
116794973531SMaxime Coquelin 			     strerror(errno));
116894973531SMaxime Coquelin 		return -1;
116994973531SMaxime Coquelin 	}
117094973531SMaxime Coquelin 
117194973531SMaxime Coquelin 	/* unmask vhost-user unsupported features */
117294973531SMaxime Coquelin 	dev->device_features &= ~(dev->unsupported_features);
117394973531SMaxime Coquelin 
1174f078c2f0SMaxime Coquelin 	dev->features &= (dev->device_features | dev->frontend_features);
117594973531SMaxime Coquelin 
117694973531SMaxime Coquelin 	/* For packed ring, resetting queues is required in reconnection. */
117794973531SMaxime Coquelin 	if (virtio_with_packed_queue(hw) &&
117894973531SMaxime Coquelin 	   (old_status & VIRTIO_CONFIG_STATUS_DRIVER_OK)) {
117994973531SMaxime Coquelin 		PMD_INIT_LOG(NOTICE, "Packets on the fly will be dropped"
118094973531SMaxime Coquelin 				" when packed ring reconnecting.");
118194973531SMaxime Coquelin 		virtio_user_dev_reset_queues_packed(eth_dev);
118294973531SMaxime Coquelin 	}
118394973531SMaxime Coquelin 
118494973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_FEATURES_OK);
118594973531SMaxime Coquelin 
118694973531SMaxime Coquelin 	/* Start the device */
118794973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
118894973531SMaxime Coquelin 	if (!dev->started)
118994973531SMaxime Coquelin 		return -1;
119094973531SMaxime Coquelin 
119194973531SMaxime Coquelin 	if (dev->queue_pairs > 1) {
119294973531SMaxime Coquelin 		ret = virtio_user_handle_mq(dev, dev->queue_pairs);
119394973531SMaxime Coquelin 		if (ret != 0) {
119494973531SMaxime Coquelin 			PMD_INIT_LOG(ERR, "Fails to enable multi-queue pairs!");
119594973531SMaxime Coquelin 			return -1;
119694973531SMaxime Coquelin 		}
119794973531SMaxime Coquelin 	}
119894973531SMaxime Coquelin 	if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC) {
119994973531SMaxime Coquelin 		if (rte_intr_disable(eth_dev->intr_handle) < 0) {
120094973531SMaxime Coquelin 			PMD_DRV_LOG(ERR, "interrupt disable failed");
120194973531SMaxime Coquelin 			return -1;
120294973531SMaxime Coquelin 		}
120323abee9dSIlya Maximets 		/*
120423abee9dSIlya Maximets 		 * This function can be called from the interrupt handler, so
120523abee9dSIlya Maximets 		 * we can't unregister interrupt handler here.  Setting
120623abee9dSIlya Maximets 		 * alarm to do that later.
120723abee9dSIlya Maximets 		 */
120823abee9dSIlya Maximets 		rte_eal_alarm_set(1,
120923abee9dSIlya Maximets 			virtio_user_dev_delayed_intr_reconfig_handler,
121023abee9dSIlya Maximets 			(void *)dev);
121194973531SMaxime Coquelin 	}
121294973531SMaxime Coquelin 	PMD_INIT_LOG(NOTICE, "server mode virtio-user reconnection succeeds!");
121394973531SMaxime Coquelin 	return 0;
121494973531SMaxime Coquelin }
1215