xref: /dpdk/lib/vhost/vhost.c (revision 15677ca2c751b3be2f02429bb006d859dccae0c0)
199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson  * Copyright(c) 2010-2017 Intel Corporation
399a2dd95SBruce Richardson  */
499a2dd95SBruce Richardson 
599a2dd95SBruce Richardson #include <linux/vhost.h>
699a2dd95SBruce Richardson #include <linux/virtio_net.h>
799a2dd95SBruce Richardson #include <stdint.h>
899a2dd95SBruce Richardson #include <stdlib.h>
92744cb6eSThomas Monjalon #include <pthread.h>
1099a2dd95SBruce Richardson #ifdef RTE_LIBRTE_VHOST_NUMA
1199a2dd95SBruce Richardson #include <numa.h>
1299a2dd95SBruce Richardson #include <numaif.h>
1399a2dd95SBruce Richardson #endif
1499a2dd95SBruce Richardson 
1599a2dd95SBruce Richardson #include <rte_errno.h>
1699a2dd95SBruce Richardson #include <rte_log.h>
1799a2dd95SBruce Richardson #include <rte_memory.h>
1899a2dd95SBruce Richardson #include <rte_malloc.h>
1999a2dd95SBruce Richardson #include <rte_vhost.h>
2099a2dd95SBruce Richardson 
2199a2dd95SBruce Richardson #include "iotlb.h"
2299a2dd95SBruce Richardson #include "vhost.h"
2399a2dd95SBruce Richardson #include "vhost_user.h"
2499a2dd95SBruce Richardson 
2553d3f477SJiayu Hu struct virtio_net *vhost_devices[RTE_MAX_VHOST_DEVICE];
2699a2dd95SBruce Richardson pthread_mutex_t vhost_dev_lock = PTHREAD_MUTEX_INITIALIZER;
27e8c3d496SXuan Ding pthread_mutex_t vhost_dma_lock = PTHREAD_MUTEX_INITIALIZER;
2899a2dd95SBruce Richardson 
29be75dc99SMaxime Coquelin struct vhost_vq_stats_name_off {
30be75dc99SMaxime Coquelin 	char name[RTE_VHOST_STATS_NAME_SIZE];
31be75dc99SMaxime Coquelin 	unsigned int offset;
32be75dc99SMaxime Coquelin };
33be75dc99SMaxime Coquelin 
34be75dc99SMaxime Coquelin static const struct vhost_vq_stats_name_off vhost_vq_stat_strings[] = {
35be75dc99SMaxime Coquelin 	{"good_packets",           offsetof(struct vhost_virtqueue, stats.packets)},
36be75dc99SMaxime Coquelin 	{"good_bytes",             offsetof(struct vhost_virtqueue, stats.bytes)},
37be75dc99SMaxime Coquelin 	{"multicast_packets",      offsetof(struct vhost_virtqueue, stats.multicast)},
38be75dc99SMaxime Coquelin 	{"broadcast_packets",      offsetof(struct vhost_virtqueue, stats.broadcast)},
39be75dc99SMaxime Coquelin 	{"undersize_packets",      offsetof(struct vhost_virtqueue, stats.size_bins[0])},
40be75dc99SMaxime Coquelin 	{"size_64_packets",        offsetof(struct vhost_virtqueue, stats.size_bins[1])},
41be75dc99SMaxime Coquelin 	{"size_65_127_packets",    offsetof(struct vhost_virtqueue, stats.size_bins[2])},
42be75dc99SMaxime Coquelin 	{"size_128_255_packets",   offsetof(struct vhost_virtqueue, stats.size_bins[3])},
43be75dc99SMaxime Coquelin 	{"size_256_511_packets",   offsetof(struct vhost_virtqueue, stats.size_bins[4])},
44be75dc99SMaxime Coquelin 	{"size_512_1023_packets",  offsetof(struct vhost_virtqueue, stats.size_bins[5])},
45be75dc99SMaxime Coquelin 	{"size_1024_1518_packets", offsetof(struct vhost_virtqueue, stats.size_bins[6])},
46be75dc99SMaxime Coquelin 	{"size_1519_max_packets",  offsetof(struct vhost_virtqueue, stats.size_bins[7])},
471ea74efdSMaxime Coquelin 	{"guest_notifications",    offsetof(struct vhost_virtqueue, stats.guest_notifications)},
48d761d455SEelco Chaudron 	{"guest_notifications_offloaded", offsetof(struct vhost_virtqueue,
49d761d455SEelco Chaudron 		stats.guest_notifications_offloaded)},
50d761d455SEelco Chaudron 	{"guest_notifications_error", offsetof(struct vhost_virtqueue,
51d761d455SEelco Chaudron 		stats.guest_notifications_error)},
5211c310c8SMaxime Coquelin 	{"guest_notifications_suppressed", offsetof(struct vhost_virtqueue,
5311c310c8SMaxime Coquelin 		stats.guest_notifications_suppressed)},
547247b746SMaxime Coquelin 	{"iotlb_hits",             offsetof(struct vhost_virtqueue, stats.iotlb_hits)},
557247b746SMaxime Coquelin 	{"iotlb_misses",           offsetof(struct vhost_virtqueue, stats.iotlb_misses)},
5669c94e35SMaxime Coquelin 	{"inflight_submitted",     offsetof(struct vhost_virtqueue, stats.inflight_submitted)},
5769c94e35SMaxime Coquelin 	{"inflight_completed",     offsetof(struct vhost_virtqueue, stats.inflight_completed)},
58458dc624SMaxime Coquelin 	{"mbuf_alloc_failed",      offsetof(struct vhost_virtqueue, stats.mbuf_alloc_failed)},
59be75dc99SMaxime Coquelin };
60be75dc99SMaxime Coquelin 
61be75dc99SMaxime Coquelin #define VHOST_NB_VQ_STATS RTE_DIM(vhost_vq_stat_strings)
62be75dc99SMaxime Coquelin 
63357b9359SMaxime Coquelin static int
64357b9359SMaxime Coquelin vhost_iotlb_miss(struct virtio_net *dev, uint64_t iova, uint8_t perm)
65357b9359SMaxime Coquelin {
66357b9359SMaxime Coquelin 	return dev->backend_ops->iotlb_miss(dev, iova, perm);
67357b9359SMaxime Coquelin }
68357b9359SMaxime Coquelin 
6999a2dd95SBruce Richardson uint64_t
7099a2dd95SBruce Richardson __vhost_iova_to_vva(struct virtio_net *dev, struct vhost_virtqueue *vq,
7199a2dd95SBruce Richardson 		    uint64_t iova, uint64_t *size, uint8_t perm)
7299a2dd95SBruce Richardson {
7399a2dd95SBruce Richardson 	uint64_t vva, tmp_size;
7499a2dd95SBruce Richardson 
7599a2dd95SBruce Richardson 	if (unlikely(!*size))
7699a2dd95SBruce Richardson 		return 0;
7799a2dd95SBruce Richardson 
7899a2dd95SBruce Richardson 	tmp_size = *size;
7999a2dd95SBruce Richardson 
80a54f046dSMaxime Coquelin 	vva = vhost_user_iotlb_cache_find(dev, iova, &tmp_size, perm);
817247b746SMaxime Coquelin 	if (tmp_size == *size) {
827247b746SMaxime Coquelin 		if (dev->flags & VIRTIO_DEV_STATS_ENABLED)
837247b746SMaxime Coquelin 			vq->stats.iotlb_hits++;
8499a2dd95SBruce Richardson 		return vva;
857247b746SMaxime Coquelin 	}
867247b746SMaxime Coquelin 
877247b746SMaxime Coquelin 	if (dev->flags & VIRTIO_DEV_STATS_ENABLED)
887247b746SMaxime Coquelin 		vq->stats.iotlb_misses++;
8999a2dd95SBruce Richardson 
9099a2dd95SBruce Richardson 	iova += tmp_size;
9199a2dd95SBruce Richardson 
92a54f046dSMaxime Coquelin 	if (!vhost_user_iotlb_pending_miss(dev, iova, perm)) {
9399a2dd95SBruce Richardson 		/*
9499a2dd95SBruce Richardson 		 * iotlb_lock is read-locked for a full burst,
9599a2dd95SBruce Richardson 		 * but it only protects the iotlb cache.
9699a2dd95SBruce Richardson 		 * In case of IOTLB miss, we might block on the socket,
9799a2dd95SBruce Richardson 		 * which could cause a deadlock with QEMU if an IOTLB update
9899a2dd95SBruce Richardson 		 * is being handled. We can safely unlock here to avoid it.
9999a2dd95SBruce Richardson 		 */
10099a2dd95SBruce Richardson 		vhost_user_iotlb_rd_unlock(vq);
10199a2dd95SBruce Richardson 
102a54f046dSMaxime Coquelin 		vhost_user_iotlb_pending_insert(dev, iova, perm);
103357b9359SMaxime Coquelin 		if (vhost_iotlb_miss(dev, iova, perm)) {
1040e21c7c0SDavid Marchand 			VHOST_DATA_LOG(dev->ifname, ERR,
1050e21c7c0SDavid Marchand 				"IOTLB miss req failed for IOVA 0x%" PRIx64,
10636c525a0SDavid Marchand 				iova);
107a54f046dSMaxime Coquelin 			vhost_user_iotlb_pending_remove(dev, iova, 1, perm);
10899a2dd95SBruce Richardson 		}
10999a2dd95SBruce Richardson 
11099a2dd95SBruce Richardson 		vhost_user_iotlb_rd_lock(vq);
11199a2dd95SBruce Richardson 	}
11299a2dd95SBruce Richardson 
113a370b630SMaxime Coquelin 	tmp_size = *size;
114a370b630SMaxime Coquelin 	/* Retry in case of VDUSE, as it is synchronous */
115a370b630SMaxime Coquelin 	vva = vhost_user_iotlb_cache_find(dev, iova, &tmp_size, perm);
116a370b630SMaxime Coquelin 	if (tmp_size == *size)
117a370b630SMaxime Coquelin 		return vva;
118a370b630SMaxime Coquelin 
11999a2dd95SBruce Richardson 	return 0;
12099a2dd95SBruce Richardson }
12199a2dd95SBruce Richardson 
12299a2dd95SBruce Richardson #define VHOST_LOG_PAGE	4096
12399a2dd95SBruce Richardson 
12499a2dd95SBruce Richardson /*
12599a2dd95SBruce Richardson  * Atomically set a bit in memory.
12699a2dd95SBruce Richardson  */
12799a2dd95SBruce Richardson static __rte_always_inline void
12899a2dd95SBruce Richardson vhost_set_bit(unsigned int nr, volatile uint8_t *addr)
12999a2dd95SBruce Richardson {
13099a2dd95SBruce Richardson #if defined(RTE_TOOLCHAIN_GCC) && (GCC_VERSION < 70100)
13199a2dd95SBruce Richardson 	/*
1325147b641STyler Retzlaff 	 * __sync_ built-ins are deprecated, but rte_atomic_ ones
13399a2dd95SBruce Richardson 	 * are sub-optimized in older GCC versions.
13499a2dd95SBruce Richardson 	 */
13599a2dd95SBruce Richardson 	__sync_fetch_and_or_1(addr, (1U << nr));
13699a2dd95SBruce Richardson #else
1375147b641STyler Retzlaff 	rte_atomic_fetch_or_explicit((volatile uint8_t __rte_atomic *)addr, (1U << nr),
1385147b641STyler Retzlaff 		rte_memory_order_relaxed);
13999a2dd95SBruce Richardson #endif
14099a2dd95SBruce Richardson }
14199a2dd95SBruce Richardson 
14299a2dd95SBruce Richardson static __rte_always_inline void
14399a2dd95SBruce Richardson vhost_log_page(uint8_t *log_base, uint64_t page)
14499a2dd95SBruce Richardson {
14599a2dd95SBruce Richardson 	vhost_set_bit(page % 8, &log_base[page / 8]);
14699a2dd95SBruce Richardson }
14799a2dd95SBruce Richardson 
14899a2dd95SBruce Richardson void
14999a2dd95SBruce Richardson __vhost_log_write(struct virtio_net *dev, uint64_t addr, uint64_t len)
15099a2dd95SBruce Richardson {
15199a2dd95SBruce Richardson 	uint64_t page;
15299a2dd95SBruce Richardson 
15399a2dd95SBruce Richardson 	if (unlikely(!dev->log_base || !len))
15499a2dd95SBruce Richardson 		return;
15599a2dd95SBruce Richardson 
15699a2dd95SBruce Richardson 	if (unlikely(dev->log_size <= ((addr + len - 1) / VHOST_LOG_PAGE / 8)))
15799a2dd95SBruce Richardson 		return;
15899a2dd95SBruce Richardson 
15999a2dd95SBruce Richardson 	/* To make sure guest memory updates are committed before logging */
1605147b641STyler Retzlaff 	rte_atomic_thread_fence(rte_memory_order_release);
16199a2dd95SBruce Richardson 
16299a2dd95SBruce Richardson 	page = addr / VHOST_LOG_PAGE;
16399a2dd95SBruce Richardson 	while (page * VHOST_LOG_PAGE < addr + len) {
16499a2dd95SBruce Richardson 		vhost_log_page((uint8_t *)(uintptr_t)dev->log_base, page);
16599a2dd95SBruce Richardson 		page += 1;
16699a2dd95SBruce Richardson 	}
16799a2dd95SBruce Richardson }
16899a2dd95SBruce Richardson 
16999a2dd95SBruce Richardson void
17099a2dd95SBruce Richardson __vhost_log_write_iova(struct virtio_net *dev, struct vhost_virtqueue *vq,
17199a2dd95SBruce Richardson 			     uint64_t iova, uint64_t len)
17299a2dd95SBruce Richardson {
17399a2dd95SBruce Richardson 	uint64_t hva, gpa, map_len;
17499a2dd95SBruce Richardson 	map_len = len;
17599a2dd95SBruce Richardson 
17699a2dd95SBruce Richardson 	hva = __vhost_iova_to_vva(dev, vq, iova, &map_len, VHOST_ACCESS_RW);
17799a2dd95SBruce Richardson 	if (map_len != len) {
1780e21c7c0SDavid Marchand 		VHOST_DATA_LOG(dev->ifname, ERR,
1790e21c7c0SDavid Marchand 			"failed to write log for IOVA 0x%" PRIx64 ". No IOTLB entry found",
18036c525a0SDavid Marchand 			iova);
18199a2dd95SBruce Richardson 		return;
18299a2dd95SBruce Richardson 	}
18399a2dd95SBruce Richardson 
18499a2dd95SBruce Richardson 	gpa = hva_to_gpa(dev, hva, len);
18599a2dd95SBruce Richardson 	if (gpa)
18699a2dd95SBruce Richardson 		__vhost_log_write(dev, gpa, len);
18799a2dd95SBruce Richardson }
18899a2dd95SBruce Richardson 
18999a2dd95SBruce Richardson void
19099a2dd95SBruce Richardson __vhost_log_cache_sync(struct virtio_net *dev, struct vhost_virtqueue *vq)
19199a2dd95SBruce Richardson {
19299a2dd95SBruce Richardson 	unsigned long *log_base;
19399a2dd95SBruce Richardson 	int i;
19499a2dd95SBruce Richardson 
19599a2dd95SBruce Richardson 	if (unlikely(!dev->log_base))
19699a2dd95SBruce Richardson 		return;
19799a2dd95SBruce Richardson 
19899a2dd95SBruce Richardson 	/* No cache, nothing to sync */
19999a2dd95SBruce Richardson 	if (unlikely(!vq->log_cache))
20099a2dd95SBruce Richardson 		return;
20199a2dd95SBruce Richardson 
2025147b641STyler Retzlaff 	rte_atomic_thread_fence(rte_memory_order_release);
20399a2dd95SBruce Richardson 
20499a2dd95SBruce Richardson 	log_base = (unsigned long *)(uintptr_t)dev->log_base;
20599a2dd95SBruce Richardson 
20699a2dd95SBruce Richardson 	for (i = 0; i < vq->log_cache_nb_elem; i++) {
20799a2dd95SBruce Richardson 		struct log_cache_entry *elem = vq->log_cache + i;
20899a2dd95SBruce Richardson 
20999a2dd95SBruce Richardson #if defined(RTE_TOOLCHAIN_GCC) && (GCC_VERSION < 70100)
21099a2dd95SBruce Richardson 		/*
2115147b641STyler Retzlaff 		 * '__sync' builtins are deprecated, but 'rte_atomic' ones
21299a2dd95SBruce Richardson 		 * are sub-optimized in older GCC versions.
21399a2dd95SBruce Richardson 		 */
21499a2dd95SBruce Richardson 		__sync_fetch_and_or(log_base + elem->offset, elem->val);
21599a2dd95SBruce Richardson #else
2165147b641STyler Retzlaff 		rte_atomic_fetch_or_explicit(
2175147b641STyler Retzlaff 			(unsigned long __rte_atomic *)(log_base + elem->offset),
2185147b641STyler Retzlaff 			elem->val, rte_memory_order_relaxed);
21999a2dd95SBruce Richardson #endif
22099a2dd95SBruce Richardson 	}
22199a2dd95SBruce Richardson 
2225147b641STyler Retzlaff 	rte_atomic_thread_fence(rte_memory_order_release);
22399a2dd95SBruce Richardson 
22499a2dd95SBruce Richardson 	vq->log_cache_nb_elem = 0;
22599a2dd95SBruce Richardson }
22699a2dd95SBruce Richardson 
22799a2dd95SBruce Richardson static __rte_always_inline void
22899a2dd95SBruce Richardson vhost_log_cache_page(struct virtio_net *dev, struct vhost_virtqueue *vq,
22999a2dd95SBruce Richardson 			uint64_t page)
23099a2dd95SBruce Richardson {
23199a2dd95SBruce Richardson 	uint32_t bit_nr = page % (sizeof(unsigned long) << 3);
23299a2dd95SBruce Richardson 	uint32_t offset = page / (sizeof(unsigned long) << 3);
23399a2dd95SBruce Richardson 	int i;
23499a2dd95SBruce Richardson 
23599a2dd95SBruce Richardson 	if (unlikely(!vq->log_cache)) {
23699a2dd95SBruce Richardson 		/* No logging cache allocated, write dirty log map directly */
2375147b641STyler Retzlaff 		rte_atomic_thread_fence(rte_memory_order_release);
23899a2dd95SBruce Richardson 		vhost_log_page((uint8_t *)(uintptr_t)dev->log_base, page);
23999a2dd95SBruce Richardson 
24099a2dd95SBruce Richardson 		return;
24199a2dd95SBruce Richardson 	}
24299a2dd95SBruce Richardson 
24399a2dd95SBruce Richardson 	for (i = 0; i < vq->log_cache_nb_elem; i++) {
24499a2dd95SBruce Richardson 		struct log_cache_entry *elem = vq->log_cache + i;
24599a2dd95SBruce Richardson 
24699a2dd95SBruce Richardson 		if (elem->offset == offset) {
24799a2dd95SBruce Richardson 			elem->val |= (1UL << bit_nr);
24899a2dd95SBruce Richardson 			return;
24999a2dd95SBruce Richardson 		}
25099a2dd95SBruce Richardson 	}
25199a2dd95SBruce Richardson 
25299a2dd95SBruce Richardson 	if (unlikely(i >= VHOST_LOG_CACHE_NR)) {
25399a2dd95SBruce Richardson 		/*
25499a2dd95SBruce Richardson 		 * No more room for a new log cache entry,
25599a2dd95SBruce Richardson 		 * so write the dirty log map directly.
25699a2dd95SBruce Richardson 		 */
2575147b641STyler Retzlaff 		rte_atomic_thread_fence(rte_memory_order_release);
25899a2dd95SBruce Richardson 		vhost_log_page((uint8_t *)(uintptr_t)dev->log_base, page);
25999a2dd95SBruce Richardson 
26099a2dd95SBruce Richardson 		return;
26199a2dd95SBruce Richardson 	}
26299a2dd95SBruce Richardson 
26399a2dd95SBruce Richardson 	vq->log_cache[i].offset = offset;
26499a2dd95SBruce Richardson 	vq->log_cache[i].val = (1UL << bit_nr);
26599a2dd95SBruce Richardson 	vq->log_cache_nb_elem++;
26699a2dd95SBruce Richardson }
26799a2dd95SBruce Richardson 
26899a2dd95SBruce Richardson void
26999a2dd95SBruce Richardson __vhost_log_cache_write(struct virtio_net *dev, struct vhost_virtqueue *vq,
27099a2dd95SBruce Richardson 			uint64_t addr, uint64_t len)
27199a2dd95SBruce Richardson {
27299a2dd95SBruce Richardson 	uint64_t page;
27399a2dd95SBruce Richardson 
27499a2dd95SBruce Richardson 	if (unlikely(!dev->log_base || !len))
27599a2dd95SBruce Richardson 		return;
27699a2dd95SBruce Richardson 
27799a2dd95SBruce Richardson 	if (unlikely(dev->log_size <= ((addr + len - 1) / VHOST_LOG_PAGE / 8)))
27899a2dd95SBruce Richardson 		return;
27999a2dd95SBruce Richardson 
28099a2dd95SBruce Richardson 	page = addr / VHOST_LOG_PAGE;
28199a2dd95SBruce Richardson 	while (page * VHOST_LOG_PAGE < addr + len) {
28299a2dd95SBruce Richardson 		vhost_log_cache_page(dev, vq, page);
28399a2dd95SBruce Richardson 		page += 1;
28499a2dd95SBruce Richardson 	}
28599a2dd95SBruce Richardson }
28699a2dd95SBruce Richardson 
28799a2dd95SBruce Richardson void
28899a2dd95SBruce Richardson __vhost_log_cache_write_iova(struct virtio_net *dev, struct vhost_virtqueue *vq,
28999a2dd95SBruce Richardson 			     uint64_t iova, uint64_t len)
29099a2dd95SBruce Richardson {
29199a2dd95SBruce Richardson 	uint64_t hva, gpa, map_len;
29299a2dd95SBruce Richardson 	map_len = len;
29399a2dd95SBruce Richardson 
29499a2dd95SBruce Richardson 	hva = __vhost_iova_to_vva(dev, vq, iova, &map_len, VHOST_ACCESS_RW);
29599a2dd95SBruce Richardson 	if (map_len != len) {
2960e21c7c0SDavid Marchand 		VHOST_DATA_LOG(dev->ifname, ERR,
2970e21c7c0SDavid Marchand 			"failed to write log for IOVA 0x%" PRIx64 ". No IOTLB entry found",
29836c525a0SDavid Marchand 			iova);
29999a2dd95SBruce Richardson 		return;
30099a2dd95SBruce Richardson 	}
30199a2dd95SBruce Richardson 
30299a2dd95SBruce Richardson 	gpa = hva_to_gpa(dev, hva, len);
30399a2dd95SBruce Richardson 	if (gpa)
30499a2dd95SBruce Richardson 		__vhost_log_cache_write(dev, vq, gpa, len);
30599a2dd95SBruce Richardson }
30699a2dd95SBruce Richardson 
30799a2dd95SBruce Richardson void *
30899a2dd95SBruce Richardson vhost_alloc_copy_ind_table(struct virtio_net *dev, struct vhost_virtqueue *vq,
30999a2dd95SBruce Richardson 		uint64_t desc_addr, uint64_t desc_len)
31099a2dd95SBruce Richardson {
31199a2dd95SBruce Richardson 	void *idesc;
31299a2dd95SBruce Richardson 	uint64_t src, dst;
31399a2dd95SBruce Richardson 	uint64_t len, remain = desc_len;
31499a2dd95SBruce Richardson 
315b81c9346SMaxime Coquelin 	idesc = rte_malloc_socket(__func__, desc_len, 0, vq->numa_node);
31699a2dd95SBruce Richardson 	if (unlikely(!idesc))
31799a2dd95SBruce Richardson 		return NULL;
31899a2dd95SBruce Richardson 
31999a2dd95SBruce Richardson 	dst = (uint64_t)(uintptr_t)idesc;
32099a2dd95SBruce Richardson 
32199a2dd95SBruce Richardson 	while (remain) {
32299a2dd95SBruce Richardson 		len = remain;
32399a2dd95SBruce Richardson 		src = vhost_iova_to_vva(dev, vq, desc_addr, &len,
32499a2dd95SBruce Richardson 				VHOST_ACCESS_RO);
32599a2dd95SBruce Richardson 		if (unlikely(!src || !len)) {
32699a2dd95SBruce Richardson 			rte_free(idesc);
32799a2dd95SBruce Richardson 			return NULL;
32899a2dd95SBruce Richardson 		}
32999a2dd95SBruce Richardson 
33099a2dd95SBruce Richardson 		rte_memcpy((void *)(uintptr_t)dst, (void *)(uintptr_t)src, len);
33199a2dd95SBruce Richardson 
33299a2dd95SBruce Richardson 		remain -= len;
33399a2dd95SBruce Richardson 		dst += len;
33499a2dd95SBruce Richardson 		desc_addr += len;
33599a2dd95SBruce Richardson 	}
33699a2dd95SBruce Richardson 
33799a2dd95SBruce Richardson 	return idesc;
33899a2dd95SBruce Richardson }
33999a2dd95SBruce Richardson 
34099a2dd95SBruce Richardson void
34199a2dd95SBruce Richardson cleanup_vq(struct vhost_virtqueue *vq, int destroy)
34299a2dd95SBruce Richardson {
34399a2dd95SBruce Richardson 	if ((vq->callfd >= 0) && (destroy != 0))
34499a2dd95SBruce Richardson 		close(vq->callfd);
34599a2dd95SBruce Richardson 	if (vq->kickfd >= 0)
34699a2dd95SBruce Richardson 		close(vq->kickfd);
34799a2dd95SBruce Richardson }
34899a2dd95SBruce Richardson 
34999a2dd95SBruce Richardson void
35099a2dd95SBruce Richardson cleanup_vq_inflight(struct virtio_net *dev, struct vhost_virtqueue *vq)
35199a2dd95SBruce Richardson {
35299a2dd95SBruce Richardson 	if (!(dev->protocol_features &
35399a2dd95SBruce Richardson 	    (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)))
35499a2dd95SBruce Richardson 		return;
35599a2dd95SBruce Richardson 
35699a2dd95SBruce Richardson 	if (vq_is_packed(dev)) {
35799a2dd95SBruce Richardson 		if (vq->inflight_packed)
35899a2dd95SBruce Richardson 			vq->inflight_packed = NULL;
35999a2dd95SBruce Richardson 	} else {
36099a2dd95SBruce Richardson 		if (vq->inflight_split)
36199a2dd95SBruce Richardson 			vq->inflight_split = NULL;
36299a2dd95SBruce Richardson 	}
36399a2dd95SBruce Richardson 
36499a2dd95SBruce Richardson 	if (vq->resubmit_inflight) {
36599a2dd95SBruce Richardson 		if (vq->resubmit_inflight->resubmit_list) {
366b3d4a18bSMaxime Coquelin 			rte_free(vq->resubmit_inflight->resubmit_list);
36799a2dd95SBruce Richardson 			vq->resubmit_inflight->resubmit_list = NULL;
36899a2dd95SBruce Richardson 		}
369b3d4a18bSMaxime Coquelin 		rte_free(vq->resubmit_inflight);
37099a2dd95SBruce Richardson 		vq->resubmit_inflight = NULL;
37199a2dd95SBruce Richardson 	}
37299a2dd95SBruce Richardson }
37399a2dd95SBruce Richardson 
37499a2dd95SBruce Richardson /*
37599a2dd95SBruce Richardson  * Unmap any memory, close any file descriptors and
37699a2dd95SBruce Richardson  * free any memory owned by a device.
37799a2dd95SBruce Richardson  */
37899a2dd95SBruce Richardson void
37999a2dd95SBruce Richardson cleanup_device(struct virtio_net *dev, int destroy)
38099a2dd95SBruce Richardson {
38199a2dd95SBruce Richardson 	uint32_t i;
38299a2dd95SBruce Richardson 
38399a2dd95SBruce Richardson 	vhost_backend_cleanup(dev);
38499a2dd95SBruce Richardson 
38599a2dd95SBruce Richardson 	for (i = 0; i < dev->nr_vring; i++) {
38699a2dd95SBruce Richardson 		cleanup_vq(dev->virtqueue[i], destroy);
38799a2dd95SBruce Richardson 		cleanup_vq_inflight(dev, dev->virtqueue[i]);
38899a2dd95SBruce Richardson 	}
38999a2dd95SBruce Richardson }
39099a2dd95SBruce Richardson 
39199a2dd95SBruce Richardson static void
39299a2dd95SBruce Richardson vhost_free_async_mem(struct vhost_virtqueue *vq)
3934b02c267SDavid Marchand 	__rte_exclusive_locks_required(&vq->access_lock)
39499a2dd95SBruce Richardson {
395ee8024b3SMaxime Coquelin 	if (!vq->async)
396ee8024b3SMaxime Coquelin 		return;
397873e8dadSCheng Jiang 
398ee8024b3SMaxime Coquelin 	rte_free(vq->async->pkts_info);
39953d3f477SJiayu Hu 	rte_free(vq->async->pkts_cmpl_flag);
400873e8dadSCheng Jiang 
401ee8024b3SMaxime Coquelin 	rte_free(vq->async->buffers_packed);
402ee8024b3SMaxime Coquelin 	vq->async->buffers_packed = NULL;
403ee8024b3SMaxime Coquelin 	rte_free(vq->async->descs_split);
404ee8024b3SMaxime Coquelin 	vq->async->descs_split = NULL;
40599a2dd95SBruce Richardson 
406ee8024b3SMaxime Coquelin 	rte_free(vq->async);
407ee8024b3SMaxime Coquelin 	vq->async = NULL;
40899a2dd95SBruce Richardson }
40999a2dd95SBruce Richardson 
41099a2dd95SBruce Richardson void
41199a2dd95SBruce Richardson free_vq(struct virtio_net *dev, struct vhost_virtqueue *vq)
41299a2dd95SBruce Richardson {
41399a2dd95SBruce Richardson 	if (vq_is_packed(dev))
41499a2dd95SBruce Richardson 		rte_free(vq->shadow_used_packed);
415873e8dadSCheng Jiang 	else
41699a2dd95SBruce Richardson 		rte_free(vq->shadow_used_split);
417873e8dadSCheng Jiang 
41803f77d66SEelco Chaudron 	rte_rwlock_write_lock(&vq->access_lock);
41999a2dd95SBruce Richardson 	vhost_free_async_mem(vq);
42003f77d66SEelco Chaudron 	rte_rwlock_write_unlock(&vq->access_lock);
42199a2dd95SBruce Richardson 	rte_free(vq->batch_copy_elems);
42299a2dd95SBruce Richardson 	rte_free(vq->log_cache);
42399a2dd95SBruce Richardson 	rte_free(vq);
42499a2dd95SBruce Richardson }
42599a2dd95SBruce Richardson 
42699a2dd95SBruce Richardson /*
42799a2dd95SBruce Richardson  * Release virtqueues and device memory.
42899a2dd95SBruce Richardson  */
42999a2dd95SBruce Richardson static void
43099a2dd95SBruce Richardson free_device(struct virtio_net *dev)
43199a2dd95SBruce Richardson {
43299a2dd95SBruce Richardson 	uint32_t i;
43399a2dd95SBruce Richardson 
43499a2dd95SBruce Richardson 	for (i = 0; i < dev->nr_vring; i++)
43599a2dd95SBruce Richardson 		free_vq(dev, dev->virtqueue[i]);
43699a2dd95SBruce Richardson 
43799a2dd95SBruce Richardson 	rte_free(dev);
43899a2dd95SBruce Richardson }
43999a2dd95SBruce Richardson 
44099a2dd95SBruce Richardson static __rte_always_inline int
44199a2dd95SBruce Richardson log_translate(struct virtio_net *dev, struct vhost_virtqueue *vq)
442bf42fb30SDavid Marchand 	__rte_shared_locks_required(&vq->iotlb_lock)
44399a2dd95SBruce Richardson {
44499a2dd95SBruce Richardson 	if (likely(!(vq->ring_addrs.flags & (1 << VHOST_VRING_F_LOG))))
44599a2dd95SBruce Richardson 		return 0;
44699a2dd95SBruce Richardson 
44799a2dd95SBruce Richardson 	vq->log_guest_addr = translate_log_addr(dev, vq,
44899a2dd95SBruce Richardson 						vq->ring_addrs.log_guest_addr);
44999a2dd95SBruce Richardson 	if (vq->log_guest_addr == 0)
45099a2dd95SBruce Richardson 		return -1;
45199a2dd95SBruce Richardson 
45299a2dd95SBruce Richardson 	return 0;
45399a2dd95SBruce Richardson }
45499a2dd95SBruce Richardson 
45599a2dd95SBruce Richardson /*
45699a2dd95SBruce Richardson  * Converts vring log address to GPA
45799a2dd95SBruce Richardson  * If IOMMU is enabled, the log address is IOVA
45899a2dd95SBruce Richardson  * If IOMMU not enabled, the log address is already GPA
45999a2dd95SBruce Richardson  */
46099a2dd95SBruce Richardson uint64_t
46199a2dd95SBruce Richardson translate_log_addr(struct virtio_net *dev, struct vhost_virtqueue *vq,
46299a2dd95SBruce Richardson 		uint64_t log_addr)
46399a2dd95SBruce Richardson {
46499a2dd95SBruce Richardson 	if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM)) {
46599a2dd95SBruce Richardson 		const uint64_t exp_size = sizeof(uint64_t);
46699a2dd95SBruce Richardson 		uint64_t hva, gpa;
46799a2dd95SBruce Richardson 		uint64_t size = exp_size;
46899a2dd95SBruce Richardson 
46999a2dd95SBruce Richardson 		hva = vhost_iova_to_vva(dev, vq, log_addr,
47099a2dd95SBruce Richardson 					&size, VHOST_ACCESS_RW);
47199a2dd95SBruce Richardson 
47299a2dd95SBruce Richardson 		if (size != exp_size)
47399a2dd95SBruce Richardson 			return 0;
47499a2dd95SBruce Richardson 
47599a2dd95SBruce Richardson 		gpa = hva_to_gpa(dev, hva, exp_size);
47699a2dd95SBruce Richardson 		if (!gpa) {
4770e21c7c0SDavid Marchand 			VHOST_DATA_LOG(dev->ifname, ERR,
47836c525a0SDavid Marchand 				"failed to find GPA for log_addr: 0x%"
4790e21c7c0SDavid Marchand 				PRIx64 " hva: 0x%" PRIx64,
48036c525a0SDavid Marchand 				log_addr, hva);
48199a2dd95SBruce Richardson 			return 0;
48299a2dd95SBruce Richardson 		}
48399a2dd95SBruce Richardson 		return gpa;
48499a2dd95SBruce Richardson 
48599a2dd95SBruce Richardson 	} else
48699a2dd95SBruce Richardson 		return log_addr;
48799a2dd95SBruce Richardson }
48899a2dd95SBruce Richardson 
48999a2dd95SBruce Richardson static int
49099a2dd95SBruce Richardson vring_translate_split(struct virtio_net *dev, struct vhost_virtqueue *vq)
491bf42fb30SDavid Marchand 	__rte_shared_locks_required(&vq->iotlb_lock)
49299a2dd95SBruce Richardson {
49399a2dd95SBruce Richardson 	uint64_t req_size, size;
49499a2dd95SBruce Richardson 
49599a2dd95SBruce Richardson 	req_size = sizeof(struct vring_desc) * vq->size;
49699a2dd95SBruce Richardson 	size = req_size;
49799a2dd95SBruce Richardson 	vq->desc = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, vq,
49899a2dd95SBruce Richardson 						vq->ring_addrs.desc_user_addr,
49999a2dd95SBruce Richardson 						&size, VHOST_ACCESS_RW);
50099a2dd95SBruce Richardson 	if (!vq->desc || size != req_size)
50199a2dd95SBruce Richardson 		return -1;
50299a2dd95SBruce Richardson 
50399a2dd95SBruce Richardson 	req_size = sizeof(struct vring_avail);
50499a2dd95SBruce Richardson 	req_size += sizeof(uint16_t) * vq->size;
50599a2dd95SBruce Richardson 	if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
50699a2dd95SBruce Richardson 		req_size += sizeof(uint16_t);
50799a2dd95SBruce Richardson 	size = req_size;
50899a2dd95SBruce Richardson 	vq->avail = (struct vring_avail *)(uintptr_t)vhost_iova_to_vva(dev, vq,
50999a2dd95SBruce Richardson 						vq->ring_addrs.avail_user_addr,
51099a2dd95SBruce Richardson 						&size, VHOST_ACCESS_RW);
51199a2dd95SBruce Richardson 	if (!vq->avail || size != req_size)
51299a2dd95SBruce Richardson 		return -1;
51399a2dd95SBruce Richardson 
51499a2dd95SBruce Richardson 	req_size = sizeof(struct vring_used);
51599a2dd95SBruce Richardson 	req_size += sizeof(struct vring_used_elem) * vq->size;
51699a2dd95SBruce Richardson 	if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
51799a2dd95SBruce Richardson 		req_size += sizeof(uint16_t);
51899a2dd95SBruce Richardson 	size = req_size;
51999a2dd95SBruce Richardson 	vq->used = (struct vring_used *)(uintptr_t)vhost_iova_to_vva(dev, vq,
52099a2dd95SBruce Richardson 						vq->ring_addrs.used_user_addr,
52199a2dd95SBruce Richardson 						&size, VHOST_ACCESS_RW);
52299a2dd95SBruce Richardson 	if (!vq->used || size != req_size)
52399a2dd95SBruce Richardson 		return -1;
52499a2dd95SBruce Richardson 
52599a2dd95SBruce Richardson 	return 0;
52699a2dd95SBruce Richardson }
52799a2dd95SBruce Richardson 
52899a2dd95SBruce Richardson static int
52999a2dd95SBruce Richardson vring_translate_packed(struct virtio_net *dev, struct vhost_virtqueue *vq)
530bf42fb30SDavid Marchand 	__rte_shared_locks_required(&vq->iotlb_lock)
53199a2dd95SBruce Richardson {
53299a2dd95SBruce Richardson 	uint64_t req_size, size;
53399a2dd95SBruce Richardson 
53499a2dd95SBruce Richardson 	req_size = sizeof(struct vring_packed_desc) * vq->size;
53599a2dd95SBruce Richardson 	size = req_size;
53699a2dd95SBruce Richardson 	vq->desc_packed = (struct vring_packed_desc *)(uintptr_t)
53799a2dd95SBruce Richardson 		vhost_iova_to_vva(dev, vq, vq->ring_addrs.desc_user_addr,
53899a2dd95SBruce Richardson 				&size, VHOST_ACCESS_RW);
53999a2dd95SBruce Richardson 	if (!vq->desc_packed || size != req_size)
54099a2dd95SBruce Richardson 		return -1;
54199a2dd95SBruce Richardson 
54299a2dd95SBruce Richardson 	req_size = sizeof(struct vring_packed_desc_event);
54399a2dd95SBruce Richardson 	size = req_size;
54499a2dd95SBruce Richardson 	vq->driver_event = (struct vring_packed_desc_event *)(uintptr_t)
54599a2dd95SBruce Richardson 		vhost_iova_to_vva(dev, vq, vq->ring_addrs.avail_user_addr,
54699a2dd95SBruce Richardson 				&size, VHOST_ACCESS_RW);
54799a2dd95SBruce Richardson 	if (!vq->driver_event || size != req_size)
54899a2dd95SBruce Richardson 		return -1;
54999a2dd95SBruce Richardson 
55099a2dd95SBruce Richardson 	req_size = sizeof(struct vring_packed_desc_event);
55199a2dd95SBruce Richardson 	size = req_size;
55299a2dd95SBruce Richardson 	vq->device_event = (struct vring_packed_desc_event *)(uintptr_t)
55399a2dd95SBruce Richardson 		vhost_iova_to_vva(dev, vq, vq->ring_addrs.used_user_addr,
55499a2dd95SBruce Richardson 				&size, VHOST_ACCESS_RW);
55599a2dd95SBruce Richardson 	if (!vq->device_event || size != req_size)
55699a2dd95SBruce Richardson 		return -1;
55799a2dd95SBruce Richardson 
55899a2dd95SBruce Richardson 	return 0;
55999a2dd95SBruce Richardson }
56099a2dd95SBruce Richardson 
56199a2dd95SBruce Richardson int
56299a2dd95SBruce Richardson vring_translate(struct virtio_net *dev, struct vhost_virtqueue *vq)
56399a2dd95SBruce Richardson {
56499a2dd95SBruce Richardson 
56599a2dd95SBruce Richardson 	if (!(dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM)))
56699a2dd95SBruce Richardson 		return -1;
56799a2dd95SBruce Richardson 
56899a2dd95SBruce Richardson 	if (vq_is_packed(dev)) {
56999a2dd95SBruce Richardson 		if (vring_translate_packed(dev, vq) < 0)
57099a2dd95SBruce Richardson 			return -1;
57199a2dd95SBruce Richardson 	} else {
57299a2dd95SBruce Richardson 		if (vring_translate_split(dev, vq) < 0)
57399a2dd95SBruce Richardson 			return -1;
57499a2dd95SBruce Richardson 	}
57599a2dd95SBruce Richardson 
57699a2dd95SBruce Richardson 	if (log_translate(dev, vq) < 0)
57799a2dd95SBruce Richardson 		return -1;
57899a2dd95SBruce Richardson 
57999a2dd95SBruce Richardson 	vq->access_ok = true;
58099a2dd95SBruce Richardson 
58199a2dd95SBruce Richardson 	return 0;
58299a2dd95SBruce Richardson }
58399a2dd95SBruce Richardson 
58499a2dd95SBruce Richardson void
58500bdbe00SDavid Marchand vring_invalidate(struct virtio_net *dev __rte_unused, struct vhost_virtqueue *vq)
58699a2dd95SBruce Richardson {
58799a2dd95SBruce Richardson 	vhost_user_iotlb_wr_lock(vq);
58899a2dd95SBruce Richardson 
58999a2dd95SBruce Richardson 	vq->access_ok = false;
59099a2dd95SBruce Richardson 	vq->desc = NULL;
59199a2dd95SBruce Richardson 	vq->avail = NULL;
59299a2dd95SBruce Richardson 	vq->used = NULL;
59399a2dd95SBruce Richardson 	vq->log_guest_addr = 0;
59499a2dd95SBruce Richardson 
59599a2dd95SBruce Richardson 	vhost_user_iotlb_wr_unlock(vq);
59699a2dd95SBruce Richardson }
59799a2dd95SBruce Richardson 
59899a2dd95SBruce Richardson static void
599a54f046dSMaxime Coquelin init_vring_queue(struct virtio_net *dev __rte_unused, struct vhost_virtqueue *vq,
60057e414e3SDavid Marchand 	uint32_t vring_idx)
60199a2dd95SBruce Richardson {
602b81c9346SMaxime Coquelin 	int numa_node = SOCKET_ID_ANY;
60399a2dd95SBruce Richardson 
60499a2dd95SBruce Richardson 	memset(vq, 0, sizeof(struct vhost_virtqueue));
60599a2dd95SBruce Richardson 
60657e414e3SDavid Marchand 	vq->index = vring_idx;
60799a2dd95SBruce Richardson 	vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD;
60899a2dd95SBruce Richardson 	vq->callfd = VIRTIO_UNINITIALIZED_EVENTFD;
60999a2dd95SBruce Richardson 	vq->notif_enable = VIRTIO_UNINITIALIZED_NOTIF;
610f31ce483SDavid Marchand 
611b81c9346SMaxime Coquelin #ifdef RTE_LIBRTE_VHOST_NUMA
612b81c9346SMaxime Coquelin 	if (get_mempolicy(&numa_node, NULL, 0, vq, MPOL_F_NODE | MPOL_F_ADDR)) {
6130e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to query numa node: %s",
61436c525a0SDavid Marchand 			rte_strerror(errno));
615b81c9346SMaxime Coquelin 		numa_node = SOCKET_ID_ANY;
616b81c9346SMaxime Coquelin 	}
617b81c9346SMaxime Coquelin #endif
618b81c9346SMaxime Coquelin 	vq->numa_node = numa_node;
61999a2dd95SBruce Richardson }
62099a2dd95SBruce Richardson 
62199a2dd95SBruce Richardson static void
62257e414e3SDavid Marchand reset_vring_queue(struct virtio_net *dev, struct vhost_virtqueue *vq)
62399a2dd95SBruce Richardson {
62499a2dd95SBruce Richardson 	int callfd;
62599a2dd95SBruce Richardson 
62699a2dd95SBruce Richardson 	callfd = vq->callfd;
62757e414e3SDavid Marchand 	init_vring_queue(dev, vq, vq->index);
62899a2dd95SBruce Richardson 	vq->callfd = callfd;
62999a2dd95SBruce Richardson }
63099a2dd95SBruce Richardson 
63199a2dd95SBruce Richardson int
63299a2dd95SBruce Richardson alloc_vring_queue(struct virtio_net *dev, uint32_t vring_idx)
63399a2dd95SBruce Richardson {
63499a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
63599a2dd95SBruce Richardson 	uint32_t i;
63699a2dd95SBruce Richardson 
63799a2dd95SBruce Richardson 	/* Also allocate holes, if any, up to requested vring index. */
63899a2dd95SBruce Richardson 	for (i = 0; i <= vring_idx; i++) {
63999a2dd95SBruce Richardson 		if (dev->virtqueue[i])
64099a2dd95SBruce Richardson 			continue;
64199a2dd95SBruce Richardson 
642678a91efSJiayu Hu 		vq = rte_zmalloc(NULL, sizeof(struct vhost_virtqueue), 0);
64399a2dd95SBruce Richardson 		if (vq == NULL) {
6440e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(dev->ifname, ERR,
6450e21c7c0SDavid Marchand 				"failed to allocate memory for vring %u.",
64636c525a0SDavid Marchand 				i);
64799a2dd95SBruce Richardson 			return -1;
64899a2dd95SBruce Richardson 		}
64999a2dd95SBruce Richardson 
65099a2dd95SBruce Richardson 		dev->virtqueue[i] = vq;
65157e414e3SDavid Marchand 		init_vring_queue(dev, vq, i);
65203f77d66SEelco Chaudron 		rte_rwlock_init(&vq->access_lock);
653a54f046dSMaxime Coquelin 		rte_rwlock_init(&vq->iotlb_lock);
65499a2dd95SBruce Richardson 		vq->avail_wrap_counter = 1;
65599a2dd95SBruce Richardson 		vq->used_wrap_counter = 1;
65699a2dd95SBruce Richardson 		vq->signalled_used_valid = false;
65799a2dd95SBruce Richardson 	}
65899a2dd95SBruce Richardson 
65999a2dd95SBruce Richardson 	dev->nr_vring = RTE_MAX(dev->nr_vring, vring_idx + 1);
66099a2dd95SBruce Richardson 
66199a2dd95SBruce Richardson 	return 0;
66299a2dd95SBruce Richardson }
66399a2dd95SBruce Richardson 
66499a2dd95SBruce Richardson /*
66599a2dd95SBruce Richardson  * Reset some variables in device structure, while keeping few
66699a2dd95SBruce Richardson  * others untouched, such as vid, ifname, nr_vring: they
66799a2dd95SBruce Richardson  * should be same unless the device is removed.
66899a2dd95SBruce Richardson  */
66999a2dd95SBruce Richardson void
67099a2dd95SBruce Richardson reset_device(struct virtio_net *dev)
67199a2dd95SBruce Richardson {
67299a2dd95SBruce Richardson 	uint32_t i;
67399a2dd95SBruce Richardson 
67499a2dd95SBruce Richardson 	dev->features = 0;
67599a2dd95SBruce Richardson 	dev->protocol_features = 0;
67699a2dd95SBruce Richardson 	dev->flags &= VIRTIO_DEV_BUILTIN_VIRTIO_NET;
67799a2dd95SBruce Richardson 
67857e414e3SDavid Marchand 	for (i = 0; i < dev->nr_vring; i++) {
67957e414e3SDavid Marchand 		struct vhost_virtqueue *vq = dev->virtqueue[i];
68057e414e3SDavid Marchand 
68157e414e3SDavid Marchand 		if (!vq) {
6820e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(dev->ifname, ERR,
6830e21c7c0SDavid Marchand 				"failed to reset vring, virtqueue not allocated (%d)", i);
68457e414e3SDavid Marchand 			continue;
68557e414e3SDavid Marchand 		}
68657e414e3SDavid Marchand 		reset_vring_queue(dev, vq);
68757e414e3SDavid Marchand 	}
68899a2dd95SBruce Richardson }
68999a2dd95SBruce Richardson 
69099a2dd95SBruce Richardson /*
69199a2dd95SBruce Richardson  * Invoked when there is a new vhost-user connection established (when
69299a2dd95SBruce Richardson  * there is a new virtio device being attached).
69399a2dd95SBruce Richardson  */
69499a2dd95SBruce Richardson int
6954dbf9316SMaxime Coquelin vhost_new_device(struct vhost_backend_ops *ops)
69699a2dd95SBruce Richardson {
69799a2dd95SBruce Richardson 	struct virtio_net *dev;
69899a2dd95SBruce Richardson 	int i;
69999a2dd95SBruce Richardson 
7004dbf9316SMaxime Coquelin 	if (ops == NULL) {
7010e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("device", ERR, "missing backend ops.");
7024dbf9316SMaxime Coquelin 		return -1;
7034dbf9316SMaxime Coquelin 	}
7044dbf9316SMaxime Coquelin 
705357b9359SMaxime Coquelin 	if (ops->iotlb_miss == NULL) {
7060e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("device", ERR, "missing IOTLB miss backend op.");
707357b9359SMaxime Coquelin 		return -1;
708357b9359SMaxime Coquelin 	}
709357b9359SMaxime Coquelin 
710a5dd9842SMaxime Coquelin 	if (ops->inject_irq == NULL) {
7110e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("device", ERR, "missing IRQ injection backend op.");
712a5dd9842SMaxime Coquelin 		return -1;
713a5dd9842SMaxime Coquelin 	}
714a5dd9842SMaxime Coquelin 
71599a2dd95SBruce Richardson 	pthread_mutex_lock(&vhost_dev_lock);
71653d3f477SJiayu Hu 	for (i = 0; i < RTE_MAX_VHOST_DEVICE; i++) {
71799a2dd95SBruce Richardson 		if (vhost_devices[i] == NULL)
71899a2dd95SBruce Richardson 			break;
71999a2dd95SBruce Richardson 	}
72099a2dd95SBruce Richardson 
72153d3f477SJiayu Hu 	if (i == RTE_MAX_VHOST_DEVICE) {
7220e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("device", ERR, "failed to find a free slot for new device.");
72399a2dd95SBruce Richardson 		pthread_mutex_unlock(&vhost_dev_lock);
72499a2dd95SBruce Richardson 		return -1;
72599a2dd95SBruce Richardson 	}
72699a2dd95SBruce Richardson 
72799a2dd95SBruce Richardson 	dev = rte_zmalloc(NULL, sizeof(struct virtio_net), 0);
72899a2dd95SBruce Richardson 	if (dev == NULL) {
7290e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("device", ERR, "failed to allocate memory for new device.");
73099a2dd95SBruce Richardson 		pthread_mutex_unlock(&vhost_dev_lock);
73199a2dd95SBruce Richardson 		return -1;
73299a2dd95SBruce Richardson 	}
73399a2dd95SBruce Richardson 
73499a2dd95SBruce Richardson 	vhost_devices[i] = dev;
73599a2dd95SBruce Richardson 	pthread_mutex_unlock(&vhost_dev_lock);
73699a2dd95SBruce Richardson 
73799a2dd95SBruce Richardson 	dev->vid = i;
73899a2dd95SBruce Richardson 	dev->flags = VIRTIO_DEV_BUILTIN_VIRTIO_NET;
73971998eb6SNobuhiro Miki 	dev->backend_req_fd = -1;
74099a2dd95SBruce Richardson 	dev->postcopy_ufd = -1;
74171998eb6SNobuhiro Miki 	rte_spinlock_init(&dev->backend_req_lock);
7424dbf9316SMaxime Coquelin 	dev->backend_ops = ops;
74399a2dd95SBruce Richardson 
74499a2dd95SBruce Richardson 	return i;
74599a2dd95SBruce Richardson }
74699a2dd95SBruce Richardson 
74799a2dd95SBruce Richardson void
74899a2dd95SBruce Richardson vhost_destroy_device_notify(struct virtio_net *dev)
74999a2dd95SBruce Richardson {
75099a2dd95SBruce Richardson 	struct rte_vdpa_device *vdpa_dev;
75199a2dd95SBruce Richardson 
75299a2dd95SBruce Richardson 	if (dev->flags & VIRTIO_DEV_RUNNING) {
75399a2dd95SBruce Richardson 		vdpa_dev = dev->vdpa_dev;
75499a2dd95SBruce Richardson 		if (vdpa_dev)
75599a2dd95SBruce Richardson 			vdpa_dev->ops->dev_close(dev->vid);
75699a2dd95SBruce Richardson 		dev->flags &= ~VIRTIO_DEV_RUNNING;
75799a2dd95SBruce Richardson 		dev->notify_ops->destroy_device(dev->vid);
75899a2dd95SBruce Richardson 	}
75999a2dd95SBruce Richardson }
76099a2dd95SBruce Richardson 
76199a2dd95SBruce Richardson /*
76299a2dd95SBruce Richardson  * Invoked when there is the vhost-user connection is broken (when
76399a2dd95SBruce Richardson  * the virtio device is being detached).
76499a2dd95SBruce Richardson  */
76599a2dd95SBruce Richardson void
76699a2dd95SBruce Richardson vhost_destroy_device(int vid)
76799a2dd95SBruce Richardson {
76899a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
76999a2dd95SBruce Richardson 
77099a2dd95SBruce Richardson 	if (dev == NULL)
77199a2dd95SBruce Richardson 		return;
77299a2dd95SBruce Richardson 
77399a2dd95SBruce Richardson 	vhost_destroy_device_notify(dev);
77499a2dd95SBruce Richardson 
77599a2dd95SBruce Richardson 	cleanup_device(dev, 1);
77699a2dd95SBruce Richardson 	free_device(dev);
77799a2dd95SBruce Richardson 
77899a2dd95SBruce Richardson 	vhost_devices[vid] = NULL;
77999a2dd95SBruce Richardson }
78099a2dd95SBruce Richardson 
78199a2dd95SBruce Richardson void
78299a2dd95SBruce Richardson vhost_attach_vdpa_device(int vid, struct rte_vdpa_device *vdpa_dev)
78399a2dd95SBruce Richardson {
78499a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
78599a2dd95SBruce Richardson 
78699a2dd95SBruce Richardson 	if (dev == NULL)
78799a2dd95SBruce Richardson 		return;
78899a2dd95SBruce Richardson 
78999a2dd95SBruce Richardson 	dev->vdpa_dev = vdpa_dev;
79099a2dd95SBruce Richardson }
79199a2dd95SBruce Richardson 
79299a2dd95SBruce Richardson void
79399a2dd95SBruce Richardson vhost_set_ifname(int vid, const char *if_name, unsigned int if_len)
79499a2dd95SBruce Richardson {
79599a2dd95SBruce Richardson 	struct virtio_net *dev;
79699a2dd95SBruce Richardson 	unsigned int len;
79799a2dd95SBruce Richardson 
79899a2dd95SBruce Richardson 	dev = get_device(vid);
79999a2dd95SBruce Richardson 	if (dev == NULL)
80099a2dd95SBruce Richardson 		return;
80199a2dd95SBruce Richardson 
80299a2dd95SBruce Richardson 	len = if_len > sizeof(dev->ifname) ?
80399a2dd95SBruce Richardson 		sizeof(dev->ifname) : if_len;
80499a2dd95SBruce Richardson 
80599a2dd95SBruce Richardson 	strncpy(dev->ifname, if_name, len);
80699a2dd95SBruce Richardson 	dev->ifname[sizeof(dev->ifname) - 1] = '\0';
80799a2dd95SBruce Richardson }
80899a2dd95SBruce Richardson 
80999a2dd95SBruce Richardson void
8101a44f67aSDavid Marchand vhost_setup_virtio_net(int vid, bool enable, bool compliant_ol_flags, bool stats_enabled,
8111a44f67aSDavid Marchand 	bool support_iommu)
81299a2dd95SBruce Richardson {
81399a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
81499a2dd95SBruce Richardson 
81599a2dd95SBruce Richardson 	if (dev == NULL)
81699a2dd95SBruce Richardson 		return;
81799a2dd95SBruce Richardson 
81899a2dd95SBruce Richardson 	if (enable)
81999a2dd95SBruce Richardson 		dev->flags |= VIRTIO_DEV_BUILTIN_VIRTIO_NET;
82099a2dd95SBruce Richardson 	else
82199a2dd95SBruce Richardson 		dev->flags &= ~VIRTIO_DEV_BUILTIN_VIRTIO_NET;
822ca7036b4SDavid Marchand 	if (!compliant_ol_flags)
823ca7036b4SDavid Marchand 		dev->flags |= VIRTIO_DEV_LEGACY_OL_FLAGS;
824ca7036b4SDavid Marchand 	else
825ca7036b4SDavid Marchand 		dev->flags &= ~VIRTIO_DEV_LEGACY_OL_FLAGS;
826be75dc99SMaxime Coquelin 	if (stats_enabled)
827be75dc99SMaxime Coquelin 		dev->flags |= VIRTIO_DEV_STATS_ENABLED;
828be75dc99SMaxime Coquelin 	else
829be75dc99SMaxime Coquelin 		dev->flags &= ~VIRTIO_DEV_STATS_ENABLED;
8301a44f67aSDavid Marchand 	if (support_iommu)
8311a44f67aSDavid Marchand 		dev->flags |= VIRTIO_DEV_SUPPORT_IOMMU;
8321a44f67aSDavid Marchand 	else
8331a44f67aSDavid Marchand 		dev->flags &= ~VIRTIO_DEV_SUPPORT_IOMMU;
834a54f046dSMaxime Coquelin 
835a54f046dSMaxime Coquelin 	if (vhost_user_iotlb_init(dev) < 0)
8360e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("device", ERR, "failed to init IOTLB");
837a54f046dSMaxime Coquelin 
83899a2dd95SBruce Richardson }
83999a2dd95SBruce Richardson 
84099a2dd95SBruce Richardson void
84199a2dd95SBruce Richardson vhost_enable_extbuf(int vid)
84299a2dd95SBruce Richardson {
84399a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
84499a2dd95SBruce Richardson 
84599a2dd95SBruce Richardson 	if (dev == NULL)
84699a2dd95SBruce Richardson 		return;
84799a2dd95SBruce Richardson 
84899a2dd95SBruce Richardson 	dev->extbuf = 1;
84999a2dd95SBruce Richardson }
85099a2dd95SBruce Richardson 
85199a2dd95SBruce Richardson void
85299a2dd95SBruce Richardson vhost_enable_linearbuf(int vid)
85399a2dd95SBruce Richardson {
85499a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
85599a2dd95SBruce Richardson 
85699a2dd95SBruce Richardson 	if (dev == NULL)
85799a2dd95SBruce Richardson 		return;
85899a2dd95SBruce Richardson 
85999a2dd95SBruce Richardson 	dev->linearbuf = 1;
86099a2dd95SBruce Richardson }
86199a2dd95SBruce Richardson 
86299a2dd95SBruce Richardson int
86399a2dd95SBruce Richardson rte_vhost_get_mtu(int vid, uint16_t *mtu)
86499a2dd95SBruce Richardson {
86599a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
86699a2dd95SBruce Richardson 
86799a2dd95SBruce Richardson 	if (dev == NULL || mtu == NULL)
86899a2dd95SBruce Richardson 		return -ENODEV;
86999a2dd95SBruce Richardson 
87099a2dd95SBruce Richardson 	if (!(dev->flags & VIRTIO_DEV_READY))
87199a2dd95SBruce Richardson 		return -EAGAIN;
87299a2dd95SBruce Richardson 
87399a2dd95SBruce Richardson 	if (!(dev->features & (1ULL << VIRTIO_NET_F_MTU)))
87499a2dd95SBruce Richardson 		return -ENOTSUP;
87599a2dd95SBruce Richardson 
87699a2dd95SBruce Richardson 	*mtu = dev->mtu;
87799a2dd95SBruce Richardson 
87899a2dd95SBruce Richardson 	return 0;
87999a2dd95SBruce Richardson }
88099a2dd95SBruce Richardson 
88199a2dd95SBruce Richardson int
88299a2dd95SBruce Richardson rte_vhost_get_numa_node(int vid)
88399a2dd95SBruce Richardson {
88499a2dd95SBruce Richardson #ifdef RTE_LIBRTE_VHOST_NUMA
88599a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
88699a2dd95SBruce Richardson 	int numa_node;
88799a2dd95SBruce Richardson 	int ret;
88899a2dd95SBruce Richardson 
88999a2dd95SBruce Richardson 	if (dev == NULL || numa_available() != 0)
89099a2dd95SBruce Richardson 		return -1;
89199a2dd95SBruce Richardson 
89299a2dd95SBruce Richardson 	ret = get_mempolicy(&numa_node, NULL, 0, dev,
89399a2dd95SBruce Richardson 			    MPOL_F_NODE | MPOL_F_ADDR);
89499a2dd95SBruce Richardson 	if (ret < 0) {
8950e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to query numa node: %s",
89636c525a0SDavid Marchand 			rte_strerror(errno));
89799a2dd95SBruce Richardson 		return -1;
89899a2dd95SBruce Richardson 	}
89999a2dd95SBruce Richardson 
90099a2dd95SBruce Richardson 	return numa_node;
90199a2dd95SBruce Richardson #else
90299a2dd95SBruce Richardson 	RTE_SET_USED(vid);
90399a2dd95SBruce Richardson 	return -1;
90499a2dd95SBruce Richardson #endif
90599a2dd95SBruce Richardson }
90699a2dd95SBruce Richardson 
90799a2dd95SBruce Richardson uint16_t
90899a2dd95SBruce Richardson rte_vhost_get_vring_num(int vid)
90999a2dd95SBruce Richardson {
91099a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
91199a2dd95SBruce Richardson 
91299a2dd95SBruce Richardson 	if (dev == NULL)
91399a2dd95SBruce Richardson 		return 0;
91499a2dd95SBruce Richardson 
91599a2dd95SBruce Richardson 	return dev->nr_vring;
91699a2dd95SBruce Richardson }
91799a2dd95SBruce Richardson 
91899a2dd95SBruce Richardson int
91999a2dd95SBruce Richardson rte_vhost_get_ifname(int vid, char *buf, size_t len)
92099a2dd95SBruce Richardson {
92199a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
92299a2dd95SBruce Richardson 
92399a2dd95SBruce Richardson 	if (dev == NULL || buf == NULL)
92499a2dd95SBruce Richardson 		return -1;
92599a2dd95SBruce Richardson 
92699a2dd95SBruce Richardson 	len = RTE_MIN(len, sizeof(dev->ifname));
92799a2dd95SBruce Richardson 
92899a2dd95SBruce Richardson 	strncpy(buf, dev->ifname, len);
92999a2dd95SBruce Richardson 	buf[len - 1] = '\0';
93099a2dd95SBruce Richardson 
93199a2dd95SBruce Richardson 	return 0;
93299a2dd95SBruce Richardson }
93399a2dd95SBruce Richardson 
93499a2dd95SBruce Richardson int
93599a2dd95SBruce Richardson rte_vhost_get_negotiated_features(int vid, uint64_t *features)
93699a2dd95SBruce Richardson {
93799a2dd95SBruce Richardson 	struct virtio_net *dev;
93899a2dd95SBruce Richardson 
93999a2dd95SBruce Richardson 	dev = get_device(vid);
94099a2dd95SBruce Richardson 	if (dev == NULL || features == NULL)
94199a2dd95SBruce Richardson 		return -1;
94299a2dd95SBruce Richardson 
94399a2dd95SBruce Richardson 	*features = dev->features;
94499a2dd95SBruce Richardson 	return 0;
94599a2dd95SBruce Richardson }
94699a2dd95SBruce Richardson 
94799a2dd95SBruce Richardson int
94899a2dd95SBruce Richardson rte_vhost_get_negotiated_protocol_features(int vid,
94999a2dd95SBruce Richardson 					   uint64_t *protocol_features)
95099a2dd95SBruce Richardson {
95199a2dd95SBruce Richardson 	struct virtio_net *dev;
95299a2dd95SBruce Richardson 
95399a2dd95SBruce Richardson 	dev = get_device(vid);
95499a2dd95SBruce Richardson 	if (dev == NULL || protocol_features == NULL)
95599a2dd95SBruce Richardson 		return -1;
95699a2dd95SBruce Richardson 
95799a2dd95SBruce Richardson 	*protocol_features = dev->protocol_features;
95899a2dd95SBruce Richardson 	return 0;
95999a2dd95SBruce Richardson }
96099a2dd95SBruce Richardson 
96199a2dd95SBruce Richardson int
96299a2dd95SBruce Richardson rte_vhost_get_mem_table(int vid, struct rte_vhost_memory **mem)
96399a2dd95SBruce Richardson {
96499a2dd95SBruce Richardson 	struct virtio_net *dev;
96599a2dd95SBruce Richardson 	struct rte_vhost_memory *m;
96699a2dd95SBruce Richardson 	size_t size;
96799a2dd95SBruce Richardson 
96899a2dd95SBruce Richardson 	dev = get_device(vid);
96999a2dd95SBruce Richardson 	if (dev == NULL || mem == NULL)
97099a2dd95SBruce Richardson 		return -1;
97199a2dd95SBruce Richardson 
97299a2dd95SBruce Richardson 	size = dev->mem->nregions * sizeof(struct rte_vhost_mem_region);
97399a2dd95SBruce Richardson 	m = malloc(sizeof(struct rte_vhost_memory) + size);
97499a2dd95SBruce Richardson 	if (!m)
97599a2dd95SBruce Richardson 		return -1;
97699a2dd95SBruce Richardson 
97799a2dd95SBruce Richardson 	m->nregions = dev->mem->nregions;
97899a2dd95SBruce Richardson 	memcpy(m->regions, dev->mem->regions, size);
97999a2dd95SBruce Richardson 	*mem = m;
98099a2dd95SBruce Richardson 
98199a2dd95SBruce Richardson 	return 0;
98299a2dd95SBruce Richardson }
98399a2dd95SBruce Richardson 
98499a2dd95SBruce Richardson int
98599a2dd95SBruce Richardson rte_vhost_get_vhost_vring(int vid, uint16_t vring_idx,
98699a2dd95SBruce Richardson 			  struct rte_vhost_vring *vring)
98799a2dd95SBruce Richardson {
98899a2dd95SBruce Richardson 	struct virtio_net *dev;
98999a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
99099a2dd95SBruce Richardson 
99199a2dd95SBruce Richardson 	dev = get_device(vid);
99299a2dd95SBruce Richardson 	if (dev == NULL || vring == NULL)
99399a2dd95SBruce Richardson 		return -1;
99499a2dd95SBruce Richardson 
99599a2dd95SBruce Richardson 	if (vring_idx >= VHOST_MAX_VRING)
99699a2dd95SBruce Richardson 		return -1;
99799a2dd95SBruce Richardson 
99899a2dd95SBruce Richardson 	vq = dev->virtqueue[vring_idx];
99999a2dd95SBruce Richardson 	if (!vq)
100099a2dd95SBruce Richardson 		return -1;
100199a2dd95SBruce Richardson 
100299a2dd95SBruce Richardson 	if (vq_is_packed(dev)) {
100399a2dd95SBruce Richardson 		vring->desc_packed = vq->desc_packed;
100499a2dd95SBruce Richardson 		vring->driver_event = vq->driver_event;
100599a2dd95SBruce Richardson 		vring->device_event = vq->device_event;
100699a2dd95SBruce Richardson 	} else {
100799a2dd95SBruce Richardson 		vring->desc = vq->desc;
100899a2dd95SBruce Richardson 		vring->avail = vq->avail;
100999a2dd95SBruce Richardson 		vring->used = vq->used;
101099a2dd95SBruce Richardson 	}
101199a2dd95SBruce Richardson 	vring->log_guest_addr  = vq->log_guest_addr;
101299a2dd95SBruce Richardson 
101399a2dd95SBruce Richardson 	vring->callfd  = vq->callfd;
101499a2dd95SBruce Richardson 	vring->kickfd  = vq->kickfd;
101599a2dd95SBruce Richardson 	vring->size    = vq->size;
101699a2dd95SBruce Richardson 
101799a2dd95SBruce Richardson 	return 0;
101899a2dd95SBruce Richardson }
101999a2dd95SBruce Richardson 
102099a2dd95SBruce Richardson int
102199a2dd95SBruce Richardson rte_vhost_get_vhost_ring_inflight(int vid, uint16_t vring_idx,
102299a2dd95SBruce Richardson 				  struct rte_vhost_ring_inflight *vring)
102399a2dd95SBruce Richardson {
102499a2dd95SBruce Richardson 	struct virtio_net *dev;
102599a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
102699a2dd95SBruce Richardson 
102799a2dd95SBruce Richardson 	dev = get_device(vid);
102899a2dd95SBruce Richardson 	if (unlikely(!dev))
102999a2dd95SBruce Richardson 		return -1;
103099a2dd95SBruce Richardson 
103199a2dd95SBruce Richardson 	if (vring_idx >= VHOST_MAX_VRING)
103299a2dd95SBruce Richardson 		return -1;
103399a2dd95SBruce Richardson 
103499a2dd95SBruce Richardson 	vq = dev->virtqueue[vring_idx];
103599a2dd95SBruce Richardson 	if (unlikely(!vq))
103699a2dd95SBruce Richardson 		return -1;
103799a2dd95SBruce Richardson 
103899a2dd95SBruce Richardson 	if (vq_is_packed(dev)) {
103999a2dd95SBruce Richardson 		if (unlikely(!vq->inflight_packed))
104099a2dd95SBruce Richardson 			return -1;
104199a2dd95SBruce Richardson 
104299a2dd95SBruce Richardson 		vring->inflight_packed = vq->inflight_packed;
104399a2dd95SBruce Richardson 	} else {
104499a2dd95SBruce Richardson 		if (unlikely(!vq->inflight_split))
104599a2dd95SBruce Richardson 			return -1;
104699a2dd95SBruce Richardson 
104799a2dd95SBruce Richardson 		vring->inflight_split = vq->inflight_split;
104899a2dd95SBruce Richardson 	}
104999a2dd95SBruce Richardson 
105099a2dd95SBruce Richardson 	vring->resubmit_inflight = vq->resubmit_inflight;
105199a2dd95SBruce Richardson 
105299a2dd95SBruce Richardson 	return 0;
105399a2dd95SBruce Richardson }
105499a2dd95SBruce Richardson 
105599a2dd95SBruce Richardson int
105699a2dd95SBruce Richardson rte_vhost_set_inflight_desc_split(int vid, uint16_t vring_idx,
105799a2dd95SBruce Richardson 				  uint16_t idx)
105899a2dd95SBruce Richardson {
105999a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
106099a2dd95SBruce Richardson 	struct virtio_net *dev;
106199a2dd95SBruce Richardson 
106299a2dd95SBruce Richardson 	dev = get_device(vid);
106399a2dd95SBruce Richardson 	if (unlikely(!dev))
106499a2dd95SBruce Richardson 		return -1;
106599a2dd95SBruce Richardson 
106699a2dd95SBruce Richardson 	if (unlikely(!(dev->protocol_features &
106799a2dd95SBruce Richardson 	    (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD))))
106899a2dd95SBruce Richardson 		return 0;
106999a2dd95SBruce Richardson 
107099a2dd95SBruce Richardson 	if (unlikely(vq_is_packed(dev)))
107199a2dd95SBruce Richardson 		return -1;
107299a2dd95SBruce Richardson 
107399a2dd95SBruce Richardson 	if (unlikely(vring_idx >= VHOST_MAX_VRING))
107499a2dd95SBruce Richardson 		return -1;
107599a2dd95SBruce Richardson 
107699a2dd95SBruce Richardson 	vq = dev->virtqueue[vring_idx];
107799a2dd95SBruce Richardson 	if (unlikely(!vq))
107899a2dd95SBruce Richardson 		return -1;
107999a2dd95SBruce Richardson 
108099a2dd95SBruce Richardson 	if (unlikely(!vq->inflight_split))
108199a2dd95SBruce Richardson 		return -1;
108299a2dd95SBruce Richardson 
108399a2dd95SBruce Richardson 	if (unlikely(idx >= vq->size))
108499a2dd95SBruce Richardson 		return -1;
108599a2dd95SBruce Richardson 
108699a2dd95SBruce Richardson 	vq->inflight_split->desc[idx].counter = vq->global_counter++;
108799a2dd95SBruce Richardson 	vq->inflight_split->desc[idx].inflight = 1;
108899a2dd95SBruce Richardson 	return 0;
108999a2dd95SBruce Richardson }
109099a2dd95SBruce Richardson 
109199a2dd95SBruce Richardson int
109299a2dd95SBruce Richardson rte_vhost_set_inflight_desc_packed(int vid, uint16_t vring_idx,
109399a2dd95SBruce Richardson 				   uint16_t head, uint16_t last,
109499a2dd95SBruce Richardson 				   uint16_t *inflight_entry)
109599a2dd95SBruce Richardson {
109699a2dd95SBruce Richardson 	struct rte_vhost_inflight_info_packed *inflight_info;
109799a2dd95SBruce Richardson 	struct virtio_net *dev;
109899a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
109999a2dd95SBruce Richardson 	struct vring_packed_desc *desc;
110099a2dd95SBruce Richardson 	uint16_t old_free_head, free_head;
110199a2dd95SBruce Richardson 
110299a2dd95SBruce Richardson 	dev = get_device(vid);
110399a2dd95SBruce Richardson 	if (unlikely(!dev))
110499a2dd95SBruce Richardson 		return -1;
110599a2dd95SBruce Richardson 
110699a2dd95SBruce Richardson 	if (unlikely(!(dev->protocol_features &
110799a2dd95SBruce Richardson 	    (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD))))
110899a2dd95SBruce Richardson 		return 0;
110999a2dd95SBruce Richardson 
111099a2dd95SBruce Richardson 	if (unlikely(!vq_is_packed(dev)))
111199a2dd95SBruce Richardson 		return -1;
111299a2dd95SBruce Richardson 
111399a2dd95SBruce Richardson 	if (unlikely(vring_idx >= VHOST_MAX_VRING))
111499a2dd95SBruce Richardson 		return -1;
111599a2dd95SBruce Richardson 
111699a2dd95SBruce Richardson 	vq = dev->virtqueue[vring_idx];
111799a2dd95SBruce Richardson 	if (unlikely(!vq))
111899a2dd95SBruce Richardson 		return -1;
111999a2dd95SBruce Richardson 
112099a2dd95SBruce Richardson 	inflight_info = vq->inflight_packed;
112199a2dd95SBruce Richardson 	if (unlikely(!inflight_info))
112299a2dd95SBruce Richardson 		return -1;
112399a2dd95SBruce Richardson 
112499a2dd95SBruce Richardson 	if (unlikely(head >= vq->size))
112599a2dd95SBruce Richardson 		return -1;
112699a2dd95SBruce Richardson 
112799a2dd95SBruce Richardson 	desc = vq->desc_packed;
112899a2dd95SBruce Richardson 	old_free_head = inflight_info->old_free_head;
112999a2dd95SBruce Richardson 	if (unlikely(old_free_head >= vq->size))
113099a2dd95SBruce Richardson 		return -1;
113199a2dd95SBruce Richardson 
113299a2dd95SBruce Richardson 	free_head = old_free_head;
113399a2dd95SBruce Richardson 
113499a2dd95SBruce Richardson 	/* init header descriptor */
113599a2dd95SBruce Richardson 	inflight_info->desc[old_free_head].num = 0;
113699a2dd95SBruce Richardson 	inflight_info->desc[old_free_head].counter = vq->global_counter++;
113799a2dd95SBruce Richardson 	inflight_info->desc[old_free_head].inflight = 1;
113899a2dd95SBruce Richardson 
113999a2dd95SBruce Richardson 	/* save desc entry in flight entry */
114099a2dd95SBruce Richardson 	while (head != ((last + 1) % vq->size)) {
114199a2dd95SBruce Richardson 		inflight_info->desc[old_free_head].num++;
114299a2dd95SBruce Richardson 		inflight_info->desc[free_head].addr = desc[head].addr;
114399a2dd95SBruce Richardson 		inflight_info->desc[free_head].len = desc[head].len;
114499a2dd95SBruce Richardson 		inflight_info->desc[free_head].flags = desc[head].flags;
114599a2dd95SBruce Richardson 		inflight_info->desc[free_head].id = desc[head].id;
114699a2dd95SBruce Richardson 
114799a2dd95SBruce Richardson 		inflight_info->desc[old_free_head].last = free_head;
114899a2dd95SBruce Richardson 		free_head = inflight_info->desc[free_head].next;
114999a2dd95SBruce Richardson 		inflight_info->free_head = free_head;
115099a2dd95SBruce Richardson 		head = (head + 1) % vq->size;
115199a2dd95SBruce Richardson 	}
115299a2dd95SBruce Richardson 
115399a2dd95SBruce Richardson 	inflight_info->old_free_head = free_head;
115499a2dd95SBruce Richardson 	*inflight_entry = old_free_head;
115599a2dd95SBruce Richardson 
115699a2dd95SBruce Richardson 	return 0;
115799a2dd95SBruce Richardson }
115899a2dd95SBruce Richardson 
115999a2dd95SBruce Richardson int
116099a2dd95SBruce Richardson rte_vhost_clr_inflight_desc_split(int vid, uint16_t vring_idx,
116199a2dd95SBruce Richardson 				  uint16_t last_used_idx, uint16_t idx)
116299a2dd95SBruce Richardson {
116399a2dd95SBruce Richardson 	struct virtio_net *dev;
116499a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
116599a2dd95SBruce Richardson 
116699a2dd95SBruce Richardson 	dev = get_device(vid);
116799a2dd95SBruce Richardson 	if (unlikely(!dev))
116899a2dd95SBruce Richardson 		return -1;
116999a2dd95SBruce Richardson 
117099a2dd95SBruce Richardson 	if (unlikely(!(dev->protocol_features &
117199a2dd95SBruce Richardson 	    (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD))))
117299a2dd95SBruce Richardson 		return 0;
117399a2dd95SBruce Richardson 
117499a2dd95SBruce Richardson 	if (unlikely(vq_is_packed(dev)))
117599a2dd95SBruce Richardson 		return -1;
117699a2dd95SBruce Richardson 
117799a2dd95SBruce Richardson 	if (unlikely(vring_idx >= VHOST_MAX_VRING))
117899a2dd95SBruce Richardson 		return -1;
117999a2dd95SBruce Richardson 
118099a2dd95SBruce Richardson 	vq = dev->virtqueue[vring_idx];
118199a2dd95SBruce Richardson 	if (unlikely(!vq))
118299a2dd95SBruce Richardson 		return -1;
118399a2dd95SBruce Richardson 
118499a2dd95SBruce Richardson 	if (unlikely(!vq->inflight_split))
118599a2dd95SBruce Richardson 		return -1;
118699a2dd95SBruce Richardson 
118799a2dd95SBruce Richardson 	if (unlikely(idx >= vq->size))
118899a2dd95SBruce Richardson 		return -1;
118999a2dd95SBruce Richardson 
11905147b641STyler Retzlaff 	rte_atomic_thread_fence(rte_memory_order_seq_cst);
119199a2dd95SBruce Richardson 
119299a2dd95SBruce Richardson 	vq->inflight_split->desc[idx].inflight = 0;
119399a2dd95SBruce Richardson 
11945147b641STyler Retzlaff 	rte_atomic_thread_fence(rte_memory_order_seq_cst);
119599a2dd95SBruce Richardson 
119699a2dd95SBruce Richardson 	vq->inflight_split->used_idx = last_used_idx;
119799a2dd95SBruce Richardson 	return 0;
119899a2dd95SBruce Richardson }
119999a2dd95SBruce Richardson 
120099a2dd95SBruce Richardson int
120199a2dd95SBruce Richardson rte_vhost_clr_inflight_desc_packed(int vid, uint16_t vring_idx,
120299a2dd95SBruce Richardson 				   uint16_t head)
120399a2dd95SBruce Richardson {
120499a2dd95SBruce Richardson 	struct rte_vhost_inflight_info_packed *inflight_info;
120599a2dd95SBruce Richardson 	struct virtio_net *dev;
120699a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
120799a2dd95SBruce Richardson 
120899a2dd95SBruce Richardson 	dev = get_device(vid);
120999a2dd95SBruce Richardson 	if (unlikely(!dev))
121099a2dd95SBruce Richardson 		return -1;
121199a2dd95SBruce Richardson 
121299a2dd95SBruce Richardson 	if (unlikely(!(dev->protocol_features &
121399a2dd95SBruce Richardson 	    (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD))))
121499a2dd95SBruce Richardson 		return 0;
121599a2dd95SBruce Richardson 
121699a2dd95SBruce Richardson 	if (unlikely(!vq_is_packed(dev)))
121799a2dd95SBruce Richardson 		return -1;
121899a2dd95SBruce Richardson 
121999a2dd95SBruce Richardson 	if (unlikely(vring_idx >= VHOST_MAX_VRING))
122099a2dd95SBruce Richardson 		return -1;
122199a2dd95SBruce Richardson 
122299a2dd95SBruce Richardson 	vq = dev->virtqueue[vring_idx];
122399a2dd95SBruce Richardson 	if (unlikely(!vq))
122499a2dd95SBruce Richardson 		return -1;
122599a2dd95SBruce Richardson 
122699a2dd95SBruce Richardson 	inflight_info = vq->inflight_packed;
122799a2dd95SBruce Richardson 	if (unlikely(!inflight_info))
122899a2dd95SBruce Richardson 		return -1;
122999a2dd95SBruce Richardson 
123099a2dd95SBruce Richardson 	if (unlikely(head >= vq->size))
123199a2dd95SBruce Richardson 		return -1;
123299a2dd95SBruce Richardson 
12335147b641STyler Retzlaff 	rte_atomic_thread_fence(rte_memory_order_seq_cst);
123499a2dd95SBruce Richardson 
123599a2dd95SBruce Richardson 	inflight_info->desc[head].inflight = 0;
123699a2dd95SBruce Richardson 
12375147b641STyler Retzlaff 	rte_atomic_thread_fence(rte_memory_order_seq_cst);
123899a2dd95SBruce Richardson 
123999a2dd95SBruce Richardson 	inflight_info->old_free_head = inflight_info->free_head;
124099a2dd95SBruce Richardson 	inflight_info->old_used_idx = inflight_info->used_idx;
124199a2dd95SBruce Richardson 	inflight_info->old_used_wrap_counter = inflight_info->used_wrap_counter;
124299a2dd95SBruce Richardson 
124399a2dd95SBruce Richardson 	return 0;
124499a2dd95SBruce Richardson }
124599a2dd95SBruce Richardson 
124699a2dd95SBruce Richardson int
124799a2dd95SBruce Richardson rte_vhost_set_last_inflight_io_split(int vid, uint16_t vring_idx,
124899a2dd95SBruce Richardson 				     uint16_t idx)
124999a2dd95SBruce Richardson {
125099a2dd95SBruce Richardson 	struct virtio_net *dev;
125199a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
125299a2dd95SBruce Richardson 
125399a2dd95SBruce Richardson 	dev = get_device(vid);
125499a2dd95SBruce Richardson 	if (unlikely(!dev))
125599a2dd95SBruce Richardson 		return -1;
125699a2dd95SBruce Richardson 
125799a2dd95SBruce Richardson 	if (unlikely(!(dev->protocol_features &
125899a2dd95SBruce Richardson 	    (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD))))
125999a2dd95SBruce Richardson 		return 0;
126099a2dd95SBruce Richardson 
126199a2dd95SBruce Richardson 	if (unlikely(vq_is_packed(dev)))
126299a2dd95SBruce Richardson 		return -1;
126399a2dd95SBruce Richardson 
126499a2dd95SBruce Richardson 	if (unlikely(vring_idx >= VHOST_MAX_VRING))
126599a2dd95SBruce Richardson 		return -1;
126699a2dd95SBruce Richardson 
126799a2dd95SBruce Richardson 	vq = dev->virtqueue[vring_idx];
126899a2dd95SBruce Richardson 	if (unlikely(!vq))
126999a2dd95SBruce Richardson 		return -1;
127099a2dd95SBruce Richardson 
127199a2dd95SBruce Richardson 	if (unlikely(!vq->inflight_split))
127299a2dd95SBruce Richardson 		return -1;
127399a2dd95SBruce Richardson 
12745a4fbe79SLi Feng 	if (unlikely(idx >= vq->size))
12755a4fbe79SLi Feng 		return -1;
12765a4fbe79SLi Feng 
127799a2dd95SBruce Richardson 	vq->inflight_split->last_inflight_io = idx;
127899a2dd95SBruce Richardson 	return 0;
127999a2dd95SBruce Richardson }
128099a2dd95SBruce Richardson 
128199a2dd95SBruce Richardson int
128299a2dd95SBruce Richardson rte_vhost_set_last_inflight_io_packed(int vid, uint16_t vring_idx,
128399a2dd95SBruce Richardson 				      uint16_t head)
128499a2dd95SBruce Richardson {
128599a2dd95SBruce Richardson 	struct rte_vhost_inflight_info_packed *inflight_info;
128699a2dd95SBruce Richardson 	struct virtio_net *dev;
128799a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
128899a2dd95SBruce Richardson 	uint16_t last;
128999a2dd95SBruce Richardson 
129099a2dd95SBruce Richardson 	dev = get_device(vid);
129199a2dd95SBruce Richardson 	if (unlikely(!dev))
129299a2dd95SBruce Richardson 		return -1;
129399a2dd95SBruce Richardson 
129499a2dd95SBruce Richardson 	if (unlikely(!(dev->protocol_features &
129599a2dd95SBruce Richardson 	    (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD))))
129699a2dd95SBruce Richardson 		return 0;
129799a2dd95SBruce Richardson 
129899a2dd95SBruce Richardson 	if (unlikely(!vq_is_packed(dev)))
129999a2dd95SBruce Richardson 		return -1;
130099a2dd95SBruce Richardson 
130199a2dd95SBruce Richardson 	if (unlikely(vring_idx >= VHOST_MAX_VRING))
130299a2dd95SBruce Richardson 		return -1;
130399a2dd95SBruce Richardson 
130499a2dd95SBruce Richardson 	vq = dev->virtqueue[vring_idx];
130599a2dd95SBruce Richardson 	if (unlikely(!vq))
130699a2dd95SBruce Richardson 		return -1;
130799a2dd95SBruce Richardson 
130899a2dd95SBruce Richardson 	inflight_info = vq->inflight_packed;
130999a2dd95SBruce Richardson 	if (unlikely(!inflight_info))
131099a2dd95SBruce Richardson 		return -1;
131199a2dd95SBruce Richardson 
131299a2dd95SBruce Richardson 	if (unlikely(head >= vq->size))
131399a2dd95SBruce Richardson 		return -1;
131499a2dd95SBruce Richardson 
131599a2dd95SBruce Richardson 	last = inflight_info->desc[head].last;
131699a2dd95SBruce Richardson 	if (unlikely(last >= vq->size))
131799a2dd95SBruce Richardson 		return -1;
131899a2dd95SBruce Richardson 
131999a2dd95SBruce Richardson 	inflight_info->desc[last].next = inflight_info->free_head;
132099a2dd95SBruce Richardson 	inflight_info->free_head = head;
132199a2dd95SBruce Richardson 	inflight_info->used_idx += inflight_info->desc[head].num;
132299a2dd95SBruce Richardson 	if (inflight_info->used_idx >= inflight_info->desc_num) {
132399a2dd95SBruce Richardson 		inflight_info->used_idx -= inflight_info->desc_num;
132499a2dd95SBruce Richardson 		inflight_info->used_wrap_counter =
132599a2dd95SBruce Richardson 			!inflight_info->used_wrap_counter;
132699a2dd95SBruce Richardson 	}
132799a2dd95SBruce Richardson 
132899a2dd95SBruce Richardson 	return 0;
132999a2dd95SBruce Richardson }
133099a2dd95SBruce Richardson 
133199a2dd95SBruce Richardson int
133299a2dd95SBruce Richardson rte_vhost_vring_call(int vid, uint16_t vring_idx)
133399a2dd95SBruce Richardson {
133499a2dd95SBruce Richardson 	struct virtio_net *dev;
133599a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
1336af7f6836SMaxime Coquelin 	int ret = 0;
133799a2dd95SBruce Richardson 
133899a2dd95SBruce Richardson 	dev = get_device(vid);
133999a2dd95SBruce Richardson 	if (!dev)
134099a2dd95SBruce Richardson 		return -1;
134199a2dd95SBruce Richardson 
134299a2dd95SBruce Richardson 	if (vring_idx >= VHOST_MAX_VRING)
134399a2dd95SBruce Richardson 		return -1;
134499a2dd95SBruce Richardson 
134599a2dd95SBruce Richardson 	vq = dev->virtqueue[vring_idx];
134699a2dd95SBruce Richardson 	if (!vq)
134799a2dd95SBruce Richardson 		return -1;
134899a2dd95SBruce Richardson 
134903f77d66SEelco Chaudron 	rte_rwlock_read_lock(&vq->access_lock);
1350c5736998SMaxime Coquelin 
1351af7f6836SMaxime Coquelin 	if (unlikely(!vq->access_ok)) {
1352af7f6836SMaxime Coquelin 		ret = -1;
1353af7f6836SMaxime Coquelin 		goto out_unlock;
1354af7f6836SMaxime Coquelin 	}
1355af7f6836SMaxime Coquelin 
135699a2dd95SBruce Richardson 	if (vq_is_packed(dev))
135799a2dd95SBruce Richardson 		vhost_vring_call_packed(dev, vq);
135899a2dd95SBruce Richardson 	else
135999a2dd95SBruce Richardson 		vhost_vring_call_split(dev, vq);
136099a2dd95SBruce Richardson 
1361af7f6836SMaxime Coquelin out_unlock:
136203f77d66SEelco Chaudron 	rte_rwlock_read_unlock(&vq->access_lock);
1363c5736998SMaxime Coquelin 
1364af7f6836SMaxime Coquelin 	return ret;
136599a2dd95SBruce Richardson }
136699a2dd95SBruce Richardson 
1367830f7e79SChangpeng Liu int
1368830f7e79SChangpeng Liu rte_vhost_vring_call_nonblock(int vid, uint16_t vring_idx)
1369830f7e79SChangpeng Liu {
1370830f7e79SChangpeng Liu 	struct virtio_net *dev;
1371830f7e79SChangpeng Liu 	struct vhost_virtqueue *vq;
1372af7f6836SMaxime Coquelin 	int ret = 0;
1373830f7e79SChangpeng Liu 
1374830f7e79SChangpeng Liu 	dev = get_device(vid);
1375830f7e79SChangpeng Liu 	if (!dev)
1376830f7e79SChangpeng Liu 		return -1;
1377830f7e79SChangpeng Liu 
1378830f7e79SChangpeng Liu 	if (vring_idx >= VHOST_MAX_VRING)
1379830f7e79SChangpeng Liu 		return -1;
1380830f7e79SChangpeng Liu 
1381830f7e79SChangpeng Liu 	vq = dev->virtqueue[vring_idx];
1382830f7e79SChangpeng Liu 	if (!vq)
1383830f7e79SChangpeng Liu 		return -1;
1384830f7e79SChangpeng Liu 
138503f77d66SEelco Chaudron 	if (rte_rwlock_read_trylock(&vq->access_lock))
1386830f7e79SChangpeng Liu 		return -EAGAIN;
1387830f7e79SChangpeng Liu 
1388af7f6836SMaxime Coquelin 	if (unlikely(!vq->access_ok)) {
1389af7f6836SMaxime Coquelin 		ret = -1;
1390af7f6836SMaxime Coquelin 		goto out_unlock;
1391af7f6836SMaxime Coquelin 	}
1392af7f6836SMaxime Coquelin 
1393830f7e79SChangpeng Liu 	if (vq_is_packed(dev))
1394830f7e79SChangpeng Liu 		vhost_vring_call_packed(dev, vq);
1395830f7e79SChangpeng Liu 	else
1396830f7e79SChangpeng Liu 		vhost_vring_call_split(dev, vq);
1397830f7e79SChangpeng Liu 
1398af7f6836SMaxime Coquelin out_unlock:
139903f77d66SEelco Chaudron 	rte_rwlock_read_unlock(&vq->access_lock);
1400830f7e79SChangpeng Liu 
1401af7f6836SMaxime Coquelin 	return ret;
1402830f7e79SChangpeng Liu }
1403830f7e79SChangpeng Liu 
140499a2dd95SBruce Richardson uint16_t
140599a2dd95SBruce Richardson rte_vhost_avail_entries(int vid, uint16_t queue_id)
140699a2dd95SBruce Richardson {
140799a2dd95SBruce Richardson 	struct virtio_net *dev;
140899a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
140999a2dd95SBruce Richardson 	uint16_t ret = 0;
141099a2dd95SBruce Richardson 
141199a2dd95SBruce Richardson 	dev = get_device(vid);
141299a2dd95SBruce Richardson 	if (!dev)
141399a2dd95SBruce Richardson 		return 0;
141499a2dd95SBruce Richardson 
141599a2dd95SBruce Richardson 	if (queue_id >= VHOST_MAX_VRING)
141699a2dd95SBruce Richardson 		return 0;
141799a2dd95SBruce Richardson 
141899a2dd95SBruce Richardson 	vq = dev->virtqueue[queue_id];
141999a2dd95SBruce Richardson 	if (!vq)
142099a2dd95SBruce Richardson 		return 0;
142199a2dd95SBruce Richardson 
142203f77d66SEelco Chaudron 	rte_rwlock_write_lock(&vq->access_lock);
142399a2dd95SBruce Richardson 
1424094c442cSMaxime Coquelin 	if (unlikely(!vq->access_ok))
1425094c442cSMaxime Coquelin 		goto out;
1426094c442cSMaxime Coquelin 
1427094c442cSMaxime Coquelin 	if (unlikely(!vq->enabled))
142899a2dd95SBruce Richardson 		goto out;
142999a2dd95SBruce Richardson 
143099a2dd95SBruce Richardson 	ret = *(volatile uint16_t *)&vq->avail->idx - vq->last_used_idx;
143199a2dd95SBruce Richardson 
143299a2dd95SBruce Richardson out:
143303f77d66SEelco Chaudron 	rte_rwlock_write_unlock(&vq->access_lock);
143499a2dd95SBruce Richardson 	return ret;
143599a2dd95SBruce Richardson }
143699a2dd95SBruce Richardson 
143799a2dd95SBruce Richardson static inline int
143899a2dd95SBruce Richardson vhost_enable_notify_split(struct virtio_net *dev,
143999a2dd95SBruce Richardson 		struct vhost_virtqueue *vq, int enable)
144099a2dd95SBruce Richardson {
144199a2dd95SBruce Richardson 	if (vq->used == NULL)
144299a2dd95SBruce Richardson 		return -1;
144399a2dd95SBruce Richardson 
144499a2dd95SBruce Richardson 	if (!(dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))) {
144599a2dd95SBruce Richardson 		if (enable)
144699a2dd95SBruce Richardson 			vq->used->flags &= ~VRING_USED_F_NO_NOTIFY;
144799a2dd95SBruce Richardson 		else
144899a2dd95SBruce Richardson 			vq->used->flags |= VRING_USED_F_NO_NOTIFY;
144999a2dd95SBruce Richardson 	} else {
145099a2dd95SBruce Richardson 		if (enable)
145199a2dd95SBruce Richardson 			vhost_avail_event(vq) = vq->last_avail_idx;
145299a2dd95SBruce Richardson 	}
145399a2dd95SBruce Richardson 	return 0;
145499a2dd95SBruce Richardson }
145599a2dd95SBruce Richardson 
145699a2dd95SBruce Richardson static inline int
145799a2dd95SBruce Richardson vhost_enable_notify_packed(struct virtio_net *dev,
145899a2dd95SBruce Richardson 		struct vhost_virtqueue *vq, int enable)
145999a2dd95SBruce Richardson {
146099a2dd95SBruce Richardson 	uint16_t flags;
146199a2dd95SBruce Richardson 
146299a2dd95SBruce Richardson 	if (vq->device_event == NULL)
146399a2dd95SBruce Richardson 		return -1;
146499a2dd95SBruce Richardson 
146599a2dd95SBruce Richardson 	if (!enable) {
146699a2dd95SBruce Richardson 		vq->device_event->flags = VRING_EVENT_F_DISABLE;
146799a2dd95SBruce Richardson 		return 0;
146899a2dd95SBruce Richardson 	}
146999a2dd95SBruce Richardson 
147099a2dd95SBruce Richardson 	flags = VRING_EVENT_F_ENABLE;
147199a2dd95SBruce Richardson 	if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX)) {
147299a2dd95SBruce Richardson 		flags = VRING_EVENT_F_DESC;
147399a2dd95SBruce Richardson 		vq->device_event->off_wrap = vq->last_avail_idx |
147499a2dd95SBruce Richardson 			vq->avail_wrap_counter << 15;
147599a2dd95SBruce Richardson 	}
147699a2dd95SBruce Richardson 
14775147b641STyler Retzlaff 	rte_atomic_thread_fence(rte_memory_order_release);
147899a2dd95SBruce Richardson 
147999a2dd95SBruce Richardson 	vq->device_event->flags = flags;
148099a2dd95SBruce Richardson 	return 0;
148199a2dd95SBruce Richardson }
148299a2dd95SBruce Richardson 
148399a2dd95SBruce Richardson int
148499a2dd95SBruce Richardson vhost_enable_guest_notification(struct virtio_net *dev,
148599a2dd95SBruce Richardson 		struct vhost_virtqueue *vq, int enable)
148699a2dd95SBruce Richardson {
148799a2dd95SBruce Richardson 	/*
148899a2dd95SBruce Richardson 	 * If the virtqueue is not ready yet, it will be applied
148999a2dd95SBruce Richardson 	 * when it will become ready.
149099a2dd95SBruce Richardson 	 */
149199a2dd95SBruce Richardson 	if (!vq->ready)
149299a2dd95SBruce Richardson 		return 0;
149399a2dd95SBruce Richardson 
149499a2dd95SBruce Richardson 	if (vq_is_packed(dev))
149599a2dd95SBruce Richardson 		return vhost_enable_notify_packed(dev, vq, enable);
149699a2dd95SBruce Richardson 	else
149799a2dd95SBruce Richardson 		return vhost_enable_notify_split(dev, vq, enable);
149899a2dd95SBruce Richardson }
149999a2dd95SBruce Richardson 
150099a2dd95SBruce Richardson int
150199a2dd95SBruce Richardson rte_vhost_enable_guest_notification(int vid, uint16_t queue_id, int enable)
150299a2dd95SBruce Richardson {
150399a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
150499a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
150599a2dd95SBruce Richardson 	int ret;
150699a2dd95SBruce Richardson 
150799a2dd95SBruce Richardson 	if (!dev)
150899a2dd95SBruce Richardson 		return -1;
150999a2dd95SBruce Richardson 
151099a2dd95SBruce Richardson 	if (queue_id >= VHOST_MAX_VRING)
151199a2dd95SBruce Richardson 		return -1;
151299a2dd95SBruce Richardson 
151399a2dd95SBruce Richardson 	vq = dev->virtqueue[queue_id];
151499a2dd95SBruce Richardson 	if (!vq)
151599a2dd95SBruce Richardson 		return -1;
151699a2dd95SBruce Richardson 
151703f77d66SEelco Chaudron 	rte_rwlock_write_lock(&vq->access_lock);
151899a2dd95SBruce Richardson 
1519094c442cSMaxime Coquelin 	if (unlikely(!vq->access_ok)) {
1520094c442cSMaxime Coquelin 		ret = -1;
1521094c442cSMaxime Coquelin 		goto out_unlock;
1522094c442cSMaxime Coquelin 	}
1523094c442cSMaxime Coquelin 
152499a2dd95SBruce Richardson 	vq->notif_enable = enable;
152599a2dd95SBruce Richardson 	ret = vhost_enable_guest_notification(dev, vq, enable);
152699a2dd95SBruce Richardson 
1527094c442cSMaxime Coquelin out_unlock:
152803f77d66SEelco Chaudron 	rte_rwlock_write_unlock(&vq->access_lock);
152999a2dd95SBruce Richardson 
153099a2dd95SBruce Richardson 	return ret;
153199a2dd95SBruce Richardson }
153299a2dd95SBruce Richardson 
153399a2dd95SBruce Richardson void
1534d761d455SEelco Chaudron rte_vhost_notify_guest(int vid, uint16_t queue_id)
1535d761d455SEelco Chaudron {
1536d761d455SEelco Chaudron 	struct virtio_net *dev = get_device(vid);
1537d761d455SEelco Chaudron 	struct vhost_virtqueue *vq;
1538d761d455SEelco Chaudron 
1539d761d455SEelco Chaudron 	if (!dev ||  queue_id >= VHOST_MAX_VRING)
1540d761d455SEelco Chaudron 		return;
1541d761d455SEelco Chaudron 
1542d761d455SEelco Chaudron 	vq = dev->virtqueue[queue_id];
1543d761d455SEelco Chaudron 	if (!vq)
1544d761d455SEelco Chaudron 		return;
1545d761d455SEelco Chaudron 
1546d761d455SEelco Chaudron 	rte_rwlock_read_lock(&vq->access_lock);
1547d761d455SEelco Chaudron 
15486252ab8cSMaxime Coquelin 	if (unlikely(!vq->access_ok))
15496252ab8cSMaxime Coquelin 		goto out_unlock;
15506252ab8cSMaxime Coquelin 
15515147b641STyler Retzlaff 	rte_atomic_store_explicit(&vq->irq_pending, false, rte_memory_order_release);
155211c310c8SMaxime Coquelin 
1553a5dd9842SMaxime Coquelin 	if (dev->backend_ops->inject_irq(dev, vq)) {
1554d761d455SEelco Chaudron 		if (dev->flags & VIRTIO_DEV_STATS_ENABLED)
15555147b641STyler Retzlaff 			rte_atomic_fetch_add_explicit(&vq->stats.guest_notifications_error,
15565147b641STyler Retzlaff 					1, rte_memory_order_relaxed);
1557d761d455SEelco Chaudron 	} else {
1558d761d455SEelco Chaudron 		if (dev->flags & VIRTIO_DEV_STATS_ENABLED)
15595147b641STyler Retzlaff 			rte_atomic_fetch_add_explicit(&vq->stats.guest_notifications,
15605147b641STyler Retzlaff 					1, rte_memory_order_relaxed);
1561d761d455SEelco Chaudron 		if (dev->notify_ops->guest_notified)
1562d761d455SEelco Chaudron 			dev->notify_ops->guest_notified(dev->vid);
1563d761d455SEelco Chaudron 	}
1564d761d455SEelco Chaudron 
15656252ab8cSMaxime Coquelin out_unlock:
1566d761d455SEelco Chaudron 	rte_rwlock_read_unlock(&vq->access_lock);
1567d761d455SEelco Chaudron }
1568d761d455SEelco Chaudron 
1569d761d455SEelco Chaudron void
157099a2dd95SBruce Richardson rte_vhost_log_write(int vid, uint64_t addr, uint64_t len)
157199a2dd95SBruce Richardson {
157299a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
157399a2dd95SBruce Richardson 
157499a2dd95SBruce Richardson 	if (dev == NULL)
157599a2dd95SBruce Richardson 		return;
157699a2dd95SBruce Richardson 
157799a2dd95SBruce Richardson 	vhost_log_write(dev, addr, len);
157899a2dd95SBruce Richardson }
157999a2dd95SBruce Richardson 
158099a2dd95SBruce Richardson void
158199a2dd95SBruce Richardson rte_vhost_log_used_vring(int vid, uint16_t vring_idx,
158299a2dd95SBruce Richardson 			 uint64_t offset, uint64_t len)
158399a2dd95SBruce Richardson {
158499a2dd95SBruce Richardson 	struct virtio_net *dev;
158599a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
158699a2dd95SBruce Richardson 
158799a2dd95SBruce Richardson 	dev = get_device(vid);
158899a2dd95SBruce Richardson 	if (dev == NULL)
158999a2dd95SBruce Richardson 		return;
159099a2dd95SBruce Richardson 
159199a2dd95SBruce Richardson 	if (vring_idx >= VHOST_MAX_VRING)
159299a2dd95SBruce Richardson 		return;
159399a2dd95SBruce Richardson 	vq = dev->virtqueue[vring_idx];
159499a2dd95SBruce Richardson 	if (!vq)
159599a2dd95SBruce Richardson 		return;
159699a2dd95SBruce Richardson 
159799a2dd95SBruce Richardson 	vhost_log_used_vring(dev, vq, offset, len);
159899a2dd95SBruce Richardson }
159999a2dd95SBruce Richardson 
160099a2dd95SBruce Richardson uint32_t
160199a2dd95SBruce Richardson rte_vhost_rx_queue_count(int vid, uint16_t qid)
160299a2dd95SBruce Richardson {
160399a2dd95SBruce Richardson 	struct virtio_net *dev;
160499a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
160599a2dd95SBruce Richardson 	uint32_t ret = 0;
160699a2dd95SBruce Richardson 
160799a2dd95SBruce Richardson 	dev = get_device(vid);
160899a2dd95SBruce Richardson 	if (dev == NULL)
160999a2dd95SBruce Richardson 		return 0;
161099a2dd95SBruce Richardson 
161199a2dd95SBruce Richardson 	if (unlikely(qid >= dev->nr_vring || (qid & 1) == 0)) {
16120e21c7c0SDavid Marchand 		VHOST_DATA_LOG(dev->ifname, ERR,
16130e21c7c0SDavid Marchand 			"%s: invalid virtqueue idx %d.",
161436c525a0SDavid Marchand 			__func__, qid);
161599a2dd95SBruce Richardson 		return 0;
161699a2dd95SBruce Richardson 	}
161799a2dd95SBruce Richardson 
161899a2dd95SBruce Richardson 	vq = dev->virtqueue[qid];
161999a2dd95SBruce Richardson 	if (vq == NULL)
162099a2dd95SBruce Richardson 		return 0;
162199a2dd95SBruce Richardson 
162203f77d66SEelco Chaudron 	rte_rwlock_write_lock(&vq->access_lock);
162399a2dd95SBruce Richardson 
1624094c442cSMaxime Coquelin 	if (unlikely(!vq->access_ok))
1625094c442cSMaxime Coquelin 		goto out;
1626094c442cSMaxime Coquelin 
1627094c442cSMaxime Coquelin 	if (unlikely(!vq->enabled))
162899a2dd95SBruce Richardson 		goto out;
162999a2dd95SBruce Richardson 
163099a2dd95SBruce Richardson 	ret = *((volatile uint16_t *)&vq->avail->idx) - vq->last_avail_idx;
163199a2dd95SBruce Richardson 
163299a2dd95SBruce Richardson out:
163303f77d66SEelco Chaudron 	rte_rwlock_write_unlock(&vq->access_lock);
163499a2dd95SBruce Richardson 	return ret;
163599a2dd95SBruce Richardson }
163699a2dd95SBruce Richardson 
163799a2dd95SBruce Richardson struct rte_vdpa_device *
163899a2dd95SBruce Richardson rte_vhost_get_vdpa_device(int vid)
163999a2dd95SBruce Richardson {
164099a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
164199a2dd95SBruce Richardson 
164299a2dd95SBruce Richardson 	if (dev == NULL)
164399a2dd95SBruce Richardson 		return NULL;
164499a2dd95SBruce Richardson 
164599a2dd95SBruce Richardson 	return dev->vdpa_dev;
164699a2dd95SBruce Richardson }
164799a2dd95SBruce Richardson 
164807ee2d75SXuan Ding int
164907ee2d75SXuan Ding rte_vhost_get_log_base(int vid, uint64_t *log_base,
165099a2dd95SBruce Richardson 		uint64_t *log_size)
165199a2dd95SBruce Richardson {
165299a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
165399a2dd95SBruce Richardson 
165499a2dd95SBruce Richardson 	if (dev == NULL || log_base == NULL || log_size == NULL)
165599a2dd95SBruce Richardson 		return -1;
165699a2dd95SBruce Richardson 
165799a2dd95SBruce Richardson 	*log_base = dev->log_base;
165899a2dd95SBruce Richardson 	*log_size = dev->log_size;
165999a2dd95SBruce Richardson 
166099a2dd95SBruce Richardson 	return 0;
166199a2dd95SBruce Richardson }
166299a2dd95SBruce Richardson 
166307ee2d75SXuan Ding int
166407ee2d75SXuan Ding rte_vhost_get_vring_base(int vid, uint16_t queue_id,
166599a2dd95SBruce Richardson 		uint16_t *last_avail_idx, uint16_t *last_used_idx)
166699a2dd95SBruce Richardson {
166799a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
166899a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
166999a2dd95SBruce Richardson 
167099a2dd95SBruce Richardson 	if (dev == NULL || last_avail_idx == NULL || last_used_idx == NULL)
167199a2dd95SBruce Richardson 		return -1;
167299a2dd95SBruce Richardson 
167399a2dd95SBruce Richardson 	if (queue_id >= VHOST_MAX_VRING)
167499a2dd95SBruce Richardson 		return -1;
167599a2dd95SBruce Richardson 
167699a2dd95SBruce Richardson 	vq = dev->virtqueue[queue_id];
167799a2dd95SBruce Richardson 	if (!vq)
167899a2dd95SBruce Richardson 		return -1;
167999a2dd95SBruce Richardson 
168099a2dd95SBruce Richardson 	if (vq_is_packed(dev)) {
168199a2dd95SBruce Richardson 		*last_avail_idx = (vq->avail_wrap_counter << 15) |
168299a2dd95SBruce Richardson 				  vq->last_avail_idx;
168399a2dd95SBruce Richardson 		*last_used_idx = (vq->used_wrap_counter << 15) |
168499a2dd95SBruce Richardson 				 vq->last_used_idx;
168599a2dd95SBruce Richardson 	} else {
168699a2dd95SBruce Richardson 		*last_avail_idx = vq->last_avail_idx;
168799a2dd95SBruce Richardson 		*last_used_idx = vq->last_used_idx;
168899a2dd95SBruce Richardson 	}
168999a2dd95SBruce Richardson 
169099a2dd95SBruce Richardson 	return 0;
169199a2dd95SBruce Richardson }
169299a2dd95SBruce Richardson 
169307ee2d75SXuan Ding int
169407ee2d75SXuan Ding rte_vhost_set_vring_base(int vid, uint16_t queue_id,
169599a2dd95SBruce Richardson 		uint16_t last_avail_idx, uint16_t last_used_idx)
169699a2dd95SBruce Richardson {
169799a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
169899a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
169999a2dd95SBruce Richardson 
170099a2dd95SBruce Richardson 	if (!dev)
170199a2dd95SBruce Richardson 		return -1;
170299a2dd95SBruce Richardson 
170399a2dd95SBruce Richardson 	if (queue_id >= VHOST_MAX_VRING)
170499a2dd95SBruce Richardson 		return -1;
170599a2dd95SBruce Richardson 
170699a2dd95SBruce Richardson 	vq = dev->virtqueue[queue_id];
170799a2dd95SBruce Richardson 	if (!vq)
170899a2dd95SBruce Richardson 		return -1;
170999a2dd95SBruce Richardson 
171099a2dd95SBruce Richardson 	if (vq_is_packed(dev)) {
171199a2dd95SBruce Richardson 		vq->last_avail_idx = last_avail_idx & 0x7fff;
171299a2dd95SBruce Richardson 		vq->avail_wrap_counter = !!(last_avail_idx & (1 << 15));
171399a2dd95SBruce Richardson 		vq->last_used_idx = last_used_idx & 0x7fff;
171499a2dd95SBruce Richardson 		vq->used_wrap_counter = !!(last_used_idx & (1 << 15));
1715*15677ca2SMaxime Coquelin 		vhost_virtqueue_reconnect_log_packed(vq);
171699a2dd95SBruce Richardson 	} else {
171799a2dd95SBruce Richardson 		vq->last_avail_idx = last_avail_idx;
171899a2dd95SBruce Richardson 		vq->last_used_idx = last_used_idx;
1719*15677ca2SMaxime Coquelin 		vhost_virtqueue_reconnect_log_split(vq);
172099a2dd95SBruce Richardson 	}
172199a2dd95SBruce Richardson 
172299a2dd95SBruce Richardson 	return 0;
172399a2dd95SBruce Richardson }
172499a2dd95SBruce Richardson 
172599a2dd95SBruce Richardson int
172699a2dd95SBruce Richardson rte_vhost_get_vring_base_from_inflight(int vid,
172799a2dd95SBruce Richardson 				       uint16_t queue_id,
172899a2dd95SBruce Richardson 				       uint16_t *last_avail_idx,
172999a2dd95SBruce Richardson 				       uint16_t *last_used_idx)
173099a2dd95SBruce Richardson {
173199a2dd95SBruce Richardson 	struct rte_vhost_inflight_info_packed *inflight_info;
173299a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
173399a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
173499a2dd95SBruce Richardson 
173599a2dd95SBruce Richardson 	if (dev == NULL || last_avail_idx == NULL || last_used_idx == NULL)
173699a2dd95SBruce Richardson 		return -1;
173799a2dd95SBruce Richardson 
173899a2dd95SBruce Richardson 	if (queue_id >= VHOST_MAX_VRING)
173999a2dd95SBruce Richardson 		return -1;
174099a2dd95SBruce Richardson 
174199a2dd95SBruce Richardson 	vq = dev->virtqueue[queue_id];
174299a2dd95SBruce Richardson 	if (!vq)
174399a2dd95SBruce Richardson 		return -1;
174499a2dd95SBruce Richardson 
174599a2dd95SBruce Richardson 	if (!vq_is_packed(dev))
174699a2dd95SBruce Richardson 		return -1;
174799a2dd95SBruce Richardson 
174899a2dd95SBruce Richardson 	inflight_info = vq->inflight_packed;
174999a2dd95SBruce Richardson 	if (!inflight_info)
175099a2dd95SBruce Richardson 		return -1;
175199a2dd95SBruce Richardson 
175299a2dd95SBruce Richardson 	*last_avail_idx = (inflight_info->old_used_wrap_counter << 15) |
175399a2dd95SBruce Richardson 			  inflight_info->old_used_idx;
175499a2dd95SBruce Richardson 	*last_used_idx = *last_avail_idx;
175599a2dd95SBruce Richardson 
175699a2dd95SBruce Richardson 	return 0;
175799a2dd95SBruce Richardson }
175899a2dd95SBruce Richardson 
175907ee2d75SXuan Ding int
176007ee2d75SXuan Ding rte_vhost_extern_callback_register(int vid,
176199a2dd95SBruce Richardson 		struct rte_vhost_user_extern_ops const * const ops, void *ctx)
176299a2dd95SBruce Richardson {
176399a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
176499a2dd95SBruce Richardson 
176599a2dd95SBruce Richardson 	if (dev == NULL || ops == NULL)
176699a2dd95SBruce Richardson 		return -1;
176799a2dd95SBruce Richardson 
176899a2dd95SBruce Richardson 	dev->extern_ops = *ops;
176999a2dd95SBruce Richardson 	dev->extern_data = ctx;
177099a2dd95SBruce Richardson 	return 0;
177199a2dd95SBruce Richardson }
177299a2dd95SBruce Richardson 
1773fa51f1aaSJiayu Hu static __rte_always_inline int
177457e414e3SDavid Marchand async_channel_register(struct virtio_net *dev, struct vhost_virtqueue *vq)
17754b02c267SDavid Marchand 	__rte_exclusive_locks_required(&vq->access_lock)
1776fa51f1aaSJiayu Hu {
1777ee8024b3SMaxime Coquelin 	struct vhost_async *async;
1778ee8024b3SMaxime Coquelin 	int node = vq->numa_node;
1779fa51f1aaSJiayu Hu 
1780ee8024b3SMaxime Coquelin 	if (unlikely(vq->async)) {
17810e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR,
17820e21c7c0SDavid Marchand 			"async register failed: already registered (qid: %d)",
178357e414e3SDavid Marchand 			vq->index);
1784fa51f1aaSJiayu Hu 		return -1;
1785fa51f1aaSJiayu Hu 	}
1786fa51f1aaSJiayu Hu 
1787ee8024b3SMaxime Coquelin 	async = rte_zmalloc_socket(NULL, sizeof(struct vhost_async), 0, node);
1788ee8024b3SMaxime Coquelin 	if (!async) {
17890e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR,
17900e21c7c0SDavid Marchand 			"failed to allocate async metadata (qid: %d)",
179157e414e3SDavid Marchand 			vq->index);
1792fa51f1aaSJiayu Hu 		return -1;
1793fa51f1aaSJiayu Hu 	}
1794fa51f1aaSJiayu Hu 
1795ee8024b3SMaxime Coquelin 	async->pkts_info = rte_malloc_socket(NULL, vq->size * sizeof(struct async_inflight_info),
1796ee8024b3SMaxime Coquelin 			RTE_CACHE_LINE_SIZE, node);
1797ee8024b3SMaxime Coquelin 	if (!async->pkts_info) {
17980e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR,
17990e21c7c0SDavid Marchand 			"failed to allocate async_pkts_info (qid: %d)",
180057e414e3SDavid Marchand 			vq->index);
1801ee8024b3SMaxime Coquelin 		goto out_free_async;
1802fa51f1aaSJiayu Hu 	}
1803fa51f1aaSJiayu Hu 
180453d3f477SJiayu Hu 	async->pkts_cmpl_flag = rte_zmalloc_socket(NULL, vq->size * sizeof(bool),
180553d3f477SJiayu Hu 			RTE_CACHE_LINE_SIZE, node);
180653d3f477SJiayu Hu 	if (!async->pkts_cmpl_flag) {
18070e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR,
18080e21c7c0SDavid Marchand 			"failed to allocate async pkts_cmpl_flag (qid: %d)",
180957e414e3SDavid Marchand 			vq->index);
181053d3f477SJiayu Hu 		goto out_free_async;
181153d3f477SJiayu Hu 	}
181253d3f477SJiayu Hu 
1813fa51f1aaSJiayu Hu 	if (vq_is_packed(dev)) {
1814ee8024b3SMaxime Coquelin 		async->buffers_packed = rte_malloc_socket(NULL,
1815fa51f1aaSJiayu Hu 				vq->size * sizeof(struct vring_used_elem_packed),
1816ee8024b3SMaxime Coquelin 				RTE_CACHE_LINE_SIZE, node);
1817ee8024b3SMaxime Coquelin 		if (!async->buffers_packed) {
18180e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(dev->ifname, ERR,
18190e21c7c0SDavid Marchand 				"failed to allocate async buffers (qid: %d)",
182057e414e3SDavid Marchand 				vq->index);
1821ee8024b3SMaxime Coquelin 			goto out_free_inflight;
1822fa51f1aaSJiayu Hu 		}
1823fa51f1aaSJiayu Hu 	} else {
1824ee8024b3SMaxime Coquelin 		async->descs_split = rte_malloc_socket(NULL,
1825fa51f1aaSJiayu Hu 				vq->size * sizeof(struct vring_used_elem),
1826ee8024b3SMaxime Coquelin 				RTE_CACHE_LINE_SIZE, node);
1827ee8024b3SMaxime Coquelin 		if (!async->descs_split) {
18280e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG(dev->ifname, ERR,
18290e21c7c0SDavid Marchand 				"failed to allocate async descs (qid: %d)",
183057e414e3SDavid Marchand 				vq->index);
1831ee8024b3SMaxime Coquelin 			goto out_free_inflight;
1832fa51f1aaSJiayu Hu 		}
1833fa51f1aaSJiayu Hu 	}
1834fa51f1aaSJiayu Hu 
1835ee8024b3SMaxime Coquelin 	vq->async = async;
1836fa51f1aaSJiayu Hu 
1837fa51f1aaSJiayu Hu 	return 0;
1838ee8024b3SMaxime Coquelin out_free_inflight:
1839ee8024b3SMaxime Coquelin 	rte_free(async->pkts_info);
1840ee8024b3SMaxime Coquelin out_free_async:
1841ee8024b3SMaxime Coquelin 	rte_free(async);
1842ee8024b3SMaxime Coquelin 
1843ee8024b3SMaxime Coquelin 	return -1;
1844fa51f1aaSJiayu Hu }
1845fa51f1aaSJiayu Hu 
1846acbc3888SJiayu Hu int
184753d3f477SJiayu Hu rte_vhost_async_channel_register(int vid, uint16_t queue_id)
184899a2dd95SBruce Richardson {
184999a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
185099a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
1851fa51f1aaSJiayu Hu 	int ret;
1852fa51f1aaSJiayu Hu 
185353d3f477SJiayu Hu 	if (dev == NULL)
1854fa51f1aaSJiayu Hu 		return -1;
1855fa51f1aaSJiayu Hu 
1856fa51f1aaSJiayu Hu 	if (queue_id >= VHOST_MAX_VRING)
1857fa51f1aaSJiayu Hu 		return -1;
1858fa51f1aaSJiayu Hu 
1859fa51f1aaSJiayu Hu 	vq = dev->virtqueue[queue_id];
1860fa51f1aaSJiayu Hu 
1861eb6b8158SDavid Marchand 	if (unlikely(vq == NULL || !dev->async_copy || dev->vdpa_dev != NULL))
1862fa51f1aaSJiayu Hu 		return -1;
1863fa51f1aaSJiayu Hu 
186403f77d66SEelco Chaudron 	rte_rwlock_write_lock(&vq->access_lock);
1865867d31beSMaxime Coquelin 
1866867d31beSMaxime Coquelin 	if (unlikely(!vq->access_ok)) {
1867867d31beSMaxime Coquelin 		ret = -1;
1868867d31beSMaxime Coquelin 		goto out_unlock;
1869867d31beSMaxime Coquelin 	}
1870867d31beSMaxime Coquelin 
187157e414e3SDavid Marchand 	ret = async_channel_register(dev, vq);
1872867d31beSMaxime Coquelin 
1873867d31beSMaxime Coquelin out_unlock:
187403f77d66SEelco Chaudron 	rte_rwlock_write_unlock(&vq->access_lock);
1875fa51f1aaSJiayu Hu 
1876fa51f1aaSJiayu Hu 	return ret;
1877fa51f1aaSJiayu Hu }
1878fa51f1aaSJiayu Hu 
1879fa51f1aaSJiayu Hu int
188053d3f477SJiayu Hu rte_vhost_async_channel_register_thread_unsafe(int vid, uint16_t queue_id)
1881fa51f1aaSJiayu Hu {
1882fa51f1aaSJiayu Hu 	struct vhost_virtqueue *vq;
1883fa51f1aaSJiayu Hu 	struct virtio_net *dev = get_device(vid);
188499a2dd95SBruce Richardson 
188553d3f477SJiayu Hu 	if (dev == NULL)
188699a2dd95SBruce Richardson 		return -1;
188799a2dd95SBruce Richardson 
188899a2dd95SBruce Richardson 	if (queue_id >= VHOST_MAX_VRING)
188999a2dd95SBruce Richardson 		return -1;
189099a2dd95SBruce Richardson 
189199a2dd95SBruce Richardson 	vq = dev->virtqueue[queue_id];
189299a2dd95SBruce Richardson 
1893eb6b8158SDavid Marchand 	if (unlikely(vq == NULL || !dev->async_copy || dev->vdpa_dev != NULL))
189499a2dd95SBruce Richardson 		return -1;
189599a2dd95SBruce Richardson 
189690d6e52bSDavid Marchand 	vq_assert_lock(dev, vq);
18970a8363efSMaxime Coquelin 
189857e414e3SDavid Marchand 	return async_channel_register(dev, vq);
189999a2dd95SBruce Richardson }
190099a2dd95SBruce Richardson 
1901acbc3888SJiayu Hu int
1902acbc3888SJiayu Hu rte_vhost_async_channel_unregister(int vid, uint16_t queue_id)
190399a2dd95SBruce Richardson {
190499a2dd95SBruce Richardson 	struct vhost_virtqueue *vq;
190599a2dd95SBruce Richardson 	struct virtio_net *dev = get_device(vid);
190699a2dd95SBruce Richardson 	int ret = -1;
190799a2dd95SBruce Richardson 
190899a2dd95SBruce Richardson 	if (dev == NULL)
190999a2dd95SBruce Richardson 		return ret;
191099a2dd95SBruce Richardson 
191199a2dd95SBruce Richardson 	if (queue_id >= VHOST_MAX_VRING)
191299a2dd95SBruce Richardson 		return ret;
191399a2dd95SBruce Richardson 
191499a2dd95SBruce Richardson 	vq = dev->virtqueue[queue_id];
191599a2dd95SBruce Richardson 
191699a2dd95SBruce Richardson 	if (vq == NULL)
191799a2dd95SBruce Richardson 		return ret;
191899a2dd95SBruce Richardson 
191903f77d66SEelco Chaudron 	if (rte_rwlock_write_trylock(&vq->access_lock)) {
19200e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR,
19210e21c7c0SDavid Marchand 			"failed to unregister async channel, virtqueue busy.");
19222d47fd3dSDavid Marchand 		return ret;
192399a2dd95SBruce Richardson 	}
192499a2dd95SBruce Richardson 
1925867d31beSMaxime Coquelin 	if (unlikely(!vq->access_ok)) {
1926867d31beSMaxime Coquelin 		ret = -1;
1927867d31beSMaxime Coquelin 		goto out_unlock;
1928867d31beSMaxime Coquelin 	}
1929867d31beSMaxime Coquelin 
19302d47fd3dSDavid Marchand 	if (!vq->async) {
19312d47fd3dSDavid Marchand 		ret = 0;
19322d47fd3dSDavid Marchand 	} else if (vq->async->pkts_inflight_n) {
19330e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to unregister async channel.");
19340e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR,
19350e21c7c0SDavid Marchand 			"inflight packets must be completed before unregistration.");
19362d47fd3dSDavid Marchand 	} else {
19372d47fd3dSDavid Marchand 		vhost_free_async_mem(vq);
19382d47fd3dSDavid Marchand 		ret = 0;
193999a2dd95SBruce Richardson 	}
194099a2dd95SBruce Richardson 
1941867d31beSMaxime Coquelin out_unlock:
194203f77d66SEelco Chaudron 	rte_rwlock_write_unlock(&vq->access_lock);
194399a2dd95SBruce Richardson 
194499a2dd95SBruce Richardson 	return ret;
194599a2dd95SBruce Richardson }
194699a2dd95SBruce Richardson 
1947fa51f1aaSJiayu Hu int
1948fa51f1aaSJiayu Hu rte_vhost_async_channel_unregister_thread_unsafe(int vid, uint16_t queue_id)
1949fa51f1aaSJiayu Hu {
1950fa51f1aaSJiayu Hu 	struct vhost_virtqueue *vq;
1951fa51f1aaSJiayu Hu 	struct virtio_net *dev = get_device(vid);
1952fa51f1aaSJiayu Hu 
1953fa51f1aaSJiayu Hu 	if (dev == NULL)
1954fa51f1aaSJiayu Hu 		return -1;
1955fa51f1aaSJiayu Hu 
1956fa51f1aaSJiayu Hu 	if (queue_id >= VHOST_MAX_VRING)
1957fa51f1aaSJiayu Hu 		return -1;
1958fa51f1aaSJiayu Hu 
1959fa51f1aaSJiayu Hu 	vq = dev->virtqueue[queue_id];
1960fa51f1aaSJiayu Hu 
1961fa51f1aaSJiayu Hu 	if (vq == NULL)
1962fa51f1aaSJiayu Hu 		return -1;
1963fa51f1aaSJiayu Hu 
196490d6e52bSDavid Marchand 	vq_assert_lock(dev, vq);
19650a8363efSMaxime Coquelin 
1966ee8024b3SMaxime Coquelin 	if (!vq->async)
1967fa51f1aaSJiayu Hu 		return 0;
1968fa51f1aaSJiayu Hu 
1969ee8024b3SMaxime Coquelin 	if (vq->async->pkts_inflight_n) {
19700e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to unregister async channel.");
19710e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, ERR,
19720e21c7c0SDavid Marchand 			"inflight packets must be completed before unregistration.");
1973fa51f1aaSJiayu Hu 		return -1;
1974fa51f1aaSJiayu Hu 	}
1975fa51f1aaSJiayu Hu 
1976fa51f1aaSJiayu Hu 	vhost_free_async_mem(vq);
1977fa51f1aaSJiayu Hu 
1978fa51f1aaSJiayu Hu 	return 0;
1979fa51f1aaSJiayu Hu }
1980fa51f1aaSJiayu Hu 
198107ee2d75SXuan Ding int
198253d3f477SJiayu Hu rte_vhost_async_dma_configure(int16_t dma_id, uint16_t vchan_id)
198353d3f477SJiayu Hu {
198453d3f477SJiayu Hu 	struct rte_dma_info info;
198553d3f477SJiayu Hu 	void *pkts_cmpl_flag_addr;
198653d3f477SJiayu Hu 	uint16_t max_desc;
198753d3f477SJiayu Hu 
1988e8c3d496SXuan Ding 	pthread_mutex_lock(&vhost_dma_lock);
1989e8c3d496SXuan Ding 
199053d3f477SJiayu Hu 	if (!rte_dma_is_valid(dma_id)) {
19910e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("dma", ERR, "DMA %d is not found.", dma_id);
1992e8c3d496SXuan Ding 		goto error;
199353d3f477SJiayu Hu 	}
199453d3f477SJiayu Hu 
19951e4bcee9SJiayu Hu 	if (rte_dma_info_get(dma_id, &info) != 0) {
19960e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("dma", ERR, "Fail to get DMA %d information.", dma_id);
1997e8c3d496SXuan Ding 		goto error;
19981e4bcee9SJiayu Hu 	}
19991e4bcee9SJiayu Hu 
200053d3f477SJiayu Hu 	if (vchan_id >= info.max_vchans) {
20010e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("dma", ERR, "Invalid DMA %d vChannel %u.", dma_id, vchan_id);
2002e8c3d496SXuan Ding 		goto error;
200353d3f477SJiayu Hu 	}
200453d3f477SJiayu Hu 
200553d3f477SJiayu Hu 	if (!dma_copy_track[dma_id].vchans) {
200653d3f477SJiayu Hu 		struct async_dma_vchan_info *vchans;
200753d3f477SJiayu Hu 
200853d3f477SJiayu Hu 		vchans = rte_zmalloc(NULL, sizeof(struct async_dma_vchan_info) * info.max_vchans,
200953d3f477SJiayu Hu 				RTE_CACHE_LINE_SIZE);
201053d3f477SJiayu Hu 		if (vchans == NULL) {
20110e21c7c0SDavid Marchand 			VHOST_CONFIG_LOG("dma", ERR,
20120e21c7c0SDavid Marchand 				"Failed to allocate vchans for DMA %d vChannel %u.",
201353d3f477SJiayu Hu 				dma_id, vchan_id);
2014e8c3d496SXuan Ding 			goto error;
201553d3f477SJiayu Hu 		}
201653d3f477SJiayu Hu 
201753d3f477SJiayu Hu 		dma_copy_track[dma_id].vchans = vchans;
201853d3f477SJiayu Hu 	}
201953d3f477SJiayu Hu 
202053d3f477SJiayu Hu 	if (dma_copy_track[dma_id].vchans[vchan_id].pkts_cmpl_flag_addr) {
20210e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("dma", INFO, "DMA %d vChannel %u already registered.",
202236c525a0SDavid Marchand 			dma_id, vchan_id);
2023e8c3d496SXuan Ding 		pthread_mutex_unlock(&vhost_dma_lock);
202453d3f477SJiayu Hu 		return 0;
202553d3f477SJiayu Hu 	}
202653d3f477SJiayu Hu 
202753d3f477SJiayu Hu 	max_desc = info.max_desc;
202853d3f477SJiayu Hu 	if (!rte_is_power_of_2(max_desc))
202953d3f477SJiayu Hu 		max_desc = rte_align32pow2(max_desc);
203053d3f477SJiayu Hu 
203153d3f477SJiayu Hu 	pkts_cmpl_flag_addr = rte_zmalloc(NULL, sizeof(bool *) * max_desc, RTE_CACHE_LINE_SIZE);
203253d3f477SJiayu Hu 	if (!pkts_cmpl_flag_addr) {
20330e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("dma", ERR,
20340e21c7c0SDavid Marchand 			"Failed to allocate pkts_cmpl_flag_addr for DMA %d vChannel %u.",
203536c525a0SDavid Marchand 			dma_id, vchan_id);
203653d3f477SJiayu Hu 
203753d3f477SJiayu Hu 		if (dma_copy_track[dma_id].nr_vchans == 0) {
203853d3f477SJiayu Hu 			rte_free(dma_copy_track[dma_id].vchans);
203953d3f477SJiayu Hu 			dma_copy_track[dma_id].vchans = NULL;
204053d3f477SJiayu Hu 		}
2041e8c3d496SXuan Ding 		goto error;
204253d3f477SJiayu Hu 	}
204353d3f477SJiayu Hu 
204453d3f477SJiayu Hu 	dma_copy_track[dma_id].vchans[vchan_id].pkts_cmpl_flag_addr = pkts_cmpl_flag_addr;
204553d3f477SJiayu Hu 	dma_copy_track[dma_id].vchans[vchan_id].ring_size = max_desc;
204653d3f477SJiayu Hu 	dma_copy_track[dma_id].vchans[vchan_id].ring_mask = max_desc - 1;
204753d3f477SJiayu Hu 	dma_copy_track[dma_id].nr_vchans++;
204853d3f477SJiayu Hu 
2049e8c3d496SXuan Ding 	pthread_mutex_unlock(&vhost_dma_lock);
205053d3f477SJiayu Hu 	return 0;
2051e8c3d496SXuan Ding 
2052e8c3d496SXuan Ding error:
2053e8c3d496SXuan Ding 	pthread_mutex_unlock(&vhost_dma_lock);
2054e8c3d496SXuan Ding 	return -1;
205553d3f477SJiayu Hu }
205653d3f477SJiayu Hu 
205753d3f477SJiayu Hu int
205807ee2d75SXuan Ding rte_vhost_async_get_inflight(int vid, uint16_t queue_id)
20590c0935c5SJiayu Hu {
20600c0935c5SJiayu Hu 	struct vhost_virtqueue *vq;
20610c0935c5SJiayu Hu 	struct virtio_net *dev = get_device(vid);
20620c0935c5SJiayu Hu 	int ret = -1;
20630c0935c5SJiayu Hu 
20640c0935c5SJiayu Hu 	if (dev == NULL)
20650c0935c5SJiayu Hu 		return ret;
20660c0935c5SJiayu Hu 
20670c0935c5SJiayu Hu 	if (queue_id >= VHOST_MAX_VRING)
20680c0935c5SJiayu Hu 		return ret;
20690c0935c5SJiayu Hu 
20700c0935c5SJiayu Hu 	vq = dev->virtqueue[queue_id];
20710c0935c5SJiayu Hu 
20720c0935c5SJiayu Hu 	if (vq == NULL)
20730c0935c5SJiayu Hu 		return ret;
20740c0935c5SJiayu Hu 
207503f77d66SEelco Chaudron 	if (rte_rwlock_write_trylock(&vq->access_lock)) {
20760e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG(dev->ifname, DEBUG,
20770e21c7c0SDavid Marchand 			"failed to check in-flight packets. virtqueue busy.");
20780c0935c5SJiayu Hu 		return ret;
20790c0935c5SJiayu Hu 	}
20800c0935c5SJiayu Hu 
2081288cd1f8SMaxime Coquelin 	if (unlikely(!vq->access_ok)) {
2082288cd1f8SMaxime Coquelin 		ret = -1;
2083288cd1f8SMaxime Coquelin 		goto out_unlock;
2084288cd1f8SMaxime Coquelin 	}
2085288cd1f8SMaxime Coquelin 
20862d47fd3dSDavid Marchand 	if (vq->async)
2087ee8024b3SMaxime Coquelin 		ret = vq->async->pkts_inflight_n;
20882d47fd3dSDavid Marchand 
2089288cd1f8SMaxime Coquelin out_unlock:
209003f77d66SEelco Chaudron 	rte_rwlock_write_unlock(&vq->access_lock);
20910c0935c5SJiayu Hu 
20920c0935c5SJiayu Hu 	return ret;
20930c0935c5SJiayu Hu }
20940c0935c5SJiayu Hu 
209534fd4373SMiao Li int
20961419e8d9SXuan Ding rte_vhost_async_get_inflight_thread_unsafe(int vid, uint16_t queue_id)
20971419e8d9SXuan Ding {
20981419e8d9SXuan Ding 	struct vhost_virtqueue *vq;
20991419e8d9SXuan Ding 	struct virtio_net *dev = get_device(vid);
21001419e8d9SXuan Ding 	int ret = -1;
21011419e8d9SXuan Ding 
21021419e8d9SXuan Ding 	if (dev == NULL)
21031419e8d9SXuan Ding 		return ret;
21041419e8d9SXuan Ding 
21051419e8d9SXuan Ding 	if (queue_id >= VHOST_MAX_VRING)
21061419e8d9SXuan Ding 		return ret;
21071419e8d9SXuan Ding 
21081419e8d9SXuan Ding 	vq = dev->virtqueue[queue_id];
21091419e8d9SXuan Ding 
21101419e8d9SXuan Ding 	if (vq == NULL)
21111419e8d9SXuan Ding 		return ret;
21121419e8d9SXuan Ding 
211390d6e52bSDavid Marchand 	vq_assert_lock(dev, vq);
21140a8363efSMaxime Coquelin 
21151419e8d9SXuan Ding 	if (!vq->async)
21161419e8d9SXuan Ding 		return ret;
21171419e8d9SXuan Ding 
21181419e8d9SXuan Ding 	ret = vq->async->pkts_inflight_n;
21191419e8d9SXuan Ding 
21201419e8d9SXuan Ding 	return ret;
21211419e8d9SXuan Ding }
21221419e8d9SXuan Ding 
21231419e8d9SXuan Ding int
212434fd4373SMiao Li rte_vhost_get_monitor_addr(int vid, uint16_t queue_id,
212534fd4373SMiao Li 		struct rte_vhost_power_monitor_cond *pmc)
212634fd4373SMiao Li {
212734fd4373SMiao Li 	struct virtio_net *dev = get_device(vid);
212834fd4373SMiao Li 	struct vhost_virtqueue *vq;
2129b4c4e567SMaxime Coquelin 	int ret = 0;
213034fd4373SMiao Li 
213134fd4373SMiao Li 	if (dev == NULL)
213234fd4373SMiao Li 		return -1;
213334fd4373SMiao Li 	if (queue_id >= VHOST_MAX_VRING)
213434fd4373SMiao Li 		return -1;
213534fd4373SMiao Li 
213634fd4373SMiao Li 	vq = dev->virtqueue[queue_id];
213734fd4373SMiao Li 	if (vq == NULL)
213834fd4373SMiao Li 		return -1;
213934fd4373SMiao Li 
2140b4c4e567SMaxime Coquelin 	rte_rwlock_read_lock(&vq->access_lock);
2141b4c4e567SMaxime Coquelin 
2142b4c4e567SMaxime Coquelin 	if (unlikely(!vq->access_ok)) {
2143b4c4e567SMaxime Coquelin 		ret = -1;
2144b4c4e567SMaxime Coquelin 		goto out_unlock;
2145b4c4e567SMaxime Coquelin 	}
2146b4c4e567SMaxime Coquelin 
214734fd4373SMiao Li 	if (vq_is_packed(dev)) {
214834fd4373SMiao Li 		struct vring_packed_desc *desc;
214934fd4373SMiao Li 		desc = vq->desc_packed;
215034fd4373SMiao Li 		pmc->addr = &desc[vq->last_avail_idx].flags;
215134fd4373SMiao Li 		if (vq->avail_wrap_counter)
215234fd4373SMiao Li 			pmc->val = VRING_DESC_F_AVAIL;
215334fd4373SMiao Li 		else
215434fd4373SMiao Li 			pmc->val = VRING_DESC_F_USED;
215534fd4373SMiao Li 		pmc->mask = VRING_DESC_F_AVAIL | VRING_DESC_F_USED;
215634fd4373SMiao Li 		pmc->size = sizeof(desc[vq->last_avail_idx].flags);
215734fd4373SMiao Li 		pmc->match = 1;
215834fd4373SMiao Li 	} else {
215934fd4373SMiao Li 		pmc->addr = &vq->avail->idx;
216034fd4373SMiao Li 		pmc->val = vq->last_avail_idx & (vq->size - 1);
216134fd4373SMiao Li 		pmc->mask = vq->size - 1;
216234fd4373SMiao Li 		pmc->size = sizeof(vq->avail->idx);
216334fd4373SMiao Li 		pmc->match = 0;
216434fd4373SMiao Li 	}
216534fd4373SMiao Li 
2166b4c4e567SMaxime Coquelin out_unlock:
2167b4c4e567SMaxime Coquelin 	rte_rwlock_read_unlock(&vq->access_lock);
2168b4c4e567SMaxime Coquelin 
2169b4c4e567SMaxime Coquelin 	return ret;
217034fd4373SMiao Li }
217134fd4373SMiao Li 
2172be75dc99SMaxime Coquelin 
2173be75dc99SMaxime Coquelin int
2174be75dc99SMaxime Coquelin rte_vhost_vring_stats_get_names(int vid, uint16_t queue_id,
2175be75dc99SMaxime Coquelin 		struct rte_vhost_stat_name *name, unsigned int size)
2176be75dc99SMaxime Coquelin {
2177be75dc99SMaxime Coquelin 	struct virtio_net *dev = get_device(vid);
2178be75dc99SMaxime Coquelin 	unsigned int i;
2179be75dc99SMaxime Coquelin 
2180be75dc99SMaxime Coquelin 	if (dev == NULL)
2181be75dc99SMaxime Coquelin 		return -1;
2182be75dc99SMaxime Coquelin 
2183be75dc99SMaxime Coquelin 	if (queue_id >= dev->nr_vring)
2184be75dc99SMaxime Coquelin 		return -1;
2185be75dc99SMaxime Coquelin 
2186be75dc99SMaxime Coquelin 	if (!(dev->flags & VIRTIO_DEV_STATS_ENABLED))
2187be75dc99SMaxime Coquelin 		return -1;
2188be75dc99SMaxime Coquelin 
2189be75dc99SMaxime Coquelin 	if (name == NULL || size < VHOST_NB_VQ_STATS)
2190be75dc99SMaxime Coquelin 		return VHOST_NB_VQ_STATS;
2191be75dc99SMaxime Coquelin 
2192be75dc99SMaxime Coquelin 	for (i = 0; i < VHOST_NB_VQ_STATS; i++)
2193be75dc99SMaxime Coquelin 		snprintf(name[i].name, sizeof(name[i].name), "%s_q%u_%s",
2194be75dc99SMaxime Coquelin 				(queue_id & 1) ? "rx" : "tx",
2195be75dc99SMaxime Coquelin 				queue_id / 2, vhost_vq_stat_strings[i].name);
2196be75dc99SMaxime Coquelin 
2197be75dc99SMaxime Coquelin 	return VHOST_NB_VQ_STATS;
2198be75dc99SMaxime Coquelin }
2199be75dc99SMaxime Coquelin 
2200be75dc99SMaxime Coquelin int
2201be75dc99SMaxime Coquelin rte_vhost_vring_stats_get(int vid, uint16_t queue_id,
2202be75dc99SMaxime Coquelin 		struct rte_vhost_stat *stats, unsigned int n)
2203be75dc99SMaxime Coquelin {
2204be75dc99SMaxime Coquelin 	struct virtio_net *dev = get_device(vid);
2205be75dc99SMaxime Coquelin 	struct vhost_virtqueue *vq;
2206be75dc99SMaxime Coquelin 	unsigned int i;
2207a004501aSMaxime Coquelin 	int ret = VHOST_NB_VQ_STATS;
2208be75dc99SMaxime Coquelin 
2209be75dc99SMaxime Coquelin 	if (dev == NULL)
2210be75dc99SMaxime Coquelin 		return -1;
2211be75dc99SMaxime Coquelin 
2212be75dc99SMaxime Coquelin 	if (queue_id >= dev->nr_vring)
2213be75dc99SMaxime Coquelin 		return -1;
2214be75dc99SMaxime Coquelin 
2215be75dc99SMaxime Coquelin 	if (!(dev->flags & VIRTIO_DEV_STATS_ENABLED))
2216be75dc99SMaxime Coquelin 		return -1;
2217be75dc99SMaxime Coquelin 
2218be75dc99SMaxime Coquelin 	if (stats == NULL || n < VHOST_NB_VQ_STATS)
2219be75dc99SMaxime Coquelin 		return VHOST_NB_VQ_STATS;
2220be75dc99SMaxime Coquelin 
2221be75dc99SMaxime Coquelin 	vq = dev->virtqueue[queue_id];
2222be75dc99SMaxime Coquelin 
222303f77d66SEelco Chaudron 	rte_rwlock_write_lock(&vq->access_lock);
2224a004501aSMaxime Coquelin 
2225a004501aSMaxime Coquelin 	if (unlikely(!vq->access_ok)) {
2226a004501aSMaxime Coquelin 		ret = -1;
2227a004501aSMaxime Coquelin 		goto out_unlock;
2228a004501aSMaxime Coquelin 	}
2229a004501aSMaxime Coquelin 
2230be75dc99SMaxime Coquelin 	for (i = 0; i < VHOST_NB_VQ_STATS; i++) {
22310f5d1e0cSEelco Chaudron 		/*
22320f5d1e0cSEelco Chaudron 		 * No need to the read atomic counters as such, due to the
22330f5d1e0cSEelco Chaudron 		 * above write access_lock preventing them to be updated.
22340f5d1e0cSEelco Chaudron 		 */
2235be75dc99SMaxime Coquelin 		stats[i].value =
2236be75dc99SMaxime Coquelin 			*(uint64_t *)(((char *)vq) + vhost_vq_stat_strings[i].offset);
2237be75dc99SMaxime Coquelin 		stats[i].id = i;
2238be75dc99SMaxime Coquelin 	}
2239a004501aSMaxime Coquelin 
2240a004501aSMaxime Coquelin out_unlock:
224103f77d66SEelco Chaudron 	rte_rwlock_write_unlock(&vq->access_lock);
2242be75dc99SMaxime Coquelin 
2243a004501aSMaxime Coquelin 	return ret;
2244be75dc99SMaxime Coquelin }
2245be75dc99SMaxime Coquelin 
2246be75dc99SMaxime Coquelin int rte_vhost_vring_stats_reset(int vid, uint16_t queue_id)
2247be75dc99SMaxime Coquelin {
2248be75dc99SMaxime Coquelin 	struct virtio_net *dev = get_device(vid);
2249be75dc99SMaxime Coquelin 	struct vhost_virtqueue *vq;
2250a004501aSMaxime Coquelin 	int ret = 0;
2251be75dc99SMaxime Coquelin 
2252be75dc99SMaxime Coquelin 	if (dev == NULL)
2253be75dc99SMaxime Coquelin 		return -1;
2254be75dc99SMaxime Coquelin 
2255be75dc99SMaxime Coquelin 	if (queue_id >= dev->nr_vring)
2256be75dc99SMaxime Coquelin 		return -1;
2257be75dc99SMaxime Coquelin 
2258be75dc99SMaxime Coquelin 	if (!(dev->flags & VIRTIO_DEV_STATS_ENABLED))
2259be75dc99SMaxime Coquelin 		return -1;
2260be75dc99SMaxime Coquelin 
2261be75dc99SMaxime Coquelin 	vq = dev->virtqueue[queue_id];
2262be75dc99SMaxime Coquelin 
226303f77d66SEelco Chaudron 	rte_rwlock_write_lock(&vq->access_lock);
2264a004501aSMaxime Coquelin 
2265a004501aSMaxime Coquelin 	if (unlikely(!vq->access_ok)) {
2266a004501aSMaxime Coquelin 		ret = -1;
2267a004501aSMaxime Coquelin 		goto out_unlock;
2268a004501aSMaxime Coquelin 	}
22690f5d1e0cSEelco Chaudron 	/*
22700f5d1e0cSEelco Chaudron 	 * No need to the reset atomic counters as such, due to the
22710f5d1e0cSEelco Chaudron 	 * above write access_lock preventing them to be updated.
22720f5d1e0cSEelco Chaudron 	 */
2273be75dc99SMaxime Coquelin 	memset(&vq->stats, 0, sizeof(vq->stats));
2274a004501aSMaxime Coquelin 
2275a004501aSMaxime Coquelin out_unlock:
227603f77d66SEelco Chaudron 	rte_rwlock_write_unlock(&vq->access_lock);
2277be75dc99SMaxime Coquelin 
2278a004501aSMaxime Coquelin 	return ret;
2279be75dc99SMaxime Coquelin }
2280be75dc99SMaxime Coquelin 
2281e8c3d496SXuan Ding int
2282e8c3d496SXuan Ding rte_vhost_async_dma_unconfigure(int16_t dma_id, uint16_t vchan_id)
2283e8c3d496SXuan Ding {
2284e8c3d496SXuan Ding 	struct rte_dma_info info;
2285e8c3d496SXuan Ding 	struct rte_dma_stats stats = { 0 };
2286e8c3d496SXuan Ding 
2287e8c3d496SXuan Ding 	pthread_mutex_lock(&vhost_dma_lock);
2288e8c3d496SXuan Ding 
2289e8c3d496SXuan Ding 	if (!rte_dma_is_valid(dma_id)) {
22900e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("dma", ERR, "DMA %d is not found.", dma_id);
2291e8c3d496SXuan Ding 		goto error;
2292e8c3d496SXuan Ding 	}
2293e8c3d496SXuan Ding 
2294e8c3d496SXuan Ding 	if (rte_dma_info_get(dma_id, &info) != 0) {
22950e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("dma", ERR, "Fail to get DMA %d information.", dma_id);
2296e8c3d496SXuan Ding 		goto error;
2297e8c3d496SXuan Ding 	}
2298e8c3d496SXuan Ding 
2299e8c3d496SXuan Ding 	if (vchan_id >= info.max_vchans || !dma_copy_track[dma_id].vchans ||
2300e8c3d496SXuan Ding 		!dma_copy_track[dma_id].vchans[vchan_id].pkts_cmpl_flag_addr) {
23010e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("dma", ERR, "Invalid channel %d:%u.", dma_id, vchan_id);
2302e8c3d496SXuan Ding 		goto error;
2303e8c3d496SXuan Ding 	}
2304e8c3d496SXuan Ding 
2305e8c3d496SXuan Ding 	if (rte_dma_stats_get(dma_id, vchan_id, &stats) != 0) {
23060e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("dma", ERR,
23070e21c7c0SDavid Marchand 				 "Failed to get stats for DMA %d vChannel %u.", dma_id, vchan_id);
2308e8c3d496SXuan Ding 		goto error;
2309e8c3d496SXuan Ding 	}
2310e8c3d496SXuan Ding 
2311e8c3d496SXuan Ding 	if (stats.submitted - stats.completed != 0) {
23120e21c7c0SDavid Marchand 		VHOST_CONFIG_LOG("dma", ERR,
23130e21c7c0SDavid Marchand 				 "Do not unconfigure when there are inflight packets.");
2314e8c3d496SXuan Ding 		goto error;
2315e8c3d496SXuan Ding 	}
2316e8c3d496SXuan Ding 
2317e8c3d496SXuan Ding 	rte_free(dma_copy_track[dma_id].vchans[vchan_id].pkts_cmpl_flag_addr);
2318e8c3d496SXuan Ding 	dma_copy_track[dma_id].vchans[vchan_id].pkts_cmpl_flag_addr = NULL;
2319e8c3d496SXuan Ding 	dma_copy_track[dma_id].nr_vchans--;
2320e8c3d496SXuan Ding 
2321e8c3d496SXuan Ding 	if (dma_copy_track[dma_id].nr_vchans == 0) {
2322e8c3d496SXuan Ding 		rte_free(dma_copy_track[dma_id].vchans);
2323e8c3d496SXuan Ding 		dma_copy_track[dma_id].vchans = NULL;
2324e8c3d496SXuan Ding 	}
2325e8c3d496SXuan Ding 
2326e8c3d496SXuan Ding 	pthread_mutex_unlock(&vhost_dma_lock);
2327e8c3d496SXuan Ding 	return 0;
2328e8c3d496SXuan Ding 
2329e8c3d496SXuan Ding error:
2330e8c3d496SXuan Ding 	pthread_mutex_unlock(&vhost_dma_lock);
2331e8c3d496SXuan Ding 	return -1;
2332e8c3d496SXuan Ding }
2333e8c3d496SXuan Ding 
2334eeded204SDavid Marchand RTE_LOG_REGISTER_SUFFIX(vhost_config_log_level, config, INFO);
2335eeded204SDavid Marchand RTE_LOG_REGISTER_SUFFIX(vhost_data_log_level, data, WARNING);
2336