xref: /dpdk/drivers/net/virtio/virtio_user/virtio_user_dev.c (revision 1af8b0b2747fe6c6267fa7bedb602e569742362e)
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>
206fdf32d1SMaxime Coquelin #include <rte_malloc.h>
210fd27826SSrujana Challa #include <rte_io.h>
227ff26957STiwei Bie 
2337a7eb2aSJianfeng Tan #include "vhost.h"
24b8094774SMaxime Coquelin #include "virtio.h"
2537a7eb2aSJianfeng Tan #include "virtio_user_dev.h"
2637a7eb2aSJianfeng Tan #include "../virtio_ethdev.h"
2737a7eb2aSJianfeng Tan 
2812ecb2f6SMaxime Coquelin #define VIRTIO_USER_MEM_EVENT_CLB_NAME "virtio_user_mem_event_clb"
2912ecb2f6SMaxime Coquelin 
30b0395dc8SAdrian Moreno const char * const virtio_user_backend_strings[] = {
31b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_UNKNOWN] = "VIRTIO_USER_BACKEND_UNKNOWN",
32b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_VHOST_USER] = "VHOST_USER",
33b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_VHOST_KERNEL] = "VHOST_NET",
34b0395dc8SAdrian Moreno 	[VIRTIO_USER_BACKEND_VHOST_VDPA] = "VHOST_VDPA",
35b0395dc8SAdrian Moreno };
36b0395dc8SAdrian Moreno 
3737a7eb2aSJianfeng Tan static int
384de6c17aSMaxime Coquelin virtio_user_uninit_notify_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
394de6c17aSMaxime Coquelin {
404de6c17aSMaxime Coquelin 	if (dev->kickfds[queue_sel] >= 0) {
414de6c17aSMaxime Coquelin 		close(dev->kickfds[queue_sel]);
424de6c17aSMaxime Coquelin 		dev->kickfds[queue_sel] = -1;
434de6c17aSMaxime Coquelin 	}
444de6c17aSMaxime Coquelin 
454de6c17aSMaxime Coquelin 	if (dev->callfds[queue_sel] >= 0) {
464de6c17aSMaxime Coquelin 		close(dev->callfds[queue_sel]);
474de6c17aSMaxime Coquelin 		dev->callfds[queue_sel] = -1;
484de6c17aSMaxime Coquelin 	}
494de6c17aSMaxime Coquelin 
504de6c17aSMaxime Coquelin 	return 0;
514de6c17aSMaxime Coquelin }
524de6c17aSMaxime Coquelin 
534de6c17aSMaxime Coquelin static int
544de6c17aSMaxime Coquelin virtio_user_init_notify_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
554de6c17aSMaxime Coquelin {
564de6c17aSMaxime Coquelin 	/* May use invalid flag, but some backend uses kickfd and
574de6c17aSMaxime Coquelin 	 * callfd as criteria to judge if dev is alive. so finally we
584de6c17aSMaxime Coquelin 	 * use real event_fd.
594de6c17aSMaxime Coquelin 	 */
604de6c17aSMaxime Coquelin 	dev->callfds[queue_sel] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
614de6c17aSMaxime Coquelin 	if (dev->callfds[queue_sel] < 0) {
624de6c17aSMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Failed to setup callfd for queue %u: %s",
634de6c17aSMaxime Coquelin 				dev->path, queue_sel, strerror(errno));
644de6c17aSMaxime Coquelin 		return -1;
654de6c17aSMaxime Coquelin 	}
664de6c17aSMaxime Coquelin 	dev->kickfds[queue_sel] = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
674de6c17aSMaxime Coquelin 	if (dev->kickfds[queue_sel] < 0) {
684de6c17aSMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Failed to setup kickfd for queue %u: %s",
694de6c17aSMaxime Coquelin 				dev->path, queue_sel, strerror(errno));
704de6c17aSMaxime Coquelin 		return -1;
714de6c17aSMaxime Coquelin 	}
724de6c17aSMaxime Coquelin 
734de6c17aSMaxime Coquelin 	return 0;
744de6c17aSMaxime Coquelin }
754de6c17aSMaxime Coquelin 
764de6c17aSMaxime Coquelin static int
77bffcdad5SMaxime Coquelin virtio_user_destroy_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
78bffcdad5SMaxime Coquelin {
79bffcdad5SMaxime Coquelin 	struct vhost_vring_state state;
80bffcdad5SMaxime Coquelin 	int ret;
81bffcdad5SMaxime Coquelin 
82bffcdad5SMaxime Coquelin 	state.index = queue_sel;
83bffcdad5SMaxime Coquelin 	ret = dev->ops->get_vring_base(dev, &state);
84bffcdad5SMaxime Coquelin 	if (ret < 0) {
85bffcdad5SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Failed to destroy queue %u", dev->path, queue_sel);
86bffcdad5SMaxime Coquelin 		return -1;
87bffcdad5SMaxime Coquelin 	}
88bffcdad5SMaxime Coquelin 
89bffcdad5SMaxime Coquelin 	return 0;
90bffcdad5SMaxime Coquelin }
91bffcdad5SMaxime Coquelin 
92bffcdad5SMaxime Coquelin static int
9357ae79a7SJianfeng Tan virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
9457ae79a7SJianfeng Tan {
9557ae79a7SJianfeng Tan 	/* Of all per virtqueue MSGs, make sure VHOST_SET_VRING_CALL come
9657ae79a7SJianfeng Tan 	 * firstly because vhost depends on this msg to allocate virtqueue
9757ae79a7SJianfeng Tan 	 * pair.
9857ae79a7SJianfeng Tan 	 */
9957ae79a7SJianfeng Tan 	struct vhost_vring_file file;
100a3fb6b1dSMaxime Coquelin 	int ret;
10157ae79a7SJianfeng Tan 
10257ae79a7SJianfeng Tan 	file.index = queue_sel;
103e6e7ad8bSJianfeng Tan 	file.fd = dev->callfds[queue_sel];
104a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_call(dev, &file);
105a3fb6b1dSMaxime Coquelin 	if (ret < 0) {
106f3854ebaSThomas Monjalon 		PMD_INIT_LOG(ERR, "(%s) Failed to create queue %u", dev->path, queue_sel);
107a3fb6b1dSMaxime Coquelin 		return -1;
108a3fb6b1dSMaxime Coquelin 	}
10957ae79a7SJianfeng Tan 
11057ae79a7SJianfeng Tan 	return 0;
11157ae79a7SJianfeng Tan }
11257ae79a7SJianfeng Tan 
11357ae79a7SJianfeng Tan static int
11437a7eb2aSJianfeng Tan virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
11537a7eb2aSJianfeng Tan {
116a3fb6b1dSMaxime Coquelin 	int ret;
11737a7eb2aSJianfeng Tan 	struct vhost_vring_file file;
11837a7eb2aSJianfeng Tan 	struct vhost_vring_state state;
1196fdf32d1SMaxime Coquelin 	struct vring *vring = &dev->vrings.split[queue_sel];
1206fdf32d1SMaxime Coquelin 	struct vring_packed *pq_vring = &dev->vrings.packed[queue_sel];
121666ef294SSrujana Challa 	uint64_t desc_addr, avail_addr, used_addr;
12237a7eb2aSJianfeng Tan 	struct vhost_vring_addr addr = {
12337a7eb2aSJianfeng Tan 		.index = queue_sel,
12437a7eb2aSJianfeng Tan 		.log_guest_addr = 0,
12537a7eb2aSJianfeng Tan 		.flags = 0, /* disable log */
12637a7eb2aSJianfeng Tan 	};
12737a7eb2aSJianfeng Tan 
12890966e8eSMaxime Coquelin 	if (queue_sel == dev->max_queue_pairs * 2) {
12990966e8eSMaxime Coquelin 		if (!dev->scvq) {
13090966e8eSMaxime Coquelin 			PMD_INIT_LOG(ERR, "(%s) Shadow control queue expected but missing",
13190966e8eSMaxime Coquelin 					dev->path);
13290966e8eSMaxime Coquelin 			goto err;
13390966e8eSMaxime Coquelin 		}
13490966e8eSMaxime Coquelin 
13590966e8eSMaxime Coquelin 		/* Use shadow control queue information */
13690966e8eSMaxime Coquelin 		vring = &dev->scvq->vq_split.ring;
13790966e8eSMaxime Coquelin 		pq_vring = &dev->scvq->vq_packed.ring;
13890966e8eSMaxime Coquelin 	}
13990966e8eSMaxime Coquelin 
14048a44640SJens Freimann 	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED)) {
141666ef294SSrujana Challa 		desc_addr = pq_vring->desc_iova;
142666ef294SSrujana Challa 		avail_addr = desc_addr + pq_vring->num * sizeof(struct vring_packed_desc);
143666ef294SSrujana Challa 		used_addr =  RTE_ALIGN_CEIL(avail_addr + sizeof(struct vring_packed_desc_event),
144666ef294SSrujana Challa 					    VIRTIO_VRING_ALIGN);
145666ef294SSrujana Challa 
146666ef294SSrujana Challa 		addr.desc_user_addr = desc_addr;
147666ef294SSrujana Challa 		addr.avail_user_addr = avail_addr;
148666ef294SSrujana Challa 		addr.used_user_addr = used_addr;
14948a44640SJens Freimann 	} else {
150666ef294SSrujana Challa 		desc_addr = vring->desc_iova;
151666ef294SSrujana Challa 		avail_addr = desc_addr + vring->num * sizeof(struct vring_desc);
152666ef294SSrujana Challa 		used_addr = RTE_ALIGN_CEIL((uintptr_t)(&vring->avail->ring[vring->num]),
153666ef294SSrujana Challa 					   VIRTIO_VRING_ALIGN);
154666ef294SSrujana Challa 
155666ef294SSrujana Challa 		addr.desc_user_addr = desc_addr;
156666ef294SSrujana Challa 		addr.avail_user_addr = avail_addr;
157666ef294SSrujana Challa 		addr.used_user_addr = used_addr;
15848a44640SJens Freimann 	}
15948a44640SJens Freimann 
16037a7eb2aSJianfeng Tan 	state.index = queue_sel;
16137a7eb2aSJianfeng Tan 	state.num = vring->num;
162a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_num(dev, &state);
163a3fb6b1dSMaxime Coquelin 	if (ret < 0)
164a3fb6b1dSMaxime Coquelin 		goto err;
16537a7eb2aSJianfeng Tan 
166be7a4707SJianfeng Tan 	state.index = queue_sel;
16737a7eb2aSJianfeng Tan 	state.num = 0; /* no reservation */
16834f3966cSYuanhan Liu 	if (dev->features & (1ULL << VIRTIO_F_RING_PACKED))
16934f3966cSYuanhan Liu 		state.num |= (1 << 15);
170a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_base(dev, &state);
171a3fb6b1dSMaxime Coquelin 	if (ret < 0)
172a3fb6b1dSMaxime Coquelin 		goto err;
17337a7eb2aSJianfeng Tan 
174a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_addr(dev, &addr);
175a3fb6b1dSMaxime Coquelin 	if (ret < 0)
176a3fb6b1dSMaxime Coquelin 		goto err;
17737a7eb2aSJianfeng Tan 
17837a7eb2aSJianfeng Tan 	/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
17937a7eb2aSJianfeng Tan 	 * lastly because vhost depends on this msg to judge if
18037a7eb2aSJianfeng Tan 	 * virtio is ready.
18137a7eb2aSJianfeng Tan 	 */
18257ae79a7SJianfeng Tan 	file.index = queue_sel;
183e6e7ad8bSJianfeng Tan 	file.fd = dev->kickfds[queue_sel];
184a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_vring_kick(dev, &file);
185a3fb6b1dSMaxime Coquelin 	if (ret < 0)
186a3fb6b1dSMaxime Coquelin 		goto err;
18737a7eb2aSJianfeng Tan 
18837a7eb2aSJianfeng Tan 	return 0;
189a3fb6b1dSMaxime Coquelin err:
190f3854ebaSThomas Monjalon 	PMD_INIT_LOG(ERR, "(%s) Failed to kick queue %u", dev->path, queue_sel);
191a3fb6b1dSMaxime Coquelin 
192a3fb6b1dSMaxime Coquelin 	return -1;
19337a7eb2aSJianfeng Tan }
19437a7eb2aSJianfeng Tan 
19557ae79a7SJianfeng Tan static int
1960f5d532aSMaxime Coquelin virtio_user_foreach_queue(struct virtio_user_dev *dev,
19757ae79a7SJianfeng Tan 			int (*fn)(struct virtio_user_dev *, uint32_t))
19857ae79a7SJianfeng Tan {
19990966e8eSMaxime Coquelin 	uint32_t i, nr_vq;
20057ae79a7SJianfeng Tan 
20190966e8eSMaxime Coquelin 	nr_vq = dev->max_queue_pairs * 2;
20290966e8eSMaxime Coquelin 	if (dev->hw_cvq)
20390966e8eSMaxime Coquelin 		nr_vq++;
20490966e8eSMaxime Coquelin 
2050f5d532aSMaxime Coquelin 	for (i = 0; i < nr_vq; i++)
2060f5d532aSMaxime Coquelin 		if (fn(dev, i) < 0)
20757ae79a7SJianfeng Tan 			return -1;
20857ae79a7SJianfeng Tan 
20957ae79a7SJianfeng Tan 	return 0;
21057ae79a7SJianfeng Tan }
21157ae79a7SJianfeng Tan 
21237a7eb2aSJianfeng Tan int
213844e4683SMaxime Coquelin virtio_user_dev_set_features(struct virtio_user_dev *dev)
21437a7eb2aSJianfeng Tan {
21537a7eb2aSJianfeng Tan 	uint64_t features;
216844e4683SMaxime Coquelin 	int ret = -1;
217844e4683SMaxime Coquelin 
218844e4683SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
219844e4683SMaxime Coquelin 
220844e4683SMaxime Coquelin 	/* Step 0: tell vhost to create queues */
2210f5d532aSMaxime Coquelin 	if (virtio_user_foreach_queue(dev, virtio_user_create_queue) < 0)
222844e4683SMaxime Coquelin 		goto error;
223844e4683SMaxime Coquelin 
224844e4683SMaxime Coquelin 	features = dev->features;
225844e4683SMaxime Coquelin 
226844e4683SMaxime Coquelin 	/* Strip VIRTIO_NET_F_MAC, as MAC address is handled in vdev init */
227844e4683SMaxime Coquelin 	features &= ~(1ull << VIRTIO_NET_F_MAC);
22866b45ceaSMaxime Coquelin 	/* Strip VIRTIO_NET_F_CTRL_VQ if the devices does not really support control VQ */
22966b45ceaSMaxime Coquelin 	if (!dev->hw_cvq)
230844e4683SMaxime Coquelin 		features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
231844e4683SMaxime Coquelin 	features &= ~(1ull << VIRTIO_NET_F_STATUS);
232cc0151b3SMaxime Coquelin 	ret = dev->ops->set_features(dev, features);
233844e4683SMaxime Coquelin 	if (ret < 0)
234844e4683SMaxime Coquelin 		goto error;
235a3fb6b1dSMaxime Coquelin 	PMD_DRV_LOG(INFO, "(%s) set features: 0x%" PRIx64, dev->path, features);
236844e4683SMaxime Coquelin error:
237844e4683SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
238844e4683SMaxime Coquelin 
239844e4683SMaxime Coquelin 	return ret;
240844e4683SMaxime Coquelin }
241844e4683SMaxime Coquelin 
242844e4683SMaxime Coquelin int
243844e4683SMaxime Coquelin virtio_user_start_device(struct virtio_user_dev *dev)
244844e4683SMaxime Coquelin {
24537a7eb2aSJianfeng Tan 	int ret;
24637a7eb2aSJianfeng Tan 
2477ff26957STiwei Bie 	/*
2487ff26957STiwei Bie 	 * XXX workaround!
2497ff26957STiwei Bie 	 *
2507ff26957STiwei Bie 	 * We need to make sure that the locks will be
2517ff26957STiwei Bie 	 * taken in the correct order to avoid deadlocks.
2527ff26957STiwei Bie 	 *
2537ff26957STiwei Bie 	 * Before releasing this lock, this thread should
2547ff26957STiwei Bie 	 * not trigger any memory hotplug events.
2557ff26957STiwei Bie 	 *
2567ff26957STiwei Bie 	 * This is a temporary workaround, and should be
2577ff26957STiwei Bie 	 * replaced when we get proper supports from the
2587ff26957STiwei Bie 	 * memory subsystem in the future.
2597ff26957STiwei Bie 	 */
26076f80881SAnatoly Burakov 	rte_mcfg_mem_read_lock();
26112ecb2f6SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
26212ecb2f6SMaxime Coquelin 
26357ae79a7SJianfeng Tan 	/* Step 2: share memory regions */
264539d910cSMaxime Coquelin 	ret = dev->ops->set_memory_table(dev);
26557ae79a7SJianfeng Tan 	if (ret < 0)
26657ae79a7SJianfeng Tan 		goto error;
26757ae79a7SJianfeng Tan 
26857ae79a7SJianfeng Tan 	/* Step 3: kick queues */
2690f5d532aSMaxime Coquelin 	ret = virtio_user_foreach_queue(dev, virtio_user_kick_queue);
270a3fb6b1dSMaxime Coquelin 	if (ret < 0)
27157ae79a7SJianfeng Tan 		goto error;
27257ae79a7SJianfeng Tan 
27357ae79a7SJianfeng Tan 	/* Step 4: enable queues
27457ae79a7SJianfeng Tan 	 * we enable the 1st queue pair by default.
27557ae79a7SJianfeng Tan 	 */
276a3fb6b1dSMaxime Coquelin 	ret = dev->ops->enable_qp(dev, 0, 1);
277a3fb6b1dSMaxime Coquelin 	if (ret < 0)
278a3fb6b1dSMaxime Coquelin 		goto error;
27957ae79a7SJianfeng Tan 
28058c89415SMaxime Coquelin 	if (dev->scvq) {
28158c89415SMaxime Coquelin 		ret = dev->ops->cvq_enable(dev, 1);
28258c89415SMaxime Coquelin 		if (ret < 0)
28358c89415SMaxime Coquelin 			goto error;
28458c89415SMaxime Coquelin 	}
28558c89415SMaxime Coquelin 
28612ecb2f6SMaxime Coquelin 	dev->started = true;
2879af79db2SMaxime Coquelin 
28812ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
28976f80881SAnatoly Burakov 	rte_mcfg_mem_read_unlock();
29012ecb2f6SMaxime Coquelin 
29137a7eb2aSJianfeng Tan 	return 0;
29237a7eb2aSJianfeng Tan error:
29312ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
29476f80881SAnatoly Burakov 	rte_mcfg_mem_read_unlock();
295a3fb6b1dSMaxime Coquelin 
296f3854ebaSThomas Monjalon 	PMD_INIT_LOG(ERR, "(%s) Failed to start device", dev->path);
297a3fb6b1dSMaxime Coquelin 
29837a7eb2aSJianfeng Tan 	/* TODO: free resource here or caller to check */
29937a7eb2aSJianfeng Tan 	return -1;
30037a7eb2aSJianfeng Tan }
30137a7eb2aSJianfeng Tan 
30237a7eb2aSJianfeng Tan int virtio_user_stop_device(struct virtio_user_dev *dev)
30337a7eb2aSJianfeng Tan {
304c12a26eeSJianfeng Tan 	uint32_t i;
305a3fb6b1dSMaxime Coquelin 	int ret;
306c12a26eeSJianfeng Tan 
30712ecb2f6SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
308f457e900STiwei Bie 	if (!dev->started)
309f457e900STiwei Bie 		goto out;
310f457e900STiwei Bie 
311a3fb6b1dSMaxime Coquelin 	for (i = 0; i < dev->max_queue_pairs; ++i) {
312a3fb6b1dSMaxime Coquelin 		ret = dev->ops->enable_qp(dev, i, 0);
313a3fb6b1dSMaxime Coquelin 		if (ret < 0)
314a3fb6b1dSMaxime Coquelin 			goto err;
315a3fb6b1dSMaxime Coquelin 	}
316c12a26eeSJianfeng Tan 
31758c89415SMaxime Coquelin 	if (dev->scvq) {
31858c89415SMaxime Coquelin 		ret = dev->ops->cvq_enable(dev, 0);
31958c89415SMaxime Coquelin 		if (ret < 0)
32058c89415SMaxime Coquelin 			goto err;
32158c89415SMaxime Coquelin 	}
32258c89415SMaxime Coquelin 
32374dc6746STiwei Bie 	/* Stop the backend. */
324bffcdad5SMaxime Coquelin 	if (virtio_user_foreach_queue(dev, virtio_user_destroy_queue) < 0)
325a3fb6b1dSMaxime Coquelin 		goto err;
32674dc6746STiwei Bie 
32712ecb2f6SMaxime Coquelin 	dev->started = false;
328a3fb6b1dSMaxime Coquelin 
329f457e900STiwei Bie out:
33012ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
3310d6a8752SJianfeng Tan 
332a3fb6b1dSMaxime Coquelin 	return 0;
333a3fb6b1dSMaxime Coquelin err:
334a3fb6b1dSMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
335a3fb6b1dSMaxime Coquelin 
336f3854ebaSThomas Monjalon 	PMD_INIT_LOG(ERR, "(%s) Failed to stop device", dev->path);
337a3fb6b1dSMaxime Coquelin 
338a3fb6b1dSMaxime Coquelin 	return -1;
33937a7eb2aSJianfeng Tan }
34037a7eb2aSJianfeng Tan 
3417be72485SMaxime Coquelin static int
3427be72485SMaxime Coquelin virtio_user_dev_init_max_queue_pairs(struct virtio_user_dev *dev, uint32_t user_max_qp)
3437be72485SMaxime Coquelin {
3447be72485SMaxime Coquelin 	int ret;
3457be72485SMaxime Coquelin 
3467be72485SMaxime Coquelin 	if (!(dev->device_features & (1ULL << VIRTIO_NET_F_MQ))) {
3477be72485SMaxime Coquelin 		dev->max_queue_pairs = 1;
3487be72485SMaxime Coquelin 		return 0;
3497be72485SMaxime Coquelin 	}
3507be72485SMaxime Coquelin 
3517be72485SMaxime Coquelin 	if (!dev->ops->get_config) {
3527be72485SMaxime Coquelin 		dev->max_queue_pairs = user_max_qp;
3537be72485SMaxime Coquelin 		return 0;
3547be72485SMaxime Coquelin 	}
3557be72485SMaxime Coquelin 
3567be72485SMaxime Coquelin 	ret = dev->ops->get_config(dev, (uint8_t *)&dev->max_queue_pairs,
3577be72485SMaxime Coquelin 			offsetof(struct virtio_net_config, max_virtqueue_pairs),
3587be72485SMaxime Coquelin 			sizeof(uint16_t));
3597be72485SMaxime Coquelin 	if (ret) {
3607be72485SMaxime Coquelin 		/*
3617be72485SMaxime Coquelin 		 * We need to know the max queue pair from the device so that
3627be72485SMaxime Coquelin 		 * the control queue gets the right index.
3637be72485SMaxime Coquelin 		 */
3647be72485SMaxime Coquelin 		dev->max_queue_pairs = 1;
3657be72485SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Failed to get max queue pairs from device", dev->path);
3667be72485SMaxime Coquelin 
3677be72485SMaxime Coquelin 		return ret;
3687be72485SMaxime Coquelin 	}
3697be72485SMaxime Coquelin 
3707be72485SMaxime Coquelin 	return 0;
3717be72485SMaxime Coquelin }
3727be72485SMaxime Coquelin 
373c995b005SMaxime Coquelin int
374dbce501eSSrujana Challa virtio_user_dev_get_rss_config(struct virtio_user_dev *dev, void *dst, size_t offset, int length)
375dbce501eSSrujana Challa {
376dbce501eSSrujana Challa 	int ret = 0;
377dbce501eSSrujana Challa 
378dbce501eSSrujana Challa 	if (!(dev->device_features & (1ULL << VIRTIO_NET_F_RSS)))
379dbce501eSSrujana Challa 		return -ENOTSUP;
380dbce501eSSrujana Challa 
381dbce501eSSrujana Challa 	if (!dev->ops->get_config)
382dbce501eSSrujana Challa 		return -ENOTSUP;
383dbce501eSSrujana Challa 
384dbce501eSSrujana Challa 	ret = dev->ops->get_config(dev, dst, offset, length);
385dbce501eSSrujana Challa 	if (ret)
386dbce501eSSrujana Challa 		PMD_DRV_LOG(ERR, "(%s) Failed to get rss config in device", dev->path);
387dbce501eSSrujana Challa 
388dbce501eSSrujana Challa 	return ret;
389dbce501eSSrujana Challa }
390dbce501eSSrujana Challa 
391dbce501eSSrujana Challa int
392c995b005SMaxime Coquelin virtio_user_dev_set_mac(struct virtio_user_dev *dev)
39337a7eb2aSJianfeng Tan {
394c995b005SMaxime Coquelin 	int ret = 0;
39537a7eb2aSJianfeng Tan 
396c995b005SMaxime Coquelin 	if (!(dev->device_features & (1ULL << VIRTIO_NET_F_MAC)))
397c995b005SMaxime Coquelin 		return -ENOTSUP;
39837a7eb2aSJianfeng Tan 
399c995b005SMaxime Coquelin 	if (!dev->ops->set_config)
400c995b005SMaxime Coquelin 		return -ENOTSUP;
401c995b005SMaxime Coquelin 
402c995b005SMaxime Coquelin 	ret = dev->ops->set_config(dev, dev->mac_addr,
403c995b005SMaxime Coquelin 			offsetof(struct virtio_net_config, mac),
404c995b005SMaxime Coquelin 			RTE_ETHER_ADDR_LEN);
405c995b005SMaxime Coquelin 	if (ret)
406c995b005SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Failed to set MAC address in device", dev->path);
407c995b005SMaxime Coquelin 
408c995b005SMaxime Coquelin 	return ret;
40937a7eb2aSJianfeng Tan }
410c995b005SMaxime Coquelin 
411c995b005SMaxime Coquelin int
412c995b005SMaxime Coquelin virtio_user_dev_get_mac(struct virtio_user_dev *dev)
413c995b005SMaxime Coquelin {
414c995b005SMaxime Coquelin 	int ret = 0;
415c995b005SMaxime Coquelin 
416c995b005SMaxime Coquelin 	if (!(dev->device_features & (1ULL << VIRTIO_NET_F_MAC)))
417c995b005SMaxime Coquelin 		return -ENOTSUP;
418c995b005SMaxime Coquelin 
419c995b005SMaxime Coquelin 	if (!dev->ops->get_config)
420c995b005SMaxime Coquelin 		return -ENOTSUP;
421c995b005SMaxime Coquelin 
422c995b005SMaxime Coquelin 	ret = dev->ops->get_config(dev, dev->mac_addr,
423c995b005SMaxime Coquelin 			offsetof(struct virtio_net_config, mac),
424c995b005SMaxime Coquelin 			RTE_ETHER_ADDR_LEN);
425c995b005SMaxime Coquelin 	if (ret)
426c995b005SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Failed to get MAC address from device", dev->path);
427c995b005SMaxime Coquelin 
428c995b005SMaxime Coquelin 	return ret;
429c995b005SMaxime Coquelin }
430c995b005SMaxime Coquelin 
431c995b005SMaxime Coquelin static void
432c995b005SMaxime Coquelin virtio_user_dev_init_mac(struct virtio_user_dev *dev, const char *mac)
433c995b005SMaxime Coquelin {
434c995b005SMaxime Coquelin 	struct rte_ether_addr cmdline_mac;
435c995b005SMaxime Coquelin 	char buf[RTE_ETHER_ADDR_FMT_SIZE];
436c995b005SMaxime Coquelin 	int ret;
437c995b005SMaxime Coquelin 
438c995b005SMaxime Coquelin 	if (mac && rte_ether_unformat_addr(mac, &cmdline_mac) == 0) {
439c995b005SMaxime Coquelin 		/*
440c995b005SMaxime Coquelin 		 * MAC address was passed from command-line, try to store
441c995b005SMaxime Coquelin 		 * it in the device if it supports it. Otherwise try to use
442c995b005SMaxime Coquelin 		 * the device one.
443c995b005SMaxime Coquelin 		 */
444c995b005SMaxime Coquelin 		memcpy(dev->mac_addr, &cmdline_mac, RTE_ETHER_ADDR_LEN);
445c995b005SMaxime Coquelin 		dev->mac_specified = 1;
446c995b005SMaxime Coquelin 
447c995b005SMaxime Coquelin 		/* Setting MAC may fail, continue to get the device one in this case */
448c995b005SMaxime Coquelin 		virtio_user_dev_set_mac(dev);
449c995b005SMaxime Coquelin 		ret = virtio_user_dev_get_mac(dev);
450c995b005SMaxime Coquelin 		if (ret == -ENOTSUP)
451c995b005SMaxime Coquelin 			goto out;
452c995b005SMaxime Coquelin 
453c995b005SMaxime Coquelin 		if (memcmp(&cmdline_mac, dev->mac_addr, RTE_ETHER_ADDR_LEN))
454c995b005SMaxime Coquelin 			PMD_DRV_LOG(INFO, "(%s) Device MAC update failed", dev->path);
455c995b005SMaxime Coquelin 	} else {
456c995b005SMaxime Coquelin 		ret = virtio_user_dev_get_mac(dev);
457c995b005SMaxime Coquelin 		if (ret) {
458c995b005SMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) No valid MAC in devargs or device, use random",
459c995b005SMaxime Coquelin 					dev->path);
460c995b005SMaxime Coquelin 			return;
461c995b005SMaxime Coquelin 		}
462c995b005SMaxime Coquelin 
463c995b005SMaxime Coquelin 		dev->mac_specified = 1;
464c995b005SMaxime Coquelin 	}
465c995b005SMaxime Coquelin out:
466c995b005SMaxime Coquelin 	rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE,
467c995b005SMaxime Coquelin 			(struct rte_ether_addr *)dev->mac_addr);
468c995b005SMaxime Coquelin 	PMD_DRV_LOG(INFO, "(%s) MAC %s specified", dev->path, buf);
46937a7eb2aSJianfeng Tan }
47037a7eb2aSJianfeng Tan 
47133d24d65SJianfeng Tan static int
472e6e7ad8bSJianfeng Tan virtio_user_dev_init_notify(struct virtio_user_dev *dev)
47333d24d65SJianfeng Tan {
47433d24d65SJianfeng Tan 
4754de6c17aSMaxime Coquelin 	if (virtio_user_foreach_queue(dev, virtio_user_init_notify_queue) < 0)
4762e4c1b50SMaxime Coquelin 		goto err;
477e6e7ad8bSJianfeng Tan 
478cd218549SMaxime Coquelin 	if (dev->device_features & (1ULL << VIRTIO_F_NOTIFICATION_DATA))
479cd218549SMaxime Coquelin 		if (dev->ops->map_notification_area &&
480cd218549SMaxime Coquelin 				dev->ops->map_notification_area(dev))
4810fd27826SSrujana Challa 			goto err;
4820fd27826SSrujana Challa 
4832e4c1b50SMaxime Coquelin 	return 0;
4842e4c1b50SMaxime Coquelin err:
4854de6c17aSMaxime Coquelin 	virtio_user_foreach_queue(dev, virtio_user_uninit_notify_queue);
486e6e7ad8bSJianfeng Tan 
487e6e7ad8bSJianfeng Tan 	return -1;
488e6e7ad8bSJianfeng Tan }
489e6e7ad8bSJianfeng Tan 
4902e4c1b50SMaxime Coquelin static void
4912e4c1b50SMaxime Coquelin virtio_user_dev_uninit_notify(struct virtio_user_dev *dev)
4922e4c1b50SMaxime Coquelin {
4934de6c17aSMaxime Coquelin 	virtio_user_foreach_queue(dev, virtio_user_uninit_notify_queue);
4942e4c1b50SMaxime Coquelin 
4950fd27826SSrujana Challa 	if (dev->ops->unmap_notification_area && dev->notify_area)
4960fd27826SSrujana Challa 		dev->ops->unmap_notification_area(dev);
497e6e7ad8bSJianfeng Tan }
498e6e7ad8bSJianfeng Tan 
499e6e7ad8bSJianfeng Tan static int
5003d4fb6fdSJianfeng Tan virtio_user_fill_intr_handle(struct virtio_user_dev *dev)
5013d4fb6fdSJianfeng Tan {
5023d4fb6fdSJianfeng Tan 	uint32_t i;
5036564ddcdSDavid Marchand 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
5043d4fb6fdSJianfeng Tan 
505d61138d4SHarman Kalra 	if (eth_dev->intr_handle == NULL) {
506d61138d4SHarman Kalra 		eth_dev->intr_handle =
507d61138d4SHarman Kalra 			rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
508d61138d4SHarman Kalra 		if (eth_dev->intr_handle == NULL) {
509a3fb6b1dSMaxime Coquelin 			PMD_DRV_LOG(ERR, "(%s) failed to allocate intr_handle", dev->path);
5103d4fb6fdSJianfeng Tan 			return -1;
5113d4fb6fdSJianfeng Tan 		}
5123d4fb6fdSJianfeng Tan 	}
5133d4fb6fdSJianfeng Tan 
514d61138d4SHarman Kalra 	for (i = 0; i < dev->max_queue_pairs; ++i) {
515d61138d4SHarman Kalra 		if (rte_intr_efds_index_set(eth_dev->intr_handle, i,
51623ab0c59SYuan Wang 				dev->callfds[2 * i + VTNET_SQ_RQ_QUEUE_IDX]))
517d61138d4SHarman Kalra 			return -rte_errno;
518d61138d4SHarman Kalra 	}
519d61138d4SHarman Kalra 
520d61138d4SHarman Kalra 	if (rte_intr_nb_efd_set(eth_dev->intr_handle, dev->max_queue_pairs))
521d61138d4SHarman Kalra 		return -rte_errno;
522d61138d4SHarman Kalra 
523d61138d4SHarman Kalra 	if (rte_intr_max_intr_set(eth_dev->intr_handle,
524d61138d4SHarman Kalra 			dev->max_queue_pairs + 1))
525d61138d4SHarman Kalra 		return -rte_errno;
526d61138d4SHarman Kalra 
527d61138d4SHarman Kalra 	if (rte_intr_type_set(eth_dev->intr_handle, RTE_INTR_HANDLE_VDEV))
528d61138d4SHarman Kalra 		return -rte_errno;
529d61138d4SHarman Kalra 
53029906b97SJingjing Wu 	/* For virtio vdev, no need to read counter for clean */
531d61138d4SHarman Kalra 	if (rte_intr_efd_counter_size_set(eth_dev->intr_handle, 0))
532d61138d4SHarman Kalra 		return -rte_errno;
533d61138d4SHarman Kalra 
534d61138d4SHarman Kalra 	if (rte_intr_fd_set(eth_dev->intr_handle, dev->ops->get_intr_fd(dev)))
535d61138d4SHarman Kalra 		return -rte_errno;
5363d4fb6fdSJianfeng Tan 
5373d4fb6fdSJianfeng Tan 	return 0;
5383d4fb6fdSJianfeng Tan }
5393d4fb6fdSJianfeng Tan 
54012ecb2f6SMaxime Coquelin static void
54112ecb2f6SMaxime Coquelin virtio_user_mem_event_cb(enum rte_mem_event type __rte_unused,
5422286291dSTiwei Bie 			 const void *addr,
54312ecb2f6SMaxime Coquelin 			 size_t len __rte_unused,
54412ecb2f6SMaxime Coquelin 			 void *arg)
54512ecb2f6SMaxime Coquelin {
54612ecb2f6SMaxime Coquelin 	struct virtio_user_dev *dev = arg;
547f32c7c9dSAnatoly Burakov 	struct rte_memseg_list *msl;
54812ecb2f6SMaxime Coquelin 	uint16_t i;
549a3fb6b1dSMaxime Coquelin 	int ret = 0;
55012ecb2f6SMaxime Coquelin 
551f32c7c9dSAnatoly Burakov 	/* ignore externally allocated memory */
552f32c7c9dSAnatoly Burakov 	msl = rte_mem_virt2memseg_list(addr);
553f32c7c9dSAnatoly Burakov 	if (msl->external)
554f32c7c9dSAnatoly Burakov 		return;
555f32c7c9dSAnatoly Burakov 
55612ecb2f6SMaxime Coquelin 	pthread_mutex_lock(&dev->mutex);
55712ecb2f6SMaxime Coquelin 
55812ecb2f6SMaxime Coquelin 	if (dev->started == false)
55912ecb2f6SMaxime Coquelin 		goto exit;
56012ecb2f6SMaxime Coquelin 
56112ecb2f6SMaxime Coquelin 	/* Step 1: pause the active queues */
562a3fb6b1dSMaxime Coquelin 	for (i = 0; i < dev->queue_pairs; i++) {
563a3fb6b1dSMaxime Coquelin 		ret = dev->ops->enable_qp(dev, i, 0);
564a3fb6b1dSMaxime Coquelin 		if (ret < 0)
565a3fb6b1dSMaxime Coquelin 			goto exit;
566a3fb6b1dSMaxime Coquelin 	}
56712ecb2f6SMaxime Coquelin 
56812ecb2f6SMaxime Coquelin 	/* Step 2: update memory regions */
569a3fb6b1dSMaxime Coquelin 	ret = dev->ops->set_memory_table(dev);
570a3fb6b1dSMaxime Coquelin 	if (ret < 0)
571a3fb6b1dSMaxime Coquelin 		goto exit;
57212ecb2f6SMaxime Coquelin 
57312ecb2f6SMaxime Coquelin 	/* Step 3: resume the active queues */
574a3fb6b1dSMaxime Coquelin 	for (i = 0; i < dev->queue_pairs; i++) {
575a3fb6b1dSMaxime Coquelin 		ret = dev->ops->enable_qp(dev, i, 1);
576a3fb6b1dSMaxime Coquelin 		if (ret < 0)
577a3fb6b1dSMaxime Coquelin 			goto exit;
578a3fb6b1dSMaxime Coquelin 	}
57912ecb2f6SMaxime Coquelin 
58012ecb2f6SMaxime Coquelin exit:
58112ecb2f6SMaxime Coquelin 	pthread_mutex_unlock(&dev->mutex);
582a3fb6b1dSMaxime Coquelin 
583a3fb6b1dSMaxime Coquelin 	if (ret < 0)
584f3854ebaSThomas Monjalon 		PMD_DRV_LOG(ERR, "(%s) Failed to update memory table", dev->path);
58512ecb2f6SMaxime Coquelin }
58612ecb2f6SMaxime Coquelin 
5873d4fb6fdSJianfeng Tan static int
588e6e7ad8bSJianfeng Tan virtio_user_dev_setup(struct virtio_user_dev *dev)
589e6e7ad8bSJianfeng Tan {
590bd8f50a4SZhiyong Yang 	if (dev->is_server) {
591f908b22eSAdrian Moreno 		if (dev->backend_type != VIRTIO_USER_BACKEND_VHOST_USER) {
592f908b22eSAdrian Moreno 			PMD_DRV_LOG(ERR, "Server mode only supports vhost-user!");
593bd8f50a4SZhiyong Yang 			return -1;
594bd8f50a4SZhiyong Yang 		}
5959af79db2SMaxime Coquelin 	}
5969af79db2SMaxime Coquelin 
59786388a3aSMaxime Coquelin 	switch (dev->backend_type) {
59886388a3aSMaxime Coquelin 	case VIRTIO_USER_BACKEND_VHOST_USER:
599520dd992SFerruh Yigit 		dev->ops = &virtio_ops_user;
60086388a3aSMaxime Coquelin 		break;
60186388a3aSMaxime Coquelin 	case VIRTIO_USER_BACKEND_VHOST_KERNEL:
602520dd992SFerruh Yigit 		dev->ops = &virtio_ops_kernel;
60386388a3aSMaxime Coquelin 		break;
60486388a3aSMaxime Coquelin 	case VIRTIO_USER_BACKEND_VHOST_VDPA:
6056b901437SMaxime Coquelin 		dev->ops = &virtio_ops_vdpa;
60686388a3aSMaxime Coquelin 		break;
60786388a3aSMaxime Coquelin 	default:
608a3fb6b1dSMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Unknown backend type", dev->path);
6096b901437SMaxime Coquelin 		return -1;
610e3b43481SJianfeng Tan 	}
6119af79db2SMaxime Coquelin 
612a3fb6b1dSMaxime Coquelin 	if (dev->ops->setup(dev) < 0) {
613f3854ebaSThomas Monjalon 		PMD_INIT_LOG(ERR, "(%s) Failed to setup backend", dev->path);
614cc4690e9SJianfeng Tan 		return -1;
615a3fb6b1dSMaxime Coquelin 	}
616cc4690e9SJianfeng Tan 
617cc4690e9SJianfeng Tan 	return 0;
61833d24d65SJianfeng Tan }
61933d24d65SJianfeng Tan 
6206fdf32d1SMaxime Coquelin static int
6216fdf32d1SMaxime Coquelin virtio_user_alloc_vrings(struct virtio_user_dev *dev)
6226fdf32d1SMaxime Coquelin {
6236fdf32d1SMaxime Coquelin 	int i, size, nr_vrings;
6246fdf32d1SMaxime Coquelin 	bool packed_ring = !!(dev->device_features & (1ull << VIRTIO_F_RING_PACKED));
6256fdf32d1SMaxime Coquelin 
6266fdf32d1SMaxime Coquelin 	nr_vrings = dev->max_queue_pairs * 2;
6278b04c3e5SMaxime Coquelin 	if (dev->frontend_features & (1ull << VIRTIO_NET_F_CTRL_VQ))
6286fdf32d1SMaxime Coquelin 		nr_vrings++;
6296fdf32d1SMaxime Coquelin 
6306fdf32d1SMaxime Coquelin 	dev->callfds = rte_zmalloc("virtio_user_dev", nr_vrings * sizeof(*dev->callfds), 0);
6316fdf32d1SMaxime Coquelin 	if (!dev->callfds) {
6326fdf32d1SMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to alloc callfds", dev->path);
6336fdf32d1SMaxime Coquelin 		return -1;
6346fdf32d1SMaxime Coquelin 	}
6356fdf32d1SMaxime Coquelin 
6366fdf32d1SMaxime Coquelin 	dev->kickfds = rte_zmalloc("virtio_user_dev", nr_vrings * sizeof(*dev->kickfds), 0);
6376fdf32d1SMaxime Coquelin 	if (!dev->kickfds) {
6386fdf32d1SMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to alloc kickfds", dev->path);
6396fdf32d1SMaxime Coquelin 		goto free_callfds;
6406fdf32d1SMaxime Coquelin 	}
6416fdf32d1SMaxime Coquelin 
6426fdf32d1SMaxime Coquelin 	for (i = 0; i < nr_vrings; i++) {
6436fdf32d1SMaxime Coquelin 		dev->callfds[i] = -1;
6446fdf32d1SMaxime Coquelin 		dev->kickfds[i] = -1;
6456fdf32d1SMaxime Coquelin 	}
6466fdf32d1SMaxime Coquelin 
6476fdf32d1SMaxime Coquelin 	if (packed_ring)
6486fdf32d1SMaxime Coquelin 		size = sizeof(*dev->vrings.packed);
6496fdf32d1SMaxime Coquelin 	else
6506fdf32d1SMaxime Coquelin 		size = sizeof(*dev->vrings.split);
6516fdf32d1SMaxime Coquelin 	dev->vrings.ptr = rte_zmalloc("virtio_user_dev", nr_vrings * size, 0);
6526fdf32d1SMaxime Coquelin 	if (!dev->vrings.ptr) {
6536fdf32d1SMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to alloc vrings metadata", dev->path);
6546fdf32d1SMaxime Coquelin 		goto free_kickfds;
6556fdf32d1SMaxime Coquelin 	}
6566fdf32d1SMaxime Coquelin 
6576fdf32d1SMaxime Coquelin 	if (packed_ring) {
6586fdf32d1SMaxime Coquelin 		dev->packed_queues = rte_zmalloc("virtio_user_dev",
6596fdf32d1SMaxime Coquelin 				nr_vrings * sizeof(*dev->packed_queues), 0);
6606fdf32d1SMaxime Coquelin 		if (!dev->packed_queues) {
6616fdf32d1SMaxime Coquelin 			PMD_INIT_LOG(ERR, "(%s) Failed to alloc packed queues metadata",
6626fdf32d1SMaxime Coquelin 					dev->path);
6636fdf32d1SMaxime Coquelin 			goto free_vrings;
6646fdf32d1SMaxime Coquelin 		}
6656fdf32d1SMaxime Coquelin 	}
6666fdf32d1SMaxime Coquelin 
6676fdf32d1SMaxime Coquelin 	dev->qp_enabled = rte_zmalloc("virtio_user_dev",
6686fdf32d1SMaxime Coquelin 			dev->max_queue_pairs * sizeof(*dev->qp_enabled), 0);
6696fdf32d1SMaxime Coquelin 	if (!dev->qp_enabled) {
6706fdf32d1SMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to alloc QP enable states", dev->path);
6716fdf32d1SMaxime Coquelin 		goto free_packed_queues;
6726fdf32d1SMaxime Coquelin 	}
6736fdf32d1SMaxime Coquelin 
6746fdf32d1SMaxime Coquelin 	return 0;
6756fdf32d1SMaxime Coquelin 
6766fdf32d1SMaxime Coquelin free_packed_queues:
6776fdf32d1SMaxime Coquelin 	rte_free(dev->packed_queues);
6786fdf32d1SMaxime Coquelin 	dev->packed_queues = NULL;
6796fdf32d1SMaxime Coquelin free_vrings:
6806fdf32d1SMaxime Coquelin 	rte_free(dev->vrings.ptr);
6816fdf32d1SMaxime Coquelin 	dev->vrings.ptr = NULL;
6826fdf32d1SMaxime Coquelin free_kickfds:
6836fdf32d1SMaxime Coquelin 	rte_free(dev->kickfds);
6846fdf32d1SMaxime Coquelin 	dev->kickfds = NULL;
6856fdf32d1SMaxime Coquelin free_callfds:
6866fdf32d1SMaxime Coquelin 	rte_free(dev->callfds);
6876fdf32d1SMaxime Coquelin 	dev->callfds = NULL;
6886fdf32d1SMaxime Coquelin 
6896fdf32d1SMaxime Coquelin 	return -1;
6906fdf32d1SMaxime Coquelin }
6916fdf32d1SMaxime Coquelin 
6926fdf32d1SMaxime Coquelin static void
6936fdf32d1SMaxime Coquelin virtio_user_free_vrings(struct virtio_user_dev *dev)
6946fdf32d1SMaxime Coquelin {
6956fdf32d1SMaxime Coquelin 	rte_free(dev->qp_enabled);
6966fdf32d1SMaxime Coquelin 	dev->qp_enabled = NULL;
6976fdf32d1SMaxime Coquelin 	rte_free(dev->packed_queues);
6986fdf32d1SMaxime Coquelin 	dev->packed_queues = NULL;
6996fdf32d1SMaxime Coquelin 	rte_free(dev->vrings.ptr);
7006fdf32d1SMaxime Coquelin 	dev->vrings.ptr = NULL;
7016fdf32d1SMaxime Coquelin 	rte_free(dev->kickfds);
7026fdf32d1SMaxime Coquelin 	dev->kickfds = NULL;
7036fdf32d1SMaxime Coquelin 	rte_free(dev->callfds);
7046fdf32d1SMaxime Coquelin 	dev->callfds = NULL;
7056fdf32d1SMaxime Coquelin }
7066fdf32d1SMaxime Coquelin 
707bed3b24cSJianfeng Tan /* Use below macro to filter features from vhost backend */
708bed3b24cSJianfeng Tan #define VIRTIO_USER_SUPPORTED_FEATURES			\
709bed3b24cSJianfeng Tan 	(1ULL << VIRTIO_NET_F_MAC		|	\
710bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_STATUS		|	\
711bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_MQ		|	\
712bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_MAC_ADDR	|	\
713bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_VQ		|	\
714bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_RX		|	\
715bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CTRL_VLAN		|	\
716bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_CSUM		|	\
717bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_HOST_TSO4		|	\
718bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_HOST_TSO6		|	\
719bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_MRG_RXBUF		|	\
720bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_RING_F_INDIRECT_DESC	|	\
721bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_GUEST_CSUM	|	\
722bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_GUEST_TSO4	|	\
723bed3b24cSJianfeng Tan 	 1ULL << VIRTIO_NET_F_GUEST_TSO6	|	\
72441e45c90SMarvin Liu 	 1ULL << VIRTIO_F_IN_ORDER		|	\
72534f3966cSYuanhan Liu 	 1ULL << VIRTIO_F_VERSION_1		|	\
7260fd27826SSrujana Challa 	 1ULL << VIRTIO_F_RING_PACKED		|	\
727dbce501eSSrujana Challa 	 1ULL << VIRTIO_F_NOTIFICATION_DATA	|	\
728735cc250SNithin Dabilpuram 	 1ULL << VIRTIO_F_ORDER_PLATFORM        |       \
729dbce501eSSrujana Challa 	 1ULL << VIRTIO_NET_F_RSS)
7308e756105SMaxime Coquelin 
73137a7eb2aSJianfeng Tan int
73252901852SMaxime Coquelin virtio_user_dev_init(struct virtio_user_dev *dev, char *path, uint16_t queues,
733488ed97aSMarvin Liu 		     int cq, int queue_size, const char *mac, char **ifname,
734f908b22eSAdrian Moreno 		     int server, int mrg_rxbuf, int in_order, int packed_vq,
735f908b22eSAdrian Moreno 		     enum virtio_user_backend_type backend_type)
73637a7eb2aSJianfeng Tan {
7375b75b63cSMaxime Coquelin 	uint64_t backend_features;
7388e756105SMaxime Coquelin 
73912ecb2f6SMaxime Coquelin 	pthread_mutex_init(&dev->mutex, NULL);
7406723c0fcSBruce Richardson 	strlcpy(dev->path, path, PATH_MAX);
7412e4c1b50SMaxime Coquelin 
74212ecb2f6SMaxime Coquelin 	dev->started = 0;
74337a7eb2aSJianfeng Tan 	dev->queue_pairs = 1; /* mq disabled by default */
744be26e898SMaxime Coquelin 	dev->max_queue_pairs = queues; /* initialize to user requested value for kernel backend */
74537a7eb2aSJianfeng Tan 	dev->queue_size = queue_size;
7461c8489daSTiwei Bie 	dev->is_server = server;
74737a7eb2aSJianfeng Tan 	dev->mac_specified = 0;
748bb97d2ddSTiwei Bie 	dev->frontend_features = 0;
7495b75b63cSMaxime Coquelin 	dev->unsupported_features = 0;
750f908b22eSAdrian Moreno 	dev->backend_type = backend_type;
7514214a1b4SWenfeng Liu 	dev->ifname = *ifname;
7524214a1b4SWenfeng Liu 
75333d24d65SJianfeng Tan 	if (virtio_user_dev_setup(dev) < 0) {
754a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) backend set up fails", dev->path);
75537a7eb2aSJianfeng Tan 		return -1;
75637a7eb2aSJianfeng Tan 	}
757bce7e905SJianfeng Tan 
75806856cabSMaxime Coquelin 	if (dev->ops->set_owner(dev) < 0) {
759a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to set backend owner", dev->path);
7607be72485SMaxime Coquelin 		goto destroy;
76137a7eb2aSJianfeng Tan 	}
76237a7eb2aSJianfeng Tan 
7635b75b63cSMaxime Coquelin 	if (dev->ops->get_backend_features(&backend_features) < 0) {
764a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to get backend features", dev->path);
7657be72485SMaxime Coquelin 		goto destroy;
76637a7eb2aSJianfeng Tan 	}
7678e756105SMaxime Coquelin 
7685b75b63cSMaxime Coquelin 	dev->unsupported_features = ~(VIRTIO_USER_SUPPORTED_FEATURES | backend_features);
7695b75b63cSMaxime Coquelin 
7705b75b63cSMaxime Coquelin 	if (dev->ops->get_features(dev, &dev->device_features) < 0) {
7715b75b63cSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to get device features", dev->path);
7727be72485SMaxime Coquelin 		goto destroy;
773a3fb6b1dSMaxime Coquelin 	}
7748e756105SMaxime Coquelin 
775c995b005SMaxime Coquelin 	virtio_user_dev_init_mac(dev, mac);
776c995b005SMaxime Coquelin 
7777be72485SMaxime Coquelin 	if (virtio_user_dev_init_max_queue_pairs(dev, queues))
7787be72485SMaxime Coquelin 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MQ);
7797be72485SMaxime Coquelin 
78058c89415SMaxime Coquelin 	if (dev->max_queue_pairs > 1 || dev->hw_cvq)
7817be72485SMaxime Coquelin 		cq = 1;
7827be72485SMaxime Coquelin 
783bd9568f3STiwei Bie 	if (!mrg_rxbuf)
784488ed97aSMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MRG_RXBUF);
785488ed97aSMarvin Liu 
786bd9568f3STiwei Bie 	if (!in_order)
787488ed97aSMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_F_IN_ORDER);
788488ed97aSMarvin Liu 
78948a44640SJens Freimann 	if (!packed_vq)
7909070f88bSTiwei Bie 		dev->unsupported_features |= (1ull << VIRTIO_F_RING_PACKED);
79134f3966cSYuanhan Liu 
7929070f88bSTiwei Bie 	if (dev->mac_specified)
7939070f88bSTiwei Bie 		dev->frontend_features |= (1ull << VIRTIO_NET_F_MAC);
7949070f88bSTiwei Bie 	else
7957c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MAC);
796f9b9d1a5SJianfeng Tan 
797142678d4SJianfeng Tan 	if (cq) {
79858c89415SMaxime Coquelin 		/* Except for vDPA, the device does not really need to know
79958c89415SMaxime Coquelin 		 * anything about CQ, so if necessary, we just claim to support
80058c89415SMaxime Coquelin 		 * control queue.
801f9b9d1a5SJianfeng Tan 		 */
802bb97d2ddSTiwei Bie 		dev->frontend_features |= (1ull << VIRTIO_NET_F_CTRL_VQ);
803142678d4SJianfeng Tan 	} else {
8047c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_VQ);
805bd9568f3STiwei Bie 		/* Also disable features that depend on VIRTIO_NET_F_CTRL_VQ */
8067c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_RX);
8077c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_CTRL_VLAN);
8087c66ff61SMarvin Liu 		dev->unsupported_features |=
8097c66ff61SMarvin Liu 			(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE);
8107c66ff61SMarvin Liu 		dev->unsupported_features |= (1ull << VIRTIO_NET_F_MQ);
8117c66ff61SMarvin Liu 		dev->unsupported_features |=
8127c66ff61SMarvin Liu 			(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
813f9b9d1a5SJianfeng Tan 	}
814f9b9d1a5SJianfeng Tan 
81535c4f855SJianfeng Tan 	/* The backend will not report this feature, we add it explicitly */
816f908b22eSAdrian Moreno 	if (dev->backend_type == VIRTIO_USER_BACKEND_VHOST_USER)
817bb97d2ddSTiwei Bie 		dev->frontend_features |= (1ull << VIRTIO_NET_F_STATUS);
81835c4f855SJianfeng Tan 
819f078c2f0SMaxime Coquelin 	dev->frontend_features &= ~dev->unsupported_features;
820bd9568f3STiwei Bie 	dev->device_features &= ~dev->unsupported_features;
821bed3b24cSJianfeng Tan 
8226fdf32d1SMaxime Coquelin 	if (virtio_user_alloc_vrings(dev) < 0) {
8236fdf32d1SMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to allocate vring metadata", dev->path);
8246fdf32d1SMaxime Coquelin 		goto destroy;
8256fdf32d1SMaxime Coquelin 	}
8266fdf32d1SMaxime Coquelin 
8276fdf32d1SMaxime Coquelin 	if (virtio_user_dev_init_notify(dev) < 0) {
8286fdf32d1SMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to init notifiers", dev->path);
8296fdf32d1SMaxime Coquelin 		goto free_vrings;
8306fdf32d1SMaxime Coquelin 	}
8316fdf32d1SMaxime Coquelin 
8326fdf32d1SMaxime Coquelin 	if (virtio_user_fill_intr_handle(dev) < 0) {
8336fdf32d1SMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) Failed to init interrupt handler", dev->path);
8346fdf32d1SMaxime Coquelin 		goto notify_uninit;
8356fdf32d1SMaxime Coquelin 	}
8366fdf32d1SMaxime Coquelin 
83712ecb2f6SMaxime Coquelin 	if (rte_mem_event_callback_register(VIRTIO_USER_MEM_EVENT_CLB_NAME,
83812ecb2f6SMaxime Coquelin 				virtio_user_mem_event_cb, dev)) {
83988e5469fSXiao Wang 		if (rte_errno != ENOTSUP) {
840f3854ebaSThomas Monjalon 			PMD_INIT_LOG(ERR, "(%s) Failed to register mem event callback",
841a3fb6b1dSMaxime Coquelin 					dev->path);
8427be72485SMaxime Coquelin 			goto notify_uninit;
84312ecb2f6SMaxime Coquelin 		}
84488e5469fSXiao Wang 	}
84512ecb2f6SMaxime Coquelin 
846c8254ef1SDavid Marchand 	*ifname = NULL;
84737a7eb2aSJianfeng Tan 	return 0;
8487be72485SMaxime Coquelin 
8497be72485SMaxime Coquelin notify_uninit:
8507be72485SMaxime Coquelin 	virtio_user_dev_uninit_notify(dev);
8516fdf32d1SMaxime Coquelin free_vrings:
8526fdf32d1SMaxime Coquelin 	virtio_user_free_vrings(dev);
8537be72485SMaxime Coquelin destroy:
8547be72485SMaxime Coquelin 	dev->ops->destroy(dev);
8557be72485SMaxime Coquelin 
8567be72485SMaxime Coquelin 	return -1;
85737a7eb2aSJianfeng Tan }
85837a7eb2aSJianfeng Tan 
85937a7eb2aSJianfeng Tan void
86037a7eb2aSJianfeng Tan virtio_user_dev_uninit(struct virtio_user_dev *dev)
86137a7eb2aSJianfeng Tan {
8627b919515SGaoxiang Liu 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
8637b919515SGaoxiang Liu 
864d61138d4SHarman Kalra 	rte_intr_instance_free(eth_dev->intr_handle);
8657b919515SGaoxiang Liu 	eth_dev->intr_handle = NULL;
8667b919515SGaoxiang Liu 
867e3b43481SJianfeng Tan 	virtio_user_stop_device(dev);
868e3b43481SJianfeng Tan 
86912ecb2f6SMaxime Coquelin 	rte_mem_event_callback_unregister(VIRTIO_USER_MEM_EVENT_CLB_NAME, dev);
87012ecb2f6SMaxime Coquelin 
8712e4c1b50SMaxime Coquelin 	virtio_user_dev_uninit_notify(dev);
8724214a1b4SWenfeng Liu 
8736fdf32d1SMaxime Coquelin 	virtio_user_free_vrings(dev);
8746fdf32d1SMaxime Coquelin 
8754214a1b4SWenfeng Liu 	free(dev->ifname);
876bd8f50a4SZhiyong Yang 
877bd8f50a4SZhiyong Yang 	if (dev->is_server)
878bd8f50a4SZhiyong Yang 		unlink(dev->path);
879748e5ea5SMaxime Coquelin 
880748e5ea5SMaxime Coquelin 	dev->ops->destroy(dev);
88137a7eb2aSJianfeng Tan }
882f9b9d1a5SJianfeng Tan 
883fcdb603aSMaxime Coquelin static uint8_t
884f9b9d1a5SJianfeng Tan virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs)
885f9b9d1a5SJianfeng Tan {
886f9b9d1a5SJianfeng Tan 	uint16_t i;
887f9b9d1a5SJianfeng Tan 	uint8_t ret = 0;
888f9b9d1a5SJianfeng Tan 
889f9b9d1a5SJianfeng Tan 	if (q_pairs > dev->max_queue_pairs) {
890a3fb6b1dSMaxime Coquelin 		PMD_INIT_LOG(ERR, "(%s) multi-q config %u, but only %u supported",
891a3fb6b1dSMaxime Coquelin 			     dev->path, q_pairs, dev->max_queue_pairs);
892f9b9d1a5SJianfeng Tan 		return -1;
893f9b9d1a5SJianfeng Tan 	}
894f9b9d1a5SJianfeng Tan 
895f9b9d1a5SJianfeng Tan 	for (i = 0; i < q_pairs; ++i)
89633d24d65SJianfeng Tan 		ret |= dev->ops->enable_qp(dev, i, 1);
897f9b9d1a5SJianfeng Tan 	for (i = q_pairs; i < dev->max_queue_pairs; ++i)
89833d24d65SJianfeng Tan 		ret |= dev->ops->enable_qp(dev, i, 0);
89994973531SMaxime Coquelin 
900f9b9d1a5SJianfeng Tan 	dev->queue_pairs = q_pairs;
901f9b9d1a5SJianfeng Tan 
902f9b9d1a5SJianfeng Tan 	return ret;
903f9b9d1a5SJianfeng Tan }
904f9b9d1a5SJianfeng Tan 
905fcdb603aSMaxime Coquelin #define CVQ_MAX_DATA_DESCS 32
906fcdb603aSMaxime Coquelin 
907cb5043cbSSrujana Challa static inline void *
90871a98628SSrujana Challa virtio_user_iova2virt(struct virtio_user_dev *dev, rte_iova_t iova)
909cb5043cbSSrujana Challa {
91071a98628SSrujana Challa 	if (rte_eal_iova_mode() == RTE_IOVA_VA || dev->hw.use_va)
911cb5043cbSSrujana Challa 		return (void *)(uintptr_t)iova;
912cb5043cbSSrujana Challa 	else
913cb5043cbSSrujana Challa 		return rte_mem_iova2virt(iova);
914cb5043cbSSrujana Challa }
915cb5043cbSSrujana Challa 
916f9b9d1a5SJianfeng Tan static uint32_t
917fcdb603aSMaxime Coquelin virtio_user_handle_ctrl_msg_split(struct virtio_user_dev *dev, struct vring *vring,
918f9b9d1a5SJianfeng Tan 			    uint16_t idx_hdr)
919f9b9d1a5SJianfeng Tan {
920f9b9d1a5SJianfeng Tan 	struct virtio_net_ctrl_hdr *hdr;
921f9b9d1a5SJianfeng Tan 	virtio_net_ctrl_ack status = ~0;
922f9b9d1a5SJianfeng Tan 	uint16_t i, idx_data, idx_status;
923f9b9d1a5SJianfeng Tan 	uint32_t n_descs = 0;
924fcdb603aSMaxime Coquelin 	int dlen[CVQ_MAX_DATA_DESCS], nb_dlen = 0;
925f9b9d1a5SJianfeng Tan 
926f9b9d1a5SJianfeng Tan 	/* locate desc for header, data, and status */
927f9b9d1a5SJianfeng Tan 	idx_data = vring->desc[idx_hdr].next;
928f9b9d1a5SJianfeng Tan 	n_descs++;
929f9b9d1a5SJianfeng Tan 
930f9b9d1a5SJianfeng Tan 	i = idx_data;
931f9b9d1a5SJianfeng Tan 	while (vring->desc[i].flags == VRING_DESC_F_NEXT) {
932fcdb603aSMaxime Coquelin 		dlen[nb_dlen++] = vring->desc[i].len;
933f9b9d1a5SJianfeng Tan 		i = vring->desc[i].next;
934f9b9d1a5SJianfeng Tan 		n_descs++;
935f9b9d1a5SJianfeng Tan 	}
936f9b9d1a5SJianfeng Tan 
937f9b9d1a5SJianfeng Tan 	/* locate desc for status */
938f9b9d1a5SJianfeng Tan 	idx_status = i;
939f9b9d1a5SJianfeng Tan 	n_descs++;
940f9b9d1a5SJianfeng Tan 
94171a98628SSrujana Challa 	hdr = virtio_user_iova2virt(dev, vring->desc[idx_hdr].addr);
942f9b9d1a5SJianfeng Tan 	if (hdr->class == VIRTIO_NET_CTRL_MQ &&
943f9b9d1a5SJianfeng Tan 	    hdr->cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
944cb5043cbSSrujana Challa 		uint16_t queues, *addr;
945f9b9d1a5SJianfeng Tan 
94671a98628SSrujana Challa 		addr = virtio_user_iova2virt(dev, vring->desc[idx_data].addr);
947cb5043cbSSrujana Challa 		queues = *addr;
948f9b9d1a5SJianfeng Tan 		status = virtio_user_handle_mq(dev, queues);
949dbce501eSSrujana Challa 	} else if (hdr->class == VIRTIO_NET_CTRL_MQ && hdr->cmd == VIRTIO_NET_CTRL_MQ_RSS_CONFIG) {
950dbce501eSSrujana Challa 		struct virtio_net_ctrl_rss *rss;
951dbce501eSSrujana Challa 
95271a98628SSrujana Challa 		rss = virtio_user_iova2virt(dev, vring->desc[idx_data].addr);
953dbce501eSSrujana Challa 		status = virtio_user_handle_mq(dev, rss->max_tx_vq);
954a76552d4SMarvin Liu 	} else if (hdr->class == VIRTIO_NET_CTRL_RX  ||
955a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_MAC ||
956a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_VLAN) {
957a76552d4SMarvin Liu 		status = 0;
958f9b9d1a5SJianfeng Tan 	}
959f9b9d1a5SJianfeng Tan 
960fcdb603aSMaxime Coquelin 	if (!status && dev->scvq)
961fcdb603aSMaxime Coquelin 		status = virtio_send_command(&dev->scvq->cq,
962fcdb603aSMaxime Coquelin 				(struct virtio_pmd_ctrl *)hdr, dlen, nb_dlen);
963fcdb603aSMaxime Coquelin 
964f9b9d1a5SJianfeng Tan 	/* Update status */
96571a98628SSrujana Challa 	*(virtio_net_ctrl_ack *)virtio_user_iova2virt(dev, vring->desc[idx_status].addr) = status;
966f9b9d1a5SJianfeng Tan 
967f9b9d1a5SJianfeng Tan 	return n_descs;
968f9b9d1a5SJianfeng Tan }
969f9b9d1a5SJianfeng Tan 
97048a44640SJens Freimann static inline int
97148a44640SJens Freimann desc_is_avail(struct vring_packed_desc *desc, bool wrap_counter)
97248a44640SJens Freimann {
973e12a0166STyler Retzlaff 	uint16_t flags = rte_atomic_load_explicit(&desc->flags, rte_memory_order_acquire);
97412e9e70cSTiwei Bie 
97512e9e70cSTiwei Bie 	return wrap_counter == !!(flags & VRING_PACKED_DESC_F_AVAIL) &&
97612e9e70cSTiwei Bie 		wrap_counter != !!(flags & VRING_PACKED_DESC_F_USED);
97748a44640SJens Freimann }
97848a44640SJens Freimann 
97948a44640SJens Freimann static uint32_t
98045c224e7STiwei Bie virtio_user_handle_ctrl_msg_packed(struct virtio_user_dev *dev,
98148a44640SJens Freimann 				   struct vring_packed *vring,
98248a44640SJens Freimann 				   uint16_t idx_hdr)
98348a44640SJens Freimann {
98448a44640SJens Freimann 	struct virtio_net_ctrl_hdr *hdr;
98548a44640SJens Freimann 	virtio_net_ctrl_ack status = ~0;
98648a44640SJens Freimann 	uint16_t idx_data, idx_status;
98748a44640SJens Freimann 	/* initialize to one, header is first */
98848a44640SJens Freimann 	uint32_t n_descs = 1;
989fcdb603aSMaxime Coquelin 	int dlen[CVQ_MAX_DATA_DESCS], nb_dlen = 0;
99048a44640SJens Freimann 
99148a44640SJens Freimann 	/* locate desc for header, data, and status */
99248a44640SJens Freimann 	idx_data = idx_hdr + 1;
99348a44640SJens Freimann 	if (idx_data >= dev->queue_size)
99448a44640SJens Freimann 		idx_data -= dev->queue_size;
99548a44640SJens Freimann 
99648a44640SJens Freimann 	n_descs++;
99748a44640SJens Freimann 
99848a44640SJens Freimann 	idx_status = idx_data;
9994cdc4d98STiwei Bie 	while (vring->desc[idx_status].flags & VRING_DESC_F_NEXT) {
1000fcdb603aSMaxime Coquelin 		dlen[nb_dlen++] = vring->desc[idx_status].len;
100148a44640SJens Freimann 		idx_status++;
100248a44640SJens Freimann 		if (idx_status >= dev->queue_size)
100348a44640SJens Freimann 			idx_status -= dev->queue_size;
100448a44640SJens Freimann 		n_descs++;
100548a44640SJens Freimann 	}
100648a44640SJens Freimann 
100771a98628SSrujana Challa 	hdr = virtio_user_iova2virt(dev, vring->desc[idx_hdr].addr);
100848a44640SJens Freimann 	if (hdr->class == VIRTIO_NET_CTRL_MQ &&
100948a44640SJens Freimann 	    hdr->cmd == VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET) {
1010cb5043cbSSrujana Challa 		uint16_t queues, *addr;
101148a44640SJens Freimann 
101271a98628SSrujana Challa 		addr = virtio_user_iova2virt(dev, vring->desc[idx_data].addr);
1013cb5043cbSSrujana Challa 		queues = *addr;
101448a44640SJens Freimann 		status = virtio_user_handle_mq(dev, queues);
1015dbce501eSSrujana Challa 	} else if (hdr->class == VIRTIO_NET_CTRL_MQ && hdr->cmd == VIRTIO_NET_CTRL_MQ_RSS_CONFIG) {
1016dbce501eSSrujana Challa 		struct virtio_net_ctrl_rss *rss;
1017dbce501eSSrujana Challa 
101871a98628SSrujana Challa 		rss = virtio_user_iova2virt(dev, vring->desc[idx_data].addr);
1019dbce501eSSrujana Challa 		status = virtio_user_handle_mq(dev, rss->max_tx_vq);
1020a76552d4SMarvin Liu 	} else if (hdr->class == VIRTIO_NET_CTRL_RX  ||
1021a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_MAC ||
1022a76552d4SMarvin Liu 		   hdr->class == VIRTIO_NET_CTRL_VLAN) {
1023a76552d4SMarvin Liu 		status = 0;
102448a44640SJens Freimann 	}
102548a44640SJens Freimann 
1026fcdb603aSMaxime Coquelin 	if (!status && dev->scvq)
1027fcdb603aSMaxime Coquelin 		status = virtio_send_command(&dev->scvq->cq,
1028fcdb603aSMaxime Coquelin 				(struct virtio_pmd_ctrl *)hdr, dlen, nb_dlen);
1029fcdb603aSMaxime Coquelin 
103048a44640SJens Freimann 	/* Update status */
103171a98628SSrujana Challa 	*(virtio_net_ctrl_ack *)virtio_user_iova2virt(dev, vring->desc[idx_status].addr) = status;
103248a44640SJens Freimann 
103345c224e7STiwei Bie 	/* Update used descriptor */
10344cdc4d98STiwei Bie 	vring->desc[idx_hdr].id = vring->desc[idx_status].id;
10354cdc4d98STiwei Bie 	vring->desc[idx_hdr].len = sizeof(status);
103645c224e7STiwei Bie 
103748a44640SJens Freimann 	return n_descs;
103848a44640SJens Freimann }
103948a44640SJens Freimann 
1040fcdb603aSMaxime Coquelin static void
104148a44640SJens Freimann virtio_user_handle_cq_packed(struct virtio_user_dev *dev, uint16_t queue_idx)
104248a44640SJens Freimann {
104348a44640SJens Freimann 	struct virtio_user_queue *vq = &dev->packed_queues[queue_idx];
10446fdf32d1SMaxime Coquelin 	struct vring_packed *vring = &dev->vrings.packed[queue_idx];
104512e9e70cSTiwei Bie 	uint16_t n_descs, flags;
104648a44640SJens Freimann 
10476094557dSJoyce Kong 	/* Perform a load-acquire barrier in desc_is_avail to
10486094557dSJoyce Kong 	 * enforce the ordering between desc flags and desc
10496094557dSJoyce Kong 	 * content.
10506094557dSJoyce Kong 	 */
10514cdc4d98STiwei Bie 	while (desc_is_avail(&vring->desc[vq->used_idx],
105248a44640SJens Freimann 			     vq->used_wrap_counter)) {
105348a44640SJens Freimann 
105445c224e7STiwei Bie 		n_descs = virtio_user_handle_ctrl_msg_packed(dev, vring,
105545c224e7STiwei Bie 				vq->used_idx);
105648a44640SJens Freimann 
105712e9e70cSTiwei Bie 		flags = VRING_DESC_F_WRITE;
105812e9e70cSTiwei Bie 		if (vq->used_wrap_counter)
105912e9e70cSTiwei Bie 			flags |= VRING_PACKED_DESC_F_AVAIL_USED;
106012e9e70cSTiwei Bie 
1061e12a0166STyler Retzlaff 		rte_atomic_store_explicit(&vring->desc[vq->used_idx].flags, flags,
1062e12a0166STyler Retzlaff 				 rte_memory_order_release);
106345c224e7STiwei Bie 
106445c224e7STiwei Bie 		vq->used_idx += n_descs;
106545c224e7STiwei Bie 		if (vq->used_idx >= dev->queue_size) {
106648a44640SJens Freimann 			vq->used_idx -= dev->queue_size;
106748a44640SJens Freimann 			vq->used_wrap_counter ^= 1;
106848a44640SJens Freimann 		}
106948a44640SJens Freimann 	}
107048a44640SJens Freimann }
107148a44640SJens Freimann 
1072fcdb603aSMaxime Coquelin static void
1073fcdb603aSMaxime Coquelin virtio_user_handle_cq_split(struct virtio_user_dev *dev, uint16_t queue_idx)
1074f9b9d1a5SJianfeng Tan {
1075f9b9d1a5SJianfeng Tan 	uint16_t avail_idx, desc_idx;
1076f9b9d1a5SJianfeng Tan 	struct vring_used_elem *uep;
1077f9b9d1a5SJianfeng Tan 	uint32_t n_descs;
10786fdf32d1SMaxime Coquelin 	struct vring *vring = &dev->vrings.split[queue_idx];
1079f9b9d1a5SJianfeng Tan 
1080f9b9d1a5SJianfeng Tan 	/* Consume avail ring, using used ring idx as first one */
1081e12a0166STyler Retzlaff 	while (rte_atomic_load_explicit(&vring->used->idx, rte_memory_order_relaxed)
1082ea5207c1SJoyce Kong 	       != vring->avail->idx) {
1083e12a0166STyler Retzlaff 		avail_idx = rte_atomic_load_explicit(&vring->used->idx, rte_memory_order_relaxed)
1084ea5207c1SJoyce Kong 			    & (vring->num - 1);
1085f9b9d1a5SJianfeng Tan 		desc_idx = vring->avail->ring[avail_idx];
1086f9b9d1a5SJianfeng Tan 
1087fcdb603aSMaxime Coquelin 		n_descs = virtio_user_handle_ctrl_msg_split(dev, vring, desc_idx);
1088f9b9d1a5SJianfeng Tan 
1089f9b9d1a5SJianfeng Tan 		/* Update used ring */
1090f9b9d1a5SJianfeng Tan 		uep = &vring->used->ring[avail_idx];
10910403e37aSTiwei Bie 		uep->id = desc_idx;
1092f9b9d1a5SJianfeng Tan 		uep->len = n_descs;
1093f9b9d1a5SJianfeng Tan 
1094e12a0166STyler Retzlaff 		rte_atomic_fetch_add_explicit(&vring->used->idx, 1, rte_memory_order_relaxed);
1095f9b9d1a5SJianfeng Tan 	}
1096f9b9d1a5SJianfeng Tan }
109757912824SMaxime Coquelin 
1098fcdb603aSMaxime Coquelin void
1099fcdb603aSMaxime Coquelin virtio_user_handle_cq(struct virtio_user_dev *dev, uint16_t queue_idx)
1100fcdb603aSMaxime Coquelin {
1101fcdb603aSMaxime Coquelin 	if (virtio_with_packed_queue(&dev->hw))
1102fcdb603aSMaxime Coquelin 		virtio_user_handle_cq_packed(dev, queue_idx);
1103fcdb603aSMaxime Coquelin 	else
1104fcdb603aSMaxime Coquelin 		virtio_user_handle_cq_split(dev, queue_idx);
1105fcdb603aSMaxime Coquelin }
1106fcdb603aSMaxime Coquelin 
110766b45ceaSMaxime Coquelin static void
110866b45ceaSMaxime Coquelin virtio_user_control_queue_notify(struct virtqueue *vq, void *cookie)
110966b45ceaSMaxime Coquelin {
111066b45ceaSMaxime Coquelin 	struct virtio_user_dev *dev = cookie;
11110fd27826SSrujana Challa 	uint64_t notify_data = 1;
111266b45ceaSMaxime Coquelin 
11130fd27826SSrujana Challa 	if (!dev->notify_area) {
11140fd27826SSrujana Challa 		if (write(dev->kickfds[vq->vq_queue_index], &notify_data, sizeof(notify_data)) < 0)
111566b45ceaSMaxime Coquelin 			PMD_DRV_LOG(ERR, "failed to kick backend: %s",
111666b45ceaSMaxime Coquelin 				    strerror(errno));
11170fd27826SSrujana Challa 		return;
11180fd27826SSrujana Challa 	} else if (!virtio_with_feature(&dev->hw, VIRTIO_F_NOTIFICATION_DATA)) {
11190fd27826SSrujana Challa 		rte_write16(vq->vq_queue_index, vq->notify_addr);
11200fd27826SSrujana Challa 		return;
11210fd27826SSrujana Challa 	}
11220fd27826SSrujana Challa 
11230fd27826SSrujana Challa 	if (virtio_with_packed_queue(&dev->hw)) {
11240fd27826SSrujana Challa 		/* Bit[0:15]: vq queue index
11250fd27826SSrujana Challa 		 * Bit[16:30]: avail index
11260fd27826SSrujana Challa 		 * Bit[31]: avail wrap counter
11270fd27826SSrujana Challa 		 */
11280fd27826SSrujana Challa 		notify_data = ((uint32_t)(!!(vq->vq_packed.cached_flags &
11290fd27826SSrujana Challa 				VRING_PACKED_DESC_F_AVAIL)) << 31) |
11300fd27826SSrujana Challa 				((uint32_t)vq->vq_avail_idx << 16) |
11310fd27826SSrujana Challa 				vq->vq_queue_index;
11320fd27826SSrujana Challa 	} else {
11330fd27826SSrujana Challa 		/* Bit[0:15]: vq queue index
11340fd27826SSrujana Challa 		 * Bit[16:31]: avail index
11350fd27826SSrujana Challa 		 */
11360fd27826SSrujana Challa 		notify_data = ((uint32_t)vq->vq_avail_idx << 16) |
11370fd27826SSrujana Challa 				vq->vq_queue_index;
11380fd27826SSrujana Challa 	}
11390fd27826SSrujana Challa 	rte_write32(notify_data, vq->notify_addr);
114066b45ceaSMaxime Coquelin }
114166b45ceaSMaxime Coquelin 
114266b45ceaSMaxime Coquelin int
114366b45ceaSMaxime Coquelin virtio_user_dev_create_shadow_cvq(struct virtio_user_dev *dev, struct virtqueue *vq)
114466b45ceaSMaxime Coquelin {
114566b45ceaSMaxime Coquelin 	char name[VIRTQUEUE_MAX_NAME_SZ];
114666b45ceaSMaxime Coquelin 	struct virtqueue *scvq;
114766b45ceaSMaxime Coquelin 
114866b45ceaSMaxime Coquelin 	snprintf(name, sizeof(name), "port%d_shadow_cvq", vq->hw->port_id);
114966b45ceaSMaxime Coquelin 	scvq = virtqueue_alloc(&dev->hw, vq->vq_queue_index, vq->vq_nentries,
115066b45ceaSMaxime Coquelin 			VTNET_CQ, SOCKET_ID_ANY, name);
115166b45ceaSMaxime Coquelin 	if (!scvq) {
1152f665790aSDavid Marchand 		PMD_INIT_LOG(ERR, "(%s) Failed to alloc shadow control vq", dev->path);
115366b45ceaSMaxime Coquelin 		return -ENOMEM;
115466b45ceaSMaxime Coquelin 	}
115566b45ceaSMaxime Coquelin 
115666b45ceaSMaxime Coquelin 	scvq->cq.notify_queue = &virtio_user_control_queue_notify;
115766b45ceaSMaxime Coquelin 	scvq->cq.notify_cookie = dev;
11580fd27826SSrujana Challa 	scvq->notify_addr = vq->notify_addr;
115966b45ceaSMaxime Coquelin 	dev->scvq = scvq;
116066b45ceaSMaxime Coquelin 
116166b45ceaSMaxime Coquelin 	return 0;
116266b45ceaSMaxime Coquelin }
116366b45ceaSMaxime Coquelin 
116466b45ceaSMaxime Coquelin void
116566b45ceaSMaxime Coquelin virtio_user_dev_destroy_shadow_cvq(struct virtio_user_dev *dev)
116666b45ceaSMaxime Coquelin {
116766b45ceaSMaxime Coquelin 	if (!dev->scvq)
116866b45ceaSMaxime Coquelin 		return;
116966b45ceaSMaxime Coquelin 
117066b45ceaSMaxime Coquelin 	virtqueue_free(dev->scvq);
117166b45ceaSMaxime Coquelin 	dev->scvq = NULL;
117266b45ceaSMaxime Coquelin }
117366b45ceaSMaxime Coquelin 
117457912824SMaxime Coquelin int
1175d7e10ea9SAdrian Moreno virtio_user_dev_set_status(struct virtio_user_dev *dev, uint8_t status)
117657912824SMaxime Coquelin {
117757912824SMaxime Coquelin 	int ret;
117857912824SMaxime Coquelin 
1179d7e10ea9SAdrian Moreno 	pthread_mutex_lock(&dev->mutex);
1180d7e10ea9SAdrian Moreno 	dev->status = status;
11818723c894SMaxime Coquelin 	ret = dev->ops->set_status(dev, status);
1182a3fb6b1dSMaxime Coquelin 	if (ret && ret != -ENOTSUP)
1183f3854ebaSThomas Monjalon 		PMD_INIT_LOG(ERR, "(%s) Failed to set backend status", dev->path);
1184d7e10ea9SAdrian Moreno 
1185d7e10ea9SAdrian Moreno 	pthread_mutex_unlock(&dev->mutex);
11865043a060SAdrian Moreno 	return ret;
118757912824SMaxime Coquelin }
11880b0dc66cSAdrian Moreno 
11890b0dc66cSAdrian Moreno int
1190d7e10ea9SAdrian Moreno virtio_user_dev_update_status(struct virtio_user_dev *dev)
11910b0dc66cSAdrian Moreno {
11928723c894SMaxime Coquelin 	int ret;
11937784e977SMaxime Coquelin 	uint8_t status;
11940b0dc66cSAdrian Moreno 
1195d7e10ea9SAdrian Moreno 	pthread_mutex_lock(&dev->mutex);
11967784e977SMaxime Coquelin 
11978723c894SMaxime Coquelin 	ret = dev->ops->get_status(dev, &status);
11988723c894SMaxime Coquelin 	if (!ret) {
11997784e977SMaxime Coquelin 		dev->status = status;
1200*1af8b0b2SDavid Marchand 		PMD_INIT_LOG(DEBUG, "Updated Device Status(0x%08x):",
1201*1af8b0b2SDavid Marchand 			dev->status);
1202*1af8b0b2SDavid Marchand 		PMD_INIT_LOG(DEBUG, "\t-RESET: %u",
1203*1af8b0b2SDavid Marchand 			(dev->status == VIRTIO_CONFIG_STATUS_RESET));
1204*1af8b0b2SDavid Marchand 		PMD_INIT_LOG(DEBUG, "\t-ACKNOWLEDGE: %u",
1205*1af8b0b2SDavid Marchand 			!!(dev->status & VIRTIO_CONFIG_STATUS_ACK));
1206*1af8b0b2SDavid Marchand 		PMD_INIT_LOG(DEBUG, "\t-DRIVER: %u",
1207*1af8b0b2SDavid Marchand 			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER));
1208*1af8b0b2SDavid Marchand 		PMD_INIT_LOG(DEBUG, "\t-DRIVER_OK: %u",
1209*1af8b0b2SDavid Marchand 			!!(dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK));
1210*1af8b0b2SDavid Marchand 		PMD_INIT_LOG(DEBUG, "\t-FEATURES_OK: %u",
1211*1af8b0b2SDavid Marchand 			!!(dev->status & VIRTIO_CONFIG_STATUS_FEATURES_OK));
1212*1af8b0b2SDavid Marchand 		PMD_INIT_LOG(DEBUG, "\t-DEVICE_NEED_RESET: %u",
1213*1af8b0b2SDavid Marchand 			!!(dev->status & VIRTIO_CONFIG_STATUS_DEV_NEED_RESET));
1214*1af8b0b2SDavid Marchand 		PMD_INIT_LOG(DEBUG, "\t-FAILED: %u",
12150b0dc66cSAdrian Moreno 			!!(dev->status & VIRTIO_CONFIG_STATUS_FAILED));
12168723c894SMaxime Coquelin 	} else if (ret != -ENOTSUP) {
1217f3854ebaSThomas Monjalon 		PMD_INIT_LOG(ERR, "(%s) Failed to get backend status", dev->path);
12185043a060SAdrian Moreno 	}
12195043a060SAdrian Moreno 
1220d7e10ea9SAdrian Moreno 	pthread_mutex_unlock(&dev->mutex);
12218723c894SMaxime Coquelin 	return ret;
12220b0dc66cSAdrian Moreno }
122394973531SMaxime Coquelin 
122494973531SMaxime Coquelin int
122594973531SMaxime Coquelin virtio_user_dev_update_link_state(struct virtio_user_dev *dev)
122694973531SMaxime Coquelin {
122794973531SMaxime Coquelin 	if (dev->ops->update_link_state)
122894973531SMaxime Coquelin 		return dev->ops->update_link_state(dev);
122994973531SMaxime Coquelin 
123094973531SMaxime Coquelin 	return 0;
123194973531SMaxime Coquelin }
123294973531SMaxime Coquelin 
123394973531SMaxime Coquelin static void
123494973531SMaxime Coquelin virtio_user_dev_reset_queues_packed(struct rte_eth_dev *eth_dev)
123594973531SMaxime Coquelin {
123694973531SMaxime Coquelin 	struct virtio_user_dev *dev = eth_dev->data->dev_private;
123794973531SMaxime Coquelin 	struct virtio_hw *hw = &dev->hw;
123894973531SMaxime Coquelin 	struct virtnet_rx *rxvq;
123994973531SMaxime Coquelin 	struct virtnet_tx *txvq;
124094973531SMaxime Coquelin 	uint16_t i;
124194973531SMaxime Coquelin 
124294973531SMaxime Coquelin 	/* Add lock to avoid queue contention. */
124394973531SMaxime Coquelin 	rte_spinlock_lock(&hw->state_lock);
124494973531SMaxime Coquelin 	hw->started = 0;
124594973531SMaxime Coquelin 
124694973531SMaxime Coquelin 	/*
124794973531SMaxime Coquelin 	 * Waiting for datapath to complete before resetting queues.
124894973531SMaxime Coquelin 	 * 1 ms should be enough for the ongoing Tx/Rx function to finish.
124994973531SMaxime Coquelin 	 */
125094973531SMaxime Coquelin 	rte_delay_ms(1);
125194973531SMaxime Coquelin 
125294973531SMaxime Coquelin 	/* Vring reset for each Tx queue and Rx queue. */
125394973531SMaxime Coquelin 	for (i = 0; i < eth_dev->data->nb_rx_queues; i++) {
125494973531SMaxime Coquelin 		rxvq = eth_dev->data->rx_queues[i];
12553169550fSMaxime Coquelin 		virtqueue_rxvq_reset_packed(virtnet_rxq_to_vq(rxvq));
125694973531SMaxime Coquelin 		virtio_dev_rx_queue_setup_finish(eth_dev, i);
125794973531SMaxime Coquelin 	}
125894973531SMaxime Coquelin 
125994973531SMaxime Coquelin 	for (i = 0; i < eth_dev->data->nb_tx_queues; i++) {
126094973531SMaxime Coquelin 		txvq = eth_dev->data->tx_queues[i];
12613169550fSMaxime Coquelin 		virtqueue_txvq_reset_packed(virtnet_txq_to_vq(txvq));
126294973531SMaxime Coquelin 	}
126394973531SMaxime Coquelin 
126494973531SMaxime Coquelin 	hw->started = 1;
126594973531SMaxime Coquelin 	rte_spinlock_unlock(&hw->state_lock);
126694973531SMaxime Coquelin }
126794973531SMaxime Coquelin 
126894973531SMaxime Coquelin void
126923abee9dSIlya Maximets virtio_user_dev_delayed_disconnect_handler(void *param)
127094973531SMaxime Coquelin {
127194973531SMaxime Coquelin 	struct virtio_user_dev *dev = param;
12726564ddcdSDavid Marchand 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
127394973531SMaxime Coquelin 
127494973531SMaxime Coquelin 	if (rte_intr_disable(eth_dev->intr_handle) < 0) {
127594973531SMaxime Coquelin 		PMD_DRV_LOG(ERR, "interrupt disable failed");
127694973531SMaxime Coquelin 		return;
127794973531SMaxime Coquelin 	}
127823abee9dSIlya Maximets 	PMD_DRV_LOG(DEBUG, "Unregistering intr fd: %d",
1279d61138d4SHarman Kalra 		    rte_intr_fd_get(eth_dev->intr_handle));
128023abee9dSIlya Maximets 	if (rte_intr_callback_unregister(eth_dev->intr_handle,
128123abee9dSIlya Maximets 					 virtio_interrupt_handler,
128223abee9dSIlya Maximets 					 eth_dev) != 1)
128323abee9dSIlya Maximets 		PMD_DRV_LOG(ERR, "interrupt unregister failed");
128423abee9dSIlya Maximets 
128594973531SMaxime Coquelin 	if (dev->is_server) {
128694973531SMaxime Coquelin 		if (dev->ops->server_disconnect)
128794973531SMaxime Coquelin 			dev->ops->server_disconnect(dev);
128823abee9dSIlya Maximets 
1289d61138d4SHarman Kalra 		rte_intr_fd_set(eth_dev->intr_handle,
1290d61138d4SHarman Kalra 			dev->ops->get_intr_fd(dev));
129123abee9dSIlya Maximets 
129223abee9dSIlya Maximets 		PMD_DRV_LOG(DEBUG, "Registering intr fd: %d",
1293d61138d4SHarman Kalra 			    rte_intr_fd_get(eth_dev->intr_handle));
129423abee9dSIlya Maximets 
129523abee9dSIlya Maximets 		if (rte_intr_callback_register(eth_dev->intr_handle,
129623abee9dSIlya Maximets 					       virtio_interrupt_handler,
129723abee9dSIlya Maximets 					       eth_dev))
129823abee9dSIlya Maximets 			PMD_DRV_LOG(ERR, "interrupt register failed");
129923abee9dSIlya Maximets 
130094973531SMaxime Coquelin 		if (rte_intr_enable(eth_dev->intr_handle) < 0) {
130194973531SMaxime Coquelin 			PMD_DRV_LOG(ERR, "interrupt enable failed");
130294973531SMaxime Coquelin 			return;
130394973531SMaxime Coquelin 		}
130494973531SMaxime Coquelin 	}
130594973531SMaxime Coquelin }
130694973531SMaxime Coquelin 
130723abee9dSIlya Maximets static void
130823abee9dSIlya Maximets virtio_user_dev_delayed_intr_reconfig_handler(void *param)
130923abee9dSIlya Maximets {
131023abee9dSIlya Maximets 	struct virtio_user_dev *dev = param;
131123abee9dSIlya Maximets 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
131223abee9dSIlya Maximets 
131323abee9dSIlya Maximets 	PMD_DRV_LOG(DEBUG, "Unregistering intr fd: %d",
1314d61138d4SHarman Kalra 		    rte_intr_fd_get(eth_dev->intr_handle));
131523abee9dSIlya Maximets 
131623abee9dSIlya Maximets 	if (rte_intr_callback_unregister(eth_dev->intr_handle,
131723abee9dSIlya Maximets 					 virtio_interrupt_handler,
131823abee9dSIlya Maximets 					 eth_dev) != 1)
131923abee9dSIlya Maximets 		PMD_DRV_LOG(ERR, "interrupt unregister failed");
132023abee9dSIlya Maximets 
1321d61138d4SHarman Kalra 	rte_intr_fd_set(eth_dev->intr_handle, dev->ops->get_intr_fd(dev));
132223abee9dSIlya Maximets 
1323d61138d4SHarman Kalra 	PMD_DRV_LOG(DEBUG, "Registering intr fd: %d",
1324d61138d4SHarman Kalra 		    rte_intr_fd_get(eth_dev->intr_handle));
132523abee9dSIlya Maximets 
132623abee9dSIlya Maximets 	if (rte_intr_callback_register(eth_dev->intr_handle,
132723abee9dSIlya Maximets 				       virtio_interrupt_handler, eth_dev))
132823abee9dSIlya Maximets 		PMD_DRV_LOG(ERR, "interrupt register failed");
132923abee9dSIlya Maximets 
133023abee9dSIlya Maximets 	if (rte_intr_enable(eth_dev->intr_handle) < 0)
133123abee9dSIlya Maximets 		PMD_DRV_LOG(ERR, "interrupt enable failed");
133223abee9dSIlya Maximets }
133323abee9dSIlya Maximets 
133494973531SMaxime Coquelin int
133594973531SMaxime Coquelin virtio_user_dev_server_reconnect(struct virtio_user_dev *dev)
133694973531SMaxime Coquelin {
133794973531SMaxime Coquelin 	int ret, old_status;
13386564ddcdSDavid Marchand 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
133994973531SMaxime Coquelin 	struct virtio_hw *hw = &dev->hw;
134094973531SMaxime Coquelin 
134194973531SMaxime Coquelin 	if (!dev->ops->server_reconnect) {
134294973531SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Missing server reconnect callback", dev->path);
134394973531SMaxime Coquelin 		return -1;
134494973531SMaxime Coquelin 	}
134594973531SMaxime Coquelin 
134694973531SMaxime Coquelin 	if (dev->ops->server_reconnect(dev)) {
134794973531SMaxime Coquelin 		PMD_DRV_LOG(ERR, "(%s) Reconnect callback call failed", dev->path);
134894973531SMaxime Coquelin 		return -1;
134994973531SMaxime Coquelin 	}
135094973531SMaxime Coquelin 
135194973531SMaxime Coquelin 	old_status = dev->status;
135294973531SMaxime Coquelin 
135394973531SMaxime Coquelin 	virtio_reset(hw);
135494973531SMaxime Coquelin 
135594973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_ACK);
135694973531SMaxime Coquelin 
135794973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER);
135894973531SMaxime Coquelin 
135994973531SMaxime Coquelin 	if (dev->ops->get_features(dev, &dev->device_features) < 0) {
136094973531SMaxime Coquelin 		PMD_INIT_LOG(ERR, "get_features failed: %s",
136194973531SMaxime Coquelin 			     strerror(errno));
136294973531SMaxime Coquelin 		return -1;
136394973531SMaxime Coquelin 	}
136494973531SMaxime Coquelin 
136594973531SMaxime Coquelin 	/* unmask vhost-user unsupported features */
136694973531SMaxime Coquelin 	dev->device_features &= ~(dev->unsupported_features);
136794973531SMaxime Coquelin 
1368f078c2f0SMaxime Coquelin 	dev->features &= (dev->device_features | dev->frontend_features);
136994973531SMaxime Coquelin 
137094973531SMaxime Coquelin 	/* For packed ring, resetting queues is required in reconnection. */
137194973531SMaxime Coquelin 	if (virtio_with_packed_queue(hw) &&
137294973531SMaxime Coquelin 	   (old_status & VIRTIO_CONFIG_STATUS_DRIVER_OK)) {
137394973531SMaxime Coquelin 		PMD_INIT_LOG(NOTICE, "Packets on the fly will be dropped"
137494973531SMaxime Coquelin 				" when packed ring reconnecting.");
137594973531SMaxime Coquelin 		virtio_user_dev_reset_queues_packed(eth_dev);
137694973531SMaxime Coquelin 	}
137794973531SMaxime Coquelin 
137894973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_FEATURES_OK);
137994973531SMaxime Coquelin 
138094973531SMaxime Coquelin 	/* Start the device */
138194973531SMaxime Coquelin 	virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER_OK);
138294973531SMaxime Coquelin 	if (!dev->started)
138394973531SMaxime Coquelin 		return -1;
138494973531SMaxime Coquelin 
138594973531SMaxime Coquelin 	if (dev->queue_pairs > 1) {
138694973531SMaxime Coquelin 		ret = virtio_user_handle_mq(dev, dev->queue_pairs);
138794973531SMaxime Coquelin 		if (ret != 0) {
138894973531SMaxime Coquelin 			PMD_INIT_LOG(ERR, "Fails to enable multi-queue pairs!");
138994973531SMaxime Coquelin 			return -1;
139094973531SMaxime Coquelin 		}
139194973531SMaxime Coquelin 	}
139294973531SMaxime Coquelin 	if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC) {
139394973531SMaxime Coquelin 		if (rte_intr_disable(eth_dev->intr_handle) < 0) {
139494973531SMaxime Coquelin 			PMD_DRV_LOG(ERR, "interrupt disable failed");
139594973531SMaxime Coquelin 			return -1;
139694973531SMaxime Coquelin 		}
139723abee9dSIlya Maximets 		/*
139823abee9dSIlya Maximets 		 * This function can be called from the interrupt handler, so
139923abee9dSIlya Maximets 		 * we can't unregister interrupt handler here.  Setting
140023abee9dSIlya Maximets 		 * alarm to do that later.
140123abee9dSIlya Maximets 		 */
140223abee9dSIlya Maximets 		rte_eal_alarm_set(1,
140323abee9dSIlya Maximets 			virtio_user_dev_delayed_intr_reconfig_handler,
140423abee9dSIlya Maximets 			(void *)dev);
140594973531SMaxime Coquelin 	}
140694973531SMaxime Coquelin 	PMD_INIT_LOG(NOTICE, "server mode virtio-user reconnection succeeds!");
140794973531SMaxime Coquelin 	return 0;
140894973531SMaxime Coquelin }
1409