Skip to contentSkip to conversation

vhost: add support for packed ring in async vhost

PATCH Superseded Cheng Jiang 5 participants 60 messages Mar 17, 2021 – May 4, 2021

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  15 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 449 +++++++++++++++++++++++++++--
 4 files changed, 436 insertions(+), 36 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..29de5df8c 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring*/
 };
 
 /**
lib/librte_vhost/vhost.c +11 -4
index 52ab93d1e..445a9f327 100644
@@ -1603,9 +1603,9 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or non-inorder mode "
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1643,10 +1643,17 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
+	}
+
+	if (!vq->async_pkts_info ||
 		!vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
 		VHOST_LOG_CONFIG(ERR,
lib/librte_vhost/vhost.h +6 -1
index 658f6fc28..d6324fbf8 100644
@@ -206,9 +206,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +418 -31

506 lines not on this page · raw message

Signature · 1 line
2.29.2
#1 lore raw

PATCH v2 1/1 vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  24 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 447 +++++++++++++++++++++++++++--
 4 files changed, 441 insertions(+), 38 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +18 -6
index 52ab93d1e..51b44d6f2 100644
@@ -330,15 +330,20 @@ vhost_free_async_mem(struct vhost_virtqueue *vq)
 {
 	if (vq->async_pkts_info)
 		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	if (vq->async_buffers_packed) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	} else {
 		rte_free(vq->async_descs_split);
+		vq->async_descs_split = NULL;
+	}
+
 	if (vq->it_pool)
 		rte_free(vq->it_pool);
 	if (vq->vec_pool)
 		rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -1603,9 +1608,9 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or non-inorder mode "
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1643,10 +1648,17 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
+	}
+
+	if (!vq->async_pkts_info ||
 		!vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
 		VHOST_LOG_CONFIG(ERR,
lib/librte_vhost/vhost.h +6 -1
index 658f6fc28..d6324fbf8 100644
@@ -206,9 +206,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +416 -31

504 lines not on this page · raw message

--
2.29.2

#2 lore raw
Quoted 261 lines

-----Original Message-----
From: dev <[email protected]> On Behalf Of Cheng Jiang
Sent: Monday, March 22, 2021 2:15 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [dpdk-dev] [PATCH v2] vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  24 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 447 +++++++++++++++++++++++++++--
 4 files changed, 441 insertions(+), 38 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +7 -2
index 52ab93d1e..51b44d6f2 100644
@@ -330,15 +330,20 @@ vhost_free_async_mem(struct vhost_virtqueue
 *vq)
 {
 	if (vq->async_pkts_info)
 		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	if (vq->async_buffers_packed) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	} else {
 		rte_free(vq->async_descs_split);
+		vq->async_descs_split = NULL;
+	}
+
 	if (vq->it_pool)
 		rte_free(vq->it_pool);
 	if (vq->vec_pool)
 		rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;

}

lib/librte_vhost/vhost.c +1 -2
@@ -1603,9 +1608,9 @@ int rte_vhost_async_channel_register(int vid,
 uint16_t queue_id,
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or

non-inorder mode "

lib/librte_vhost/vhost.c +10 -2
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1643,10 +1648,17 @@ int rte_vhost_async_channel_register(int vid,
 uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
+	}
+
+	if (!vq->async_pkts_info ||
 		!vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
lib/librte_vhost/vhost.c +0 -0
 		VHOST_LOG_CONFIG(ERR,
lib/librte_vhost/vhost.h +6 -1
index 658f6fc28..d6324fbf8 100644
@@ -206,9 +206,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +1 -2
index 583bf379c..fa8c4f4fe 100644
@@ -363,8 +363,7 @@
 vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue *vq,
 }
 
 static __rte_always_inline void
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-				   struct vhost_virtqueue *vq,
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
 				   uint32_t len[],
 				   uint16_t id[],
lib/librte_vhost/virtio_net.c +11 -0
 				   uint16_t count[],
@@ -382,6 +381,17 @@ vhost_shadow_enqueue_single_packed(struct
 virtio_net *dev,
 		vq->shadow_aligned_idx += count[i];
 		vq->shadow_used_idx++;
 	}
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+				   struct vhost_virtqueue *vq,
+				   uint32_t len[],
+				   uint16_t id[],
+				   uint16_t count[],
+				   uint16_t num_buffers)
+{
+	vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 	if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {
lib/librte_vhost/virtio_net.c +95 -0
 		do_data_copy_enqueue(dev, vq);
@@ -1633,12 +1643,343 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    struct buf_vector *buf_vec,
+			    uint16_t *nr_descs,
+			    uint16_t *nr_buffers,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it)
+{
+	uint16_t nr_vec = 0;
+	uint16_t avail_idx = vq->last_avail_idx;
+	uint16_t max_tries, tries = 0;
+	uint16_t buf_id = 0;
+	uint32_t len = 0;
+	uint16_t desc_count;
+	uint32_t size = pkt->pkt_len + sizeof(struct
 virtio_net_hdr_mrg_rxbuf);
+	uint32_t buffer_len[vq->size];
+	uint16_t buffer_buf_id[vq->size];
+	uint16_t buffer_desc_count[vq->size];
+	*nr_buffers = 0;
+
+	if (rxvq_is_mergeable(dev))
+		max_tries = vq->size - 1;
+	else
+		max_tries = 1;
+
+	while (size > 0) {
+		/*
+		 * if we tried all available ring items, and still
+		 * can't get enough buf, it means something abnormal
+		 * happened.
+		 */
+		if (unlikely(++tries > max_tries))
+			return -1;
+
+		if (unlikely(fill_vec_buf_packed(dev, vq,
+						avail_idx, &desc_count,
+						buf_vec, &nr_vec,
+						&buf_id, &len,
+						VHOST_ACCESS_RW) < 0))
+			return -1;
+
+		len = RTE_MIN(len, size);
+		size -= len;
+
+		buffer_len[*nr_buffers] = len;
+		buffer_buf_id[*nr_buffers] = buf_id;
+		buffer_desc_count[*nr_buffers] = desc_count;
+		*nr_buffers += 1;
+
+		*nr_descs += desc_count;
+		avail_idx += desc_count;
+		if (avail_idx >= vq->size)
+			avail_idx -= vq->size;
+	}
+
+	if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,
+		src_iovec, dst_iovec, src_it, dst_it) < 0)
+		return -1;
+
+	vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,
+					   buffer_desc_count, *nr_buffers);
+
+	return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    uint16_t *nr_descs, uint16_t *nr_buffers,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it)
+{
+	struct buf_vector buf_vec[BUF_VECTOR_MAX];
+	*nr_descs = 0;
+	*nr_buffers = 0;
+
+	if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,
 buf_vec,
+						 nr_descs,
+						 nr_buffers,
+						 src_iovec, dst_iovec,
+						 src_it, dst_it) < 0)) {
+		VHOST_LOG_DATA(DEBUG,
+				"(%d) failed to get enough desc from vring\n",
+				dev->vid);
+		return -1;
+	}
+
+	VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end
 index %d\n",
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + *nr_descs);

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct · line 1754

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+
+	return 0;
+}
+
+static __rte_noinline uint32_t
+virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
+	struct vhost_virtqueue *vq, uint16_t queue_id,
+	struct rte_mbuf **pkts, uint32_t count,
+	struct rte_mbuf **comp_pkts, uint32_t *comp_count)
+{

Hi Cheng,
There're some common parts in virtio_dev_rx_async_submit_packed and virtio_dev_rx_async_submit_split. We can abstract some functions for those common parts which can bring more clarity.

Also this patch may be too huge for reviewing, please separate it into few parts for better understanding.

Thanks,
Marvin

Quoted 149 lines
lib/librte_vhost/virtio_net.c +90 -0
+	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint16_t num_buffers;
+	uint16_t num_desc;
+
+	struct rte_vhost_iov_iter *it_pool = vq->it_pool;
+	struct iovec *vec_pool = vq->vec_pool;
+	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
+	struct iovec *src_iovec = vec_pool;
+	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
+	struct rte_vhost_iov_iter *src_it = it_pool;
+	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
+	uint16_t slot_idx = 0;
+	uint16_t segs_await = 0;
+	struct async_inflight_info *pkts_info = vq->async_pkts_info;
+	uint32_t n_pkts = 0, pkt_err = 0;
+	uint32_t num_async_pkts = 0, num_done_pkts = 0;
+	struct {
+		uint16_t pkt_idx;
+		uint16_t last_avail_idx;
+	} async_pkts_log[MAX_PKT_BURST];
+
+	rte_prefetch0(&vq->desc[vq->last_avail_idx & (vq->size - 1)]);
+
+	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
+		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq,
+						pkts[pkt_idx],
+						&num_desc, &num_buffers,
+						src_iovec, dst_iovec,
+						src_it, dst_it) < 0)) {
+			break;
+		}
+
+		VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end
 index %d\n",
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + num_desc);
+
+		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
+			(vq->size - 1);
+		if (src_it->count) {
+			uint16_t from, to;
+
+			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			pkts_info[slot_idx].descs = num_desc;
+			pkts_info[slot_idx].nr_buffers = num_buffers;
+			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
+			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
+			async_pkts_log[num_async_pkts++].last_avail_idx =
+				vq->last_avail_idx;
+			src_iovec += src_it->nr_segs;
+			dst_iovec += dst_it->nr_segs;
+			src_it += 2;
+			dst_it += 2;
+			segs_await += src_it->nr_segs;
+
+			/**
+			 * recover shadow used ring and keep DMA-occupied
+			 * descriptors.
+			 */
+			from = vq->shadow_used_idx - num_buffers;
+			to = vq->async_packed_buffer_idx & (vq->size - 1);
+			if (num_buffers + to <= vq->size) {
+				rte_memcpy(&vq->async_buffers_packed[to],
+					&vq->shadow_used_packed[from],
+					num_buffers *
+					sizeof(struct
 vring_used_elem_packed));
+			} else {
+				int size = vq->size - to;
+
+				rte_memcpy(&vq->async_buffers_packed[to],
+					&vq->shadow_used_packed[from],
+					size *
+					sizeof(struct
 vring_used_elem_packed));
+				rte_memcpy(vq->async_buffers_packed,
+					&vq->shadow_used_packed[from +
+					size], (num_buffers - size) *
+					sizeof(struct
 vring_used_elem_packed));
+			}
+			vq->async_packed_buffer_idx += num_buffers;
+			vq->shadow_used_idx -= num_buffers;
+		} else
+			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
+
+		vq_inc_last_avail_packed(vq, num_desc);
+
+		/*
+		 * conditions to trigger async device transfer:
+		 * - buffered packet number reaches transfer threshold
+		 * - unused async iov number is less than max vhost vector
+		 */
+		if (unlikely(pkt_burst_idx >=

VHOST_ASYNC_BATCH_THRESHOLD ||

lib/librte_vhost/virtio_net.c +6 -0
+			((VHOST_MAX_ASYNC_VEC >> 1) - segs_await <
+			BUF_VECTOR_MAX))) {
+			n_pkts = vq->async_ops.transfer_data(dev->vid,
+					queue_id, tdes, 0, pkt_burst_idx);
+			src_iovec = vec_pool;
+			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >>

1);

lib/librte_vhost/virtio_net.c +8 -0
+			src_it = it_pool;
+			dst_it = it_pool + 1;
+			segs_await = 0;
+			vq->async_pkts_inflight_n += n_pkts;
+
+			if (unlikely(n_pkts < pkt_burst_idx)) {
+				/*
+				 * log error packets number here and do

actual

lib/librte_vhost/virtio_net.c +37 -0
+				 * error processing when applications poll
+				 * completion
+				 */
+				pkt_err = pkt_burst_idx - n_pkts;
+				pkt_burst_idx = 0;
+				break;
+			}
+
+			pkt_burst_idx = 0;
+		}
+	}
+
+	if (pkt_burst_idx) {
+		n_pkts = vq->async_ops.transfer_data(dev->vid,
+				queue_id, tdes, 0, pkt_burst_idx);
+		vq->async_pkts_inflight_n += n_pkts;
+
+		if (unlikely(n_pkts < pkt_burst_idx))
+			pkt_err = pkt_burst_idx - n_pkts;
+	}
+
+	do_data_copy_enqueue(dev, vq);
+
+	if (unlikely(pkt_err)) {
+		uint16_t num_buffers = 0;
+
+		num_async_pkts -= pkt_err;
+		/* calculate the sum of descriptors of DMA-error packets. */
+		while (pkt_err-- > 0) {
+			num_buffers +=
+				pkts_info[slot_idx & (vq->size - 1)].nr_buffers;
+			slot_idx--;
+		}
+		vq->async_packed_buffer_idx -= num_buffers;
+		/* recover shadow used ring and available ring */
+		vq->shadow_used_idx -= (vq->last_avail_idx -
+

async_pkts_log[num_async_pkts].last_avail_idx -

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1892

Quoted 1 line
lib/librte_vhost/virtio_net.c +1 -0
+				num_buffers);

Could it possible that vq->last_avail_idx smaller than async_pkts_log[num_async_pkts].last_avail_idx when operations near the ring's boundary?

Quoted 246 lines
lib/librte_vhost/virtio_net.c +21 -0
+		vq->last_avail_idx =
+			async_pkts_log[num_async_pkts].last_avail_idx;
+		pkt_idx = async_pkts_log[num_async_pkts].pkt_idx;
+		num_done_pkts = pkt_idx - num_async_pkts;
+	}
+
+	vq->async_pkts_idx += num_async_pkts;
+	*comp_count = num_done_pkts;
+
+	if (likely(vq->shadow_used_idx)) {
+		vhost_flush_enqueue_shadow_packed(dev, vq);
+		vhost_vring_call_packed(dev, vq);
+	}
+
+	return pkt_idx;
+}
+
+static __rte_always_inline void
+vhost_update_used_packed(struct virtio_net *dev,
+				  struct vhost_virtqueue *vq,
+				  struct vring_used_elem_packed

*shadow_ring,

lib/librte_vhost/virtio_net.c +127 -24
+				  uint16_t count)
+{
+	if (count == 0)
+		return;
+	int i;
+	uint16_t used_idx = vq->last_used_idx;
+	uint16_t head_idx = vq->last_used_idx;
+	uint16_t head_flags = 0;
+
+	/* Split loop in two to save memory barriers */
+	for (i = 0; i < count; i++) {
+		vq->desc_packed[used_idx].id = shadow_ring[i].id;
+		vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+		used_idx += shadow_ring[i].count;
+		if (used_idx >= vq->size)
+			used_idx -= vq->size;
+	}
+
+	/* The ordering for storing desc flags needs to be enforced. */
+	rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+	for (i = 0; i < count; i++) {
+		uint16_t flags;
+
+		if (vq->shadow_used_packed[i].len)
+			flags = VRING_DESC_F_WRITE;
+		else
+			flags = 0;
+
+		if (vq->used_wrap_counter) {
+			flags |= VRING_DESC_F_USED;
+			flags |= VRING_DESC_F_AVAIL;
+		} else {
+			flags &= ~VRING_DESC_F_USED;
+			flags &= ~VRING_DESC_F_AVAIL;
+		}
+
+		if (i > 0) {
+			vq->desc_packed[vq->last_used_idx].flags = flags;
+
+			vhost_log_cache_used_vring(dev, vq,
+					vq->last_used_idx *
+					sizeof(struct vring_packed_desc),
+					sizeof(struct vring_packed_desc));
+		} else {
+			head_idx = vq->last_used_idx;
+			head_flags = flags;
+		}
+
+		vq_inc_last_used_packed(vq, shadow_ring[i].count);
+	}
+
+	vq->desc_packed[head_idx].flags = head_flags;
+
+	vhost_log_cache_used_vring(dev, vq,
+				head_idx *
+				sizeof(struct vring_packed_desc),
+				sizeof(struct vring_packed_desc));
+
+	vhost_log_cache_sync(dev, vq);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
 	struct virtio_net *dev = get_device(vid);
 	struct vhost_virtqueue *vq;
-	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0;
+	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0, n_buffers = 0;
 	uint16_t start_idx, pkts_idx, vq_size;
 	struct async_inflight_info *pkts_info;
 	uint16_t from, i;
@@ -1680,53 +2021,96 @@ uint16_t
 rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		goto done;
 	}
 
-	for (i = 0; i < n_pkts_put; i++) {
-		from = (start_idx + i) & (vq_size - 1);
-		n_descs += pkts_info[from].descs;
-		pkts[i] = pkts_info[from].mbuf;
+	if (vq_is_packed(dev)) {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);
+			n_buffers += pkts_info[from].nr_buffers;
+			pkts[i] = pkts_info[from].mbuf;
+		}
+	} else {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);
+			n_descs += pkts_info[from].descs;
+			pkts[i] = pkts_info[from].mbuf;
+		}
 	}
+
 	vq->async_last_pkts_n = n_pkts_cpl - n_pkts_put;
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
 		uint16_t nr_copy;
 		uint16_t to;
 
 		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
+		if (vq_is_packed(dev)) {
+			uint16_t nr_left = n_buffers;
+			uint16_t to;
+			do {
+				from = vq->last_async_buffer_idx &
+								(vq->size - 1);
+				to = (from + nr_left) & (vq->size - 1);
+
+				if (to > from) {
+					vhost_update_used_packed(dev, vq,
+						vq->async_buffers_packed +
 from,
+						to - from);
+					vq->last_async_buffer_idx += nr_left;
+					nr_left = 0;
+				} else {
+					vhost_update_used_packed(dev, vq,
+						vq->async_buffers_packed +
 from,
+						vq->size - from);
+					vq->last_async_buffer_idx +=
+								vq->size -
 from;
+					nr_left -= vq->size - from;
+				}
+			} while (nr_left > 0);
+			vhost_vring_call_packed(dev, vq);
+		} else {
+			uint16_t nr_left = n_descs;
+			do {
+				from = vq->last_async_desc_idx & (vq->size -
 1);
+				nr_copy = nr_left + from <= vq->size ? nr_left :
+					vq->size - from;
+				to = vq->last_used_idx & (vq->size - 1);
+
+				if (to + nr_copy <= vq->size) {
+					rte_memcpy(&vq->used->ring[to],
 						&vq-
 >async_descs_split[from],
 						nr_copy *
 						sizeof(struct
 vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
+				} else {
+					uint16_t size = vq->size - to;
 
-				rte_memcpy(&vq->used->ring[to],
+					rte_memcpy(&vq->used->ring[to],
 						&vq-
 >async_descs_split[from],
 						size *
 						sizeof(struct
 vring_used_elem));
-				rte_memcpy(vq->used->ring,
+					rte_memcpy(vq->used->ring,
 						&vq->async_descs_split[from
+
 						size], (nr_copy - size) *
 						sizeof(struct
 vring_used_elem));
-			}
+				}
+
+				vq->last_async_desc_idx += nr_copy;
+				vq->last_used_idx += nr_copy;
+				nr_left -= nr_copy;
+			} while (nr_left > 0);
+
+			__atomic_add_fetch(&vq->used->idx, n_descs,
+					__ATOMIC_RELEASE);
+			vhost_vring_call_split(dev, vq);
+		}
 
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
 
-		__atomic_add_fetch(&vq->used->idx, n_descs,

__ATOMIC_RELEASE);

lib/librte_vhost/virtio_net.c +10 -5
-		vhost_vring_call_split(dev, vq);
-	} else
-		vq->last_async_desc_idx += n_descs;
+
+	} else {
+		if (vq_is_packed(dev))
+			vq->last_async_buffer_idx += n_buffers;
+		else
+			vq->last_async_desc_idx += n_descs;
+	}
 
 done:
 	rte_spinlock_unlock(&vq->access_lock);
@@ -1767,9 +2151,10 @@ virtio_dev_rx_async_submit(struct virtio_net
 *dev, uint16_t queue_id,
 	if (count == 0)
 		goto out;
 
-	/* TODO: packed queue not implemented */
 	if (vq_is_packed(dev))
-		nb_tx = 0;
+		nb_tx = virtio_dev_rx_async_submit_packed(dev,
+				vq, queue_id, pkts, count, comp_pkts,
+				comp_count);
 	else
 		nb_tx = virtio_dev_rx_async_submit_split(dev,

vq, queue_id, pkts, count, comp_pkts,
--
2.29.2

#3 lore raw

Hi,

Quoted 256 lines

-----Original Message-----
From: Liu, Yong <[email protected]>
Sent: Wednesday, March 24, 2021 5:19 PM
To: Jiang, Cheng1 <[email protected]>; [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: RE: [dpdk-dev] [PATCH v2] vhost: add support for packed ring in async vhost

Quoted 68 lines

-----Original Message-----
From: dev <[email protected]> On Behalf Of Cheng Jiang
Sent: Monday, March 22, 2021 2:15 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [dpdk-dev] [PATCH v2] vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  24 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 447 +++++++++++++++++++++++++++--
 4 files changed, 441 insertions(+), 38 deletions(-)
lib/librte_vhost/rte_vhost_async.h +0 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {  struct

async_inflight_info { struct rte_mbuf *mbuf; uint16_t descs; /* num of descs inflight */

lib/librte_vhost/rte_vhost_async.h +1 -0
+uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c index renamed from lib/librte_vhost/vhost.c +0 -0

52ab93d1e..51b44d6f2 100644

lib/librte_vhost/vhost.c +7 -2
@@ -330,15 +330,20 @@ vhost_free_async_mem(struct vhost_virtqueue
 *vq)
 {
 if (vq->async_pkts_info)
 rte_free(vq->async_pkts_info);
-if (vq->async_descs_split)
+if (vq->async_buffers_packed) {
+rte_free(vq->async_buffers_packed);
+vq->async_buffers_packed = NULL;
+} else {
 rte_free(vq->async_descs_split);
+vq->async_descs_split = NULL;
+}
+
 if (vq->it_pool)
 rte_free(vq->it_pool);
 if (vq->vec_pool)
 rte_free(vq->vec_pool);
 
 vq->async_pkts_info = NULL;
-vq->async_descs_split = NULL;
 vq->it_pool = NULL;
 vq->vec_pool = NULL;

}

lib/librte_vhost/vhost.c +0 -0
@@ -1603,9 +1608,9 @@ int rte_vhost_async_channel_register(int vid,
 uint16_t queue_id,  return -1;

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, · line 1616

Quoted 10 lines
lib/librte_vhost/vhost.c +3 -2
 
 /* packed queue is not supported */
-if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+if (unlikely(!f.async_inorder)) {
 VHOST_LOG_CONFIG(ERR,
-"async copy is not supported on packed queue or non-inorder mode "
+"async copy is not supported on non-inorder mode "
 "(vid %d, qid: %d)\n", vid, queue_id);  return -1;  } @@ -1643,10
+1648,17 @@ int rte_vhost_async_channel_register(int vid, uint16_t
 queue_id,  vq->vec_pool = rte_malloc_socket(NULL,

VHOST_MAX_ASYNC_VEC

Quoted 38 lines

* sizeof(struct iovec), RTE_CACHE_LINE_SIZE, node);

lib/librte_vhost/vhost.c +6 -1
-vq->async_descs_split = rte_malloc_socket(NULL,
+if (vq_is_packed(dev)) {
+vq->async_buffers_packed = rte_malloc_socket(NULL, size *
+vq->sizeof(struct vring_used_elem_packed),
+RTE_CACHE_LINE_SIZE, node);
+} else {
+vq->async_descs_split = rte_malloc_socket(NULL,
 vq->size * sizeof(struct vring_used_elem),  RTE_CACHE_LINE_SIZE,

node); -if (!vq->async_descs_split || !vq->async_pkts_info ||

lib/librte_vhost/vhost.c +3 -0
+}
+
+if (!vq->async_pkts_info ||
 !vq->it_pool || !vq->vec_pool) {
 vhost_free_async_mem(vq);
 VHOST_LOG_CONFIG(ERR,
lib/librte_vhost/vhost.h index renamed from lib/librte_vhost/vhost.h +0 -0

658f6fc28..d6324fbf8 100644

lib/librte_vhost/vhost.h +0 -0
@@ -206,9 +206,14 @@ struct vhost_virtqueue {  uint16_tasync_pkts_idx;

uint16_tasync_pkts_inflight_n; uint16_tasync_last_pkts_n; -struct vring_used_elem *async_descs_split;

lib/librte_vhost/vhost.h +5 -0
+union {
+struct vring_used_elem  *async_descs_split; struct
+vring_used_elem_packed *async_buffers_packed; };
 uint16_t async_desc_idx;
+uint16_t async_packed_buffer_idx;
 uint16_t last_async_desc_idx;
+uint16_t last_async_buffer_idx;
 
 /* vq async features */
 boolasync_inorder;
(quoted diff) +0 -0

b/lib/librte_vhost/virtio_net.c index 583bf379c..fa8c4f4fe 100644

lib/librte_vhost/virtio_net.c +0 -0
@@ -363,8 +363,7 @@
Quoted 1 line

vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue

*vq,

Quoted 124 lines

}

static __rte_always_inline void

lib/librte_vhost/virtio_net.c +12 -2
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-   struct vhost_virtqueue *vq,
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
    uint32_t len[],
    uint16_t id[],
    uint16_t count[],
@@ -382,6 +381,17 @@ vhost_shadow_enqueue_single_packed(struct
 virtio_net *dev,
 vq->shadow_aligned_idx += count[i];
 vq->shadow_used_idx++;
 }
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+   struct vhost_virtqueue *vq,
+   uint32_t len[],
+   uint16_t id[],
+   uint16_t count[],
+   uint16_t num_buffers)
+{
+vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {

do_data_copy_enqueue(dev, vq); @@ -1633,12 +1643,343 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
return pkt_idx;
}

lib/librte_vhost/virtio_net.c +13 -0
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+    struct vhost_virtqueue *vq,
+    struct rte_mbuf *pkt,
+    struct buf_vector *buf_vec,
+    uint16_t *nr_descs,
+    uint16_t *nr_buffers,
+    struct iovec *src_iovec, struct iovec *dst_iovec,
+    struct rte_vhost_iov_iter *src_it,
+    struct rte_vhost_iov_iter *dst_it) { uint16_t nr_vec = 0;
+uint16_t avail_idx = vq->last_avail_idx; uint16_t max_tries, tries =
+0; uint16_t buf_id = 0; uint32_t len = 0; uint16_t desc_count;
+uint32_t size = pkt->pkt_len + sizeof(struct

virtio_net_hdr_mrg_rxbuf);

lib/librte_vhost/virtio_net.c +56 -0
+uint32_t buffer_len[vq->size];
+uint16_t buffer_buf_id[vq->size];
+uint16_t buffer_desc_count[vq->size]; *nr_buffers = 0;
+
+if (rxvq_is_mergeable(dev))
+max_tries = vq->size - 1;
+else
+max_tries = 1;
+
+while (size > 0) {
+/*
+ * if we tried all available ring items, and still
+ * can't get enough buf, it means something abnormal
+ * happened.
+ */
+if (unlikely(++tries > max_tries))
+return -1;
+
+if (unlikely(fill_vec_buf_packed(dev, vq, avail_idx, &desc_count,
+buf_vec, &nr_vec, &buf_id, &len,
+VHOST_ACCESS_RW) < 0))
+return -1;
+
+len = RTE_MIN(len, size);
+size -= len;
+
+buffer_len[*nr_buffers] = len;
+buffer_buf_id[*nr_buffers] = buf_id;
+buffer_desc_count[*nr_buffers] = desc_count; *nr_buffers += 1;
+
+*nr_descs += desc_count;
+avail_idx += desc_count;
+if (avail_idx >= vq->size)
+avail_idx -= vq->size;
+}
+
+if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,
+src_iovec, dst_iovec, src_it, dst_it) < 0) return -1;
+
+vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,
+   buffer_desc_count, *nr_buffers);
+
+return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev,
+    struct vhost_virtqueue *vq,
+    struct rte_mbuf *pkt,
+    uint16_t *nr_descs, uint16_t *nr_buffers,
+    struct iovec *src_iovec, struct iovec *dst_iovec,
+    struct rte_vhost_iov_iter *src_it,
+    struct rte_vhost_iov_iter *dst_it) { struct buf_vector
+buf_vec[BUF_VECTOR_MAX]; *nr_descs = 0; *nr_buffers = 0;
+
+if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,

buf_vec,
+ nr_descs,
+ nr_buffers,
+ src_iovec, dst_iovec,
+ src_it, dst_it) < 0)) {

lib/librte_vhost/virtio_net.c +7 -0
+VHOST_LOG_DATA(DEBUG,
+"(%d) failed to get enough desc from vring\n",
+dev->vid);
+return -1;
+}
+
+VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

lib/librte_vhost/virtio_net.c +8 -0
+dev->vid, vq->last_avail_idx,
+vq->last_avail_idx + *nr_descs);
+
+return 0;
+}
+
+static __rte_noinline uint32_t
+virtio_dev_rx_async_submit_packed(struct virtio_net *dev, struct
Quoted 8 lines

lib/librte_vhost/virtio_net.c

Quoted 2 lines
lib/librte_vhost/virtio_net.c +2 -0
+vhost_virtqueue *vq, uint16_t queue_id, struct rte_mbuf **pkts,
+uint32_t count, struct rte_mbuf **comp_pkts, uint32_t *comp_count) {

Hi Cheng,
There're some common parts in virtio_dev_rx_async_submit_packed and virtio_dev_rx_async_submit_split.
We can abstract some functions for those common parts which can bring more clarity.

Sure, but the structure or variable used by packed ring and split ring are different, it may not be very suitable for abstraction, I will consider it again, thank you.

Quoted 3 lines

Also this patch may be too huge for reviewing, please separate it into few parts for better understanding.

I'll make it better in the next version.

Quoted 130 lines

Thanks,
Marvin

Quoted 126 lines
lib/librte_vhost/virtio_net.c +22 -0
+uint32_t pkt_idx = 0, pkt_burst_idx = 0; uint16_t num_buffers;
+uint16_t num_desc;
+
+struct rte_vhost_iov_iter *it_pool = vq->it_pool; struct iovec
+*vec_pool = vq->vec_pool; struct rte_vhost_async_desc
+tdes[MAX_PKT_BURST]; struct iovec *src_iovec = vec_pool; struct iovec
+*dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1); struct
+rte_vhost_iov_iter *src_it = it_pool; struct rte_vhost_iov_iter
+*dst_it = it_pool + 1; uint16_t slot_idx = 0; uint16_t segs_await =
+0; struct async_inflight_info *pkts_info = vq->async_pkts_info;
+uint32_t n_pkts = 0, pkt_err = 0; uint32_t num_async_pkts = 0,
+num_done_pkts = 0; struct { uint16_t pkt_idx; uint16_t
+last_avail_idx; } async_pkts_log[MAX_PKT_BURST];
+
+rte_prefetch0(&vq->desc[vq->last_avail_idx & (vq->size - 1)]);
+
+for (pkt_idx = 0; pkt_idx < count; pkt_idx++) { if
+(unlikely(virtio_dev_rx_async_single_packed(dev, vq, pkts[pkt_idx],
+&num_desc, &num_buffers, src_iovec, dst_iovec, src_it, dst_it) < 0))
+{ break; }
+
+VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

lib/librte_vhost/virtio_net.c +28 -0
+dev->vid, vq->last_avail_idx,
+vq->last_avail_idx + num_desc);
+
+slot_idx = (vq->async_pkts_idx + num_async_pkts) & (vq->size - 1); if
+(src_it->count) { uint16_t from, to;
+
+async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+pkts_info[slot_idx].descs = num_desc; pkts_info[slot_idx].nr_buffers
+= num_buffers; pkts_info[slot_idx].mbuf = pkts[pkt_idx];
+async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
+async_pkts_log[num_async_pkts++].last_avail_idx =
+vq->last_avail_idx;
+src_iovec += src_it->nr_segs;
+dst_iovec += dst_it->nr_segs;
+src_it += 2;
+dst_it += 2;
+segs_await += src_it->nr_segs;
+
+/**
+ * recover shadow used ring and keep DMA-occupied
+ * descriptors.
+ */
+from = vq->shadow_used_idx - num_buffers; to =
+vq->async_packed_buffer_idx & (vq->size - 1); if (num_buffers + to <=
+vq->size) { rte_memcpy(&vq->async_buffers_packed[to],
+&vq->shadow_used_packed[from],
+num_buffers *
+sizeof(struct

vring_used_elem_packed));

lib/librte_vhost/virtio_net.c +7 -0
+} else {
+int size = vq->size - to;
+
+rte_memcpy(&vq->async_buffers_packed[to],
+&vq->shadow_used_packed[from],
+size *
+sizeof(struct

vring_used_elem_packed));

lib/librte_vhost/virtio_net.c +4 -0
+rte_memcpy(vq->async_buffers_packed,
+&vq->shadow_used_packed[from +
+size], (num_buffers - size) *
+sizeof(struct

vring_used_elem_packed));

lib/librte_vhost/virtio_net.c +13 -0
+}
+vq->async_packed_buffer_idx += num_buffers; shadow_used_idx -=
+vq->num_buffers;
+} else
+comp_pkts[num_done_pkts++] = pkts[pkt_idx];
+
+vq_inc_last_avail_packed(vq, num_desc);
+
+/*
+ * conditions to trigger async device transfer:
+ * - buffered packet number reaches transfer threshold
+ * - unused async iov number is less than max vhost vector  */ if
+(unlikely(pkt_burst_idx >=

VHOST_ASYNC_BATCH_THRESHOLD ||

lib/librte_vhost/virtio_net.c +6 -0
+((VHOST_MAX_ASYNC_VEC >> 1) - segs_await <
+BUF_VECTOR_MAX))) {
+n_pkts = vq->async_ops.transfer_data(dev->vid,
+queue_id, tdes, 0, pkt_burst_idx);
+src_iovec = vec_pool;
+dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >>

1);

lib/librte_vhost/virtio_net.c +8 -0
+src_it = it_pool;
+dst_it = it_pool + 1;
+segs_await = 0;
+vq->async_pkts_inflight_n += n_pkts;
+
+if (unlikely(n_pkts < pkt_burst_idx)) {
+/*
+ * log error packets number here and do

actual
+ * error processing when applications poll
+ * completion
+ */

lib/librte_vhost/virtio_net.c +28 -0
+pkt_err = pkt_burst_idx - n_pkts;
+pkt_burst_idx = 0;
+break;
+}
+
+pkt_burst_idx = 0;
+}
+}
+
+if (pkt_burst_idx) {
+n_pkts = vq->async_ops.transfer_data(dev->vid,
+queue_id, tdes, 0, pkt_burst_idx);
+vq->async_pkts_inflight_n += n_pkts;
+
+if (unlikely(n_pkts < pkt_burst_idx)) pkt_err = pkt_burst_idx -
+n_pkts; }
+
+do_data_copy_enqueue(dev, vq);
+
+if (unlikely(pkt_err)) {
+uint16_t num_buffers = 0;
+
+num_async_pkts -= pkt_err;
+/* calculate the sum of descriptors of DMA-error packets. */ while
+(pkt_err-- > 0) { num_buffers += pkts_info[slot_idx & (vq->size -
+1)].nr_buffers; slot_idx--; }
+vq->async_packed_buffer_idx -= num_buffers;
+/* recover shadow used ring and available ring */
Quoted 8 lines
Quoted 4 lines
lib/librte_vhost/virtio_net.c +2 -0
+vq->shadow_used_idx -= (vq->last_avail_idx -
+

async_pkts_log[num_async_pkts].last_avail_idx -
+num_buffers);

Could it possible that vq->last_avail_idx smaller than async_pkts_log[num_async_pkts].last_avail_idx when operations near the ring's boundary?

Yes, you are right. Will be fixed, thanks.

Cheng

Quoted 222 lines
Quoted 221 lines
lib/librte_vhost/virtio_net.c +19 -0
+vq->last_avail_idx =
+async_pkts_log[num_async_pkts].last_avail_idx;
+pkt_idx = async_pkts_log[num_async_pkts].pkt_idx;
+num_done_pkts = pkt_idx - num_async_pkts; }
+
+vq->async_pkts_idx += num_async_pkts;
+*comp_count = num_done_pkts;
+
+if (likely(vq->shadow_used_idx)) {
+vhost_flush_enqueue_shadow_packed(dev, vq);
+vhost_vring_call_packed(dev, vq); }
+
+return pkt_idx;
+}
+
+static __rte_always_inline void
+vhost_update_used_packed(struct virtio_net *dev,
+  struct vhost_virtqueue *vq,
+  struct vring_used_elem_packed

*shadow_ring,

lib/librte_vhost/virtio_net.c +61 -0
+  uint16_t count)
+{
+if (count == 0)
+return;
+int i;
+uint16_t used_idx = vq->last_used_idx; uint16_t head_idx =
+vq->last_used_idx; uint16_t head_flags = 0;
+
+/* Split loop in two to save memory barriers */ for (i = 0; i <
+count; i++) {
+vq->desc_packed[used_idx].id = shadow_ring[i].id;
+vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+used_idx += shadow_ring[i].count;
+if (used_idx >= vq->size)
+used_idx -= vq->size;
+}
+
+/* The ordering for storing desc flags needs to be enforced. */
+rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+for (i = 0; i < count; i++) {
+uint16_t flags;
+
+if (vq->shadow_used_packed[i].len)
+flags = VRING_DESC_F_WRITE;
+else
+flags = 0;
+
+if (vq->used_wrap_counter) {
+flags |= VRING_DESC_F_USED;
+flags |= VRING_DESC_F_AVAIL;
+} else {
+flags &= ~VRING_DESC_F_USED;
+flags &= ~VRING_DESC_F_AVAIL;
+}
+
+if (i > 0) {
+vq->desc_packed[vq->last_used_idx].flags = flags;
+
+vhost_log_cache_used_vring(dev, vq,
+vq->last_used_idx *
+sizeof(struct vring_packed_desc),
+sizeof(struct vring_packed_desc));
+} else {
+head_idx = vq->last_used_idx;
+head_flags = flags;
+}
+
+vq_inc_last_used_packed(vq, shadow_ring[i].count); }
+
+vq->desc_packed[head_idx].flags = head_flags;
+
+vhost_log_cache_used_vring(dev, vq,
+head_idx *
+sizeof(struct vring_packed_desc),
+sizeof(struct vring_packed_desc));
+
+vhost_log_cache_sync(dev, vq);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,

struct rte_mbuf **pkts, uint16_t count) { struct virtio_net *dev = get_device(vid); struct vhost_virtqueue *vq; -uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0;
+uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0, n_buffers = 0;
uint16_t start_idx, pkts_idx, vq_size; struct async_inflight_info
*pkts_info; uint16_t from, i; @@ -1680,53 +2021,96 @@ uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id, goto done; }

lib/librte_vhost/virtio_net.c +0 -2
-for (i = 0; i < n_pkts_put; i++) {
-from = (start_idx + i) & (vq_size - 1); -n_descs +=

pkts_info[from].descs; -pkts[i] = pkts_info[from].mbuf;

lib/librte_vhost/virtio_net.c +8 -0
+if (vq_is_packed(dev)) {
+for (i = 0; i < n_pkts_put; i++) {
+from = (start_idx + i) & (vq_size - 1); n_buffers +=
+pkts_info[from].nr_buffers; pkts[i] = pkts_info[from].mbuf; } } else
+{ for (i = 0; i < n_pkts_put; i++) { from = (start_idx + i) &
+(vq_size - 1); n_descs += pkts_info[from].descs; pkts[i] =
+pkts_info[from].mbuf; }
 }
+
 vq->async_last_pkts_n = n_pkts_cpl - n_pkts_put;

vq->async_pkts_inflight_n -= n_pkts_put;

if (likely(vq->enabled && vq->access_ok)) { -uint16_t nr_left =
n_descs; uint16_t nr_copy; uint16_t to;

/* write back completed descriptors to used ring */ -do { -from =
vq->last_async_desc_idx & (vq->size - 1); -nr_copy = nr_left + from <= vq->size ? nr_left :

lib/librte_vhost/virtio_net.c +11 -5
-vq->size - from;
-to = vq->last_used_idx & (vq->size - 1);
-
-if (to + nr_copy <= vq->size) {
-rte_memcpy(&vq->used->ring[to],
+if (vq_is_packed(dev)) {
+uint16_t nr_left = n_buffers;
+uint16_t to;
+do {
+from = vq->last_async_buffer_idx &
+(vq->size - 1);
+to = (from + nr_left) & (vq->size - 1);
+
+if (to > from) {
+vhost_update_used_packed(dev, vq,
+vq->async_buffers_packed +

from,

lib/librte_vhost/virtio_net.c +6 -0
+to - from);
+vq->last_async_buffer_idx += nr_left;
+nr_left = 0;
+} else {
+vhost_update_used_packed(dev, vq,
+vq->async_buffers_packed +

from,

lib/librte_vhost/virtio_net.c +3 -0
+vq->size - from);
+vq->last_async_buffer_idx +=
+vq->size -

from;

lib/librte_vhost/virtio_net.c +8 -0
+nr_left -= vq->size - from;
+}
+} while (nr_left > 0);
+vhost_vring_call_packed(dev, vq);
+} else {
+uint16_t nr_left = n_descs;
+do {
+from = vq->last_async_desc_idx & (vq->size -

1);

lib/librte_vhost/virtio_net.c +6 -0
+nr_copy = nr_left + from <= vq->size ? nr_left :
+vq->size - from;
+to = vq->last_used_idx & (vq->size - 1);
+
+if (to + nr_copy <= vq->size) {
+rte_memcpy(&vq->used->ring[to],
 &vq-
Quoted 1 line

async_descs_split[from],

nr_copy *
sizeof(struct
vring_used_elem));

lib/librte_vhost/virtio_net.c +3 -3
-} else {
-uint16_t size = vq->size - to;
+} else {
+uint16_t size = vq->size - to;
 
-rte_memcpy(&vq->used->ring[to],
+rte_memcpy(&vq->used->ring[to],
 &vq-
Quoted 1 line

async_descs_split[from],

size *
sizeof(struct
vring_used_elem));

lib/librte_vhost/virtio_net.c +2 -1
-rte_memcpy(vq->used->ring,
+rte_memcpy(vq->used->ring,
 &vq->async_descs_split[from
+
 size], (nr_copy - size) *
 sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +18 -11
-}
+}
+
+vq->last_async_desc_idx += nr_copy;
+vq->last_used_idx += nr_copy;
+nr_left -= nr_copy;
+} while (nr_left > 0);
+
+__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
+vhost_vring_call_split(dev, vq); }
 
-vq->last_async_desc_idx += nr_copy;
-vq->last_used_idx += nr_copy;
-nr_left -= nr_copy;
-} while (nr_left > 0);
 
-__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
-vhost_vring_call_split(dev, vq); -} else
-vq->last_async_desc_idx += n_descs;
+
+} else {
+if (vq_is_packed(dev))
+vq->last_async_buffer_idx += n_buffers;
+else
+vq->last_async_desc_idx += n_descs;
+}
 
 done:
 rte_spinlock_unlock(&vq->access_lock);
@@ -1767,9 +2151,10 @@ virtio_dev_rx_async_submit(struct virtio_net
 *dev, uint16_t queue_id,  if (count == 0)  goto out;
 
-/* TODO: packed queue not implemented */  if (vq_is_packed(dev))
-nb_tx = 0;
+nb_tx = virtio_dev_rx_async_submit_packed(dev,
+vq, queue_id, pkts, count, comp_pkts, comp_count);
 else
 nb_tx = virtio_dev_rx_async_submit_split(dev,
 vq, queue_id, pkts, count, comp_pkts,
--

2.29.2

#4 lore raw

PATCH v3 1/1 vhost: add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
v3:
* fix error handler for DMA-copy packet
* remove variables that are no longer needed
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  24 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 463 +++++++++++++++++++++++++++--
 4 files changed, 457 insertions(+), 38 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +18 -6
index 52ab93d1e..51b44d6f2 100644
@@ -330,15 +330,20 @@ vhost_free_async_mem(struct vhost_virtqueue *vq)
 {
 	if (vq->async_pkts_info)
 		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	if (vq->async_buffers_packed) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	} else {
 		rte_free(vq->async_descs_split);
+		vq->async_descs_split = NULL;
+	}
+
 	if (vq->it_pool)
 		rte_free(vq->it_pool);
 	if (vq->vec_pool)
 		rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -1603,9 +1608,9 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or non-inorder mode "
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1643,10 +1648,17 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
+	}
+
+	if (!vq->async_pkts_info ||
 		!vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
 		VHOST_LOG_CONFIG(ERR,
lib/librte_vhost/vhost.h +6 -1
index 658f6fc28..d6324fbf8 100644
@@ -206,9 +206,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +432 -31

526 lines not on this page · raw message

--
2.29.2

#5 lore raw

Hi Cheng,

Some comments are inline.

Quoted 91 lines

-----Original Message-----
From: Jiang, Cheng1 <[email protected]>
Sent: Wednesday, March 31, 2021 10:06 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [PATCH v3] vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
v3:
* fix error handler for DMA-copy packet
* remove variables that are no longer needed
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  24 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 463 +++++++++++++++++++++++++++--
 4 files changed, 457 insertions(+), 38 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +7 -2
index 52ab93d1e..51b44d6f2 100644
@@ -330,15 +330,20 @@ vhost_free_async_mem(struct vhost_virtqueue
 *vq)
 {
 	if (vq->async_pkts_info)
 		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	if (vq->async_buffers_packed) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	} else {
 		rte_free(vq->async_descs_split);
+		vq->async_descs_split = NULL;
+	}
+
 	if (vq->it_pool)
 		rte_free(vq->it_pool);
 	if (vq->vec_pool)
 		rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;

}

lib/librte_vhost/vhost.c +1 -2
@@ -1603,9 +1608,9 @@ int rte_vhost_async_channel_register(int vid,
 uint16_t queue_id,
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or

non-inorder mode "

lib/librte_vhost/vhost.c +3 -1
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1643,10 +1648,17 @@ int rte_vhost_async_channel_register(int vid,
 uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, · line 1662

Quoted 10 lines
lib/librte_vhost/vhost.c +7 -1
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
+	}
+
+	if (!vq->async_pkts_info ||

Need to check if malloc fails for async_buffers_packed.

Quoted 118 lines
lib/librte_vhost/vhost.c +0 -0
 		!vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
lib/librte_vhost/vhost.c +0 -0
 		VHOST_LOG_CONFIG(ERR,
lib/librte_vhost/vhost.h +6 -1
index 658f6fc28..d6324fbf8 100644
@@ -206,9 +206,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +1 -2
index 583bf379c..fa2dfde02 100644
@@ -363,8 +363,7 @@
 vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue *vq,
 }
 
 static __rte_always_inline void
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-				   struct vhost_virtqueue *vq,
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
 				   uint32_t len[],
 				   uint16_t id[],
lib/librte_vhost/virtio_net.c +11 -0
 				   uint16_t count[],
@@ -382,6 +381,17 @@ vhost_shadow_enqueue_single_packed(struct
 virtio_net *dev,
 		vq->shadow_aligned_idx += count[i];
 		vq->shadow_used_idx++;
 	}
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+				   struct vhost_virtqueue *vq,
+				   uint32_t len[],
+				   uint16_t id[],
+				   uint16_t count[],
+				   uint16_t num_buffers)
+{
+	vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 	if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {
lib/librte_vhost/virtio_net.c +55 -0
 		do_data_copy_enqueue(dev, vq);
@@ -1452,6 +1462,73 @@ virtio_dev_rx_async_get_info_idx(uint16_t
 pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+vhost_update_used_packed(struct virtio_net *dev,
+				  struct vhost_virtqueue *vq,
+				  struct vring_used_elem_packed
 *shadow_ring,
+				  uint16_t count)
+{
+	if (count == 0)
+		return;
+	int i;
+	uint16_t used_idx = vq->last_used_idx;
+	uint16_t head_idx = vq->last_used_idx;
+	uint16_t head_flags = 0;
+
+	/* Split loop in two to save memory barriers */
+	for (i = 0; i < count; i++) {
+		vq->desc_packed[used_idx].id = shadow_ring[i].id;
+		vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+		used_idx += shadow_ring[i].count;
+		if (used_idx >= vq->size)
+			used_idx -= vq->size;
+	}
+
+	/* The ordering for storing desc flags needs to be enforced. */
+	rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+	for (i = 0; i < count; i++) {
+		uint16_t flags;
+
+		if (vq->shadow_used_packed[i].len)
+			flags = VRING_DESC_F_WRITE;
+		else
+			flags = 0;
+
+		if (vq->used_wrap_counter) {
+			flags |= VRING_DESC_F_USED;
+			flags |= VRING_DESC_F_AVAIL;
+		} else {
+			flags &= ~VRING_DESC_F_USED;
+			flags &= ~VRING_DESC_F_AVAIL;
+		}
+
+		if (i > 0) {
+			vq->desc_packed[vq->last_used_idx].flags = flags;
+
+			vhost_log_cache_used_vring(dev, vq,
+					vq->last_used_idx *
+					sizeof(struct vring_packed_desc),
+					sizeof(struct vring_packed_desc));
+		} else {
+			head_idx = vq->last_used_idx;
+			head_flags = flags;
+		}
+
+		vq_inc_last_used_packed(vq, shadow_ring[i].count);

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_get_info_idx(uint16_t · line 1531

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+	}
+
+	vq->desc_packed[head_idx].flags = head_flags;
+
+	vhost_log_cache_used_vring(dev, vq,
+				head_idx *
+				sizeof(struct vring_packed_desc),
+				sizeof(struct vring_packed_desc));
+
+	vhost_log_cache_sync(dev, vq);

Async enqueue of packed ring has no support of live migration. The above code is not needed.

Quoted 276 lines
lib/librte_vhost/virtio_net.c +2 -0
+}
+
 static __rte_noinline uint32_t

virtio_dev_rx_async_submit_split(struct virtio_net *dev,

lib/librte_vhost/virtio_net.c +189 -0
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1633,12 +1710,292 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    struct buf_vector *buf_vec,
+			    uint16_t *nr_descs,
+			    uint16_t *nr_buffers,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it)
+{
+	uint16_t nr_vec = 0;
+	uint16_t avail_idx = vq->last_avail_idx;
+	uint16_t max_tries, tries = 0;
+	uint16_t buf_id = 0;
+	uint32_t len = 0;
+	uint16_t desc_count;
+	uint32_t size = pkt->pkt_len + sizeof(struct
 virtio_net_hdr_mrg_rxbuf);
+	uint32_t buffer_len[vq->size];
+	uint16_t buffer_buf_id[vq->size];
+	uint16_t buffer_desc_count[vq->size];
+	*nr_buffers = 0;
+
+	if (rxvq_is_mergeable(dev))
+		max_tries = vq->size - 1;
+	else
+		max_tries = 1;
+
+	while (size > 0) {
+		/*
+		 * if we tried all available ring items, and still
+		 * can't get enough buf, it means something abnormal
+		 * happened.
+		 */
+		if (unlikely(++tries > max_tries))
+			return -1;
+
+		if (unlikely(fill_vec_buf_packed(dev, vq,
+						avail_idx, &desc_count,
+						buf_vec, &nr_vec,
+						&buf_id, &len,
+						VHOST_ACCESS_RW) < 0))
+			return -1;
+
+		len = RTE_MIN(len, size);
+		size -= len;
+
+		buffer_len[*nr_buffers] = len;
+		buffer_buf_id[*nr_buffers] = buf_id;
+		buffer_desc_count[*nr_buffers] = desc_count;
+		*nr_buffers += 1;
+
+		*nr_descs += desc_count;
+		avail_idx += desc_count;
+		if (avail_idx >= vq->size)
+			avail_idx -= vq->size;
+	}
+
+	if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,
+		src_iovec, dst_iovec, src_it, dst_it) < 0)
+		return -1;
+
+	vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,
+					   buffer_desc_count, *nr_buffers);
+
+	return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    uint16_t *nr_descs, uint16_t *nr_buffers,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it)
+{
+	struct buf_vector buf_vec[BUF_VECTOR_MAX];
+	*nr_descs = 0;
+	*nr_buffers = 0;
+
+	if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,
 buf_vec,
+						 nr_descs,
+						 nr_buffers,
+						 src_iovec, dst_iovec,
+						 src_it, dst_it) < 0)) {
+		VHOST_LOG_DATA(DEBUG,
+				"(%d) failed to get enough desc from vring\n",
+				dev->vid);
+		return -1;
+	}
+
+	VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end
 index %d\n",
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + *nr_descs);
+
+	return 0;
+}
+
+static __rte_noinline uint32_t
+virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
+	struct vhost_virtqueue *vq, uint16_t queue_id,
+	struct rte_mbuf **pkts, uint32_t count,
+	struct rte_mbuf **comp_pkts, uint32_t *comp_count)
+{
+	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint16_t num_buffers;
+	uint16_t num_desc;
+
+	struct rte_vhost_iov_iter *it_pool = vq->it_pool;
+	struct iovec *vec_pool = vq->vec_pool;
+	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
+	struct iovec *src_iovec = vec_pool;
+	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
+	struct rte_vhost_iov_iter *src_it = it_pool;
+	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
+	uint16_t slot_idx = 0;
+	uint16_t segs_await = 0;
+	struct async_inflight_info *pkts_info = vq->async_pkts_info;
+	uint32_t n_pkts = 0, pkt_err = 0;
+	uint32_t num_async_pkts = 0, num_done_pkts = 0;
+
+	rte_prefetch0(&vq->desc[vq->last_avail_idx & (vq->size - 1)]);
+
+	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
+		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq,
+						pkts[pkt_idx],
+						&num_desc, &num_buffers,
+						src_iovec, dst_iovec,
+						src_it, dst_it) < 0)) {
+			break;
+		}
+
+		VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end
 index %d\n",
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + num_desc);
+
+		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
+			(vq->size - 1);
+		if (src_it->count) {
+			uint16_t from, to;
+
+			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			pkts_info[slot_idx].descs = num_desc;
+			pkts_info[slot_idx].nr_buffers = num_buffers;
+			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
+			num_async_pkts++;
+			src_iovec += src_it->nr_segs;
+			dst_iovec += dst_it->nr_segs;
+			src_it += 2;
+			dst_it += 2;
+			segs_await += src_it->nr_segs;
+
+			/**
+			 * recover shadow used ring and keep DMA-occupied
+			 * descriptors.
+			 */
+			from = vq->shadow_used_idx - num_buffers;
+			to = vq->async_packed_buffer_idx & (vq->size - 1);
+			if (num_buffers + to <= vq->size) {
+				rte_memcpy(&vq->async_buffers_packed[to],
+					&vq->shadow_used_packed[from],
+					num_buffers *
+					sizeof(struct
 vring_used_elem_packed));
+			} else {
+				int size = vq->size - to;
+
+				rte_memcpy(&vq->async_buffers_packed[to],
+					&vq->shadow_used_packed[from],
+					size *
+					sizeof(struct
 vring_used_elem_packed));
+				rte_memcpy(vq->async_buffers_packed,
+					&vq->shadow_used_packed[from +
+					size], (num_buffers - size) *
+					sizeof(struct
 vring_used_elem_packed));
+			}
+			vq->async_packed_buffer_idx += num_buffers;
+			vq->shadow_used_idx -= num_buffers;
+		} else
+			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
+
+		vq_inc_last_avail_packed(vq, num_desc);
+
+		/*
+		 * conditions to trigger async device transfer:
+		 * - buffered packet number reaches transfer threshold
+		 * - unused async iov number is less than max vhost vector
+		 */
+		if (unlikely(pkt_burst_idx >=

VHOST_ASYNC_BATCH_THRESHOLD ||

lib/librte_vhost/virtio_net.c +6 -0
+			((VHOST_MAX_ASYNC_VEC >> 1) - segs_await <
+			BUF_VECTOR_MAX))) {
+			n_pkts = vq->async_ops.transfer_data(dev->vid,
+					queue_id, tdes, 0, pkt_burst_idx);
+			src_iovec = vec_pool;
+			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >>

1);

lib/librte_vhost/virtio_net.c +8 -0
+			src_it = it_pool;
+			dst_it = it_pool + 1;
+			segs_await = 0;
+			vq->async_pkts_inflight_n += n_pkts;
+
+			if (unlikely(n_pkts < pkt_burst_idx)) {
+				/*
+				 * log error packets number here and do

actual

lib/librte_vhost/virtio_net.c +53 -0
+				 * error processing when applications poll
+				 * completion
+				 */
+				pkt_err = pkt_burst_idx - n_pkts;
+				pkt_burst_idx = 0;
+				pkt_idx++;
+				break;
+			}
+
+			pkt_burst_idx = 0;
+		}
+	}
+
+	if (pkt_burst_idx) {
+		n_pkts = vq->async_ops.transfer_data(dev->vid,
+				queue_id, tdes, 0, pkt_burst_idx);
+		vq->async_pkts_inflight_n += n_pkts;
+
+		if (unlikely(n_pkts < pkt_burst_idx))
+			pkt_err = pkt_burst_idx - n_pkts;
+	}
+
+	do_data_copy_enqueue(dev, vq);
+
+	if (unlikely(pkt_err)) {
+		uint16_t buffers_err = 0;
+		uint16_t async_buffer_idx;
+		uint16_t i;
+
+		num_async_pkts -= pkt_err;
+		pkt_idx -= pkt_err;
+		/* calculate the sum of buffers of DMA-error packets. */
+		while (pkt_err-- > 0) {
+			buffers_err +=
+				pkts_info[slot_idx & (vq->size - 1)].nr_buffers;
+			slot_idx--;
+		}
+
+		vq->async_packed_buffer_idx -= buffers_err;
+		async_buffer_idx = vq->async_packed_buffer_idx;
+		/* set 0 to the length of descriptors of DMA-error packets */
+		for (i = 0; i < buffers_err; i++) {
+			vq->async_buffers_packed[(async_buffer_idx + i)
+						& (vq->size - 1)].len = 0;
+		}
+		/* write back DMA-error descriptors to used ring */
+		do {
+			uint16_t from = async_buffer_idx & (vq->size - 1);
+			uint16_t to = (from + buffers_err) & (vq->size - 1);
+
+			if (to > from) {
+				vhost_update_used_packed(dev, vq,
+					vq->async_buffers_packed + from,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1978

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+					to - from);
+				buffers_err = 0;
+			} else {
+				vhost_update_used_packed(dev, vq,
+					vq->async_buffers_packed + from,
+					vq->size - from);
+				buffers_err -= vq->size - from;
+			}
+		} while (buffers_err > 0);
+		vhost_vring_call_packed(dev, vq);

Why notify front-end here?

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1991

Quoted 13 lines
lib/librte_vhost/virtio_net.c +13 -0
+		num_done_pkts = pkt_idx - num_async_pkts;
+	}
+
+	vq->async_pkts_idx += num_async_pkts;
+	*comp_count = num_done_pkts;
+
+	if (likely(vq->shadow_used_idx)) {
+		vhost_flush_enqueue_shadow_packed(dev, vq);
+		vhost_vring_call_packed(dev, vq);
+	}
+
+	return pkt_idx;
+}

virtio_dev_rx_async_submit_packed is too long and it has several parts are similar with split ring. I think you need to abstract common parts into inline functions to make the code easier to read.

Quoted 133 lines
lib/librte_vhost/virtio_net.c +65 -24
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
 	struct virtio_net *dev = get_device(vid);
 	struct vhost_virtqueue *vq;
-	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0;
+	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0, n_buffers = 0;
 	uint16_t start_idx, pkts_idx, vq_size;
 	struct async_inflight_info *pkts_info;
 	uint16_t from, i;
@@ -1680,53 +2037,96 @@ uint16_t
 rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		goto done;
 	}
 
-	for (i = 0; i < n_pkts_put; i++) {
-		from = (start_idx + i) & (vq_size - 1);
-		n_descs += pkts_info[from].descs;
-		pkts[i] = pkts_info[from].mbuf;
+	if (vq_is_packed(dev)) {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);
+			n_buffers += pkts_info[from].nr_buffers;
+			pkts[i] = pkts_info[from].mbuf;
+		}
+	} else {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);
+			n_descs += pkts_info[from].descs;
+			pkts[i] = pkts_info[from].mbuf;
+		}
 	}
+
 	vq->async_last_pkts_n = n_pkts_cpl - n_pkts_put;
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
 		uint16_t nr_copy;
 		uint16_t to;
 
 		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
+		if (vq_is_packed(dev)) {
+			uint16_t nr_left = n_buffers;
+			uint16_t to;
+			do {
+				from = vq->last_async_buffer_idx &
+								(vq->size - 1);
+				to = (from + nr_left) & (vq->size - 1);
+
+				if (to > from) {
+					vhost_update_used_packed(dev, vq,
+						vq->async_buffers_packed +
 from,
+						to - from);
+					vq->last_async_buffer_idx += nr_left;
+					nr_left = 0;
+				} else {
+					vhost_update_used_packed(dev, vq,
+						vq->async_buffers_packed +
 from,
+						vq->size - from);
+					vq->last_async_buffer_idx +=
+								vq->size -
 from;
+					nr_left -= vq->size - from;
+				}
+			} while (nr_left > 0);
+			vhost_vring_call_packed(dev, vq);
+		} else {
+			uint16_t nr_left = n_descs;
+			do {
+				from = vq->last_async_desc_idx & (vq->size -
 1);
+				nr_copy = nr_left + from <= vq->size ? nr_left :
+					vq->size - from;
+				to = vq->last_used_idx & (vq->size - 1);
+
+				if (to + nr_copy <= vq->size) {
+					rte_memcpy(&vq->used->ring[to],
 						&vq-
 >async_descs_split[from],
 						nr_copy *
 						sizeof(struct
 vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
+				} else {
+					uint16_t size = vq->size - to;
 
-				rte_memcpy(&vq->used->ring[to],
+					rte_memcpy(&vq->used->ring[to],
 						&vq-
 >async_descs_split[from],
 						size *
 						sizeof(struct
 vring_used_elem));
-				rte_memcpy(vq->used->ring,
+					rte_memcpy(vq->used->ring,
 						&vq->async_descs_split[from
+
 						size], (nr_copy - size) *
 						sizeof(struct
 vring_used_elem));
-			}
+				}
+
+				vq->last_async_desc_idx += nr_copy;
+				vq->last_used_idx += nr_copy;
+				nr_left -= nr_copy;
+			} while (nr_left > 0);
+
+			__atomic_add_fetch(&vq->used->idx, n_descs,
+					__ATOMIC_RELEASE);
+			vhost_vring_call_split(dev, vq);
+		}
 
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
 
-		__atomic_add_fetch(&vq->used->idx, n_descs,

__ATOMIC_RELEASE);

lib/librte_vhost/virtio_net.c uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id, · line 2129

Quoted 10 lines
lib/librte_vhost/virtio_net.c +7 -3
-		vhost_vring_call_split(dev, vq);
-	} else
-		vq->last_async_desc_idx += n_descs;
+
+	} else {
+		if (vq_is_packed(dev))
+			vq->last_async_buffer_idx += n_buffers;
+		else
+			vq->last_async_desc_idx += n_descs;
+	}

rte_vhost_poll_enqueue_completed is too long and not easy to read. Save suggestion as above.

Thanks,
Jiayu

Quoted 19 lines

done:

lib/librte_vhost/virtio_net.c +3 -2
 	rte_spinlock_unlock(&vq->access_lock);
@@ -1767,9 +2167,10 @@ virtio_dev_rx_async_submit(struct virtio_net
 *dev, uint16_t queue_id,
 	if (count == 0)
 		goto out;
 
-	/* TODO: packed queue not implemented */
 	if (vq_is_packed(dev))
-		nb_tx = 0;
+		nb_tx = virtio_dev_rx_async_submit_packed(dev,
+				vq, queue_id, pkts, count, comp_pkts,
+				comp_count);
 	else
 		nb_tx = virtio_dev_rx_async_submit_split(dev,

vq, queue_id, pkts, count, comp_pkts,
--
2.29.2

#6 lore raw

Hi Jiayu,

Quoted 94 lines

-----Original Message-----
From: Hu, Jiayu <[email protected]>
Sent: Wednesday, April 7, 2021 2:27 PM
To: Jiang, Cheng1 <[email protected]>; [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>
Subject: RE: [PATCH v3] vhost: add support for packed ring in async vhost

Hi Cheng,

Some comments are inline.

Quoted 70 lines

-----Original Message-----
From: Jiang, Cheng1 <[email protected]>
Sent: Wednesday, March 31, 2021 10:06 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [PATCH v3] vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
v3:
* fix error handler for DMA-copy packet
* remove variables that are no longer needed
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  24 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 463 +++++++++++++++++++++++++++--
 4 files changed, 457 insertions(+), 38 deletions(-)
lib/librte_vhost/rte_vhost_async.h +0 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {  struct

async_inflight_info { struct rte_mbuf *mbuf; uint16_t descs; /* num of descs inflight */

lib/librte_vhost/rte_vhost_async.h +1 -0
+uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c index renamed from lib/librte_vhost/vhost.c +0 -0

52ab93d1e..51b44d6f2 100644

lib/librte_vhost/vhost.c +7 -2
@@ -330,15 +330,20 @@ vhost_free_async_mem(struct vhost_virtqueue
 *vq)
 {
 if (vq->async_pkts_info)
 rte_free(vq->async_pkts_info);
-if (vq->async_descs_split)
+if (vq->async_buffers_packed) {
+rte_free(vq->async_buffers_packed);
+vq->async_buffers_packed = NULL;
+} else {
 rte_free(vq->async_descs_split);
+vq->async_descs_split = NULL;
+}
+
 if (vq->it_pool)
 rte_free(vq->it_pool);
 if (vq->vec_pool)
 rte_free(vq->vec_pool);
 
 vq->async_pkts_info = NULL;
-vq->async_descs_split = NULL;
 vq->it_pool = NULL;
 vq->vec_pool = NULL;

}

lib/librte_vhost/vhost.c +0 -0
@@ -1603,9 +1608,9 @@ int rte_vhost_async_channel_register(int vid,
 uint16_t queue_id,  return -1;

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, · line 1616

Quoted 10 lines
lib/librte_vhost/vhost.c +3 -2
 
 /* packed queue is not supported */
-if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+if (unlikely(!f.async_inorder)) {
 VHOST_LOG_CONFIG(ERR,
-"async copy is not supported on packed queue or non-inorder mode "
+"async copy is not supported on non-inorder mode "
 "(vid %d, qid: %d)\n", vid, queue_id);  return -1;  } @@ -1643,10
+1648,17 @@ int rte_vhost_async_channel_register(int vid, uint16_t
 queue_id,  vq->vec_pool = rte_malloc_socket(NULL,

VHOST_MAX_ASYNC_VEC

Quoted 15 lines

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id, · line 1661

Quoted 13 lines

* sizeof(struct iovec), RTE_CACHE_LINE_SIZE, node);

lib/librte_vhost/vhost.c +6 -1
-vq->async_descs_split = rte_malloc_socket(NULL,
+if (vq_is_packed(dev)) {
+vq->async_buffers_packed = rte_malloc_socket(NULL, size *
+vq->sizeof(struct vring_used_elem_packed),
+RTE_CACHE_LINE_SIZE, node);
+} else {
+vq->async_descs_split = rte_malloc_socket(NULL,
 vq->size * sizeof(struct vring_used_elem),  RTE_CACHE_LINE_SIZE,

node); -if (!vq->async_descs_split || !vq->async_pkts_info ||

lib/librte_vhost/vhost.c +3 -0
+}
+
+if (!vq->async_pkts_info ||

Need to check if malloc fails for async_buffers_packed.

Sure, It will be fixed in the next version.

Quoted 119 lines
Quoted 26 lines

!vq->it_pool || !vq->vec_pool) {
vhost_free_async_mem(vq);
VHOST_LOG_CONFIG(ERR,

lib/librte_vhost/vhost.h index renamed from lib/librte_vhost/vhost.h +0 -0

658f6fc28..d6324fbf8 100644

lib/librte_vhost/vhost.h +0 -0
@@ -206,9 +206,14 @@ struct vhost_virtqueue {  uint16_tasync_pkts_idx;

uint16_tasync_pkts_inflight_n; uint16_tasync_last_pkts_n; -struct vring_used_elem *async_descs_split;

lib/librte_vhost/vhost.h +5 -0
+union {
+struct vring_used_elem  *async_descs_split; struct
+vring_used_elem_packed *async_buffers_packed; };
 uint16_t async_desc_idx;
+uint16_t async_packed_buffer_idx;
 uint16_t last_async_desc_idx;
+uint16_t last_async_buffer_idx;
 
 /* vq async features */
 boolasync_inorder;
(quoted diff) +0 -0

b/lib/librte_vhost/virtio_net.c index 583bf379c..fa2dfde02 100644

lib/librte_vhost/virtio_net.c +0 -0
@@ -363,8 +363,7 @@
Quoted 1 line

vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue

*vq,

Quoted 91 lines

}

static __rte_always_inline void

lib/librte_vhost/virtio_net.c +12 -2
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-   struct vhost_virtqueue *vq,
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
    uint32_t len[],
    uint16_t id[],
    uint16_t count[],
@@ -382,6 +381,17 @@ vhost_shadow_enqueue_single_packed(struct
 virtio_net *dev,
 vq->shadow_aligned_idx += count[i];
 vq->shadow_used_idx++;
 }
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+   struct vhost_virtqueue *vq,
+   uint32_t len[],
+   uint16_t id[],
+   uint16_t count[],
+   uint16_t num_buffers)
+{
+vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {

do_data_copy_enqueue(dev, vq); @@ -1452,6 +1462,73 @@ virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,
(vq_size - n_inflight + pkts_idx) & (vq_size - 1); }

lib/librte_vhost/virtio_net.c +4 -0
+static __rte_always_inline void
+vhost_update_used_packed(struct virtio_net *dev,
+  struct vhost_virtqueue *vq,
+  struct vring_used_elem_packed

*shadow_ring,

lib/librte_vhost/virtio_net.c +54 -0
+  uint16_t count)
+{
+if (count == 0)
+return;
+int i;
+uint16_t used_idx = vq->last_used_idx; uint16_t head_idx =
+vq->last_used_idx; uint16_t head_flags = 0;
+
+/* Split loop in two to save memory barriers */ for (i = 0; i <
+count; i++) {
+vq->desc_packed[used_idx].id = shadow_ring[i].id;
+vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+used_idx += shadow_ring[i].count;
+if (used_idx >= vq->size)
+used_idx -= vq->size;
+}
+
+/* The ordering for storing desc flags needs to be enforced. */
+rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+for (i = 0; i < count; i++) {
+uint16_t flags;
+
+if (vq->shadow_used_packed[i].len)
+flags = VRING_DESC_F_WRITE;
+else
+flags = 0;
+
+if (vq->used_wrap_counter) {
+flags |= VRING_DESC_F_USED;
+flags |= VRING_DESC_F_AVAIL;
+} else {
+flags &= ~VRING_DESC_F_USED;
+flags &= ~VRING_DESC_F_AVAIL;
+}
+
+if (i > 0) {
+vq->desc_packed[vq->last_used_idx].flags = flags;
+
+vhost_log_cache_used_vring(dev, vq,
+vq->last_used_idx *
+sizeof(struct vring_packed_desc),
+sizeof(struct vring_packed_desc));
+} else {
+head_idx = vq->last_used_idx;
+head_flags = flags;
+}
+
+vq_inc_last_used_packed(vq, shadow_ring[i].count); }
+
+vq->desc_packed[head_idx].flags = head_flags;
+
+vhost_log_cache_used_vring(dev, vq,
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx, · line 1529

Quoted 5 lines
lib/librte_vhost/virtio_net.c +5 -0
+head_idx *
+sizeof(struct vring_packed_desc),
+sizeof(struct vring_packed_desc));
+
+vhost_log_cache_sync(dev, vq);

Async enqueue of packed ring has no support of live migration. The above code is not needed.

It will be removed.

Quoted 252 lines
Quoted 252 lines
lib/librte_vhost/virtio_net.c +2 -0
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,  struct

vhost_virtqueue *vq, uint16_t queue_id, @@ -1633,12 +1710,292 @@ virtio_dev_rx_async_submit_split(struct
virtio_net *dev,
return pkt_idx;
}

lib/librte_vhost/virtio_net.c +13 -0
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+    struct vhost_virtqueue *vq,
+    struct rte_mbuf *pkt,
+    struct buf_vector *buf_vec,
+    uint16_t *nr_descs,
+    uint16_t *nr_buffers,
+    struct iovec *src_iovec, struct iovec *dst_iovec,
+    struct rte_vhost_iov_iter *src_it,
+    struct rte_vhost_iov_iter *dst_it) { uint16_t nr_vec = 0;
+uint16_t avail_idx = vq->last_avail_idx; uint16_t max_tries, tries =
+0; uint16_t buf_id = 0; uint32_t len = 0; uint16_t desc_count;
+uint32_t size = pkt->pkt_len + sizeof(struct

virtio_net_hdr_mrg_rxbuf);

lib/librte_vhost/virtio_net.c +56 -0
+uint32_t buffer_len[vq->size];
+uint16_t buffer_buf_id[vq->size];
+uint16_t buffer_desc_count[vq->size]; *nr_buffers = 0;
+
+if (rxvq_is_mergeable(dev))
+max_tries = vq->size - 1;
+else
+max_tries = 1;
+
+while (size > 0) {
+/*
+ * if we tried all available ring items, and still
+ * can't get enough buf, it means something abnormal
+ * happened.
+ */
+if (unlikely(++tries > max_tries))
+return -1;
+
+if (unlikely(fill_vec_buf_packed(dev, vq, avail_idx, &desc_count,
+buf_vec, &nr_vec, &buf_id, &len,
+VHOST_ACCESS_RW) < 0))
+return -1;
+
+len = RTE_MIN(len, size);
+size -= len;
+
+buffer_len[*nr_buffers] = len;
+buffer_buf_id[*nr_buffers] = buf_id;
+buffer_desc_count[*nr_buffers] = desc_count; *nr_buffers += 1;
+
+*nr_descs += desc_count;
+avail_idx += desc_count;
+if (avail_idx >= vq->size)
+avail_idx -= vq->size;
+}
+
+if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,
+src_iovec, dst_iovec, src_it, dst_it) < 0) return -1;
+
+vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,
+   buffer_desc_count, *nr_buffers);
+
+return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev,
+    struct vhost_virtqueue *vq,
+    struct rte_mbuf *pkt,
+    uint16_t *nr_descs, uint16_t *nr_buffers,
+    struct iovec *src_iovec, struct iovec *dst_iovec,
+    struct rte_vhost_iov_iter *src_it,
+    struct rte_vhost_iov_iter *dst_it) { struct buf_vector
+buf_vec[BUF_VECTOR_MAX]; *nr_descs = 0; *nr_buffers = 0;
+
+if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,

buf_vec,
+ nr_descs,
+ nr_buffers,
+ src_iovec, dst_iovec,
+ src_it, dst_it) < 0)) {

lib/librte_vhost/virtio_net.c +7 -0
+VHOST_LOG_DATA(DEBUG,
+"(%d) failed to get enough desc from vring\n",
+dev->vid);
+return -1;
+}
+
+VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

lib/librte_vhost/virtio_net.c +31 -0
+dev->vid, vq->last_avail_idx,
+vq->last_avail_idx + *nr_descs);
+
+return 0;
+}
+
+static __rte_noinline uint32_t
+virtio_dev_rx_async_submit_packed(struct virtio_net *dev, struct
+vhost_virtqueue *vq, uint16_t queue_id, struct rte_mbuf **pkts,
+uint32_t count, struct rte_mbuf **comp_pkts, uint32_t *comp_count) {
+uint32_t pkt_idx = 0, pkt_burst_idx = 0; uint16_t num_buffers;
+uint16_t num_desc;
+
+struct rte_vhost_iov_iter *it_pool = vq->it_pool; struct iovec
+*vec_pool = vq->vec_pool; struct rte_vhost_async_desc
+tdes[MAX_PKT_BURST]; struct iovec *src_iovec = vec_pool; struct iovec
+*dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1); struct
+rte_vhost_iov_iter *src_it = it_pool; struct rte_vhost_iov_iter
+*dst_it = it_pool + 1; uint16_t slot_idx = 0; uint16_t segs_await =
+0; struct async_inflight_info *pkts_info = vq->async_pkts_info;
+uint32_t n_pkts = 0, pkt_err = 0; uint32_t num_async_pkts = 0,
+num_done_pkts = 0;
+
+rte_prefetch0(&vq->desc[vq->last_avail_idx & (vq->size - 1)]);
+
+for (pkt_idx = 0; pkt_idx < count; pkt_idx++) { if
+(unlikely(virtio_dev_rx_async_single_packed(dev, vq, pkts[pkt_idx],
+&num_desc, &num_buffers, src_iovec, dst_iovec, src_it, dst_it) < 0))
+{ break; }
+
+VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

lib/librte_vhost/virtio_net.c +24 -0
+dev->vid, vq->last_avail_idx,
+vq->last_avail_idx + num_desc);
+
+slot_idx = (vq->async_pkts_idx + num_async_pkts) & (vq->size - 1); if
+(src_it->count) { uint16_t from, to;
+
+async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+pkts_info[slot_idx].descs = num_desc; pkts_info[slot_idx].nr_buffers
+= num_buffers; pkts_info[slot_idx].mbuf = pkts[pkt_idx];
+num_async_pkts++; src_iovec += src_it->nr_segs; dst_iovec +=
+dst_it->nr_segs; src_it += 2; dst_it += 2; segs_await +=
+src_it->nr_segs;
+
+/**
+ * recover shadow used ring and keep DMA-occupied
+ * descriptors.
+ */
+from = vq->shadow_used_idx - num_buffers;
+to = vq->async_packed_buffer_idx & (vq->size - 1);
+if (num_buffers + to <= vq->size) {
+rte_memcpy(&vq->async_buffers_packed[to],
+&vq->shadow_used_packed[from],
+num_buffers *
+sizeof(struct

vring_used_elem_packed));

lib/librte_vhost/virtio_net.c +7 -0
+} else {
+int size = vq->size - to;
+
+rte_memcpy(&vq->async_buffers_packed[to],
+&vq->shadow_used_packed[from],
+size *
+sizeof(struct

vring_used_elem_packed));

lib/librte_vhost/virtio_net.c +4 -0
+rte_memcpy(vq->async_buffers_packed,
+&vq->shadow_used_packed[from +
+size], (num_buffers - size) *
+sizeof(struct

vring_used_elem_packed));

lib/librte_vhost/virtio_net.c +14 -0
+}
+vq->async_packed_buffer_idx += num_buffers;
+vq->shadow_used_idx -= num_buffers;
+} else
+comp_pkts[num_done_pkts++] = pkts[pkt_idx];
+
+vq_inc_last_avail_packed(vq, num_desc);
+
+/*
+ * conditions to trigger async device transfer:
+ * - buffered packet number reaches transfer threshold
+ * - unused async iov number is less than max vhost vector
+ */
+if (unlikely(pkt_burst_idx >=

VHOST_ASYNC_BATCH_THRESHOLD ||

lib/librte_vhost/virtio_net.c +6 -0
+((VHOST_MAX_ASYNC_VEC >> 1) - segs_await <
+BUF_VECTOR_MAX))) {
+n_pkts = vq->async_ops.transfer_data(dev->vid,
+queue_id, tdes, 0, pkt_burst_idx);
+src_iovec = vec_pool;
+dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >>

1);

lib/librte_vhost/virtio_net.c +8 -0
+src_it = it_pool;
+dst_it = it_pool + 1;
+segs_await = 0;
+vq->async_pkts_inflight_n += n_pkts;
+
+if (unlikely(n_pkts < pkt_burst_idx)) {
+/*
+ * log error packets number here and do

actual
+ * error processing when applications poll
+ * completion
+ */

lib/librte_vhost/virtio_net.c +54 -0
+pkt_err = pkt_burst_idx - n_pkts;
+pkt_burst_idx = 0;
+pkt_idx++;
+break;
+}
+
+pkt_burst_idx = 0;
+}
+}
+
+if (pkt_burst_idx) {
+n_pkts = vq->async_ops.transfer_data(dev->vid,
+queue_id, tdes, 0, pkt_burst_idx);
+vq->async_pkts_inflight_n += n_pkts;
+
+if (unlikely(n_pkts < pkt_burst_idx))
+pkt_err = pkt_burst_idx - n_pkts;
+}
+
+do_data_copy_enqueue(dev, vq);
+
+if (unlikely(pkt_err)) {
+uint16_t buffers_err = 0;
+uint16_t async_buffer_idx;
+uint16_t i;
+
+num_async_pkts -= pkt_err;
+pkt_idx -= pkt_err;
+/* calculate the sum of buffers of DMA-error packets. */
+while (pkt_err-- > 0) {
+buffers_err +=
+pkts_info[slot_idx & (vq->size - 1)].nr_buffers;
+slot_idx--;
+}
+
+vq->async_packed_buffer_idx -= buffers_err;
+async_buffer_idx = vq->async_packed_buffer_idx;
+/* set 0 to the length of descriptors of DMA-error packets */
+for (i = 0; i < buffers_err; i++) {
+vq->async_buffers_packed[(async_buffer_idx + i)
+& (vq->size - 1)].len = 0;
+}
+/* write back DMA-error descriptors to used ring */
+do {
+uint16_t from = async_buffer_idx & (vq->size - 1);
+uint16_t to = (from + buffers_err) & (vq->size - 1);
+
+if (to > from) {
+vhost_update_used_packed(dev, vq,
+vq->async_buffers_packed + from,
+to - from);
+buffers_err = 0;
+} else {
+vhost_update_used_packed(dev, vq,
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1978

Quoted 6 lines
lib/librte_vhost/virtio_net.c +6 -0
+vq->async_buffers_packed + from,
+vq->size - from);
+buffers_err -= vq->size - from;
+}
+} while (buffers_err > 0);
+vhost_vring_call_packed(dev, vq);

Why notify front-end here?

The error handling method will be changed in the next version, so this notification will be removed.

Quoted 10 lines
Quoted 10 lines
lib/librte_vhost/virtio_net.c +9 -0
+num_done_pkts = pkt_idx - num_async_pkts;
+}
+
+vq->async_pkts_idx += num_async_pkts;
+*comp_count = num_done_pkts;
+
+if (likely(vq->shadow_used_idx)) {
+vhost_flush_enqueue_shadow_packed(dev, vq);
+vhost_vring_call_packed(dev, vq);
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1991

Quoted 4 lines
lib/librte_vhost/virtio_net.c +4 -0
+}
+
+return pkt_idx;
+}

virtio_dev_rx_async_submit_packed is too long and it has several parts are similar with split ring. I think you need to abstract common parts into inline functions to make the code easier to read.

I'm not sure which parts can be easily processed into functions. Maybe we can have a discussion offline.

Quoted 132 lines
Quoted 132 lines
lib/librte_vhost/virtio_net.c +65 -23
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 struct rte_mbuf **pkts, uint16_t count)
 {
 struct virtio_net *dev = get_device(vid);
 struct vhost_virtqueue *vq;
-uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0;
+uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0, n_buffers = 0;
 uint16_t start_idx, pkts_idx, vq_size;
 struct async_inflight_info *pkts_info;
 uint16_t from, i;
@@ -1680,53 +2037,96 @@ uint16_t
 rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 goto done;
 }
 
-for (i = 0; i < n_pkts_put; i++) {
-from = (start_idx + i) & (vq_size - 1);
-n_descs += pkts_info[from].descs;
-pkts[i] = pkts_info[from].mbuf;
+if (vq_is_packed(dev)) {
+for (i = 0; i < n_pkts_put; i++) {
+from = (start_idx + i) & (vq_size - 1);
+n_buffers += pkts_info[from].nr_buffers;
+pkts[i] = pkts_info[from].mbuf;
+}
+} else {
+for (i = 0; i < n_pkts_put; i++) {
+from = (start_idx + i) & (vq_size - 1);
+n_descs += pkts_info[from].descs;
+pkts[i] = pkts_info[from].mbuf;
+}
 }
+
 vq->async_last_pkts_n = n_pkts_cpl - n_pkts_put;
 vq->async_pkts_inflight_n -= n_pkts_put;
 
 if (likely(vq->enabled && vq->access_ok)) {
-uint16_t nr_left = n_descs;
 uint16_t nr_copy;
 uint16_t to;
 
 /* write back completed descriptors to used ring */
-do {
-from = vq->last_async_desc_idx & (vq->size - 1);
-nr_copy = nr_left + from <= vq->size ? nr_left :
-vq->size - from;
-to = vq->last_used_idx & (vq->size - 1);
-
-if (to + nr_copy <= vq->size) {
-rte_memcpy(&vq->used->ring[to],
+if (vq_is_packed(dev)) {
+uint16_t nr_left = n_buffers;
+uint16_t to;
+do {
+from = vq->last_async_buffer_idx &
+(vq->size - 1);
+to = (from + nr_left) & (vq->size - 1);
+
+if (to > from) {
+vhost_update_used_packed(dev, vq,
+vq->async_buffers_packed +
 from,
+to - from);
+vq->last_async_buffer_idx += nr_left;
+nr_left = 0;
+} else {
+vhost_update_used_packed(dev, vq,
+vq->async_buffers_packed +
 from,
+vq->size - from);
+vq->last_async_buffer_idx +=
+vq->size -
 from;
+nr_left -= vq->size - from;
+}
+} while (nr_left > 0);
+vhost_vring_call_packed(dev, vq);
+} else {
+uint16_t nr_left = n_descs;
+do {
+from = vq->last_async_desc_idx & (vq->size -
 1);
+nr_copy = nr_left + from <= vq->size ? nr_left :
+vq->size - from;
+to = vq->last_used_idx & (vq->size - 1);
+
+if (to + nr_copy <= vq->size) {
+rte_memcpy(&vq->used->ring[to],
 &vq-
 >async_descs_split[from],
 nr_copy *
 sizeof(struct
 vring_used_elem));
-} else {
-uint16_t size = vq->size - to;
+} else {
+uint16_t size = vq->size - to;
 
-rte_memcpy(&vq->used->ring[to],
+rte_memcpy(&vq->used->ring[to],
 &vq-
 >async_descs_split[from],
 size *
 sizeof(struct
 vring_used_elem));
-rte_memcpy(vq->used->ring,
+rte_memcpy(vq->used->ring,
 &vq->async_descs_split[from
+
 size], (nr_copy - size) *
 sizeof(struct
 vring_used_elem));
-}
+}
+
+vq->last_async_desc_idx += nr_copy;
+vq->last_used_idx += nr_copy;
+nr_left -= nr_copy;
+} while (nr_left > 0);
+
+__atomic_add_fetch(&vq->used->idx, n_descs,
+__ATOMIC_RELEASE);
+vhost_vring_call_split(dev, vq);
+}
 
-vq->last_async_desc_idx += nr_copy;
-vq->last_used_idx += nr_copy;
-nr_left -= nr_copy;
-} while (nr_left > 0);
Quoted 17 lines

lib/librte_vhost/virtio_net.c uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id, · line 2129

Quoted 12 lines

-__atomic_add_fetch(&vq->used->idx, n_descs,
__ATOMIC_RELEASE);

lib/librte_vhost/virtio_net.c +7 -3
-vhost_vring_call_split(dev, vq);
-} else
-vq->last_async_desc_idx += n_descs;
+
+} else {
+if (vq_is_packed(dev))
+vq->last_async_buffer_idx += n_buffers;
+else
+vq->last_async_desc_idx += n_descs;
+}

rte_vhost_poll_enqueue_completed is too long and not easy to read. Save suggestion
as above.

I can try to process some code into functions, but I'm not sure if this is necessary, I will discuss it with you later.

Thanks,
Cheng

Quoted 23 lines

Thanks,
Jiayu

Quoted 19 lines

done:
rte_spinlock_unlock(&vq->access_lock);

lib/librte_vhost/virtio_net.c +3 -2
@@ -1767,9 +2167,10 @@ virtio_dev_rx_async_submit(struct virtio_net
 *dev, uint16_t queue_id,
 if (count == 0)
 goto out;
 
-/* TODO: packed queue not implemented */
 if (vq_is_packed(dev))
-nb_tx = 0;
+nb_tx = virtio_dev_rx_async_submit_packed(dev,
+vq, queue_id, pkts, count, comp_pkts,
+comp_count);
 else
 nb_tx = virtio_dev_rx_async_submit_split(dev,

vq, queue_id, pkts, count, comp_pkts,
--
2.29.2

#7 lore raw

v4 cover add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch set cleans async split ring codes and enables packed ring in async vhost data path. Batch datapath is also enabled in packed ring.

v4:
* change the patch structure
* clean code for async split ring
* reuse some code from split ring
* change the error handler for DMA-copy packet
* add check for malloc
* remove useless code
* add doc update
v3:
* fix error handler for DMA-copy packet
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

Cheng Jiang (4):
vhost: abstract and reorganize async split ring code
vhost: add support for packed ring in async vhost
vhost: add batch datapath for async vhost packed ring
doc: add release note for vhost async packed ring

 doc/guides/rel_notes/release_21_05.rst |   4 +
 lib/librte_vhost/rte_vhost_async.h     |   1 +
 lib/librte_vhost/vhost.c               |  27 +-
 lib/librte_vhost/vhost.h               |   7 +-
 lib/librte_vhost/virtio_net.c          | 603 ++++++++++++++++++++++---
 5 files changed, 560 insertions(+), 82 deletions(-)

--
2.29.2

#8 lore raw

PATCH v4 1/4 vhost: abstract and reorganize async split ring code

In order to improve code efficiency and readability when async packed ring support is enabled. This patch abstract some functions like shadow_ring_store and write_back_completed_descs_split. And improve the efficiency of some pointer offset calculation.

---
 lib/librte_vhost/virtio_net.c | 146 +++++++++++++++++++---------------
 1 file changed, 84 insertions(+), 62 deletions(-)
lib/librte_vhost/virtio_net.c +84 -62
index ff3987860..69553e7c3 100644
@@ -1458,6 +1458,29 @@ virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+shadow_ring_store(struct vhost_virtqueue *vq,  void *shadow_ring, void *d_ring,
+		uint16_t s_idx, uint16_t d_idx,
+		uint16_t count, uint16_t elem_size)
+{
+	if (s_idx + count <= vq->size) {
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			count * elem_size);
+	} else {
+		uint16_t size = vq->size - d_idx;
+
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			size * elem_size);
+
+		rte_memcpy((void *)((uintptr_t)d_ring),
+			(void *)((uintptr_t)shadow_ring +
+				(s_idx + size) * elem_size),
+			(count - size) * elem_size);
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1478,6 +1501,7 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
@@ -1513,27 +1537,32 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 
 		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
 				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+				&src_iovec[iovec_idx],
+				&dst_iovec[iovec_idx],
+				&src_it[it_idx],
+				&dst_it[it_idx]) < 0) {
 			vq->shadow_used_idx -= num_buffers;
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (src_it[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&src_it[it_idx],
+				&dst_it[it_idx]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += src_it[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += src_it[it_idx].nr_segs;
 
 			/**
 			 * recover shadow used ring and keep DMA-occupied
@@ -1541,23 +1570,12 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						num_buffers *
-						sizeof(struct vring_used_elem));
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->async_descs_split,
-						&vq->shadow_used_split[from +
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			shadow_ring_store(vq, vq->shadow_used_split,
+					vq->async_descs_split,
+					from, to, num_buffers,
+					sizeof(struct vring_used_elem));
+
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1593,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
 			vq->async_pkts_inflight_n += n_pkts;
 
@@ -1639,6 +1656,43 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)
+{
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left :
+			vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					nr_copy *
+					sizeof(struct vring_used_elem));
+		} else {
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					size *
+					sizeof(struct vring_used_elem));
+			rte_memcpy(vq->used->ring,
+					&vq->async_descs_split[from +
+					size], (nr_copy - size) *
+					sizeof(struct vring_used_elem));
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
@@ -1695,39 +1749,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						nr_copy *
-						sizeof(struct vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from +
-						size], (nr_copy - size) *
-						sizeof(struct vring_used_elem));
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
 		vhost_vring_call_split(dev, vq);
Signature · 1 line
2.29.2
#9 lore raw

PATCH v4 1/4 vhost: abstract and reorganize async split ring code

In reply to Cheng Jiang

In order to improve code efficiency and readability when async packed ring support is enabled. This patch abstract some functions like shadow_ring_store and write_back_completed_descs_split. And improve the efficiency of some pointer offset calculation.

---
 lib/librte_vhost/virtio_net.c | 146 +++++++++++++++++++---------------
 1 file changed, 84 insertions(+), 62 deletions(-)
lib/librte_vhost/virtio_net.c +84 -62
index ff3987860..69553e7c3 100644
@@ -1458,6 +1458,29 @@ virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+shadow_ring_store(struct vhost_virtqueue *vq,  void *shadow_ring, void *d_ring,
+		uint16_t s_idx, uint16_t d_idx,
+		uint16_t count, uint16_t elem_size)
+{
+	if (s_idx + count <= vq->size) {
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			count * elem_size);
+	} else {
+		uint16_t size = vq->size - d_idx;
+
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			size * elem_size);
+
+		rte_memcpy((void *)((uintptr_t)d_ring),
+			(void *)((uintptr_t)shadow_ring +
+				(s_idx + size) * elem_size),
+			(count - size) * elem_size);
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1478,6 +1501,7 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
@@ -1513,27 +1537,32 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 
 		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
 				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+				&src_iovec[iovec_idx],
+				&dst_iovec[iovec_idx],
+				&src_it[it_idx],
+				&dst_it[it_idx]) < 0) {
 			vq->shadow_used_idx -= num_buffers;
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (src_it[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&src_it[it_idx],
+				&dst_it[it_idx]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += src_it[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += src_it[it_idx].nr_segs;
 
 			/**
 			 * recover shadow used ring and keep DMA-occupied
@@ -1541,23 +1570,12 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						num_buffers *
-						sizeof(struct vring_used_elem));
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->async_descs_split,
-						&vq->shadow_used_split[from +
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			shadow_ring_store(vq, vq->shadow_used_split,
+					vq->async_descs_split,
+					from, to, num_buffers,
+					sizeof(struct vring_used_elem));
+
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1593,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
 			vq->async_pkts_inflight_n += n_pkts;
 
@@ -1639,6 +1656,43 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)
+{
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left :
+			vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					nr_copy *
+					sizeof(struct vring_used_elem));
+		} else {
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					size *
+					sizeof(struct vring_used_elem));
+			rte_memcpy(vq->used->ring,
+					&vq->async_descs_split[from +
+					size], (nr_copy - size) *
+					sizeof(struct vring_used_elem));
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
@@ -1695,39 +1749,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						nr_copy *
-						sizeof(struct vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from +
-						size], (nr_copy - size) *
-						sizeof(struct vring_used_elem));
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
 		vhost_vring_call_split(dev, vq);
Signature · 1 line
2.29.2
#10 lore raw

PATCH v4 2/4 vhost: add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  27 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 428 ++++++++++++++++++++++++++++-
 4 files changed, 441 insertions(+), 22 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +20 -7
index a70fe01d8..8c9935c0f 100644
@@ -342,15 +342,21 @@ vhost_free_async_mem(struct vhost_virtqueue *vq)
 {
 	if (vq->async_pkts_info)
 		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	if (vq->async_buffers_packed) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	}
+	if (vq->async_descs_split) {
 		rte_free(vq->async_descs_split);
+		vq->async_descs_split = NULL;
+	}
+
 	if (vq->it_pool)
 		rte_free(vq->it_pool);
 	if (vq->vec_pool)
 		rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -1627,9 +1633,9 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or non-inorder mode "
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1667,11 +1673,18 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
-		!vq->it_pool || !vq->vec_pool) {
+	}
+
+	if (!vq->async_buffers_packed || !vq->async_descs_split ||
+		!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
 		VHOST_LOG_CONFIG(ERR,
 				"async register failed: cannot allocate memory for vq data "
lib/librte_vhost/vhost.h +6 -1
index f628714c2..fe131ae8f 100644
@@ -201,9 +201,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +414 -14

482 lines not on this page · raw message

Signature · 1 line
2.29.2
#11 lore raw

PATCH v4 3/4 vhost: add batch datapath for async vhost packed ring

In reply to Cheng Jiang

Add batch datapath for async vhost packed ring to improve the performance of small packet.

---
 lib/librte_vhost/virtio_net.c | 43 +++++++++++++++++++++++++++++++----
 1 file changed, 38 insertions(+), 5 deletions(-)
lib/librte_vhost/virtio_net.c +38 -5
index 2b8b873ca..c98fe6dbb 100644
@@ -1721,6 +1721,29 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline int
+virtio_dev_rx_async_batch_packed(struct virtio_net *dev,
+			   struct vhost_virtqueue *vq,
+			   struct rte_mbuf **pkts,
+			   struct rte_mbuf **comp_pkts, uint32_t *pkt_done)
+{
+	uint16_t i;
+	uint32_t cpy_threshold = vq->async_threshold;
+
+	vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE) {
+		if (unlikely(pkts[i]->pkt_len >= cpy_threshold))
+			return -1;
+	}
+	if (!virtio_dev_rx_batch_packed(dev, vq, pkts)) {
+		vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE)
+			comp_pkts[(*pkt_done)++] = pkts[i];
+
+		return 0;
+	}
+
+	return -1;
+}
+
 static __rte_always_inline int
 vhost_enqueue_async_single_packed(struct virtio_net *dev,
 			    struct vhost_virtqueue *vq,
@@ -1844,6 +1867,7 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	struct rte_mbuf **comp_pkts, uint32_t *comp_count)
 {
 	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint32_t remained = count;
 	uint16_t async_descs_idx = 0;
 	uint16_t num_buffers;
 	uint16_t num_desc;
@@ -1863,9 +1887,17 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
 	struct vring_packed_desc async_descs[vq->size];
 
-	rte_prefetch0(&vq->desc_packed[vq->last_avail_idx & (vq->size - 1)]);
-
-	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
+	do {
+		rte_prefetch0(&vq->desc_packed[vq->last_avail_idx &
+							(vq->size - 1)]);
+		if (remained >= PACKED_BATCH_SIZE) {
+			if (!virtio_dev_rx_async_batch_packed(dev, vq,
+				&pkts[pkt_idx], comp_pkts, &num_done_pkts)) {
+				pkt_idx += PACKED_BATCH_SIZE;
+				remained -= PACKED_BATCH_SIZE;
+				continue;
+			}
+		}
 		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq,
 						pkts[pkt_idx],
 						&num_desc, &num_buffers,
@@ -1912,6 +1944,8 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 		} else
 			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
 
+		pkt_idx++;
+		remained--;
 		vq_inc_last_avail_packed(vq, num_desc);
 
 		/*
@@ -1937,13 +1971,12 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 				 */
 				pkt_err = pkt_burst_idx - n_pkts;
 				pkt_burst_idx = 0;
-				pkt_idx++;
 				break;
 			}
 
 			pkt_burst_idx = 0;
 		}
-	}
+	} while (pkt_idx < count);
 
 	if (pkt_burst_idx) {
 		n_pkts = vq->async_ops.transfer_data(dev->vid,
Signature · 1 line
2.29.2
#12 lore raw

PATCH v4 4/4 doc: add release note for vhost async packed ring

In reply to Cheng Jiang

Add release note for the support of vhost async packed ring.

---
 doc/guides/rel_notes/release_21_05.rst | 4 ++++
 1 file changed, 4 insertions(+)
doc/guides/rel_notes/release_21_05.rst +4 -0
index 374d6d98e..eb5200669 100644
@@ -131,6 +131,10 @@ New Features
   * Added command to display Rx queue used descriptor count.
     ``show port (port_id) rxq (queue_id) desc used count``
 
+* **Added support for vhost async packed ring data path.**
+
+  Added packed ring support for async vhost.
+
 
 Removed Items
 -------------
Signature · 1 line
2.29.2
#13 lore raw

v5 cover add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch set cleans async split ring codes and enables packed ring in async vhost data path. Batch datapath is also enabled in async vhost packed ring.

v5:
* clean some codes for packed ring datapath
* fix an index error in shadow_ring_store()
v4:
* change the patch structure
* clean code for async split ring
* reuse some code from split ring
* change the error handler for DMA-copy packet
* add check for malloc
* remove useless code
* add doc update
v3:
* fix error handler for DMA-copy packet
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

Cheng Jiang (4):
vhost: abstract and reorganize async split ring code
vhost: add support for packed ring in async vhost
vhost: add batch datapath for async vhost packed ring
doc: add release note for vhost async packed ring

 doc/guides/rel_notes/release_21_05.rst |   4 +
 lib/librte_vhost/rte_vhost_async.h     |   1 +
 lib/librte_vhost/vhost.c               |  27 +-
 lib/librte_vhost/vhost.h               |   7 +-
 lib/librte_vhost/virtio_net.c          | 613 +++++++++++++++++++++----
 5 files changed, 567 insertions(+), 85 deletions(-)

--
2.29.2

#14 lore raw

PATCH v5 1/4 vhost: abstract and reorganize async split ring code

In order to improve code efficiency and readability when async packed ring support is enabled. This patch abstract some functions like shadow_ring_store and write_back_completed_descs_split. And improve the efficiency of some pointer offset calculation.

---
 lib/librte_vhost/virtio_net.c | 146 +++++++++++++++++++---------------
 1 file changed, 84 insertions(+), 62 deletions(-)
lib/librte_vhost/virtio_net.c +84 -62
index ff3987860..c43ab0093 100644
@@ -1458,6 +1458,29 @@ virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+shadow_ring_store(struct vhost_virtqueue *vq,  void *shadow_ring, void *d_ring,
+		uint16_t s_idx, uint16_t d_idx,
+		uint16_t count, uint16_t elem_size)
+{
+	if (d_idx + count <= vq->size) {
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			count * elem_size);
+	} else {
+		uint16_t size = vq->size - d_idx;
+
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			size * elem_size);
+
+		rte_memcpy((void *)((uintptr_t)d_ring),
+			(void *)((uintptr_t)shadow_ring +
+				(s_idx + size) * elem_size),
+			(count - size) * elem_size);
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1478,6 +1501,7 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
@@ -1513,27 +1537,32 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 
 		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
 				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+				&src_iovec[iovec_idx],
+				&dst_iovec[iovec_idx],
+				&src_it[it_idx],
+				&dst_it[it_idx]) < 0) {
 			vq->shadow_used_idx -= num_buffers;
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (src_it[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&src_it[it_idx],
+				&dst_it[it_idx]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += src_it[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += src_it[it_idx].nr_segs;
 
 			/**
 			 * recover shadow used ring and keep DMA-occupied
@@ -1541,23 +1570,12 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						num_buffers *
-						sizeof(struct vring_used_elem));
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->async_descs_split,
-						&vq->shadow_used_split[from +
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			shadow_ring_store(vq, vq->shadow_used_split,
+					vq->async_descs_split,
+					from, to, num_buffers,
+					sizeof(struct vring_used_elem));
+
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1593,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
 			vq->async_pkts_inflight_n += n_pkts;
 
@@ -1639,6 +1656,43 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)
+{
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left :
+			vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					nr_copy *
+					sizeof(struct vring_used_elem));
+		} else {
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					size *
+					sizeof(struct vring_used_elem));
+			rte_memcpy(vq->used->ring,
+					&vq->async_descs_split[from +
+					size], (nr_copy - size) *
+					sizeof(struct vring_used_elem));
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
@@ -1695,39 +1749,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						nr_copy *
-						sizeof(struct vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from +
-						size], (nr_copy - size) *
-						sizeof(struct vring_used_elem));
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
 		vhost_vring_call_split(dev, vq);
Signature · 1 line
2.29.2
#15 lore raw

Re: [dpdk-dev] [PATCH v5 1/4] vhost: abstract and reorganize async split ring code

Hi Cheng,

Some comments inline.

Quoted 9 lines

-----Original Message-----
From: Jiang, Cheng1 <[email protected]>
Sent: Monday, April 12, 2021 7:34 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [PATCH v5 1/4] vhost: abstract and reorganize async split ring code

Quoted 4 lines

In order to improve code efficiency and readability when async packed ring support is enabled. This patch abstract some functions like shadow_ring_store and write_back_completed_descs_split. And improve the efficiency of some pointer offset calculation.

Need to improve grammar for commit log, as there is typo and incomplete sentence.

Quoted 52 lines
---
 lib/librte_vhost/virtio_net.c | 146 +++++++++++++++++++---------------
 1 file changed, 84 insertions(+), 62 deletions(-)
lib/librte_vhost/virtio_net.c +23 -0
index ff3987860..c43ab0093 100644
@@ -1458,6 +1458,29 @@ virtio_dev_rx_async_get_info_idx(uint16_t
 pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+shadow_ring_store(struct vhost_virtqueue *vq,  void *shadow_ring, void
 *d_ring,
+		uint16_t s_idx, uint16_t d_idx,
+		uint16_t count, uint16_t elem_size)
+{
+	if (d_idx + count <= vq->size) {
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			count * elem_size);
+	} else {
+		uint16_t size = vq->size - d_idx;
+
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			size * elem_size);
+
+		rte_memcpy((void *)((uintptr_t)d_ring),
+			(void *)((uintptr_t)shadow_ring +
+				(s_idx + size) * elem_size),
+			(count - size) * elem_size);
+	}
+}
+
 static __rte_noinline uint32_t

virtio_dev_rx_async_submit_split(struct virtio_net *dev,

lib/librte_vhost/virtio_net.c +1 -0
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1478,6 +1501,7 @@ virtio_dev_rx_async_submit_split(struct virtio_net
 *dev,
 	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
lib/librte_vhost/virtio_net.c +0 -0
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct · line 1544

Quoted 9 lines
lib/librte_vhost/virtio_net.c +4 -1
@@ -1513,27 +1537,32 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 
 		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
 				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+				&src_iovec[iovec_idx],
+				&dst_iovec[iovec_idx],
+				&src_it[it_idx],
+				&dst_it[it_idx]) < 0) {

When use index, it's strange to get src and dst iov_iter from dst_it and src_it respectively, as they are not start addresses of two separated iov_iter arrays but have overlapped elements. IMO, there is no need to use src/dst_it, as they can be simply indexed by it_pool[it_idx] and it_pool[it_idx+1].

Quoted 56 lines
lib/librte_vhost/virtio_net.c +9 -7
 			vq->shadow_used_idx -= num_buffers;
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (src_it[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&src_it[it_idx],
+				&dst_it[it_idx]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += src_it[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += src_it[it_idx].nr_segs;
 
 			/**
lib/librte_vhost/virtio_net.c +0 -14
 			 * recover shadow used ring and keep DMA-occupied
@@ -1541,23 +1570,12 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq-
 >shadow_used_split[from],
-						num_buffers *
-						sizeof(struct
 vring_used_elem));
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq-
 >shadow_used_split[from],
-						size *
-						sizeof(struct
 vring_used_elem));
-				rte_memcpy(vq->async_descs_split,
-						&vq-
Quoted 1 line

shadow_used_split[from +

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1577

Quoted 8 lines
lib/librte_vhost/virtio_net.c +5 -3
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			shadow_ring_store(vq, vq->shadow_used_split,
+					vq->async_descs_split,
+					from, to, num_buffers,
+					sizeof(struct vring_used_elem));

This function is to store DMA-occupied desc, but " shadow_ring_store" is not a good name for it. In addition, I think there is no need to pass vq as a parameter. What you need is the size of shadow ring and async desc ring.

Thanks,
Jiayu

Quoted 117 lines
lib/librte_vhost/virtio_net.c +4 -4
+
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1593,9 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >>
 1);
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
lib/librte_vhost/virtio_net.c +37 -0
 			vq->async_pkts_inflight_n += n_pkts;
@@ -1639,6 +1656,43 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t
 n_descs)
+{
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left :
+			vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					nr_copy *
+					sizeof(struct vring_used_elem));
+		} else {
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					size *
+					sizeof(struct vring_used_elem));
+			rte_memcpy(vq->used->ring,
+					&vq->async_descs_split[from +
+					size], (nr_copy - size) *
+					sizeof(struct vring_used_elem));
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
lib/librte_vhost/virtio_net.c +0 -23
 		struct rte_mbuf **pkts, uint16_t count)
 {
@@ -1695,39 +1749,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int
 vid, uint16_t queue_id,
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq-
 >async_descs_split[from],
-						nr_copy *
-						sizeof(struct
 vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq-
 >async_descs_split[from],
-						size *
-						sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +1 -4
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from
+
-						size], (nr_copy - size) *
-						sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +1 -6
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs,

__ATOMIC_RELEASE);
vhost_vring_call_split(dev, vq);
--
2.29.2

#16 lore raw

Re: [dpdk-dev] [PATCH v5 1/4] vhost: abstract and reorganize async split ring code

Hi Jiayu,

Quoted 24 lines

-----Original Message-----
From: Hu, Jiayu <[email protected]>
Sent: Tuesday, April 13, 2021 10:44 AM
To: Jiang, Cheng1 <[email protected]>; [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>
Subject: RE: [PATCH v5 1/4] vhost: abstract and reorganize async split ring code

Hi Cheng,

Some comments inline.

Quoted 10 lines

-----Original Message-----
From: Jiang, Cheng1 <[email protected]>
Sent: Monday, April 12, 2021 7:34 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [PATCH v5 1/4] vhost: abstract and reorganize async split ring code

Quoted 8 lines
Quoted 4 lines

In order to improve code efficiency and readability when async packed ring support is enabled. This patch abstract some functions like shadow_ring_store and write_back_completed_descs_split. And improve the efficiency of some pointer offset calculation.

Need to improve grammar for commit log, as there is typo and incomplete sentence.

Sure, I'll fix it in the next version.

Quoted 47 lines
Quoted 47 lines
---
 lib/librte_vhost/virtio_net.c | 146
+++++++++++++++++++---------------
 1 file changed, 84 insertions(+), 62 deletions(-)
(quoted diff) +0 -0

b/lib/librte_vhost/virtio_net.c index ff3987860..c43ab0093 100644

lib/librte_vhost/virtio_net.c +18 -0
@@ -1458,6 +1458,29 @@ virtio_dev_rx_async_get_info_idx(uint16_t
 pkts_idx,
 (vq_size - n_inflight + pkts_idx) & (vq_size - 1);  }
 
+static __rte_always_inline void
+shadow_ring_store(struct vhost_virtqueue *vq,  void *shadow_ring,
+void
 *d_ring,
+uint16_t s_idx, uint16_t d_idx,
+uint16_t count, uint16_t elem_size)
+{
+if (d_idx + count <= vq->size) {
+rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size), (void
+*)((uintptr_t)shadow_ring + s_idx * elem_size), count * elem_size); }
+else { uint16_t size = vq->size - d_idx;
+
+rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size), (void
+*)((uintptr_t)shadow_ring + s_idx * elem_size), size * elem_size);
+
+rte_memcpy((void *)((uintptr_t)d_ring), (void
+*)((uintptr_t)shadow_ring + (s_idx + size) * elem_size), (count -
+size) * elem_size); } }
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,  struct

vhost_virtqueue *vq, uint16_t queue_id, @@ -1478,6 +1501,7 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev, struct rte_vhost_iov_iter *dst_it = it_pool + 1; uint16_t slot_idx = 0; uint16_t segs_await = 0;
+uint16_t iovec_idx = 0, it_idx = 0;
struct async_inflight_info *pkts_info = vq->async_pkts_info;
uint32_t n_pkts = 0, pkt_err = 0; uint32_t num_async_pkts = 0, num_done_pkts = 0; @@ -1513,27 +1537,32 @@ virtio_dev_rx_async_submit_split(struct
virtio_net *dev,

Quoted 11 lines
Quoted 6 lines

if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx], buf_vec, nr_vec,
num_buffers, -src_iovec, dst_iovec, src_it, dst_it) < 0) { +&src_iovec[iovec_idx],
+&dst_iovec[iovec_idx],
+&src_it[it_idx],
+&dst_it[it_idx]) < 0) {

When use index, it's strange to get src and dst iov_iter from dst_it and src_it respectively, as they are not start addresses of two separated iov_iter arrays but have overlapped elements. IMO, there is no need to use src/dst_it, as they can be simply indexed by it_pool[it_idx] and it_pool[it_idx+1].

Yes, I think it make sense, I'll fix it in the next version.

Quoted 50 lines
Quoted 50 lines

vq->shadow_used_idx -= num_buffers;
break;
}

slot_idx = (vq->async_pkts_idx + num_async_pkts) & (vq->size - 1);

lib/librte_vhost/virtio_net.c +4 -2
-if (src_it->count) {
+if (src_it[it_idx].count) {
 uint16_t from, to;
 
-async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+async_fill_desc(&tdes[pkt_burst_idx++],
+&src_it[it_idx],
+&dst_it[it_idx]);
 pkts_info[slot_idx].descs = num_buffers;  pkts_info[slot_idx].mbuf =

pkts[pkt_idx]; async_pkts_log[num_async_pkts].pkt_idx = pkt_idx; async_pkts_log[num_async_pkts++].last_avail_idx = vq->last_avail_idx;

lib/librte_vhost/virtio_net.c +7 -1
-src_iovec += src_it->nr_segs; -dst_iovec += dst_it->nr_segs; -src_it
+= 2; -dst_it += 2; -segs_await += src_it->nr_segs;
+
+iovec_idx += src_it[it_idx].nr_segs;
+it_idx += 2;
+
+segs_await += src_it[it_idx].nr_segs;
 
 /**
  * recover shadow used ring and keep DMA-occupied @@ -1541,23
+1570,12 @@ virtio_dev_rx_async_submit_split(struct

virtio_net *dev,
*/
from = vq->shadow_used_idx - num_buffers; to = vq->async_desc_idx &
(vq->size - 1); -if (num_buffers + to <= vq->size) { -rte_memcpy(&vq->async_descs_split[to],
-&vq-

Quoted 1 line

shadow_used_split[from],

lib/librte_vhost/virtio_net.c +0 -2
-num_buffers *
-sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +0 -5
-} else {
-int size = vq->size - to;
-
-rte_memcpy(&vq->async_descs_split[to],
-&vq-
Quoted 1 line

shadow_used_split[from],

lib/librte_vhost/virtio_net.c +0 -2
-size *
-sizeof(struct

vring_used_elem));
-rte_memcpy(vq->async_descs_split,
-&vq-

Quoted 1 line

shadow_used_split[from +

Quoted 12 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1577

Quoted 8 lines
lib/librte_vhost/virtio_net.c +5 -3
-size], (num_buffers - size) *
-   sizeof(struct vring_used_elem));
-}
+
+shadow_ring_store(vq, vq->shadow_used_split,
+vq->async_descs_split,
+from, to, num_buffers,
+sizeof(struct vring_used_elem));

This function is to store DMA-occupied desc, but " shadow_ring_store" is not a good name for it. In addition, I think there is no need to pass vq as a parameter. What you need is the size of shadow ring and async desc ring.

Ok, I think we can use the name " store_dma_desc_info()".

Thanks a lot.
Cheng

Quoted 114 lines

Thanks,
Jiayu

Quoted 110 lines
lib/librte_vhost/virtio_net.c +41 -3
+
 vq->async_desc_idx += num_buffers;
 vq->shadow_used_idx -= num_buffers;
 } else
@@ -1575,10 +1593,9 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 BUF_VECTOR_MAX))) {
 n_pkts = vq->async_ops.transfer_data(dev->vid,
 queue_id, tdes, 0, pkt_burst_idx);
-src_iovec = vec_pool;
-dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1); -src_it = it_pool;
-dst_it = it_pool + 1;
+iovec_idx = 0;
+it_idx = 0;
+
 segs_await = 0;
 vq->async_pkts_inflight_n += n_pkts;
@@ -1639,6 +1656,43 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t
 n_descs)
+{
+uint16_t nr_left = n_descs;
+uint16_t nr_copy;
+uint16_t to, from;
+
+do {
+from = vq->last_async_desc_idx & (vq->size - 1); nr_copy = nr_left +
+from <= vq->size ? nr_left :
+vq->size - from;
+to = vq->last_used_idx & (vq->size - 1);
+
+if (to + nr_copy <= vq->size) {
+rte_memcpy(&vq->used->ring[to],
+&vq->async_descs_split[from],
+nr_copy *
+sizeof(struct vring_used_elem));
+} else {
+uint16_t size = vq->size - to;
+
+rte_memcpy(&vq->used->ring[to],
+&vq->async_descs_split[from],
+size *
+sizeof(struct vring_used_elem));
+rte_memcpy(vq->used->ring,
+&vq->async_descs_split[from +
+size], (nr_copy - size) *
+sizeof(struct vring_used_elem));
+}
+
+vq->last_async_desc_idx += nr_copy;
+vq->last_used_idx += nr_copy;
+nr_left -= nr_copy;
+} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,

struct rte_mbuf **pkts, uint16_t count) { @@ -1695,39 +1749,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int
vid, uint16_t queue_id,
vq->async_pkts_inflight_n -= n_pkts_put;

if (likely(vq->enabled && vq->access_ok)) { -uint16_t nr_left =
n_descs; -uint16_t nr_copy; -uint16_t to;

lib/librte_vhost/virtio_net.c +0 -2
-
-/* write back completed descriptors to used ring */ -do { -from =

vq->last_async_desc_idx & (vq->size - 1); -nr_copy = nr_left + from <= vq->size ? nr_left :

lib/librte_vhost/virtio_net.c +0 -6
-vq->size - from;
-to = vq->last_used_idx & (vq->size - 1);
-
-if (to + nr_copy <= vq->size) {
-rte_memcpy(&vq->used->ring[to],
-&vq-
Quoted 1 line

async_descs_split[from],

lib/librte_vhost/virtio_net.c +0 -2
-nr_copy *
-sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +0 -5
-} else {
-uint16_t size = vq->size - to;
-
-rte_memcpy(&vq->used->ring[to],
-&vq-
Quoted 1 line

async_descs_split[from],

lib/librte_vhost/virtio_net.c +0 -2
-size *
-sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +1 -4
-rte_memcpy(vq->used->ring,
-&vq->async_descs_split[from
+
-size], (nr_copy - size) *
-sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +1 -6
-}
-
-vq->last_async_desc_idx += nr_copy;
-vq->last_used_idx += nr_copy;
-nr_left -= nr_copy;
-} while (nr_left > 0);
+write_back_completed_descs_split(vq, n_descs);
 
 __atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);

vhost_vring_call_split(dev, vq);
--
2.29.2

#17 lore raw

Re: [dpdk-dev] [PATCH v5 1/4] vhost: abstract and reorganize async split ring code

In reply to Cheng Jiang

Hi Cheng,

Quoted 110 lines · Cheng Jiang

In order to improve code efficiency and readability when async packed ring support is enabled. This patch abstract some functions like shadow_ring_store and write_back_completed_descs_split. And improve the efficiency of some pointer offset calculation.

---
 lib/librte_vhost/virtio_net.c | 146 +++++++++++++++++++---------------
 1 file changed, 84 insertions(+), 62 deletions(-)
lib/librte_vhost/virtio_net.c +37 -21
index ff3987860..c43ab0093 100644
@@ -1458,6 +1458,29 @@ virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+shadow_ring_store(struct vhost_virtqueue *vq,  void *shadow_ring, void *d_ring,
+		uint16_t s_idx, uint16_t d_idx,
+		uint16_t count, uint16_t elem_size)
+{
+	if (d_idx + count <= vq->size) {
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			count * elem_size);
+	} else {
+		uint16_t size = vq->size - d_idx;
+
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			size * elem_size);
+
+		rte_memcpy((void *)((uintptr_t)d_ring),
+			(void *)((uintptr_t)shadow_ring +
+				(s_idx + size) * elem_size),
+			(count - size) * elem_size);
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1478,6 +1501,7 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
@@ -1513,27 +1537,32 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 
 		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
 				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+				&src_iovec[iovec_idx],
+				&dst_iovec[iovec_idx],
+				&src_it[it_idx],
+				&dst_it[it_idx]) < 0) {
 			vq->shadow_used_idx -= num_buffers;
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (src_it[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&src_it[it_idx],
+				&dst_it[it_idx]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += src_it[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += src_it[it_idx].nr_segs;
 
 			/**
 			 * recover shadow used ring and keep DMA-occupied
@@ -1541,23 +1570,12 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						num_buffers *
-						sizeof(struct vring_used_elem));
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->async_descs_split,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1578

Quoted 10 lines
lib/librte_vhost/virtio_net.c +6 -4
-						&vq->shadow_used_split[from +
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			shadow_ring_store(vq, vq->shadow_used_split,
+					vq->async_descs_split,
+					from, to, num_buffers,
+					sizeof(struct vring_used_elem));
+

I'm not convinced with this rework.

I think it is good to create a dedicated function for this to simplify this huge virtio_dev_rx_async_submit_split() function. But we should have a dedicated version for split ring. Having a single function for both split and packed ring does not improve readability, and unlikely improve performance.

Quoted 37 lines
lib/librte_vhost/virtio_net.c +19 -4
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1593,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
 			vq->async_pkts_inflight_n += n_pkts;
 
@@ -1639,6 +1656,43 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)
+{
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left :
+			vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1684

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+					nr_copy *
+					sizeof(struct vring_used_elem));
+		} else {
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					size *
+					sizeof(struct vring_used_elem));
+			rte_memcpy(vq->used->ring,

&vq->used->ring[0] for consistency

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1687

Quoted 3 lines
lib/librte_vhost/virtio_net.c +3 -0
+					&vq->async_descs_split[from +
+					size], (nr_copy - size) *
+					sizeof(struct vring_used_elem));

Lines can now be up to 100 chars.
Please take the opportunity to indent properly not to have parts of each args being put on the same line. It will help readability.

Quoted 53 lines
lib/librte_vhost/virtio_net.c +9 -33
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
@@ -1695,39 +1749,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						nr_copy *
-						sizeof(struct vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from +
-						size], (nr_copy - size) *
-						sizeof(struct vring_used_elem));
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
 		vhost_vring_call_split(dev, vq);
#18 lore raw

Re: [dpdk-dev] [PATCH v5 1/4] vhost: abstract and reorganize async split ring code

Hi Maxime,

Quoted 147 lines

-----Original Message-----
From: Maxime Coquelin <[email protected]>
Sent: Tuesday, April 13, 2021 3:12 PM
To: Jiang, Cheng1 <[email protected]>; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>
Subject: Re: [PATCH v5 1/4] vhost: abstract and reorganize async split ring code

Hi Cheng,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_get_info_idx(uint16_t

Quoted 16 lines · Cheng Jiang

In order to improve code efficiency and readability when async packed ring support is enabled. This patch abstract some functions like shadow_ring_store and write_back_completed_descs_split. And improve the efficiency of some pointer offset calculation.

---
 lib/librte_vhost/virtio_net.c | 146
+++++++++++++++++++---------------
 1 file changed, 84 insertions(+), 62 deletions(-)
(quoted diff) +0 -0

b/lib/librte_vhost/virtio_net.c index ff3987860..c43ab0093 100644

lib/librte_vhost/virtio_net.c +0 -0
@@ -1458,6 +1458,29 @@ virtio_dev_rx_async_get_info_idx(uint16_t

pkts_idx,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,

Quoted 4 lines
lib/librte_vhost/virtio_net.c +2 -0
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);  }
 
+static __rte_always_inline void
+shadow_ring_store(struct vhost_virtqueue *vq,  void *shadow_ring, void

*d_ring,

Quoted 13 lines
lib/librte_vhost/virtio_net.c +13 -0
+		uint16_t s_idx, uint16_t d_idx,
+		uint16_t count, uint16_t elem_size) {
+	if (d_idx + count <= vq->size) {
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			count * elem_size);
+	} else {
+		uint16_t size = vq->size - d_idx;
+
+		rte_memcpy((void *)((uintptr_t)d_ring + d_idx * elem_size),
+			(void *)((uintptr_t)shadow_ring + s_idx * elem_size),
+			size * elem_size);
+

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +7 -0
+		rte_memcpy((void *)((uintptr_t)d_ring),
+			(void *)((uintptr_t)shadow_ring +
+				(s_idx + size) * elem_size),
+			(count - size) * elem_size);
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id, @@ -1478,6 +1501,7

@@

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 8 lines
lib/librte_vhost/virtio_net.c +1 -0
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
 	uint32_t num_async_pkts = 0, num_done_pkts = 0; @@ -1513,27

+1537,32

Quoted 10 lines

@@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,

lib/librte_vhost/virtio_net.c +4 -1
 		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
 				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+				&src_iovec[iovec_idx],
+				&dst_iovec[iovec_idx],
+				&src_it[it_idx],
+				&dst_it[it_idx]) < 0) {
 			vq->shadow_used_idx -= num_buffers;

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +1 -2
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (src_it[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it,

dst_it);

Quoted 11 lines
lib/librte_vhost/virtio_net.c +3 -3
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&src_it[it_idx],
+				&dst_it[it_idx]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1568

Quoted 10 lines
lib/librte_vhost/virtio_net.c +5 -2
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += src_it[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += src_it[it_idx].nr_segs;
 
 			/**
 			 * recover shadow used ring and keep DMA-occupied

@@ -1541,23

lib/librte_vhost/virtio_net.c

Quoted 10 lines

+1570,12 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,

lib/librte_vhost/virtio_net.c +0 -3
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq-

shadow_used_split[from],

lib/librte_vhost/virtio_net.c +0 -2
-						num_buffers *
-						sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c

Quoted 8 lines
lib/librte_vhost/virtio_net.c +0 -5
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq-

shadow_used_split[from],

lib/librte_vhost/virtio_net.c +0 -2
-						size *
-						sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1578

Quoted 12 lines
lib/librte_vhost/virtio_net.c +0 -2
-				rte_memcpy(vq->async_descs_split,
-						&vq-

shadow_used_split[from +

lib/librte_vhost/virtio_net.c +6 -3
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			shadow_ring_store(vq, vq->shadow_used_split,
+					vq->async_descs_split,
+					from, to, num_buffers,
+					sizeof(struct vring_used_elem));
+

I'm not convinced with this rework.

Quoted 4 lines

I think it is good to create a dedicated function for this to simplify this huge virtio_dev_rx_async_submit_split() function. But we should have a dedicated version for split ring. Having a single function for both split and packed ring does not improve readability, and unlikely improve performance.

Sure, I'm agree with you. I will use two functions for split and packed separately in the next version.

Quoted 44 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct

Quoted 5 lines
lib/librte_vhost/virtio_net.c +0 -0
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1593,9 @@ virtio_dev_rx_async_submit_split(struct

virtio_net *dev,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 5 lines
lib/librte_vhost/virtio_net.c +0 -2
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >>

1);

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct

Quoted 9 lines
lib/librte_vhost/virtio_net.c +3 -2
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
 			vq->async_pkts_inflight_n += n_pkts;
 
@@ -1639,6 +1656,43 @@ virtio_dev_rx_async_submit_split(struct

virtio_net *dev,

Quoted 22 lines
lib/librte_vhost/virtio_net.c +19 -0
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t
+n_descs) {
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left :
+			vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					nr_copy *
+					sizeof(struct vring_used_elem));
+		} else {
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1684

Quoted 7 lines
lib/librte_vhost/virtio_net.c +7 -0
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to],
+					&vq->async_descs_split[from],
+					size *
+					sizeof(struct vring_used_elem));
+			rte_memcpy(vq->used->ring,

&vq->used->ring[0] for consistency

It will be fixed in the next version.

Quoted 7 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1687

Quoted 3 lines
lib/librte_vhost/virtio_net.c +3 -0
+					&vq->async_descs_split[from +
+					size], (nr_copy - size) *
+					sizeof(struct vring_used_elem));

Lines can now be up to 100 chars.
Please take the opportunity to indent properly not to have parts of each args being put on the same line. It will help readability.

Ok, glad to know. I will fix them in the next version.

Thanks a lot.
Cheng

Quoted 61 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 11 lines
lib/librte_vhost/virtio_net.c +8 -0
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)  { @@ -1695,39

+1749,7 @@

Quoted 19 lines
lib/librte_vhost/virtio_net.c +0 -0
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
lib/librte_vhost/virtio_net.c +0 -14
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq-

async_descs_split[from],

lib/librte_vhost/virtio_net.c

Quoted 2 lines
lib/librte_vhost/virtio_net.c +0 -2
-						nr_copy *
-						sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c

Quoted 8 lines
lib/librte_vhost/virtio_net.c +0 -5
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq-

async_descs_split[from],

lib/librte_vhost/virtio_net.c +0 -2
-						size *
-						sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c

Quoted 2 lines
lib/librte_vhost/virtio_net.c +0 -2
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from

+

lib/librte_vhost/virtio_net.c

Quoted 2 lines
lib/librte_vhost/virtio_net.c +0 -2
-						size], (nr_copy - size) *
-						sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,

Quoted 9 lines
lib/librte_vhost/virtio_net.c +1 -6
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs,

__ATOMIC_RELEASE);

Quoted 2 lines

vhost_vring_call_split(dev, vq);

#19 lore raw

PATCH v5 2/4 vhost: add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  27 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 438 +++++++++++++++++++++++++++--
 4 files changed, 448 insertions(+), 25 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +20 -7
index a70fe01d8..8c9935c0f 100644
@@ -342,15 +342,21 @@ vhost_free_async_mem(struct vhost_virtqueue *vq)
 {
 	if (vq->async_pkts_info)
 		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	if (vq->async_buffers_packed) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	}
+	if (vq->async_descs_split) {
 		rte_free(vq->async_descs_split);
+		vq->async_descs_split = NULL;
+	}
+
 	if (vq->it_pool)
 		rte_free(vq->it_pool);
 	if (vq->vec_pool)
 		rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -1627,9 +1633,9 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or non-inorder mode "
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1667,11 +1673,18 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
-		!vq->it_pool || !vq->vec_pool) {
+	}
+
+	if (!vq->async_buffers_packed || !vq->async_descs_split ||
+		!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
 		VHOST_LOG_CONFIG(ERR,
 				"async register failed: cannot allocate memory for vq data "
lib/librte_vhost/vhost.h +6 -1
index f628714c2..fe131ae8f 100644
@@ -201,9 +201,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +421 -17

495 lines not on this page · raw message

Signature · 1 line
2.29.2
#20 lore raw
Quoted 29 lines · Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  27 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 438 +++++++++++++++++++++++++++--
 4 files changed, 448 insertions(+), 25 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +0 -0
index a70fe01d8..8c9935c0f 100644

lib/librte_vhost/vhost.c vhost_free_async_mem(struct vhost_virtqueue *vq) · line 349

Quoted 9 lines
lib/librte_vhost/vhost.c +5 -1
@@ -342,15 +342,21 @@ vhost_free_async_mem(struct vhost_virtqueue *vq)
 {
 	if (vq->async_pkts_info)
 		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	if (vq->async_buffers_packed) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	}
+	if (vq->async_descs_split) {

You can remove the check, rte_free is safe with NULL pointers. You can do the same for the other ones in this function.

Quoted 53 lines
lib/librte_vhost/vhost.c +15 -6
 		rte_free(vq->async_descs_split);
+		vq->async_descs_split = NULL;
+	}
+
 	if (vq->it_pool)
 		rte_free(vq->it_pool);
 	if (vq->vec_pool)
 		rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -1627,9 +1633,9 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or non-inorder mode "
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1667,11 +1673,18 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
-		!vq->it_pool || !vq->vec_pool) {
+	}
+
+	if (!vq->async_buffers_packed || !vq->async_descs_split ||
+		!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
 		VHOST_LOG_CONFIG(ERR,
 				"async register failed: cannot allocate memory for vq data "
lib/librte_vhost/vhost.h +0 -0
index f628714c2..fe131ae8f 100644

lib/librte_vhost/vhost.h struct vhost_virtqueue { · line 209

Quoted 10 lines
lib/librte_vhost/vhost.h +5 -1
@@ -201,9 +201,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;

Don't dupplicate variable names, async_desc_idx can be reused for packed ring. Also, they are representing the same thing, why use desc in one case and buffer in the other?

lib/librte_vhost/vhost.h struct vhost_virtqueue { · line 211

Quoted 2 lines
lib/librte_vhost/vhost.h +1 -0
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;

Same remark here.

Quoted 45 lines
lib/librte_vhost/vhost.h +0 -0
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +16 -5
index c43ab0093..410be9678 100644
@@ -363,14 +363,14 @@ vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue *vq,
 }
 
 static __rte_always_inline void
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-				   struct vhost_virtqueue *vq,
-				   uint32_t len[],
-				   uint16_t id[],
-				   uint16_t count[],
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
 				   uint16_t num_buffers)
 {
 	uint16_t i;
+
 	for (i = 0; i < num_buffers; i++) {
 		/* enqueue shadow flush action aligned with batch num */
 		if (!vq->shadow_used_idx)
@@ -382,6 +382,17 @@ vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
 		vq->shadow_aligned_idx += count[i];
 		vq->shadow_used_idx++;
 	}
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+				   struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
+				   uint16_t num_buffers)
+{
+	vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 	if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {
 		do_data_copy_enqueue(dev, vq);

lib/librte_vhost/virtio_net.c shadow_ring_store(struct vhost_virtqueue *vq, void *shadow_ring, void *d_ring, · line 1501

Quoted 10 lines
lib/librte_vhost/virtio_net.c +7 -0
@@ -1481,6 +1492,62 @@ shadow_ring_store(struct vhost_virtqueue *vq,  void *shadow_ring, void *d_ring,
 	}
 }
 
+static __rte_always_inline void
+vhost_update_used_packed(struct vhost_virtqueue *vq,
+			struct vring_used_elem_packed *shadow_ring,
+			uint16_t count)
+{
+	if (count == 0)
+		return;

Move this after the variables declaration.

Quoted 113 lines
lib/librte_vhost/virtio_net.c +106 -0
+
+	int i;
+	uint16_t used_idx = vq->last_used_idx;
+	uint16_t head_idx = vq->last_used_idx;
+	uint16_t head_flags = 0;
+
+	/* Split loop in two to save memory barriers */
+	for (i = 0; i < count; i++) {
+		vq->desc_packed[used_idx].id = shadow_ring[i].id;
+		vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+		used_idx += shadow_ring[i].count;
+		if (used_idx >= vq->size)
+			used_idx -= vq->size;
+	}
+
+	/* The ordering for storing desc flags needs to be enforced. */
+	rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+	for (i = 0; i < count; i++) {
+		uint16_t flags;
+
+		if (vq->shadow_used_packed[i].len)
+			flags = VRING_DESC_F_WRITE;
+		else
+			flags = 0;
+
+		if (vq->used_wrap_counter) {
+			flags |= VRING_DESC_F_USED;
+			flags |= VRING_DESC_F_AVAIL;
+		} else {
+			flags &= ~VRING_DESC_F_USED;
+			flags &= ~VRING_DESC_F_AVAIL;
+		}
+
+		if (i > 0) {
+			vq->desc_packed[vq->last_used_idx].flags = flags;
+
+		} else {
+			head_idx = vq->last_used_idx;
+			head_flags = flags;
+		}
+
+		vq_inc_last_used_packed(vq, shadow_ring[i].count);
+	}
+
+	vq->desc_packed[head_idx].flags = head_flags;
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1656,6 +1723,294 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    struct buf_vector *buf_vec,
+			    uint16_t *nr_descs,
+			    uint16_t *nr_buffers,
+			    struct vring_packed_desc *async_descs,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it)
+{
+	uint16_t nr_vec = 0;
+	uint16_t avail_idx = vq->last_avail_idx;
+	uint16_t max_tries, tries = 0;
+	uint16_t buf_id = 0;
+	uint32_t len = 0;
+	uint16_t desc_count = 0;
+	uint32_t size = pkt->pkt_len + sizeof(struct virtio_net_hdr_mrg_rxbuf);
+	uint32_t buffer_len[vq->size];
+	uint16_t buffer_buf_id[vq->size];
+	uint16_t buffer_desc_count[vq->size];
+	*nr_buffers = 0;
+
+	if (rxvq_is_mergeable(dev))
+		max_tries = vq->size - 1;
+	else
+		max_tries = 1;
+
+	while (size > 0) {
+		/*
+		 * if we tried all available ring items, and still
+		 * can't get enough buf, it means something abnormal
+		 * happened.
+		 */
+		if (unlikely(++tries > max_tries))
+			return -1;
+
+		if (unlikely(fill_vec_buf_packed(dev, vq,
+						avail_idx, &desc_count,
+						buf_vec, &nr_vec,
+						&buf_id, &len,
+						VHOST_ACCESS_RW) < 0))
+			return -1;
+
+		len = RTE_MIN(len, size);
+		size -= len;
+
+		buffer_len[*nr_buffers] = len;
+		buffer_buf_id[*nr_buffers] = buf_id;
+		buffer_desc_count[*nr_buffers] = desc_count;
+		*nr_buffers += 1;
+
+		*nr_descs += desc_count;
+		avail_idx += desc_count;
+		if (avail_idx >= vq->size)
+			avail_idx -= vq->size;

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1792

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+	}
+
+	if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,
+		src_iovec, dst_iovec, src_it, dst_it) < 0)
+		return -1;
+	/* store descriptors for DMA */
+	if (avail_idx >= *nr_descs)
+		rte_memcpy(async_descs,
+			&vq->desc_packed[vq->last_avail_idx],
+			*nr_descs * sizeof(struct vring_packed_desc));

Please add brackets for the 'if' since there are for the 'else'.

Quoted 66 lines
lib/librte_vhost/virtio_net.c +66 -0
+	else {
+		uint16_t nr_copy = vq->size - vq->last_avail_idx;
+		rte_memcpy(async_descs,
+			&vq->desc_packed[vq->last_avail_idx],
+			nr_copy * sizeof(struct vring_packed_desc));
+		rte_memcpy(async_descs + nr_copy,
+			vq->desc_packed, (*nr_descs - nr_copy) *
+			sizeof(struct vring_packed_desc));
+	}
+
+	vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,
+					   buffer_desc_count, *nr_buffers);
+
+	return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    uint16_t *nr_descs, uint16_t *nr_buffers,
+			    struct vring_packed_desc *async_descs,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it)
+{
+	struct buf_vector buf_vec[BUF_VECTOR_MAX];
+	*nr_descs = 0;
+	*nr_buffers = 0;
+
+	if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt, buf_vec,
+						 nr_descs,
+						 nr_buffers,
+						 async_descs,
+						 src_iovec, dst_iovec,
+						 src_it, dst_it) < 0)) {
+		VHOST_LOG_DATA(DEBUG,
+				"(%d) failed to get enough desc from vring\n",
+				dev->vid);
+		return -1;
+	}
+
+	VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end index %d\n",
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + *nr_descs);
+
+	return 0;
+}
+
+static __rte_noinline uint32_t
+virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
+	struct vhost_virtqueue *vq, uint16_t queue_id,
+	struct rte_mbuf **pkts, uint32_t count,
+	struct rte_mbuf **comp_pkts, uint32_t *comp_count)
+{
+	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint16_t async_descs_idx = 0;
+	uint16_t num_buffers;
+	uint16_t num_desc;
+
+	struct rte_vhost_iov_iter *it_pool = vq->it_pool;
+	struct iovec *vec_pool = vq->vec_pool;
+	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
+	struct iovec *src_iovec = vec_pool;
+	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
+	struct rte_vhost_iov_iter *src_it = it_pool;

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1868

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
+	uint16_t slot_idx = 0;
+	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
+	struct async_inflight_info *pkts_info = vq->async_pkts_info;
+	uint32_t n_pkts = 0, pkt_err = 0;
+	uint32_t num_async_pkts = 0, num_done_pkts = 0;
+	struct vring_packed_desc async_descs[vq->size];
+
+	rte_prefetch0(&vq->desc_packed[vq->last_avail_idx & (vq->size - 1)]);

The size of the ring is not necessarily a power of two with packed ring.

Quoted 8 lines
lib/librte_vhost/virtio_net.c +8 -0
+
+	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
+		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq,
+						pkts[pkt_idx],
+						&num_desc, &num_buffers,
+						&async_descs[async_descs_idx],
+						&src_iovec[iovec_idx],
+						&dst_iovec[iovec_idx],

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1886

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+						&src_it[it_idx],
+						&dst_it[it_idx]) < 0))
+			break;
+
+		VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end index %d\n",
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + num_desc);
+
+		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
+			(vq->size - 1);

Same here.

Quoted 19 lines
lib/librte_vhost/virtio_net.c +19 -0
+		if (src_it[it_idx].count) {
+			uint16_t from, to;
+
+			async_descs_idx += num_desc;
+			async_fill_desc(&tdes[pkt_burst_idx++], &src_it[it_idx],
+					&dst_it[it_idx]);
+			pkts_info[slot_idx].descs = num_desc;
+			pkts_info[slot_idx].nr_buffers = num_buffers;
+			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
+			num_async_pkts++;
+			iovec_idx += src_it[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += src_it[it_idx].nr_segs;
+
+			/**
+			 * recover shadow used ring and keep DMA-occupied
+			 * descriptors.
+			 */

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1915

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			from = vq->shadow_used_idx - num_buffers;
+			to = vq->async_packed_buffer_idx & (vq->size - 1);
+			shadow_ring_store(vq, vq->shadow_used_packed,
+					vq->async_buffers_packed,
+					from, to, num_buffers,
+					sizeof(struct vring_used_elem_packed));
+
+			vq->async_packed_buffer_idx += num_buffers;
+			vq->shadow_used_idx -= num_buffers;
+		} else

Brackets needed.

Quoted 46 lines
lib/librte_vhost/virtio_net.c +46 -0
+			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
+
+		vq_inc_last_avail_packed(vq, num_desc);
+
+		/*
+		 * conditions to trigger async device transfer:
+		 * - buffered packet number reaches transfer threshold
+		 * - unused async iov number is less than max vhost vector
+		 */
+		if (unlikely(pkt_burst_idx >= VHOST_ASYNC_BATCH_THRESHOLD ||
+			((VHOST_MAX_ASYNC_VEC >> 1) - segs_await <
+			BUF_VECTOR_MAX))) {
+			n_pkts = vq->async_ops.transfer_data(dev->vid,
+					queue_id, tdes, 0, pkt_burst_idx);
+			iovec_idx = 0;
+			it_idx = 0;
+			segs_await = 0;
+			vq->async_pkts_inflight_n += n_pkts;
+
+			if (unlikely(n_pkts < pkt_burst_idx)) {
+				/*
+				 * log error packets number here and do actual
+				 * error processing when applications poll
+				 * completion
+				 */
+				pkt_err = pkt_burst_idx - n_pkts;
+				pkt_burst_idx = 0;
+				pkt_idx++;
+				break;
+			}
+
+			pkt_burst_idx = 0;
+		}
+	}
+
+	if (pkt_burst_idx) {
+		n_pkts = vq->async_ops.transfer_data(dev->vid,
+				queue_id, tdes, 0, pkt_burst_idx);
+		vq->async_pkts_inflight_n += n_pkts;
+
+		if (unlikely(n_pkts < pkt_burst_idx))
+			pkt_err = pkt_burst_idx - n_pkts;
+	}
+
+	do_data_copy_enqueue(dev, vq);
+

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1971

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+	if (unlikely(pkt_err)) {
+		uint16_t descs_err = 0;
+		uint16_t buffers_err = 0;
+
+		num_async_pkts -= pkt_err;
+		pkt_idx -= pkt_err;
+	/* calculate the sum of buffers and descs of DMA-error packets. */
+		while (pkt_err-- > 0) {
+			descs_err +=
+				pkts_info[slot_idx & (vq->size - 1)].descs;

The size of the ring is not necessarily a power of two with packed ring.

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1973

Quoted 2 lines
lib/librte_vhost/virtio_net.c +2 -0
+			buffers_err +=
+				pkts_info[slot_idx & (vq->size - 1)].nr_buffers;

Ditto.

Quoted 19 lines
lib/librte_vhost/virtio_net.c +19 -0
+			slot_idx--;
+		}
+
+		vq->async_packed_buffer_idx -= buffers_err;
+
+		if (vq->last_avail_idx >= descs_err) {
+			vq->last_avail_idx -= descs_err;
+
+			rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+				&async_descs[async_descs_idx - descs_err],
+				descs_err * sizeof(struct vring_packed_desc));
+		} else {
+			uint16_t nr_copy;
+
+			vq->last_avail_idx = vq->last_avail_idx + vq->size
+						- descs_err;
+			nr_copy = vq->size - vq->last_avail_idx;
+			rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+				&async_descs[async_descs_idx - descs_err],

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 2002

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+				nr_copy * sizeof(struct vring_packed_desc));
+			descs_err -= nr_copy;
+			rte_memcpy(vq->desc_packed,
+				&async_descs[async_descs_idx - descs_err],
+				descs_err * sizeof(struct vring_packed_desc));
+			vq->avail_wrap_counter ^= 1;
+		}
+
+		num_done_pkts = pkt_idx - num_async_pkts;
+	}

This error handling could be moved in a dedicated function.

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 2012

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+	vq->async_pkts_idx += num_async_pkts;
+	*comp_count = num_done_pkts;
+
+	if (likely(vq->shadow_used_idx)) {
+		vhost_flush_enqueue_shadow_packed(dev, vq);
+		vhost_vring_call_packed(dev, vq);
+	}
+
+	return pkt_idx;
+}

Above function is very big and complex, it should be possible to split it in several ones to make it maintainable.

Quoted 9 lines
lib/librte_vhost/virtio_net.c +2 -0
+
 static __rte_always_inline void
 write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)
 {
@@ -1693,12 +2048,40 @@ write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)
 	} while (nr_left > 0);
 }
 
+static __rte_always_inline void

lib/librte_vhost/virtio_net.c write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs) · line 2061

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+write_back_completed_descs_packed(struct vhost_virtqueue *vq,
+				uint16_t n_buffers)
+{
+	uint16_t nr_left = n_buffers;
+	uint16_t from, to;
+
+	do {
+		from = vq->last_async_buffer_idx &
+						(vq->size - 1);
+		to = (from + nr_left) & (vq->size - 1);

The size of the ring is not necessarily a power of two with packed ring.

Quoted 28 lines
lib/librte_vhost/virtio_net.c +18 -1
+		if (to > from) {
+			vhost_update_used_packed(vq,
+				vq->async_buffers_packed + from,
+				to - from);
+			vq->last_async_buffer_idx += nr_left;
+			nr_left = 0;
+		} else {
+			vhost_update_used_packed(vq,
+				vq->async_buffers_packed + from,
+				vq->size - from);
+			vq->last_async_buffer_idx +=
+						vq->size - from;
+			nr_left -= vq->size - from;
+		}
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
 	struct virtio_net *dev = get_device(vid);
 	struct vhost_virtqueue *vq;
-	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0;
+	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0, n_buffers = 0;
 	uint16_t start_idx, pkts_idx, vq_size;
 	struct async_inflight_info *pkts_info;
 	uint16_t from, i;

lib/librte_vhost/virtio_net.c uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id, · line 2128

Quoted 10 lines
lib/librte_vhost/virtio_net.c +3 -4
@@ -1740,21 +2123,41 @@ uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		goto done;
 	}
 
-	for (i = 0; i < n_pkts_put; i++) {
-		from = (start_idx + i) & (vq_size - 1);
-		n_descs += pkts_info[from].descs;
-		pkts[i] = pkts_info[from].mbuf;
+	if (vq_is_packed(dev)) {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);

Unlike split ring, packed ring size is not necessarily a power of 2.

Quoted 54 lines
lib/librte_vhost/virtio_net.c +29 -7
+			n_buffers += pkts_info[from].nr_buffers;
+			pkts[i] = pkts_info[from].mbuf;
+		}
+	} else {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);
+			n_descs += pkts_info[from].descs;
+			pkts[i] = pkts_info[from].mbuf;
+		}
 	}
+
 	vq->async_last_pkts_n = n_pkts_cpl - n_pkts_put;
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		write_back_completed_descs_split(vq, n_descs);
+		if (vq_is_packed(dev)) {
+			write_back_completed_descs_packed(vq, n_buffers);
 
-		__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
-		vhost_vring_call_split(dev, vq);
-	} else
-		vq->last_async_desc_idx += n_descs;
+			vhost_vring_call_packed(dev, vq);
+		} else {
+			write_back_completed_descs_split(vq, n_descs);
+
+			__atomic_add_fetch(&vq->used->idx, n_descs,
+					__ATOMIC_RELEASE);
+			vhost_vring_call_split(dev, vq);
+		}
+	} else {
+		if (vq_is_packed(dev))
+			vq->last_async_buffer_idx += n_buffers;
+		else
+			vq->last_async_desc_idx += n_descs;
+	}
 
 done:
 	rte_spinlock_unlock(&vq->access_lock);
@@ -1795,9 +2198,10 @@ virtio_dev_rx_async_submit(struct virtio_net *dev, uint16_t queue_id,
 	if (count == 0)
 		goto out;
 
-	/* TODO: packed queue not implemented */
 	if (vq_is_packed(dev))
-		nb_tx = 0;
+		nb_tx = virtio_dev_rx_async_submit_packed(dev,
+				vq, queue_id, pkts, count, comp_pkts,
+				comp_count);
 	else
 		nb_tx = virtio_dev_rx_async_submit_split(dev,
 				vq, queue_id, pkts, count, comp_pkts,
#21 lore raw

Hi Maxime,

Quoted 44 lines

-----Original Message-----
From: Maxime Coquelin <[email protected]>
Sent: Tuesday, April 13, 2021 4:37 PM
To: Jiang, Cheng1 <[email protected]>; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>
Subject: Re: [PATCH v5 2/4] vhost: add support for packed ring in async vhost

lib/librte_vhost/vhost.c vhost_free_async_mem(struct vhost_virtqueue

Quoted 30 lines · Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  27 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 438 +++++++++++++++++++++++++++--
 4 files changed, 448 insertions(+), 25 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {  struct
 async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c index renamed from lib/librte_vhost/vhost.c +0 -0

a70fe01d8..8c9935c0f 100644

lib/librte_vhost/vhost.c +0 -0
@@ -342,15 +342,21 @@ vhost_free_async_mem(struct vhost_virtqueue

*vq)

Quoted 12 lines

lib/librte_vhost/vhost.c vhost_free_async_mem(struct vhost_virtqueue *vq) · line 349

Quoted 9 lines
lib/librte_vhost/vhost.c +5 -1
 {
 	if (vq->async_pkts_info)
 		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	if (vq->async_buffers_packed) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	}
+	if (vq->async_descs_split) {

You can remove the check, rte_free is safe with NULL pointers. You can do the same for the other ones in this function.

OK, it will be fixed.

Quoted 64 lines

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid,

Quoted 16 lines
lib/librte_vhost/vhost.c +3 -1
 		rte_free(vq->async_descs_split);
+		vq->async_descs_split = NULL;
+	}
+
 	if (vq->it_pool)
 		rte_free(vq->it_pool);
 	if (vq->vec_pool)
 		rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -1627,9 +1633,9 @@ int rte_vhost_async_channel_register(int vid,

uint16_t queue_id,

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id,

Quoted 7 lines
lib/librte_vhost/vhost.c +1 -2
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or

non-inorder mode "

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid,

Quoted 5 lines
lib/librte_vhost/vhost.c +1 -0
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1667,11 +1673,18 @@ int rte_vhost_async_channel_register(int vid,

uint16_t queue_id,

Quoted 11 lines
lib/librte_vhost/vhost.c +6 -1
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id,

Quoted 10 lines
lib/librte_vhost/vhost.c +4 -2
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
-		!vq->it_pool || !vq->vec_pool) {
+	}
+
+	if (!vq->async_buffers_packed || !vq->async_descs_split ||
+		!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
 		VHOST_LOG_CONFIG(ERR,
 				"async register failed: cannot allocate

memory for vq data "

Quoted 11 lines
lib/librte_vhost/vhost.h index renamed from lib/librte_vhost/vhost.h +0 -0

f628714c2..fe131ae8f 100644

lib/librte_vhost/vhost.h +2 -1
@@ -201,9 +201,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
Quoted 8 lines

lib/librte_vhost/vhost.h struct vhost_virtqueue { · line 209

Quoted 4 lines
lib/librte_vhost/vhost.h +3 -0
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;

Don't dupplicate variable names, async_desc_idx can be reused for packed ring. Also, they are representing the same thing, why use desc in one case and buffer in the other?

The main reason is that the unit of the packed used ring is buffer, which can contain many desc.
I think using desc_idx will cause ambiguity, but if you think that I should reuse the desc_idx, I have no problem with that.

Quoted 58 lines

lib/librte_vhost/vhost.h struct vhost_virtqueue { · line 211

Quoted 3 lines
lib/librte_vhost/vhost.h +1 -0
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;

Same remark here.

Quoted 8 lines
lib/librte_vhost/vhost.h +0 -0
 	/* vq async features */
 	bool		async_inorder;
(quoted diff) +0 -0

b/lib/librte_vhost/virtio_net.c index c43ab0093..410be9678 100644

lib/librte_vhost/virtio_net.c +0 -0
@@ -363,14 +363,14 @@

vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue

*vq,

lib/librte_vhost/virtio_net.c vhost_shadow_enqueue_single_packed(struct

Quoted 20 lines

}

static __rte_always_inline void

lib/librte_vhost/virtio_net.c +5 -5
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-				   struct vhost_virtqueue *vq,
-				   uint32_t len[],
-				   uint16_t id[],
-				   uint16_t count[],
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
 				   uint16_t num_buffers)
 {
 	uint16_t i;
+
 	for (i = 0; i < num_buffers; i++) {
 		/* enqueue shadow flush action aligned with batch num */
 		if (!vq->shadow_used_idx)
@@ -382,6 +382,17 @@ vhost_shadow_enqueue_single_packed(struct

virtio_net *dev,

lib/librte_vhost/virtio_net.c shadow_ring_store(struct vhost_virtqueue *vq,

Quoted 18 lines
lib/librte_vhost/virtio_net.c +11 -0
 		vq->shadow_aligned_idx += count[i];
 		vq->shadow_used_idx++;
 	}
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+				   struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
+				   uint16_t num_buffers)
+{
+	vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 	if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {
 		do_data_copy_enqueue(dev, vq);
@@ -1481,6 +1492,62 @@ shadow_ring_store(struct vhost_virtqueue *vq,

void *shadow_ring, void *d_ring,

Quoted 3 lines
lib/librte_vhost/virtio_net.c +0 -0
 	}
 }
 
Quoted 9 lines

lib/librte_vhost/virtio_net.c shadow_ring_store(struct vhost_virtqueue *vq, void *shadow_ring, void *d_ring, · line 1501

Quoted 7 lines
lib/librte_vhost/virtio_net.c +7 -0
+static __rte_always_inline void
+vhost_update_used_packed(struct vhost_virtqueue *vq,
+			struct vring_used_elem_packed *shadow_ring,
+			uint16_t count)
+{
+	if (count == 0)
+		return;

Move this after the variables declaration.

Sure.

Quoted 118 lines
Quoted 42 lines
lib/librte_vhost/virtio_net.c +41 -0
+
+	int i;
+	uint16_t used_idx = vq->last_used_idx;
+	uint16_t head_idx = vq->last_used_idx;
+	uint16_t head_flags = 0;
+
+	/* Split loop in two to save memory barriers */
+	for (i = 0; i < count; i++) {
+		vq->desc_packed[used_idx].id = shadow_ring[i].id;
+		vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+		used_idx += shadow_ring[i].count;
+		if (used_idx >= vq->size)
+			used_idx -= vq->size;
+	}
+
+	/* The ordering for storing desc flags needs to be enforced. */
+	rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+	for (i = 0; i < count; i++) {
+		uint16_t flags;
+
+		if (vq->shadow_used_packed[i].len)
+			flags = VRING_DESC_F_WRITE;
+		else
+			flags = 0;
+
+		if (vq->used_wrap_counter) {
+			flags |= VRING_DESC_F_USED;
+			flags |= VRING_DESC_F_AVAIL;
+		} else {
+			flags &= ~VRING_DESC_F_USED;
+			flags &= ~VRING_DESC_F_AVAIL;
+		}
+
+		if (i > 0) {
+			vq->desc_packed[vq->last_used_idx].flags = flags;
+
+		} else {
+			head_idx = vq->last_used_idx;
+			head_flags = flags;

lib/librte_vhost/virtio_net.c shadow_ring_store(struct vhost_virtqueue *vq, void *shadow_ring, void *d_ring,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +7 -0
+		}
+
+		vq_inc_last_used_packed(vq, shadow_ring[i].count);
+	}
+
+	vq->desc_packed[head_idx].flags = head_flags; }
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id, @@ -1656,6

+1723,294

Quoted 12 lines

@@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,

lib/librte_vhost/virtio_net.c +8 -0
 	return pkt_idx;
 }
 
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    struct buf_vector *buf_vec,
+			    uint16_t *nr_descs,
+			    uint16_t *nr_buffers,
+			    struct vring_packed_desc *async_descs,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it) {
+	uint16_t nr_vec = 0;
+	uint16_t avail_idx = vq->last_avail_idx;
+	uint16_t max_tries, tries = 0;
+	uint16_t buf_id = 0;
+	uint32_t len = 0;
+	uint16_t desc_count = 0;
+	uint32_t size = pkt->pkt_len + sizeof(struct

virtio_net_hdr_mrg_rxbuf);

Quoted 42 lines
lib/librte_vhost/virtio_net.c +42 -0
+	uint32_t buffer_len[vq->size];
+	uint16_t buffer_buf_id[vq->size];
+	uint16_t buffer_desc_count[vq->size];
+	*nr_buffers = 0;
+
+	if (rxvq_is_mergeable(dev))
+		max_tries = vq->size - 1;
+	else
+		max_tries = 1;
+
+	while (size > 0) {
+		/*
+		 * if we tried all available ring items, and still
+		 * can't get enough buf, it means something abnormal
+		 * happened.
+		 */
+		if (unlikely(++tries > max_tries))
+			return -1;
+
+		if (unlikely(fill_vec_buf_packed(dev, vq,
+						avail_idx, &desc_count,
+						buf_vec, &nr_vec,
+						&buf_id, &len,
+						VHOST_ACCESS_RW) < 0))
+			return -1;
+
+		len = RTE_MIN(len, size);
+		size -= len;
+
+		buffer_len[*nr_buffers] = len;
+		buffer_buf_id[*nr_buffers] = buf_id;
+		buffer_desc_count[*nr_buffers] = desc_count;
+		*nr_buffers += 1;
+
+		*nr_descs += desc_count;
+		avail_idx += desc_count;
+		if (avail_idx >= vq->size)
+			avail_idx -= vq->size;
+	}
+
+	if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,
+		src_iovec, dst_iovec, src_it, dst_it) < 0)
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1792

Quoted 6 lines
lib/librte_vhost/virtio_net.c +6 -0
+		return -1;
+	/* store descriptors for DMA */
+	if (avail_idx >= *nr_descs)
+		rte_memcpy(async_descs,
+			&vq->desc_packed[vq->last_avail_idx],
+			*nr_descs * sizeof(struct vring_packed_desc));

Please add brackets for the 'if' since there are for the 'else'.

Sure, sorry for that.

Quoted 67 lines
Quoted 21 lines
lib/librte_vhost/virtio_net.c +20 -0
+	else {
+		uint16_t nr_copy = vq->size - vq->last_avail_idx;
+		rte_memcpy(async_descs,
+			&vq->desc_packed[vq->last_avail_idx],
+			nr_copy * sizeof(struct vring_packed_desc));
+		rte_memcpy(async_descs + nr_copy,
+			vq->desc_packed, (*nr_descs - nr_copy) *
+			sizeof(struct vring_packed_desc));
+	}
+
+	vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,
+					   buffer_desc_count, *nr_buffers);
+
+	return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			    uint16_t *nr_descs, uint16_t *nr_buffers,
+			    struct vring_packed_desc *async_descs,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it) {
+	struct buf_vector buf_vec[BUF_VECTOR_MAX];
+	*nr_descs = 0;
+	*nr_buffers = 0;
+
+	if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,

buf_vec,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +12 -0
+						 nr_descs,
+						 nr_buffers,
+						 async_descs,
+						 src_iovec, dst_iovec,
+						 src_it, dst_it) < 0)) {
+		VHOST_LOG_DATA(DEBUG,
+				"(%d) failed to get enough desc from vring\n",
+				dev->vid);
+		return -1;
+	}
+
+	VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

Quoted 11 lines
lib/librte_vhost/virtio_net.c +11 -0
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + *nr_descs);
+
+	return 0;
+}
+
+static __rte_noinline uint32_t
+virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
+	struct vhost_virtqueue *vq, uint16_t queue_id,
+	struct rte_mbuf **pkts, uint32_t count,
+	struct rte_mbuf **comp_pkts, uint32_t *comp_count) {

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint16_t async_descs_idx = 0;
+	uint16_t num_buffers;
+	uint16_t num_desc;
+
+	struct rte_vhost_iov_iter *it_pool = vq->it_pool;
+	struct iovec *vec_pool = vq->vec_pool;
+	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
+	struct iovec *src_iovec = vec_pool;
+	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >>

1);

Quoted 14 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +12 -0
+	struct rte_vhost_iov_iter *src_it = it_pool;
+	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
+	uint16_t slot_idx = 0;
+	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
+	struct async_inflight_info *pkts_info = vq->async_pkts_info;
+	uint32_t n_pkts = 0, pkt_err = 0;
+	uint32_t num_async_pkts = 0, num_done_pkts = 0;
+	struct vring_packed_desc async_descs[vq->size];
+
+	rte_prefetch0(&vq->desc_packed[vq->last_avail_idx & (vq->size -
+1)]);

The size of the ring is not necessarily a power of two with packed ring.

For the size of the ring is not necessarily a power of two, so maybe I can use codes like
Indx % vq->size ?
I'm not sure if it's a good way to do that.

Quoted 16 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1880

Quoted 7 lines
lib/librte_vhost/virtio_net.c +6 -0
+
+	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
+		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq,
+						pkts[pkt_idx],
+						&num_desc, &num_buffers,
+

&async_descs[async_descs_idx],

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 7 lines
lib/librte_vhost/virtio_net.c +7 -0
+						&src_iovec[iovec_idx],
+						&dst_iovec[iovec_idx],
+						&src_it[it_idx],
+						&dst_it[it_idx]) < 0))
+			break;
+
+		VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

Quoted 7 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1886

Quoted 5 lines
lib/librte_vhost/virtio_net.c +5 -0
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + num_desc);
+
+		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
+			(vq->size - 1);

Same here.

Sure.

Quoted 28 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 6 lines
lib/librte_vhost/virtio_net.c +5 -0
+		if (src_it[it_idx].count) {
+			uint16_t from, to;
+
+			async_descs_idx += num_desc;
+			async_fill_desc(&tdes[pkt_burst_idx++],

&src_it[it_idx],

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+					&dst_it[it_idx]);
+			pkts_info[slot_idx].descs = num_desc;
+			pkts_info[slot_idx].nr_buffers = num_buffers;
+			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
+			num_async_pkts++;
+			iovec_idx += src_it[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += src_it[it_idx].nr_segs;
+

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			/**
+			 * recover shadow used ring and keep DMA-occupied
+			 * descriptors.
+			 */
+			from = vq->shadow_used_idx - num_buffers;
+			to = vq->async_packed_buffer_idx & (vq->size - 1);
+			shadow_ring_store(vq, vq->shadow_used_packed,
+					vq->async_buffers_packed,
+					from, to, num_buffers,
+					sizeof(struct

vring_used_elem_packed));

Quoted 6 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1915

Quoted 4 lines
lib/librte_vhost/virtio_net.c +4 -0
+
+			vq->async_packed_buffer_idx += num_buffers;
+			vq->shadow_used_idx -= num_buffers;
+		} else

Brackets needed.

Sure.

Quoted 53 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 11 lines
lib/librte_vhost/virtio_net.c +10 -0
+			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
+
+		vq_inc_last_avail_packed(vq, num_desc);
+
+		/*
+		 * conditions to trigger async device transfer:
+		 * - buffered packet number reaches transfer threshold
+		 * - unused async iov number is less than max vhost vector
+		 */
+		if (unlikely(pkt_burst_idx >=

VHOST_ASYNC_BATCH_THRESHOLD ||

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +12 -0
+			((VHOST_MAX_ASYNC_VEC >> 1) - segs_await <
+			BUF_VECTOR_MAX))) {
+			n_pkts = vq->async_ops.transfer_data(dev->vid,
+					queue_id, tdes, 0, pkt_burst_idx);
+			iovec_idx = 0;
+			it_idx = 0;
+			segs_await = 0;
+			vq->async_pkts_inflight_n += n_pkts;
+
+			if (unlikely(n_pkts < pkt_burst_idx)) {
+				/*
+				 * log error packets number here and do

actual

Quoted 28 lines
lib/librte_vhost/virtio_net.c +28 -0
+				 * error processing when applications poll
+				 * completion
+				 */
+				pkt_err = pkt_burst_idx - n_pkts;
+				pkt_burst_idx = 0;
+				pkt_idx++;
+				break;
+			}
+
+			pkt_burst_idx = 0;
+		}
+	}
+
+	if (pkt_burst_idx) {
+		n_pkts = vq->async_ops.transfer_data(dev->vid,
+				queue_id, tdes, 0, pkt_burst_idx);
+		vq->async_pkts_inflight_n += n_pkts;
+
+		if (unlikely(n_pkts < pkt_burst_idx))
+			pkt_err = pkt_burst_idx - n_pkts;
+	}
+
+	do_data_copy_enqueue(dev, vq);
+
+	if (unlikely(pkt_err)) {
+		uint16_t descs_err = 0;
+		uint16_t buffers_err = 0;
+
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1971

Quoted 6 lines
lib/librte_vhost/virtio_net.c +6 -0
+		num_async_pkts -= pkt_err;
+		pkt_idx -= pkt_err;
+	/* calculate the sum of buffers and descs of DMA-error packets. */
+		while (pkt_err-- > 0) {
+			descs_err +=
+				pkts_info[slot_idx & (vq->size - 1)].descs;

The size of the ring is not necessarily a power of two with packed ring.

Will be fixed.

Quoted 6 lines

lib/librte_vhost/virtio_net.c

Quoted 3 lines
lib/librte_vhost/virtio_net.c +2 -0
+			buffers_err +=
+				pkts_info[slot_idx & (vq->size -

1)].nr_buffers;

Ditto.

Will be fixed.

Quoted 27 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +11 -0
+			slot_idx--;
+		}
+
+		vq->async_packed_buffer_idx -= buffers_err;
+
+		if (vq->last_avail_idx >= descs_err) {
+			vq->last_avail_idx -= descs_err;
+
+			rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+				&async_descs[async_descs_idx - descs_err],
+				descs_err * sizeof(struct

vring_packed_desc));

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 13 lines
lib/librte_vhost/virtio_net.c +13 -0
+		} else {
+			uint16_t nr_copy;
+
+			vq->last_avail_idx = vq->last_avail_idx + vq->size
+						- descs_err;
+			nr_copy = vq->size - vq->last_avail_idx;
+			rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+				&async_descs[async_descs_idx - descs_err],
+				nr_copy * sizeof(struct vring_packed_desc));
+			descs_err -= nr_copy;
+			rte_memcpy(vq->desc_packed,
+				&async_descs[async_descs_idx - descs_err],
+				descs_err * sizeof(struct

vring_packed_desc));

Quoted 7 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 2002

Quoted 5 lines
lib/librte_vhost/virtio_net.c +5 -0
+			vq->avail_wrap_counter ^= 1;
+		}
+
+		num_done_pkts = pkt_idx - num_async_pkts;
+	}

This error handling could be moved in a dedicated function.

Sure, will fix it in the next version.

Quoted 14 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 2012

Quoted 11 lines
lib/librte_vhost/virtio_net.c +10 -0
+	vq->async_pkts_idx += num_async_pkts;
+	*comp_count = num_done_pkts;
+
+	if (likely(vq->shadow_used_idx)) {
+		vhost_flush_enqueue_shadow_packed(dev, vq);
+		vhost_vring_call_packed(dev, vq);
+	}
+
+	return pkt_idx;
+}

Above function is very big and complex, it should be possible to split it in several ones to make it maintainable.

I think move the error handling code will make it smaller.

Thanks.
Cheng

Quoted 10 lines
Quoted 6 lines

+
static __rte_always_inline void write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t
n_descs) { @@ -1693,12 +2048,40 @@ write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t

n_descs)

Quoted 3 lines

} while (nr_left > 0);
}

Quoted 13 lines

lib/librte_vhost/virtio_net.c write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs) · line 2061

Quoted 11 lines
lib/librte_vhost/virtio_net.c +11 -0
+static __rte_always_inline void
+write_back_completed_descs_packed(struct vhost_virtqueue *vq,
+				uint16_t n_buffers)
+{
+	uint16_t nr_left = n_buffers;
+	uint16_t from, to;
+
+	do {
+		from = vq->last_async_buffer_idx &
+						(vq->size - 1);
+		to = (from + nr_left) & (vq->size - 1);

The size of the ring is not necessarily a power of two with packed ring.

Sure.

Quoted 32 lines

lib/librte_vhost/virtio_net.c uint16_t

Quoted 28 lines
lib/librte_vhost/virtio_net.c +18 -1
+		if (to > from) {
+			vhost_update_used_packed(vq,
+				vq->async_buffers_packed + from,
+				to - from);
+			vq->last_async_buffer_idx += nr_left;
+			nr_left = 0;
+		} else {
+			vhost_update_used_packed(vq,
+				vq->async_buffers_packed + from,
+				vq->size - from);
+			vq->last_async_buffer_idx +=
+						vq->size - from;
+			nr_left -= vq->size - from;
+		}
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)  {
 	struct virtio_net *dev = get_device(vid);
 	struct vhost_virtqueue *vq;
-	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0;
+	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0, n_buffers = 0;
 	uint16_t start_idx, pkts_idx, vq_size;
 	struct async_inflight_info *pkts_info;
 	uint16_t from, i;
@@ -1740,21 +2123,41 @@ uint16_t

rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,

Quoted 3 lines
lib/librte_vhost/virtio_net.c +0 -0
 		goto done;
 	}
 
Quoted 9 lines

lib/librte_vhost/virtio_net.c uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id, · line 2128

Quoted 7 lines
lib/librte_vhost/virtio_net.c +3 -4
-	for (i = 0; i < n_pkts_put; i++) {
-		from = (start_idx + i) & (vq_size - 1);
-		n_descs += pkts_info[from].descs;
-		pkts[i] = pkts_info[from].mbuf;
+	if (vq_is_packed(dev)) {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);

Unlike split ring, packed ring size is not necessarily a power of 2.

Sure.
Thanks.

Quoted 58 lines
Quoted 9 lines
lib/librte_vhost/virtio_net.c +8 -0
+			n_buffers += pkts_info[from].nr_buffers;
+			pkts[i] = pkts_info[from].mbuf;
+		}
+	} else {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);
+			n_descs += pkts_info[from].descs;
+			pkts[i] = pkts_info[from].mbuf;

lib/librte_vhost/virtio_net.c uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +4 -1
+		}
 	}
+
 	vq->async_last_pkts_n = n_pkts_cpl - n_pkts_put;
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		write_back_completed_descs_split(vq, n_descs);
+		if (vq_is_packed(dev)) {
+			write_back_completed_descs_packed(vq,

n_buffers);

lib/librte_vhost/virtio_net.c uint16_t · line 1753

Quoted 2 lines
lib/librte_vhost/virtio_net.c +0 -1
-		__atomic_add_fetch(&vq->used->idx, n_descs,

__ATOMIC_RELEASE);

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit(struct virtio_net

Quoted 21 lines
lib/librte_vhost/virtio_net.c +14 -3
-		vhost_vring_call_split(dev, vq);
-	} else
-		vq->last_async_desc_idx += n_descs;
+			vhost_vring_call_packed(dev, vq);
+		} else {
+			write_back_completed_descs_split(vq, n_descs);
+
+			__atomic_add_fetch(&vq->used->idx, n_descs,
+					__ATOMIC_RELEASE);
+			vhost_vring_call_split(dev, vq);
+		}
+	} else {
+		if (vq_is_packed(dev))
+			vq->last_async_buffer_idx += n_buffers;
+		else
+			vq->last_async_desc_idx += n_descs;
+	}
 
 done:
 	rte_spinlock_unlock(&vq->access_lock);
@@ -1795,9 +2198,10 @@ virtio_dev_rx_async_submit(struct virtio_net

*dev, uint16_t queue_id,

Quoted 13 lines
lib/librte_vhost/virtio_net.c +3 -2
 	if (count == 0)
 		goto out;
 
-	/* TODO: packed queue not implemented */
 	if (vq_is_packed(dev))
-		nb_tx = 0;
+		nb_tx = virtio_dev_rx_async_submit_packed(dev,
+				vq, queue_id, pkts, count, comp_pkts,
+				comp_count);
 	else
 		nb_tx = virtio_dev_rx_async_submit_split(dev,
 				vq, queue_id, pkts, count, comp_pkts,
#22 lore raw
Quoted 134 lines · On 4/13/21 1:48 PM, Jiang, Cheng1 wrote:

Hi Maxime,

Quoted 44 lines

-----Original Message-----
From: Maxime Coquelin <[email protected]>
Sent: Tuesday, April 13, 2021 4:37 PM
To: Jiang, Cheng1 <[email protected]>; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>
Subject: Re: [PATCH v5 2/4] vhost: add support for packed ring in async vhost

lib/librte_vhost/vhost.c vhost_free_async_mem(struct vhost_virtqueue

Quoted 30 lines · Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  27 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 438 +++++++++++++++++++++++++++--
 4 files changed, 448 insertions(+), 25 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {  struct
 async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c index renamed from lib/librte_vhost/vhost.c +0 -0

a70fe01d8..8c9935c0f 100644

lib/librte_vhost/vhost.c +0 -0
@@ -342,15 +342,21 @@ vhost_free_async_mem(struct vhost_virtqueue

*vq)

Quoted 12 lines

lib/librte_vhost/vhost.c vhost_free_async_mem(struct vhost_virtqueue *vq) · line 349

Quoted 9 lines
lib/librte_vhost/vhost.c +5 -1
 {
 	if (vq->async_pkts_info)
 		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	if (vq->async_buffers_packed) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	}
+	if (vq->async_descs_split) {

You can remove the check, rte_free is safe with NULL pointers. You can do the same for the other ones in this function.

OK, it will be fixed.

Quoted 64 lines

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid,

Quoted 16 lines
lib/librte_vhost/vhost.c +3 -1
 		rte_free(vq->async_descs_split);
+		vq->async_descs_split = NULL;
+	}
+
 	if (vq->it_pool)
 		rte_free(vq->it_pool);
 	if (vq->vec_pool)
 		rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -1627,9 +1633,9 @@ int rte_vhost_async_channel_register(int vid,

uint16_t queue_id,

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id,

Quoted 7 lines
lib/librte_vhost/vhost.c +1 -2
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or

non-inorder mode "

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid,

Quoted 5 lines
lib/librte_vhost/vhost.c +1 -0
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1667,11 +1673,18 @@ int rte_vhost_async_channel_register(int vid,

uint16_t queue_id,

Quoted 11 lines
lib/librte_vhost/vhost.c +6 -1
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id,

Quoted 10 lines
lib/librte_vhost/vhost.c +4 -2
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
-		!vq->it_pool || !vq->vec_pool) {
+	}
+
+	if (!vq->async_buffers_packed || !vq->async_descs_split ||
+		!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
 		VHOST_LOG_CONFIG(ERR,
 				"async register failed: cannot allocate

memory for vq data "

Quoted 11 lines
lib/librte_vhost/vhost.h index renamed from lib/librte_vhost/vhost.h +0 -0

f628714c2..fe131ae8f 100644

lib/librte_vhost/vhost.h +2 -1
@@ -201,9 +201,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
Quoted 8 lines

lib/librte_vhost/vhost.h struct vhost_virtqueue { · line 209

Quoted 4 lines
lib/librte_vhost/vhost.h +3 -0
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;

Don't dupplicate variable names, async_desc_idx can be reused for packed ring. Also, they are representing the same thing, why use desc in one case and buffer in the other?

Quoted 2 lines

The main reason is that the unit of the packed used ring is buffer, which can contain many desc.
I think using desc_idx will cause ambiguity, but if you think that I should reuse the desc_idx, I have no problem with that.

OK, in this case please use a union not to waste memory.

Quoted 281 lines
Quoted 58 lines

lib/librte_vhost/vhost.h struct vhost_virtqueue { · line 211

Quoted 3 lines
lib/librte_vhost/vhost.h +1 -0
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;

Same remark here.

Quoted 8 lines
lib/librte_vhost/vhost.h +0 -0
 	/* vq async features */
 	bool		async_inorder;
(quoted diff) +0 -0

b/lib/librte_vhost/virtio_net.c index c43ab0093..410be9678 100644

lib/librte_vhost/virtio_net.c +0 -0
@@ -363,14 +363,14 @@

vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue

*vq,

lib/librte_vhost/virtio_net.c vhost_shadow_enqueue_single_packed(struct

Quoted 20 lines

}

static __rte_always_inline void

lib/librte_vhost/virtio_net.c +5 -5
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-				   struct vhost_virtqueue *vq,
-				   uint32_t len[],
-				   uint16_t id[],
-				   uint16_t count[],
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
 				   uint16_t num_buffers)
 {
 	uint16_t i;
+
 	for (i = 0; i < num_buffers; i++) {
 		/* enqueue shadow flush action aligned with batch num */
 		if (!vq->shadow_used_idx)
@@ -382,6 +382,17 @@ vhost_shadow_enqueue_single_packed(struct

virtio_net *dev,

lib/librte_vhost/virtio_net.c shadow_ring_store(struct vhost_virtqueue *vq,

Quoted 18 lines
lib/librte_vhost/virtio_net.c +11 -0
 		vq->shadow_aligned_idx += count[i];
 		vq->shadow_used_idx++;
 	}
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+				   struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
+				   uint16_t num_buffers)
+{
+	vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 	if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {
 		do_data_copy_enqueue(dev, vq);
@@ -1481,6 +1492,62 @@ shadow_ring_store(struct vhost_virtqueue *vq,

void *shadow_ring, void *d_ring,

Quoted 3 lines
lib/librte_vhost/virtio_net.c +0 -0
 	}
 }
 
Quoted 9 lines

lib/librte_vhost/virtio_net.c shadow_ring_store(struct vhost_virtqueue *vq, void *shadow_ring, void *d_ring, · line 1501

Quoted 7 lines
lib/librte_vhost/virtio_net.c +7 -0
+static __rte_always_inline void
+vhost_update_used_packed(struct vhost_virtqueue *vq,
+			struct vring_used_elem_packed *shadow_ring,
+			uint16_t count)
+{
+	if (count == 0)
+		return;

Move this after the variables declaration.

Sure.

Quoted 118 lines
Quoted 42 lines
lib/librte_vhost/virtio_net.c +41 -0
+
+	int i;
+	uint16_t used_idx = vq->last_used_idx;
+	uint16_t head_idx = vq->last_used_idx;
+	uint16_t head_flags = 0;
+
+	/* Split loop in two to save memory barriers */
+	for (i = 0; i < count; i++) {
+		vq->desc_packed[used_idx].id = shadow_ring[i].id;
+		vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+		used_idx += shadow_ring[i].count;
+		if (used_idx >= vq->size)
+			used_idx -= vq->size;
+	}
+
+	/* The ordering for storing desc flags needs to be enforced. */
+	rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+	for (i = 0; i < count; i++) {
+		uint16_t flags;
+
+		if (vq->shadow_used_packed[i].len)
+			flags = VRING_DESC_F_WRITE;
+		else
+			flags = 0;
+
+		if (vq->used_wrap_counter) {
+			flags |= VRING_DESC_F_USED;
+			flags |= VRING_DESC_F_AVAIL;
+		} else {
+			flags &= ~VRING_DESC_F_USED;
+			flags &= ~VRING_DESC_F_AVAIL;
+		}
+
+		if (i > 0) {
+			vq->desc_packed[vq->last_used_idx].flags = flags;
+
+		} else {
+			head_idx = vq->last_used_idx;
+			head_flags = flags;

lib/librte_vhost/virtio_net.c shadow_ring_store(struct vhost_virtqueue *vq, void *shadow_ring, void *d_ring,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +7 -0
+		}
+
+		vq_inc_last_used_packed(vq, shadow_ring[i].count);
+	}
+
+	vq->desc_packed[head_idx].flags = head_flags; }
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id, @@ -1656,6

+1723,294

Quoted 12 lines

@@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,

lib/librte_vhost/virtio_net.c +8 -0
 	return pkt_idx;
 }
 
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    struct buf_vector *buf_vec,
+			    uint16_t *nr_descs,
+			    uint16_t *nr_buffers,
+			    struct vring_packed_desc *async_descs,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it) {
+	uint16_t nr_vec = 0;
+	uint16_t avail_idx = vq->last_avail_idx;
+	uint16_t max_tries, tries = 0;
+	uint16_t buf_id = 0;
+	uint32_t len = 0;
+	uint16_t desc_count = 0;
+	uint32_t size = pkt->pkt_len + sizeof(struct

virtio_net_hdr_mrg_rxbuf);

Quoted 42 lines
lib/librte_vhost/virtio_net.c +42 -0
+	uint32_t buffer_len[vq->size];
+	uint16_t buffer_buf_id[vq->size];
+	uint16_t buffer_desc_count[vq->size];
+	*nr_buffers = 0;
+
+	if (rxvq_is_mergeable(dev))
+		max_tries = vq->size - 1;
+	else
+		max_tries = 1;
+
+	while (size > 0) {
+		/*
+		 * if we tried all available ring items, and still
+		 * can't get enough buf, it means something abnormal
+		 * happened.
+		 */
+		if (unlikely(++tries > max_tries))
+			return -1;
+
+		if (unlikely(fill_vec_buf_packed(dev, vq,
+						avail_idx, &desc_count,
+						buf_vec, &nr_vec,
+						&buf_id, &len,
+						VHOST_ACCESS_RW) < 0))
+			return -1;
+
+		len = RTE_MIN(len, size);
+		size -= len;
+
+		buffer_len[*nr_buffers] = len;
+		buffer_buf_id[*nr_buffers] = buf_id;
+		buffer_desc_count[*nr_buffers] = desc_count;
+		*nr_buffers += 1;
+
+		*nr_descs += desc_count;
+		avail_idx += desc_count;
+		if (avail_idx >= vq->size)
+			avail_idx -= vq->size;
+	}
+
+	if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,
+		src_iovec, dst_iovec, src_it, dst_it) < 0)
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1792

Quoted 6 lines
lib/librte_vhost/virtio_net.c +6 -0
+		return -1;
+	/* store descriptors for DMA */
+	if (avail_idx >= *nr_descs)
+		rte_memcpy(async_descs,
+			&vq->desc_packed[vq->last_avail_idx],
+			*nr_descs * sizeof(struct vring_packed_desc));

Please add brackets for the 'if' since there are for the 'else'.

Sure, sorry for that.

Quoted 67 lines
Quoted 21 lines
lib/librte_vhost/virtio_net.c +20 -0
+	else {
+		uint16_t nr_copy = vq->size - vq->last_avail_idx;
+		rte_memcpy(async_descs,
+			&vq->desc_packed[vq->last_avail_idx],
+			nr_copy * sizeof(struct vring_packed_desc));
+		rte_memcpy(async_descs + nr_copy,
+			vq->desc_packed, (*nr_descs - nr_copy) *
+			sizeof(struct vring_packed_desc));
+	}
+
+	vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,
+					   buffer_desc_count, *nr_buffers);
+
+	return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			    uint16_t *nr_descs, uint16_t *nr_buffers,
+			    struct vring_packed_desc *async_descs,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it) {
+	struct buf_vector buf_vec[BUF_VECTOR_MAX];
+	*nr_descs = 0;
+	*nr_buffers = 0;
+
+	if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,

buf_vec,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +12 -0
+						 nr_descs,
+						 nr_buffers,
+						 async_descs,
+						 src_iovec, dst_iovec,
+						 src_it, dst_it) < 0)) {
+		VHOST_LOG_DATA(DEBUG,
+				"(%d) failed to get enough desc from vring\n",
+				dev->vid);
+		return -1;
+	}
+
+	VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

Quoted 11 lines
lib/librte_vhost/virtio_net.c +11 -0
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + *nr_descs);
+
+	return 0;
+}
+
+static __rte_noinline uint32_t
+virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
+	struct vhost_virtqueue *vq, uint16_t queue_id,
+	struct rte_mbuf **pkts, uint32_t count,
+	struct rte_mbuf **comp_pkts, uint32_t *comp_count) {

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint16_t async_descs_idx = 0;
+	uint16_t num_buffers;
+	uint16_t num_desc;
+
+	struct rte_vhost_iov_iter *it_pool = vq->it_pool;
+	struct iovec *vec_pool = vq->vec_pool;
+	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
+	struct iovec *src_iovec = vec_pool;
+	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >>

1);

Quoted 14 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +12 -0
+	struct rte_vhost_iov_iter *src_it = it_pool;
+	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
+	uint16_t slot_idx = 0;
+	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
+	struct async_inflight_info *pkts_info = vq->async_pkts_info;
+	uint32_t n_pkts = 0, pkt_err = 0;
+	uint32_t num_async_pkts = 0, num_done_pkts = 0;
+	struct vring_packed_desc async_descs[vq->size];
+
+	rte_prefetch0(&vq->desc_packed[vq->last_avail_idx & (vq->size -
+1)]);

The size of the ring is not necessarily a power of two with packed ring.

Quoted 4 lines

For the size of the ring is not necessarily a power of two, so maybe I can use codes like
Indx % vq->size ?
I'm not sure if it's a good way to do that.

In this case it is OK.

Quoted 323 lines
Quoted 16 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1880

Quoted 7 lines
lib/librte_vhost/virtio_net.c +6 -0
+
+	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
+		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq,
+						pkts[pkt_idx],
+						&num_desc, &num_buffers,
+

&async_descs[async_descs_idx],

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 7 lines
lib/librte_vhost/virtio_net.c +7 -0
+						&src_iovec[iovec_idx],
+						&dst_iovec[iovec_idx],
+						&src_it[it_idx],
+						&dst_it[it_idx]) < 0))
+			break;
+
+		VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

Quoted 7 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1886

Quoted 5 lines
lib/librte_vhost/virtio_net.c +5 -0
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + num_desc);
+
+		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
+			(vq->size - 1);

Same here.

Sure.

Quoted 28 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 6 lines
lib/librte_vhost/virtio_net.c +5 -0
+		if (src_it[it_idx].count) {
+			uint16_t from, to;
+
+			async_descs_idx += num_desc;
+			async_fill_desc(&tdes[pkt_burst_idx++],

&src_it[it_idx],

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+					&dst_it[it_idx]);
+			pkts_info[slot_idx].descs = num_desc;
+			pkts_info[slot_idx].nr_buffers = num_buffers;
+			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
+			num_async_pkts++;
+			iovec_idx += src_it[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += src_it[it_idx].nr_segs;
+

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			/**
+			 * recover shadow used ring and keep DMA-occupied
+			 * descriptors.
+			 */
+			from = vq->shadow_used_idx - num_buffers;
+			to = vq->async_packed_buffer_idx & (vq->size - 1);
+			shadow_ring_store(vq, vq->shadow_used_packed,
+					vq->async_buffers_packed,
+					from, to, num_buffers,
+					sizeof(struct

vring_used_elem_packed));

Quoted 6 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1915

Quoted 4 lines
lib/librte_vhost/virtio_net.c +4 -0
+
+			vq->async_packed_buffer_idx += num_buffers;
+			vq->shadow_used_idx -= num_buffers;
+		} else

Brackets needed.

Sure.

Quoted 53 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 11 lines
lib/librte_vhost/virtio_net.c +10 -0
+			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
+
+		vq_inc_last_avail_packed(vq, num_desc);
+
+		/*
+		 * conditions to trigger async device transfer:
+		 * - buffered packet number reaches transfer threshold
+		 * - unused async iov number is less than max vhost vector
+		 */
+		if (unlikely(pkt_burst_idx >=

VHOST_ASYNC_BATCH_THRESHOLD ||

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +12 -0
+			((VHOST_MAX_ASYNC_VEC >> 1) - segs_await <
+			BUF_VECTOR_MAX))) {
+			n_pkts = vq->async_ops.transfer_data(dev->vid,
+					queue_id, tdes, 0, pkt_burst_idx);
+			iovec_idx = 0;
+			it_idx = 0;
+			segs_await = 0;
+			vq->async_pkts_inflight_n += n_pkts;
+
+			if (unlikely(n_pkts < pkt_burst_idx)) {
+				/*
+				 * log error packets number here and do

actual

Quoted 28 lines
lib/librte_vhost/virtio_net.c +28 -0
+				 * error processing when applications poll
+				 * completion
+				 */
+				pkt_err = pkt_burst_idx - n_pkts;
+				pkt_burst_idx = 0;
+				pkt_idx++;
+				break;
+			}
+
+			pkt_burst_idx = 0;
+		}
+	}
+
+	if (pkt_burst_idx) {
+		n_pkts = vq->async_ops.transfer_data(dev->vid,
+				queue_id, tdes, 0, pkt_burst_idx);
+		vq->async_pkts_inflight_n += n_pkts;
+
+		if (unlikely(n_pkts < pkt_burst_idx))
+			pkt_err = pkt_burst_idx - n_pkts;
+	}
+
+	do_data_copy_enqueue(dev, vq);
+
+	if (unlikely(pkt_err)) {
+		uint16_t descs_err = 0;
+		uint16_t buffers_err = 0;
+
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1971

Quoted 6 lines
lib/librte_vhost/virtio_net.c +6 -0
+		num_async_pkts -= pkt_err;
+		pkt_idx -= pkt_err;
+	/* calculate the sum of buffers and descs of DMA-error packets. */
+		while (pkt_err-- > 0) {
+			descs_err +=
+				pkts_info[slot_idx & (vq->size - 1)].descs;

The size of the ring is not necessarily a power of two with packed ring.

Will be fixed.

Quoted 6 lines

lib/librte_vhost/virtio_net.c

Quoted 3 lines
lib/librte_vhost/virtio_net.c +2 -0
+			buffers_err +=
+				pkts_info[slot_idx & (vq->size -

1)].nr_buffers;

Ditto.

Will be fixed.

Quoted 27 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +11 -0
+			slot_idx--;
+		}
+
+		vq->async_packed_buffer_idx -= buffers_err;
+
+		if (vq->last_avail_idx >= descs_err) {
+			vq->last_avail_idx -= descs_err;
+
+			rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+				&async_descs[async_descs_idx - descs_err],
+				descs_err * sizeof(struct

vring_packed_desc));

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 13 lines
lib/librte_vhost/virtio_net.c +13 -0
+		} else {
+			uint16_t nr_copy;
+
+			vq->last_avail_idx = vq->last_avail_idx + vq->size
+						- descs_err;
+			nr_copy = vq->size - vq->last_avail_idx;
+			rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+				&async_descs[async_descs_idx - descs_err],
+				nr_copy * sizeof(struct vring_packed_desc));
+			descs_err -= nr_copy;
+			rte_memcpy(vq->desc_packed,
+				&async_descs[async_descs_idx - descs_err],
+				descs_err * sizeof(struct

vring_packed_desc));

Quoted 7 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 2002

Quoted 5 lines
lib/librte_vhost/virtio_net.c +5 -0
+			vq->avail_wrap_counter ^= 1;
+		}
+
+		num_done_pkts = pkt_idx - num_async_pkts;
+	}

This error handling could be moved in a dedicated function.

Sure, will fix it in the next version.

Quoted 14 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 2012

Quoted 11 lines
lib/librte_vhost/virtio_net.c +10 -0
+	vq->async_pkts_idx += num_async_pkts;
+	*comp_count = num_done_pkts;
+
+	if (likely(vq->shadow_used_idx)) {
+		vhost_flush_enqueue_shadow_packed(dev, vq);
+		vhost_vring_call_packed(dev, vq);
+	}
+
+	return pkt_idx;
+}

Above function is very big and complex, it should be possible to split it in several ones to make it maintainable.

I think move the error handling code will make it smaller.

Thanks.
Cheng

Quoted 10 lines
Quoted 6 lines

+
static __rte_always_inline void write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t
n_descs) { @@ -1693,12 +2048,40 @@ write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t

n_descs)

Quoted 3 lines

} while (nr_left > 0);
}

Quoted 13 lines

lib/librte_vhost/virtio_net.c write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs) · line 2061

Quoted 11 lines
lib/librte_vhost/virtio_net.c +11 -0
+static __rte_always_inline void
+write_back_completed_descs_packed(struct vhost_virtqueue *vq,
+				uint16_t n_buffers)
+{
+	uint16_t nr_left = n_buffers;
+	uint16_t from, to;
+
+	do {
+		from = vq->last_async_buffer_idx &
+						(vq->size - 1);
+		to = (from + nr_left) & (vq->size - 1);

The size of the ring is not necessarily a power of two with packed ring.

Sure.

Quoted 32 lines

lib/librte_vhost/virtio_net.c uint16_t

Quoted 28 lines
lib/librte_vhost/virtio_net.c +18 -1
+		if (to > from) {
+			vhost_update_used_packed(vq,
+				vq->async_buffers_packed + from,
+				to - from);
+			vq->last_async_buffer_idx += nr_left;
+			nr_left = 0;
+		} else {
+			vhost_update_used_packed(vq,
+				vq->async_buffers_packed + from,
+				vq->size - from);
+			vq->last_async_buffer_idx +=
+						vq->size - from;
+			nr_left -= vq->size - from;
+		}
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)  {
 	struct virtio_net *dev = get_device(vid);
 	struct vhost_virtqueue *vq;
-	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0;
+	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0, n_buffers = 0;
 	uint16_t start_idx, pkts_idx, vq_size;
 	struct async_inflight_info *pkts_info;
 	uint16_t from, i;
@@ -1740,21 +2123,41 @@ uint16_t

rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,

Quoted 3 lines
lib/librte_vhost/virtio_net.c +0 -0
 		goto done;
 	}
 
Quoted 9 lines

lib/librte_vhost/virtio_net.c uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id, · line 2128

Quoted 7 lines
lib/librte_vhost/virtio_net.c +3 -4
-	for (i = 0; i < n_pkts_put; i++) {
-		from = (start_idx + i) & (vq_size - 1);
-		n_descs += pkts_info[from].descs;
-		pkts[i] = pkts_info[from].mbuf;
+	if (vq_is_packed(dev)) {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);

Unlike split ring, packed ring size is not necessarily a power of 2.

Sure.
Thanks.

Quoted 58 lines
Quoted 9 lines
lib/librte_vhost/virtio_net.c +8 -0
+			n_buffers += pkts_info[from].nr_buffers;
+			pkts[i] = pkts_info[from].mbuf;
+		}
+	} else {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);
+			n_descs += pkts_info[from].descs;
+			pkts[i] = pkts_info[from].mbuf;

lib/librte_vhost/virtio_net.c uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +4 -1
+		}
 	}
+
 	vq->async_last_pkts_n = n_pkts_cpl - n_pkts_put;
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		write_back_completed_descs_split(vq, n_descs);
+		if (vq_is_packed(dev)) {
+			write_back_completed_descs_packed(vq,

n_buffers);

lib/librte_vhost/virtio_net.c uint16_t · line 1753

Quoted 2 lines
lib/librte_vhost/virtio_net.c +0 -1
-		__atomic_add_fetch(&vq->used->idx, n_descs,

__ATOMIC_RELEASE);

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit(struct virtio_net

Quoted 21 lines
lib/librte_vhost/virtio_net.c +14 -3
-		vhost_vring_call_split(dev, vq);
-	} else
-		vq->last_async_desc_idx += n_descs;
+			vhost_vring_call_packed(dev, vq);
+		} else {
+			write_back_completed_descs_split(vq, n_descs);
+
+			__atomic_add_fetch(&vq->used->idx, n_descs,
+					__ATOMIC_RELEASE);
+			vhost_vring_call_split(dev, vq);
+		}
+	} else {
+		if (vq_is_packed(dev))
+			vq->last_async_buffer_idx += n_buffers;
+		else
+			vq->last_async_desc_idx += n_descs;
+	}
 
 done:
 	rte_spinlock_unlock(&vq->access_lock);
@@ -1795,9 +2198,10 @@ virtio_dev_rx_async_submit(struct virtio_net

*dev, uint16_t queue_id,

Quoted 13 lines
lib/librte_vhost/virtio_net.c +3 -2
 	if (count == 0)
 		goto out;
 
-	/* TODO: packed queue not implemented */
 	if (vq_is_packed(dev))
-		nb_tx = 0;
+		nb_tx = virtio_dev_rx_async_submit_packed(dev,
+				vq, queue_id, pkts, count, comp_pkts,
+				comp_count);
 	else
 		nb_tx = virtio_dev_rx_async_submit_split(dev,
 				vq, queue_id, pkts, count, comp_pkts,
#23 lore raw
Quoted 153 lines

-----Original Message-----
From: Maxime Coquelin <[email protected]>
Sent: Tuesday, April 13, 2021 9:08 PM
To: Jiang, Cheng1 <[email protected]>; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>
Subject: Re: [PATCH v5 2/4] vhost: add support for packed ring in async vhost

Quoted 26 lines · On 4/13/21 1:48 PM, Jiang, Cheng1 wrote:

Hi Maxime,

Quoted 24 lines

-----Original Message-----
From: Maxime Coquelin <[email protected]>
Sent: Tuesday, April 13, 2021 4:37 PM
To: Jiang, Cheng1 <[email protected]>; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>
Subject: Re: [PATCH v5 2/4] vhost: add support for packed ring in async vhost

Quoted 10 lines · Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch enables packed ring in async vhost data path.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  27 +-
 lib/librte_vhost/vhost.h           |   7 +-
 lib/librte_vhost/virtio_net.c      | 438

+++++++++++++++++++++++++++--

Quoted 14 lines
Quoted 14 lines
Quoted 14 lines
 4 files changed, 448 insertions(+), 25 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {  struct
 async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
Quoted 6 lines
Quoted 6 lines

lib/librte_vhost/vhost.c vhost_free_async_mem(struct

Quoted 6 lines
lib/librte_vhost/rte_vhost_async.h +0 -0
 /**
lib/librte_vhost/vhost.c +0 -0
index a70fe01d8..8c9935c0f 100644
@@ -342,15 +342,21 @@ vhost_free_async_mem(struct

vhost_virtqueue

Quoted 90 lines
Quoted 13 lines

*vq)

lib/librte_vhost/vhost.c vhost_free_async_mem(struct vhost_virtqueue *vq) · line 349

Quoted 9 lines
lib/librte_vhost/vhost.c +5 -1
 {
 	if (vq->async_pkts_info)
 		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	if (vq->async_buffers_packed) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	}
+	if (vq->async_descs_split) {

You can remove the check, rte_free is safe with NULL pointers. You can do the same for the other ones in this function.

OK, it will be fixed.

Quoted 73 lines

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid,

Quoted 16 lines
lib/librte_vhost/vhost.c +3 -1
 		rte_free(vq->async_descs_split);
+		vq->async_descs_split = NULL;
+	}
+
 	if (vq->it_pool)
 		rte_free(vq->it_pool);
 	if (vq->vec_pool)
 		rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -1627,9 +1633,9 @@ int rte_vhost_async_channel_register(int vid,

uint16_t queue_id,

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id,

Quoted 7 lines
lib/librte_vhost/vhost.c +1 -2
 		return -1;
 
 	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or

non-inorder mode "

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int · line 1673

Quoted 6 lines
lib/librte_vhost/vhost.c +1 -0
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1667,11 +1673,18 @@ int rte_vhost_async_channel_register(int
 vid,

uint16_t queue_id,

Quoted 20 lines
lib/librte_vhost/vhost.c +10 -3
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
-		!vq->it_pool || !vq->vec_pool) {
+	}
+
+	if (!vq->async_buffers_packed || !vq->async_descs_split ||
+		!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
 		VHOST_LOG_CONFIG(ERR,
Quoted 1 line

"async register failed: cannot allocate

memory for vq data "

Quoted 5 lines
lib/librte_vhost/vhost.h +0 -0
index f628714c2..fe131ae8f 100644

lib/librte_vhost/vhost.h struct vhost_virtqueue { · line 209

Quoted 10 lines
lib/librte_vhost/vhost.h +5 -1
@@ -201,9 +201,14 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
 	uint16_t async_desc_idx;
+	uint16_t async_packed_buffer_idx;

Don't dupplicate variable names, async_desc_idx can be reused for packed ring. Also, they are representing the same thing, why use desc in one case and buffer in the other?

Quoted 6 lines
Quoted 1 line

The main reason is that the unit of the packed used ring is buffer, which can

contain many desc.

Quoted 1 line

I think using desc_idx will cause ambiguity, but if you think that I should

reuse the desc_idx, I have no problem with that.

OK, in this case please use a union not to waste memory.

Sure, I'll fix it.

Quoted 284 lines
Quoted 7 lines
Quoted 7 lines

lib/librte_vhost/vhost.h struct vhost_virtqueue { · line 211

Quoted 4 lines
lib/librte_vhost/vhost.h +1 -0
 	uint16_t last_async_desc_idx;
+	uint16_t last_async_buffer_idx;

Same remark here.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
lib/librte_vhost/vhost.h +0 -0
 	/* vq async features */
 	bool		async_inorder;
(quoted diff) +0 -0

b/lib/librte_vhost/virtio_net.c index c43ab0093..410be9678 100644

lib/librte_vhost/virtio_net.c +0 -0
@@ -363,14 +363,14 @@

vhost_shadow_dequeue_single_packed_inorder(struct

vhost_virtqueue

Quoted 37 lines
Quoted 37 lines

*vq,

lib/librte_vhost/virtio_net.c vhost_shadow_enqueue_single_packed(struct

Quoted 20 lines

}

static __rte_always_inline void

lib/librte_vhost/virtio_net.c +5 -5
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-				   struct vhost_virtqueue *vq,
-				   uint32_t len[],
-				   uint16_t id[],
-				   uint16_t count[],
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
 				   uint16_t num_buffers)
 {
 	uint16_t i;
+
 	for (i = 0; i < num_buffers; i++) {
 		/* enqueue shadow flush action aligned with batch num */
 		if (!vq->shadow_used_idx)
@@ -382,6 +382,17 @@ vhost_shadow_enqueue_single_packed(struct

virtio_net *dev,

Quoted 15 lines
lib/librte_vhost/virtio_net.c +11 -0
 		vq->shadow_aligned_idx += count[i];
 		vq->shadow_used_idx++;
 	}
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+				   struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
+				   uint16_t num_buffers)
+{
+	vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
Quoted 3 lines
Quoted 3 lines

lib/librte_vhost/virtio_net.c shadow_ring_store(struct vhost_virtqueue

Quoted 3 lines
lib/librte_vhost/virtio_net.c +0 -0
 	if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {
 		do_data_copy_enqueue(dev, vq);
@@ -1481,6 +1492,62 @@ shadow_ring_store(struct vhost_virtqueue

*vq,

Quoted 227 lines
Quoted 4 lines

void *shadow_ring, void *d_ring,

Quoted 3 lines
lib/librte_vhost/virtio_net.c +0 -0
 	}
 }
 
Quoted 9 lines

lib/librte_vhost/virtio_net.c shadow_ring_store(struct vhost_virtqueue *vq, void *shadow_ring, void *d_ring, · line 1501

Quoted 7 lines
lib/librte_vhost/virtio_net.c +7 -0
+static __rte_always_inline void
+vhost_update_used_packed(struct vhost_virtqueue *vq,
+			struct vring_used_elem_packed *shadow_ring,
+			uint16_t count)
+{
+	if (count == 0)
+		return;

Move this after the variables declaration.

Sure.

Quoted 118 lines
Quoted 42 lines
lib/librte_vhost/virtio_net.c +41 -0
+
+	int i;
+	uint16_t used_idx = vq->last_used_idx;
+	uint16_t head_idx = vq->last_used_idx;
+	uint16_t head_flags = 0;
+
+	/* Split loop in two to save memory barriers */
+	for (i = 0; i < count; i++) {
+		vq->desc_packed[used_idx].id = shadow_ring[i].id;
+		vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+		used_idx += shadow_ring[i].count;
+		if (used_idx >= vq->size)
+			used_idx -= vq->size;
+	}
+
+	/* The ordering for storing desc flags needs to be enforced. */
+	rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+	for (i = 0; i < count; i++) {
+		uint16_t flags;
+
+		if (vq->shadow_used_packed[i].len)
+			flags = VRING_DESC_F_WRITE;
+		else
+			flags = 0;
+
+		if (vq->used_wrap_counter) {
+			flags |= VRING_DESC_F_USED;
+			flags |= VRING_DESC_F_AVAIL;
+		} else {
+			flags &= ~VRING_DESC_F_USED;
+			flags &= ~VRING_DESC_F_AVAIL;
+		}
+
+		if (i > 0) {
+			vq->desc_packed[vq->last_used_idx].flags = flags;
+
+		} else {
+			head_idx = vq->last_used_idx;
+			head_flags = flags;

lib/librte_vhost/virtio_net.c shadow_ring_store(struct vhost_virtqueue *vq, void *shadow_ring, void *d_ring,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +7 -0
+		}
+
+		vq_inc_last_used_packed(vq, shadow_ring[i].count);
+	}
+
+	vq->desc_packed[head_idx].flags = head_flags; }
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id, @@ -1656,6

+1723,294

Quoted 12 lines

@@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,

lib/librte_vhost/virtio_net.c +8 -0
 	return pkt_idx;
 }
 
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    struct buf_vector *buf_vec,
+			    uint16_t *nr_descs,
+			    uint16_t *nr_buffers,
+			    struct vring_packed_desc *async_descs,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it) {
+	uint16_t nr_vec = 0;
+	uint16_t avail_idx = vq->last_avail_idx;
+	uint16_t max_tries, tries = 0;
+	uint16_t buf_id = 0;
+	uint32_t len = 0;
+	uint16_t desc_count = 0;
+	uint32_t size = pkt->pkt_len + sizeof(struct

virtio_net_hdr_mrg_rxbuf);

Quoted 42 lines
lib/librte_vhost/virtio_net.c +42 -0
+	uint32_t buffer_len[vq->size];
+	uint16_t buffer_buf_id[vq->size];
+	uint16_t buffer_desc_count[vq->size];
+	*nr_buffers = 0;
+
+	if (rxvq_is_mergeable(dev))
+		max_tries = vq->size - 1;
+	else
+		max_tries = 1;
+
+	while (size > 0) {
+		/*
+		 * if we tried all available ring items, and still
+		 * can't get enough buf, it means something abnormal
+		 * happened.
+		 */
+		if (unlikely(++tries > max_tries))
+			return -1;
+
+		if (unlikely(fill_vec_buf_packed(dev, vq,
+						avail_idx, &desc_count,
+						buf_vec, &nr_vec,
+						&buf_id, &len,
+						VHOST_ACCESS_RW) < 0))
+			return -1;
+
+		len = RTE_MIN(len, size);
+		size -= len;
+
+		buffer_len[*nr_buffers] = len;
+		buffer_buf_id[*nr_buffers] = buf_id;
+		buffer_desc_count[*nr_buffers] = desc_count;
+		*nr_buffers += 1;
+
+		*nr_descs += desc_count;
+		avail_idx += desc_count;
+		if (avail_idx >= vq->size)
+			avail_idx -= vq->size;
+	}
+
+	if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,
+		src_iovec, dst_iovec, src_it, dst_it) < 0)
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1792

Quoted 6 lines
lib/librte_vhost/virtio_net.c +6 -0
+		return -1;
+	/* store descriptors for DMA */
+	if (avail_idx >= *nr_descs)
+		rte_memcpy(async_descs,
+			&vq->desc_packed[vq->last_avail_idx],
+			*nr_descs * sizeof(struct vring_packed_desc));

Please add brackets for the 'if' since there are for the 'else'.

Sure, sorry for that.

Quoted 81 lines
Quoted 21 lines
lib/librte_vhost/virtio_net.c +20 -0
+	else {
+		uint16_t nr_copy = vq->size - vq->last_avail_idx;
+		rte_memcpy(async_descs,
+			&vq->desc_packed[vq->last_avail_idx],
+			nr_copy * sizeof(struct vring_packed_desc));
+		rte_memcpy(async_descs + nr_copy,
+			vq->desc_packed, (*nr_descs - nr_copy) *
+			sizeof(struct vring_packed_desc));
+	}
+
+	vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,
+					   buffer_desc_count, *nr_buffers);
+
+	return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			    uint16_t *nr_descs, uint16_t *nr_buffers,
+			    struct vring_packed_desc *async_descs,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it) {
+	struct buf_vector buf_vec[BUF_VECTOR_MAX];
+	*nr_descs = 0;
+	*nr_buffers = 0;
+
+	if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,

buf_vec,

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +12 -0
+						 nr_descs,
+						 nr_buffers,
+						 async_descs,
+						 src_iovec, dst_iovec,
+						 src_it, dst_it) < 0)) {
+		VHOST_LOG_DATA(DEBUG,
+				"(%d) failed to get enough desc from vring\n",
+				dev->vid);
+		return -1;
+	}
+
+	VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

Quoted 11 lines
lib/librte_vhost/virtio_net.c +11 -0
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + *nr_descs);
+
+	return 0;
+}
+
+static __rte_noinline uint32_t
+virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
+	struct vhost_virtqueue *vq, uint16_t queue_id,
+	struct rte_mbuf **pkts, uint32_t count,
+	struct rte_mbuf **comp_pkts, uint32_t *comp_count) {

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint16_t async_descs_idx = 0;
+	uint16_t num_buffers;
+	uint16_t num_desc;
+
+	struct rte_vhost_iov_iter *it_pool = vq->it_pool;
+	struct iovec *vec_pool = vq->vec_pool;
+	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
+	struct iovec *src_iovec = vec_pool;
+	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >>

1);

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +12 -0
+	struct rte_vhost_iov_iter *src_it = it_pool;
+	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
+	uint16_t slot_idx = 0;
+	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
+	struct async_inflight_info *pkts_info = vq->async_pkts_info;
+	uint32_t n_pkts = 0, pkt_err = 0;
+	uint32_t num_async_pkts = 0, num_done_pkts = 0;
+	struct vring_packed_desc async_descs[vq->size];
+
+	rte_prefetch0(&vq->desc_packed[vq->last_avail_idx & (vq->size -
+1)]);

The size of the ring is not necessarily a power of two with packed ring.

Quoted 5 lines
Quoted 3 lines

For the size of the ring is not necessarily a power of two, so maybe I can use codes like Indx % vq->size ?
I'm not sure if it's a good way to do that.

In this case it is OK.

OK. I'll fix them in the next version.

Thanks a lot.
Cheng

Quoted 328 lines
Quoted 233 lines
Quoted 17 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1880

Quoted 8 lines
lib/librte_vhost/virtio_net.c +6 -0
+
+	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
+		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq,
+						pkts[pkt_idx],
+						&num_desc, &num_buffers,
+

&async_descs[async_descs_idx],

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 7 lines
lib/librte_vhost/virtio_net.c +7 -0
+						&src_iovec[iovec_idx],
+						&dst_iovec[iovec_idx],
+						&src_it[it_idx],
+						&dst_it[it_idx]) < 0))
+			break;
+
+		VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

Quoted 7 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1886

Quoted 5 lines
lib/librte_vhost/virtio_net.c +5 -0
+			dev->vid, vq->last_avail_idx,
+			vq->last_avail_idx + num_desc);
+
+		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
+			(vq->size - 1);

Same here.

Sure.

Quoted 28 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 6 lines
lib/librte_vhost/virtio_net.c +5 -0
+		if (src_it[it_idx].count) {
+			uint16_t from, to;
+
+			async_descs_idx += num_desc;
+			async_fill_desc(&tdes[pkt_burst_idx++],

&src_it[it_idx],

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+					&dst_it[it_idx]);
+			pkts_info[slot_idx].descs = num_desc;
+			pkts_info[slot_idx].nr_buffers = num_buffers;
+			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
+			num_async_pkts++;
+			iovec_idx += src_it[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += src_it[it_idx].nr_segs;
+

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			/**
+			 * recover shadow used ring and keep DMA-occupied
+			 * descriptors.
+			 */
+			from = vq->shadow_used_idx - num_buffers;
+			to = vq->async_packed_buffer_idx & (vq->size - 1);
+			shadow_ring_store(vq, vq->shadow_used_packed,
+					vq->async_buffers_packed,
+					from, to, num_buffers,
+					sizeof(struct

vring_used_elem_packed));

Quoted 6 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1915

Quoted 4 lines
lib/librte_vhost/virtio_net.c +4 -0
+
+			vq->async_packed_buffer_idx += num_buffers;
+			vq->shadow_used_idx -= num_buffers;
+		} else

Brackets needed.

Sure.

Quoted 53 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 11 lines
lib/librte_vhost/virtio_net.c +10 -0
+			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
+
+		vq_inc_last_avail_packed(vq, num_desc);
+
+		/*
+		 * conditions to trigger async device transfer:
+		 * - buffered packet number reaches transfer threshold
+		 * - unused async iov number is less than max vhost vector
+		 */
+		if (unlikely(pkt_burst_idx >=

VHOST_ASYNC_BATCH_THRESHOLD ||

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +12 -0
+			((VHOST_MAX_ASYNC_VEC >> 1) - segs_await <
+			BUF_VECTOR_MAX))) {
+			n_pkts = vq->async_ops.transfer_data(dev->vid,
+					queue_id, tdes, 0, pkt_burst_idx);
+			iovec_idx = 0;
+			it_idx = 0;
+			segs_await = 0;
+			vq->async_pkts_inflight_n += n_pkts;
+
+			if (unlikely(n_pkts < pkt_burst_idx)) {
+				/*
+				 * log error packets number here and do

actual

Quoted 28 lines
lib/librte_vhost/virtio_net.c +28 -0
+				 * error processing when applications poll
+				 * completion
+				 */
+				pkt_err = pkt_burst_idx - n_pkts;
+				pkt_burst_idx = 0;
+				pkt_idx++;
+				break;
+			}
+
+			pkt_burst_idx = 0;
+		}
+	}
+
+	if (pkt_burst_idx) {
+		n_pkts = vq->async_ops.transfer_data(dev->vid,
+				queue_id, tdes, 0, pkt_burst_idx);
+		vq->async_pkts_inflight_n += n_pkts;
+
+		if (unlikely(n_pkts < pkt_burst_idx))
+			pkt_err = pkt_burst_idx - n_pkts;
+	}
+
+	do_data_copy_enqueue(dev, vq);
+
+	if (unlikely(pkt_err)) {
+		uint16_t descs_err = 0;
+		uint16_t buffers_err = 0;
+
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1971

Quoted 6 lines
lib/librte_vhost/virtio_net.c +6 -0
+		num_async_pkts -= pkt_err;
+		pkt_idx -= pkt_err;
+	/* calculate the sum of buffers and descs of DMA-error packets. */
+		while (pkt_err-- > 0) {
+			descs_err +=
+				pkts_info[slot_idx & (vq->size - 1)].descs;

The size of the ring is not necessarily a power of two with packed ring.

Will be fixed.

Quoted 6 lines

lib/librte_vhost/virtio_net.c

Quoted 3 lines
lib/librte_vhost/virtio_net.c +2 -0
+			buffers_err +=
+				pkts_info[slot_idx & (vq->size -

1)].nr_buffers;

Ditto.

Will be fixed.

Quoted 27 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 12 lines
lib/librte_vhost/virtio_net.c +11 -0
+			slot_idx--;
+		}
+
+		vq->async_packed_buffer_idx -= buffers_err;
+
+		if (vq->last_avail_idx >= descs_err) {
+			vq->last_avail_idx -= descs_err;
+
+			rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+				&async_descs[async_descs_idx - descs_err],
+				descs_err * sizeof(struct

vring_packed_desc));

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 13 lines
lib/librte_vhost/virtio_net.c +13 -0
+		} else {
+			uint16_t nr_copy;
+
+			vq->last_avail_idx = vq->last_avail_idx + vq->size
+						- descs_err;
+			nr_copy = vq->size - vq->last_avail_idx;
+			rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+				&async_descs[async_descs_idx - descs_err],
+				nr_copy * sizeof(struct vring_packed_desc));
+			descs_err -= nr_copy;
+			rte_memcpy(vq->desc_packed,
+				&async_descs[async_descs_idx - descs_err],
+				descs_err * sizeof(struct

vring_packed_desc));

Quoted 7 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 2002

Quoted 5 lines
lib/librte_vhost/virtio_net.c +5 -0
+			vq->avail_wrap_counter ^= 1;
+		}
+
+		num_done_pkts = pkt_idx - num_async_pkts;
+	}

This error handling could be moved in a dedicated function.

Sure, will fix it in the next version.

Quoted 14 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 2012

Quoted 11 lines
lib/librte_vhost/virtio_net.c +10 -0
+	vq->async_pkts_idx += num_async_pkts;
+	*comp_count = num_done_pkts;
+
+	if (likely(vq->shadow_used_idx)) {
+		vhost_flush_enqueue_shadow_packed(dev, vq);
+		vhost_vring_call_packed(dev, vq);
+	}
+
+	return pkt_idx;
+}

Above function is very big and complex, it should be possible to split it in several ones to make it maintainable.

I think move the error handling code will make it smaller.

Thanks.
Cheng

Quoted 12 lines
Quoted 8 lines

+
static __rte_always_inline void write_back_completed_descs_split(struct vhost_virtqueue *vq,
uint16_t
n_descs) { @@ -1693,12 +2048,40 @@ write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t

n_descs)

Quoted 3 lines

} while (nr_left > 0);
}

Quoted 13 lines

lib/librte_vhost/virtio_net.c write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs) · line 2061

Quoted 11 lines
lib/librte_vhost/virtio_net.c +11 -0
+static __rte_always_inline void
+write_back_completed_descs_packed(struct vhost_virtqueue *vq,
+				uint16_t n_buffers)
+{
+	uint16_t nr_left = n_buffers;
+	uint16_t from, to;
+
+	do {
+		from = vq->last_async_buffer_idx &
+						(vq->size - 1);
+		to = (from + nr_left) & (vq->size - 1);

The size of the ring is not necessarily a power of two with packed ring.

Sure.

Quoted 11 lines
Quoted 11 lines
lib/librte_vhost/virtio_net.c +10 -0
+		if (to > from) {
+			vhost_update_used_packed(vq,
+				vq->async_buffers_packed + from,
+				to - from);
+			vq->last_async_buffer_idx += nr_left;
+			nr_left = 0;
+		} else {
+			vhost_update_used_packed(vq,
+				vq->async_buffers_packed + from,
+				vq->size - from);
Quoted 8 lines
Quoted 8 lines

lib/librte_vhost/virtio_net.c write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)

Quoted 8 lines
lib/librte_vhost/virtio_net.c +7 -0
+			vq->last_async_buffer_idx +=
+						vq->size - from;
+			nr_left -= vq->size - from;
+		}
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t

queue_id,

Quoted 86 lines
Quoted 14 lines

lib/librte_vhost/virtio_net.c uint16_t

Quoted 10 lines
lib/librte_vhost/virtio_net.c +2 -1
 		struct rte_mbuf **pkts, uint16_t count)  {
 	struct virtio_net *dev = get_device(vid);
 	struct vhost_virtqueue *vq;
-	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0;
+	uint16_t n_pkts_cpl = 0, n_pkts_put = 0, n_descs = 0, n_buffers =
+0;
 	uint16_t start_idx, pkts_idx, vq_size;
 	struct async_inflight_info *pkts_info;
 	uint16_t from, i;
@@ -1740,21 +2123,41 @@ uint16_t

rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,

Quoted 3 lines
lib/librte_vhost/virtio_net.c +0 -0
 		goto done;
 	}
 
Quoted 9 lines

lib/librte_vhost/virtio_net.c uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id, · line 2128

Quoted 7 lines
lib/librte_vhost/virtio_net.c +3 -4
-	for (i = 0; i < n_pkts_put; i++) {
-		from = (start_idx + i) & (vq_size - 1);
-		n_descs += pkts_info[from].descs;
-		pkts[i] = pkts_info[from].mbuf;
+	if (vq_is_packed(dev)) {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);

Unlike split ring, packed ring size is not necessarily a power of 2.

Sure.
Thanks.

Quoted 58 lines
Quoted 9 lines
lib/librte_vhost/virtio_net.c +8 -0
+			n_buffers += pkts_info[from].nr_buffers;
+			pkts[i] = pkts_info[from].mbuf;
+		}
+	} else {
+		for (i = 0; i < n_pkts_put; i++) {
+			from = (start_idx + i) & (vq_size - 1);
+			n_descs += pkts_info[from].descs;
+			pkts[i] = pkts_info[from].mbuf;

lib/librte_vhost/virtio_net.c uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,

Quoted 10 lines
lib/librte_vhost/virtio_net.c +4 -1
+		}
 	}
+
 	vq->async_last_pkts_n = n_pkts_cpl - n_pkts_put;
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		write_back_completed_descs_split(vq, n_descs);
+		if (vq_is_packed(dev)) {
+			write_back_completed_descs_packed(vq,

n_buffers);

lib/librte_vhost/virtio_net.c uint16_t · line 1753

Quoted 2 lines
lib/librte_vhost/virtio_net.c +0 -1
-		__atomic_add_fetch(&vq->used->idx, n_descs,

__ATOMIC_RELEASE);

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit(struct virtio_net

Quoted 21 lines
lib/librte_vhost/virtio_net.c +14 -3
-		vhost_vring_call_split(dev, vq);
-	} else
-		vq->last_async_desc_idx += n_descs;
+			vhost_vring_call_packed(dev, vq);
+		} else {
+			write_back_completed_descs_split(vq, n_descs);
+
+			__atomic_add_fetch(&vq->used->idx, n_descs,
+					__ATOMIC_RELEASE);
+			vhost_vring_call_split(dev, vq);
+		}
+	} else {
+		if (vq_is_packed(dev))
+			vq->last_async_buffer_idx += n_buffers;
+		else
+			vq->last_async_desc_idx += n_descs;
+	}
 
 done:
 	rte_spinlock_unlock(&vq->access_lock);
@@ -1795,9 +2198,10 @@ virtio_dev_rx_async_submit(struct virtio_net

*dev, uint16_t queue_id,

Quoted 13 lines
lib/librte_vhost/virtio_net.c +3 -2
 	if (count == 0)
 		goto out;
 
-	/* TODO: packed queue not implemented */
 	if (vq_is_packed(dev))
-		nb_tx = 0;
+		nb_tx = virtio_dev_rx_async_submit_packed(dev,
+				vq, queue_id, pkts, count, comp_pkts,
+				comp_count);
 	else
 		nb_tx = virtio_dev_rx_async_submit_split(dev,
 				vq, queue_id, pkts, count, comp_pkts,
#24 lore raw

PATCH v5 3/4 vhost: add batch datapath for async vhost packed ring

In reply to Cheng Jiang

Add batch datapath for async vhost packed ring to improve the performance of small packet.

---
 lib/librte_vhost/virtio_net.c | 43 +++++++++++++++++++++++++++++++----
 1 file changed, 38 insertions(+), 5 deletions(-)
lib/librte_vhost/virtio_net.c +38 -5
index 410be9678..854f7afd6 100644
@@ -1723,6 +1723,29 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline int
+virtio_dev_rx_async_batch_packed(struct virtio_net *dev,
+			   struct vhost_virtqueue *vq,
+			   struct rte_mbuf **pkts,
+			   struct rte_mbuf **comp_pkts, uint32_t *pkt_done)
+{
+	uint16_t i;
+	uint32_t cpy_threshold = vq->async_threshold;
+
+	vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE) {
+		if (unlikely(pkts[i]->pkt_len >= cpy_threshold))
+			return -1;
+	}
+	if (!virtio_dev_rx_batch_packed(dev, vq, pkts)) {
+		vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE)
+			comp_pkts[(*pkt_done)++] = pkts[i];
+
+		return 0;
+	}
+
+	return -1;
+}
+
 static __rte_always_inline int
 vhost_enqueue_async_single_packed(struct virtio_net *dev,
 			    struct vhost_virtqueue *vq,
@@ -1846,6 +1869,7 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	struct rte_mbuf **comp_pkts, uint32_t *comp_count)
 {
 	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint32_t remained = count;
 	uint16_t async_descs_idx = 0;
 	uint16_t num_buffers;
 	uint16_t num_desc;
@@ -1865,9 +1889,17 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
 	struct vring_packed_desc async_descs[vq->size];
 
-	rte_prefetch0(&vq->desc_packed[vq->last_avail_idx & (vq->size - 1)]);
-
-	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
+	do {
+		rte_prefetch0(&vq->desc_packed[vq->last_avail_idx &
+							(vq->size - 1)]);
+		if (remained >= PACKED_BATCH_SIZE) {
+			if (!virtio_dev_rx_async_batch_packed(dev, vq,
+				&pkts[pkt_idx], comp_pkts, &num_done_pkts)) {
+				pkt_idx += PACKED_BATCH_SIZE;
+				remained -= PACKED_BATCH_SIZE;
+				continue;
+			}
+		}
 		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq,
 						pkts[pkt_idx],
 						&num_desc, &num_buffers,
@@ -1915,6 +1947,8 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 		} else
 			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
 
+		pkt_idx++;
+		remained--;
 		vq_inc_last_avail_packed(vq, num_desc);
 
 		/*
@@ -1940,13 +1974,12 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 				 */
 				pkt_err = pkt_burst_idx - n_pkts;
 				pkt_burst_idx = 0;
-				pkt_idx++;
 				break;
 			}
 
 			pkt_burst_idx = 0;
 		}
-	}
+	} while (pkt_idx < count);
 
 	if (pkt_burst_idx) {
 		n_pkts = vq->async_ops.transfer_data(dev->vid,
Signature · 1 line
2.29.2
#25 lore raw

PATCH v5 4/4 doc: add release note for vhost async packed ring

In reply to Cheng Jiang

Add release note for the support of vhost async packed ring.

---
 doc/guides/rel_notes/release_21_05.rst | 4 ++++
 1 file changed, 4 insertions(+)
doc/guides/rel_notes/release_21_05.rst +4 -0
index 374d6d98e..eb5200669 100644
@@ -131,6 +131,10 @@ New Features
   * Added command to display Rx queue used descriptor count.
     ``show port (port_id) rxq (queue_id) desc used count``
 
+* **Added support for vhost async packed ring data path.**
+
+  Added packed ring support for async vhost.
+
 
 Removed Items
 -------------
Signature · 1 line
2.29.2
#26 lore raw

v6 cover add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch set cleans async split ring codes and enables packed ring in async vhost data path. Batch datapath is also enabled in async vhost packed ring.

v6:
* fix some typos in commit log
* improve index usage
* remove shadow_ring_store()
* add store_dma_desc_info_split() store_dma_desc_info_packed()
* remove some checks in vhost_free_async_mem()
* change index calculation since the size isn't necessarily a power of 2
* move error handling in a dedicated function
* clean codes
v5:
* clean some codes for packed ring datapath
* fix an index error in shadow_ring_store()
v4:
* change the patch structure
* clean code for async split ring
* reuse some code from split ring
* change the error handler for DMA-copy packet
* add check for malloc
* remove useless code
* add doc update
v3:
* fix error handler for DMA-copy packet
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

Cheng Jiang (4):
vhost: abstract and reorganize async split ring code
vhost: add support for packed ring in async vhost
vhost: add batch datapath for async vhost packed ring
doc: add release note for vhost async packed ring

 doc/guides/rel_notes/release_21_05.rst |   4 +
 lib/librte_vhost/rte_vhost_async.h     |   1 +
 lib/librte_vhost/vhost.c               |  37 +-
 lib/librte_vhost/vhost.h               |  15 +-
 lib/librte_vhost/virtio_net.c          | 593 +++++++++++++++++++++----
 5 files changed, 550 insertions(+), 100 deletions(-)

--
2.29.2

#27 lore raw

PATCH v6 1/4 vhost: abstract and reorganize async split ring code

This patch puts some codes of async vhost split ring into inline functions to improve the readability of the code. And, it changes the pointer index style of iterator to make the code more concise.

---
 lib/librte_vhost/virtio_net.c | 132 +++++++++++++++++-----------------
 1 file changed, 66 insertions(+), 66 deletions(-)
lib/librte_vhost/virtio_net.c +66 -66
index ff3987860..438bdafd1 100644
@@ -1458,6 +1458,22 @@ virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+store_dma_desc_info_split(struct vring_used_elem *s_ring, struct vring_used_elem *d_ring,
+		uint16_t ring_size, uint16_t s_idx, uint16_t d_idx, uint16_t count)
+{
+	uint16_t elem_size = sizeof(struct vring_used_elem);
+
+	if (d_idx + count <= ring_size) {
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, count * elem_size);
+	} else {
+		uint16_t size = ring_size - d_idx;
+
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, size * elem_size);
+		rte_memcpy(d_ring, s_ring + s_idx + size, (count - size) * elem_size);
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1474,10 +1490,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
 	struct iovec *src_iovec = vec_pool;
 	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-	struct rte_vhost_iov_iter *src_it = it_pool;
-	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
@@ -1511,29 +1526,30 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			dev->vid, vq->last_avail_idx,
 			vq->last_avail_idx + num_buffers);
 
-		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
-				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx], buf_vec, nr_vec, num_buffers,
+				&src_iovec[iovec_idx], &dst_iovec[iovec_idx],
+				&it_pool[it_idx], &it_pool[it_idx + 1]) < 0) {
 			vq->shadow_used_idx -= num_buffers;
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (it_pool[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&it_pool[it_idx], &it_pool[it_idx + 1]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += it_pool[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += it_pool[it_idx].nr_segs;
 
 			/**
 			 * recover shadow used ring and keep DMA-occupied
@@ -1541,23 +1557,10 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						num_buffers *
-						sizeof(struct vring_used_elem));
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->async_descs_split,
-						&vq->shadow_used_split[from +
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			store_dma_desc_info_split(vq->shadow_used_split,
+					vq->async_descs_split, vq->size, from, to, num_buffers);
+
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1578,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
 			vq->async_pkts_inflight_n += n_pkts;
 
@@ -1639,6 +1641,36 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)
+{
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left : vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to], &vq->async_descs_split[from],
+					nr_copy * sizeof(struct vring_used_elem));
+		} else {
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to], &vq->async_descs_split[from],
+					size * sizeof(struct vring_used_elem));
+			rte_memcpy(&vq->used->ring[0], &vq->async_descs_split[from + size],
+					(nr_copy - size) * sizeof(struct vring_used_elem));
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
@@ -1695,39 +1727,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						nr_copy *
-						sizeof(struct vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from +
-						size], (nr_copy - size) *
-						sizeof(struct vring_used_elem));
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
 		vhost_vring_call_split(dev, vq);
Signature · 1 line
2.29.2
#28 lore raw

PATCH v6 2/4 vhost: add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  37 ++-
 lib/librte_vhost/vhost.h           |  15 +-
 lib/librte_vhost/virtio_net.c      | 432 +++++++++++++++++++++++++++--
 4 files changed, 449 insertions(+), 36 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +22 -15
index a70fe01d8..467d1d5a2 100644
@@ -340,17 +340,18 @@ cleanup_device(struct virtio_net *dev, int destroy)
 static void
 vhost_free_async_mem(struct vhost_virtqueue *vq)
 {
-	if (vq->async_pkts_info)
-		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
-		rte_free(vq->async_descs_split);
-	if (vq->it_pool)
-		rte_free(vq->it_pool);
-	if (vq->vec_pool)
-		rte_free(vq->vec_pool);
+	rte_free(vq->async_pkts_info);
 
-	vq->async_pkts_info = NULL;
+	rte_free(vq->async_buffers_packed);
+	rte_free(vq->async_descs_split);
+
+	rte_free(vq->it_pool);
+	rte_free(vq->vec_pool);
+
+	vq->async_buffers_packed = NULL;
 	vq->async_descs_split = NULL;
+
+	vq->async_pkts_info = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -1626,10 +1627,9 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 	if (unlikely(vq == NULL || !dev->async_copy))
 		return -1;
 
-	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or non-inorder mode "
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1667,11 +1667,18 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
-		!vq->it_pool || !vq->vec_pool) {
+	}
+
+	if (!vq->async_buffers_packed || !vq->async_descs_split ||
+		!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) {
 		vhost_free_async_mem(vq);
 		VHOST_LOG_CONFIG(ERR,
 				"async register failed: cannot allocate memory for vq data "
lib/librte_vhost/vhost.h +12 -3
index f628714c2..673335217 100644
@@ -201,9 +201,18 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
-	uint16_t async_desc_idx;
-	uint16_t last_async_desc_idx;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
+	union {
+		uint16_t async_desc_idx;
+		uint16_t async_packed_buffer_idx;
+	};
+	union {
+		uint16_t last_async_desc_idx;
+		uint16_t last_async_buffer_idx;
+	};
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +414 -18

495 lines not on this page · raw message

Signature · 1 line
2.29.2
#29 lore raw

PATCH v6 3/4 vhost: add batch datapath for async vhost packed ring

In reply to Cheng Jiang

Add batch datapath for async vhost packed ring to improve the performance of small packet processing.

---
 lib/librte_vhost/virtio_net.c | 41 +++++++++++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 4 deletions(-)
lib/librte_vhost/virtio_net.c +37 -4
index e2b35a319..42439a86d 100644
@@ -1725,6 +1725,29 @@ vhost_update_used_packed(struct vhost_virtqueue *vq,
 	vq->desc_packed[head_idx].flags = head_flags;
 }
 
+static __rte_always_inline int
+virtio_dev_rx_async_batch_packed(struct virtio_net *dev,
+			   struct vhost_virtqueue *vq,
+			   struct rte_mbuf **pkts,
+			   struct rte_mbuf **comp_pkts, uint32_t *pkt_done)
+{
+	uint16_t i;
+	uint32_t cpy_threshold = vq->async_threshold;
+
+	vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE) {
+		if (unlikely(pkts[i]->pkt_len >= cpy_threshold))
+			return -1;
+	}
+	if (!virtio_dev_rx_batch_packed(dev, vq, pkts)) {
+		vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE)
+			comp_pkts[(*pkt_done)++] = pkts[i];
+
+		return 0;
+	}
+
+	return -1;
+}
+
 static __rte_always_inline int
 vhost_enqueue_async_single_packed(struct virtio_net *dev,
 			    struct vhost_virtqueue *vq,
@@ -1875,6 +1898,7 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	struct rte_mbuf **comp_pkts, uint32_t *comp_count)
 {
 	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint32_t remained = count;
 	uint16_t async_descs_idx = 0;
 	uint16_t num_buffers;
 	uint16_t num_desc;
@@ -1892,9 +1916,17 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
 	struct vring_packed_desc async_descs[vq->size];
 
-	rte_prefetch0(&vq->desc_packed[vq->last_avail_idx]);
+	do {
+		rte_prefetch0(&vq->desc_packed[vq->last_avail_idx]);
+		if (remained >= PACKED_BATCH_SIZE) {
+			if (!virtio_dev_rx_async_batch_packed(dev, vq,
+				&pkts[pkt_idx], comp_pkts, &num_done_pkts)) {
+				pkt_idx += PACKED_BATCH_SIZE;
+				remained -= PACKED_BATCH_SIZE;
+				continue;
+			}
+		}
 
-	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
 		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq, pkts[pkt_idx],
 						&num_desc, &num_buffers,
 						&async_descs[async_descs_idx],
@@ -1937,6 +1969,8 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
 		}
 
+		pkt_idx++;
+		remained--;
 		vq_inc_last_avail_packed(vq, num_desc);
 
 		/*
@@ -1961,13 +1995,12 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 				 */
 				pkt_err = pkt_burst_idx - n_pkts;
 				pkt_burst_idx = 0;
-				pkt_idx++;
 				break;
 			}
 
 			pkt_burst_idx = 0;
 		}
-	}
+	} while (pkt_idx < count);
 
 	if (pkt_burst_idx) {
 		n_pkts = vq->async_ops.transfer_data(dev->vid, queue_id, tdes, 0, pkt_burst_idx);
Signature · 1 line
2.29.2
#30 lore raw

PATCH v6 4/4 doc: add release note for vhost async packed ring

In reply to Cheng Jiang

Add release note for the support of vhost async packed ring.

---
 doc/guides/rel_notes/release_21_05.rst | 4 ++++
 1 file changed, 4 insertions(+)
doc/guides/rel_notes/release_21_05.rst +4 -0
index 374d6d98e..eb5200669 100644
@@ -131,6 +131,10 @@ New Features
   * Added command to display Rx queue used descriptor count.
     ``show port (port_id) rxq (queue_id) desc used count``
 
+* **Added support for vhost async packed ring data path.**
+
+  Added packed ring support for async vhost.
+
 
 Removed Items
 -------------
Signature · 1 line
2.29.2
#31 lore raw

v7 cover add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch set cleans async split ring codes and enables packed ring in async vhost data path. Batch datapath is also enabled in async vhost packed ring.

v7:
* fix compile issues
* add argument *dev in vhost_free_async_mem() for ring type decision
v6:
* fix some typos in commit log
* improve index usage
* remove shadow_ring_store()
* add store_dma_desc_info_split() store_dma_desc_info_packed()
* remove some checks in vhost_free_async_mem()
* change index calculation since the size isn't necessarily a power of 2
* move error handling in a dedicated function
* clean codes
v5:
* clean some codes for packed ring datapath
* fix an index error in shadow_ring_store()
v4:
* change the patch structure
* clean code for async split ring
* reuse some code from split ring
* change the error handler for DMA-copy packet
* add check for malloc
* remove useless code
* add doc update
v3:
* fix error handler for DMA-copy packet
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

Cheng Jiang (4):
vhost: abstract and reorganize async split ring code
vhost: add support for packed ring in async vhost
vhost: add batch datapath for async vhost packed ring
doc: add release note for vhost async packed ring

 doc/guides/rel_notes/release_21_05.rst |   4 +
 lib/librte_vhost/rte_vhost_async.h     |   1 +
 lib/librte_vhost/vhost.c               |  49 +-
 lib/librte_vhost/vhost.h               |  15 +-
 lib/librte_vhost/virtio_net.c          | 593 +++++++++++++++++++++----
 5 files changed, 557 insertions(+), 105 deletions(-)

--
2.29.2

#32 lore raw

PATCH v7 1/4 vhost: abstract and reorganize async split ring code

This patch puts some codes of async vhost split ring into inline functions to improve the readability of the code. And, it changes the pointer index style of iterator to make the code more concise.

---
 lib/librte_vhost/virtio_net.c | 132 +++++++++++++++++-----------------
 1 file changed, 66 insertions(+), 66 deletions(-)
lib/librte_vhost/virtio_net.c +66 -66
index ff3987860..438bdafd1 100644
@@ -1458,6 +1458,22 @@ virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+store_dma_desc_info_split(struct vring_used_elem *s_ring, struct vring_used_elem *d_ring,
+		uint16_t ring_size, uint16_t s_idx, uint16_t d_idx, uint16_t count)
+{
+	uint16_t elem_size = sizeof(struct vring_used_elem);
+
+	if (d_idx + count <= ring_size) {
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, count * elem_size);
+	} else {
+		uint16_t size = ring_size - d_idx;
+
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, size * elem_size);
+		rte_memcpy(d_ring, s_ring + s_idx + size, (count - size) * elem_size);
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1474,10 +1490,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
 	struct iovec *src_iovec = vec_pool;
 	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-	struct rte_vhost_iov_iter *src_it = it_pool;
-	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
@@ -1511,29 +1526,30 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			dev->vid, vq->last_avail_idx,
 			vq->last_avail_idx + num_buffers);
 
-		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
-				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx], buf_vec, nr_vec, num_buffers,
+				&src_iovec[iovec_idx], &dst_iovec[iovec_idx],
+				&it_pool[it_idx], &it_pool[it_idx + 1]) < 0) {
 			vq->shadow_used_idx -= num_buffers;
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (it_pool[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&it_pool[it_idx], &it_pool[it_idx + 1]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += it_pool[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += it_pool[it_idx].nr_segs;
 
 			/**
 			 * recover shadow used ring and keep DMA-occupied
@@ -1541,23 +1557,10 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						num_buffers *
-						sizeof(struct vring_used_elem));
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->async_descs_split,
-						&vq->shadow_used_split[from +
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			store_dma_desc_info_split(vq->shadow_used_split,
+					vq->async_descs_split, vq->size, from, to, num_buffers);
+
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1578,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
 			vq->async_pkts_inflight_n += n_pkts;
 
@@ -1639,6 +1641,36 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)
+{
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left : vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to], &vq->async_descs_split[from],
+					nr_copy * sizeof(struct vring_used_elem));
+		} else {
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to], &vq->async_descs_split[from],
+					size * sizeof(struct vring_used_elem));
+			rte_memcpy(&vq->used->ring[0], &vq->async_descs_split[from + size],
+					(nr_copy - size) * sizeof(struct vring_used_elem));
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
@@ -1695,39 +1727,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						nr_copy *
-						sizeof(struct vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from +
-						size], (nr_copy - size) *
-						sizeof(struct vring_used_elem));
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
 		vhost_vring_call_split(dev, vq);
Signature · 1 line
2.29.2
Reviewed-by on v7 1/4
#33 lore raw

Re: [dpdk-dev] [PATCH v7 1/4] vhost: abstract and reorganize async split ring code

Quoted 9 lines · Cheng Jiang

This patch puts some codes of async vhost split ring into inline functions to improve the readability of the code. And, it changes the pointer index style of iterator to make the code more concise.

---
 lib/librte_vhost/virtio_net.c | 132 +++++++++++++++++-----------------
 1 file changed, 66 insertions(+), 66 deletions(-)

Thanks,
Maxime

#34 lore raw

PATCH v7 2/4 vhost: add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  49 ++--
 lib/librte_vhost/vhost.h           |  15 +-
 lib/librte_vhost/virtio_net.c      | 432 +++++++++++++++++++++++++++--
 4 files changed, 456 insertions(+), 41 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +29 -20

87 lines not on this page · raw message

lib/librte_vhost/vhost.h +12 -3
index f628714c2..673335217 100644
@@ -201,9 +201,18 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
-	uint16_t async_desc_idx;
-	uint16_t last_async_desc_idx;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
+	union {
+		uint16_t async_desc_idx;
+		uint16_t async_packed_buffer_idx;
+	};
+	union {
+		uint16_t last_async_desc_idx;
+		uint16_t last_async_buffer_idx;
+	};
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +414 -18

495 lines not on this page · raw message

Signature · 1 line
2.29.2
#35 lore raw
Quoted 34 lines · Cheng Jiang

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  49 ++--
 lib/librte_vhost/vhost.h           |  15 +-
 lib/librte_vhost/virtio_net.c      | 432 +++++++++++++++++++++++++++--
 4 files changed, 456 insertions(+), 41 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +1 -1
index a70fe01d8..f509186c6 100644
@@ -338,19 +338,22 @@ cleanup_device(struct virtio_net *dev, int destroy)
 }
 
 static void
-vhost_free_async_mem(struct vhost_virtqueue *vq)
+vhost_free_async_mem(struct virtio_net *dev, struct vhost_virtqueue *vq)

lib/librte_vhost/vhost.c cleanup_device(struct virtio_net *dev, int destroy) · line 348

Quoted 10 lines
lib/librte_vhost/vhost.c +6 -3
 {
-	if (vq->async_pkts_info)
-		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	rte_free(vq->async_pkts_info);
+
+	if (vq_is_packed(dev)) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	} else {

Doing this is not necessary:

	rte_free(vq->async_buffers_packed);
	vq->async_buffers_packed = NULL;
	rte_free(vq->async_descs_split);
	vq->async_descs_split = NULL;

Above will just work and will avoid adding dev parameter.

Quoted 53 lines
lib/librte_vhost/vhost.c +15 -12
 		rte_free(vq->async_descs_split);
-	if (vq->it_pool)
-		rte_free(vq->it_pool);
-	if (vq->vec_pool)
-		rte_free(vq->vec_pool);
+		vq->async_descs_split = NULL;
+	}
+
+	rte_free(vq->it_pool);
+	rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -360,10 +363,10 @@ free_vq(struct virtio_net *dev, struct vhost_virtqueue *vq)
 {
 	if (vq_is_packed(dev))
 		rte_free(vq->shadow_used_packed);
-	else {
+	else
 		rte_free(vq->shadow_used_split);
-		vhost_free_async_mem(vq);
-	}
+
+	vhost_free_async_mem(dev, vq);
 	rte_free(vq->batch_copy_elems);
 	if (vq->iotlb_pool)
 		rte_mempool_free(vq->iotlb_pool);
@@ -1626,10 +1629,9 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 	if (unlikely(vq == NULL || !dev->async_copy))
 		return -1;
 
-	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or non-inorder mode "
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1667,12 +1669,19 @@ int rte_vhost_async_channel_register(int vid, uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id, · line 1683

Quoted 10 lines
lib/librte_vhost/vhost.c +5 -3
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
-		!vq->it_pool || !vq->vec_pool) {
-		vhost_free_async_mem(vq);
+	}
+
+	if (!vq->async_buffers_packed || !vq->async_descs_split ||
+		!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) {

Not really than of this error handling. Checking after every malloc if it suceed would be cleaner.

Quoted 26 lines
lib/librte_vhost/vhost.c +2 -1
+		vhost_free_async_mem(dev, vq);
 		VHOST_LOG_CONFIG(ERR,
 				"async register failed: cannot allocate memory for vq data "
 				"(vid %d, qid: %d)\n", vid, queue_id);
@@ -1728,7 +1737,7 @@ int rte_vhost_async_channel_unregister(int vid, uint16_t queue_id)
 		goto out;
 	}
 
-	vhost_free_async_mem(vq);
+	vhost_free_async_mem(dev, vq);
 
 	vq->async_ops.transfer_data = NULL;
 	vq->async_ops.check_completed_copies = NULL;
lib/librte_vhost/vhost.h +2 -3
index f628714c2..673335217 100644
@@ -201,9 +201,18 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
-	uint16_t async_desc_idx;
-	uint16_t last_async_desc_idx;
+	union {
+		struct vring_used_elem  *async_descs_split;

lib/librte_vhost/vhost.h struct vhost_virtqueue { · line 215

Quoted 10 lines
lib/librte_vhost/vhost.h +10 -0
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
+	union {
+		uint16_t async_desc_idx;
+		uint16_t async_packed_buffer_idx;
+	};
+	union {
+		uint16_t last_async_desc_idx;
+		uint16_t last_async_buffer_idx;
+	};

Looks almost good to me now, thanks for doing the change. Only minor issue is the naming which is not consistent in the different fields. Sometimes it contains split or packed, sometimes not.

Maxime

#36 lore raw

Hi Maxime,

Quoted 62 lines

-----Original Message-----
From: Maxime Coquelin <[email protected]>
Sent: Wednesday, April 14, 2021 9:41 PM
To: Jiang, Cheng1 <[email protected]>; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>
Subject: Re: [PATCH v7 2/4] vhost: add support for packed ring in async vhost

Quoted 36 lines · Cheng Jiang

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  49 ++--
 lib/librte_vhost/vhost.h           |  15 +-
 lib/librte_vhost/virtio_net.c      | 432 +++++++++++++++++++++++++++--
 4 files changed, 456 insertions(+), 41 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {  struct
 async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c index renamed from lib/librte_vhost/vhost.c +0 -0

a70fe01d8..f509186c6 100644

lib/librte_vhost/vhost.c +2 -1
@@ -338,19 +338,22 @@ cleanup_device(struct virtio_net *dev, int
 destroy)  }
 
 static void
-vhost_free_async_mem(struct vhost_virtqueue *vq)
+vhost_free_async_mem(struct virtio_net *dev, struct vhost_virtqueue
+*vq)

lib/librte_vhost/vhost.c cleanup_device(struct virtio_net *dev, int · line 349

Quoted 10 lines
lib/librte_vhost/vhost.c +6 -3
 {
-	if (vq->async_pkts_info)
-		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	rte_free(vq->async_pkts_info);
+
+	if (vq_is_packed(dev)) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	} else {

Doing this is not necessary:

Quoted 6 lines
	rte_free(vq->async_buffers_packed);
	vq->async_buffers_packed = NULL;
	rte_free(vq->async_descs_split);
	vq->async_descs_split = NULL;

Above will just work and will avoid adding dev parameter.

async_buffers_packed and async_descs_split are two members of a union.
If I rte_free(vq->async_buffers_packed), then there is no need to rte_free(vq->async_descs_split).
So I use dev to determine which one I need to free.
Sure, I can free then both with no condition(I can also only free any one of them), but I think it's a little strange, Right?

Quoted 63 lines

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid,

Quoted 33 lines
lib/librte_vhost/vhost.c +8 -8
 		rte_free(vq->async_descs_split);
-	if (vq->it_pool)
-		rte_free(vq->it_pool);
-	if (vq->vec_pool)
-		rte_free(vq->vec_pool);
+		vq->async_descs_split = NULL;
+	}
+
+	rte_free(vq->it_pool);
+	rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -360,10 +363,10 @@ free_vq(struct virtio_net *dev, struct
 vhost_virtqueue *vq)  {
 	if (vq_is_packed(dev))
 		rte_free(vq->shadow_used_packed);
-	else {
+	else
 		rte_free(vq->shadow_used_split);
-		vhost_free_async_mem(vq);
-	}
+
+	vhost_free_async_mem(dev, vq);
 	rte_free(vq->batch_copy_elems);
 	if (vq->iotlb_pool)
 		rte_mempool_free(vq->iotlb_pool);
@@ -1626,10 +1629,9 @@ int rte_vhost_async_channel_register(int vid,

uint16_t queue_id,

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id,

Quoted 8 lines
lib/librte_vhost/vhost.c +1 -3
 	if (unlikely(vq == NULL || !dev->async_copy))
 		return -1;
 
-	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or

non-inorder mode "

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid,

Quoted 5 lines
lib/librte_vhost/vhost.c +1 -0
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1667,12 +1669,19 @@ int rte_vhost_async_channel_register(int vid,

uint16_t queue_id,

Quoted 14 lines
lib/librte_vhost/vhost.c +6 -3
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
-		!vq->it_pool || !vq->vec_pool) {
Quoted 8 lines

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id, · line 1683

Quoted 5 lines
lib/librte_vhost/vhost.c +4 -1
-		vhost_free_async_mem(vq);
+	}
+
+	if (!vq->async_buffers_packed || !vq->async_descs_split ||
+		!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) {

Not really than of this error handling. Checking after every malloc if it suceed would be cleaner.

OK, make sense to me. But that means I need to add log(which can be more specific) after every check, do you thinks it's ok? If you thinks it's ok, then I'll fix it in the next version.

Quoted 35 lines

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id,

Quoted 4 lines
lib/librte_vhost/vhost.c +1 -0
+		vhost_free_async_mem(dev, vq);
 		VHOST_LOG_CONFIG(ERR,
 				"async register failed: cannot allocate

memory for vq data "

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, · line 1687

Quoted 1 line
lib/librte_vhost/vhost.c +0 -0
 				"(vid %d, qid: %d)\n", vid, queue_id); @@ -

1728,7 +1737,7 @@ int

Quoted 28 lines

rte_vhost_async_channel_unregister(int vid, uint16_t queue_id)

lib/librte_vhost/vhost.c +1 -1
 		goto out;
 	}
 
-	vhost_free_async_mem(vq);
+	vhost_free_async_mem(dev, vq);
 
 	vq->async_ops.transfer_data = NULL;
 	vq->async_ops.check_completed_copies = NULL; diff --git

a/lib/librte_vhost/vhost.h b/lib/librte_vhost/vhost.h index f628714c2..673335217 100644

lib/librte_vhost/vhost.h +8 -3
@@ -201,9 +201,18 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
-	uint16_t async_desc_idx;
-	uint16_t last_async_desc_idx;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
+	union {
+		uint16_t async_desc_idx;
+		uint16_t async_packed_buffer_idx;
+	};
Quoted 8 lines

lib/librte_vhost/vhost.h struct vhost_virtqueue { · line 215

Quoted 4 lines
lib/librte_vhost/vhost.h +4 -0
+	union {
+		uint16_t last_async_desc_idx;
+		uint16_t last_async_buffer_idx;
+	};

Looks almost good to me now, thanks for doing the change.
Only minor issue is the naming which is not consistent in the different fields. Sometimes it contains split or packed, sometimes not.

OK, I'll check these names, and fix them.

Thanks a lot.
Cheng

Quoted 2 lines

Maxime

#37 lore raw
In reply to Cheng Jiang

Hi Cheng,

Quoted 110 lines

-----Original Message-----
From: Jiang, Cheng1 <[email protected]>
Sent: Wednesday, April 14, 2021 2:14 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [PATCH v7 2/4] vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  49 ++--
 lib/librte_vhost/vhost.h           |  15 +-
 lib/librte_vhost/virtio_net.c      | 432 +++++++++++++++++++++++++++--
 4 files changed, 456 insertions(+), 41 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +15 -12
index a70fe01d8..f509186c6 100644
@@ -338,19 +338,22 @@ cleanup_device(struct virtio_net *dev, int destroy)
 }
 
 static void
-vhost_free_async_mem(struct vhost_virtqueue *vq)
+vhost_free_async_mem(struct virtio_net *dev, struct vhost_virtqueue *vq)
 {
-	if (vq->async_pkts_info)
-		rte_free(vq->async_pkts_info);
-	if (vq->async_descs_split)
+	rte_free(vq->async_pkts_info);
+
+	if (vq_is_packed(dev)) {
+		rte_free(vq->async_buffers_packed);
+		vq->async_buffers_packed = NULL;
+	} else {
 		rte_free(vq->async_descs_split);
-	if (vq->it_pool)
-		rte_free(vq->it_pool);
-	if (vq->vec_pool)
-		rte_free(vq->vec_pool);
+		vq->async_descs_split = NULL;
+	}
+
+	rte_free(vq->it_pool);
+	rte_free(vq->vec_pool);
 
 	vq->async_pkts_info = NULL;
-	vq->async_descs_split = NULL;
 	vq->it_pool = NULL;
 	vq->vec_pool = NULL;
 }
@@ -360,10 +363,10 @@ free_vq(struct virtio_net *dev, struct
 vhost_virtqueue *vq)
 {
 	if (vq_is_packed(dev))
 		rte_free(vq->shadow_used_packed);
-	else {
+	else
 		rte_free(vq->shadow_used_split);
-		vhost_free_async_mem(vq);
-	}
+
+	vhost_free_async_mem(dev, vq);
 	rte_free(vq->batch_copy_elems);
 	if (vq->iotlb_pool)
lib/librte_vhost/vhost.c +1 -3
 		rte_mempool_free(vq->iotlb_pool);
@@ -1626,10 +1629,9 @@ int rte_vhost_async_channel_register(int vid,
 uint16_t queue_id,
 	if (unlikely(vq == NULL || !dev->async_copy))
 		return -1;
 
-	/* packed queue is not supported */
-	if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+	if (unlikely(!f.async_inorder)) {
 		VHOST_LOG_CONFIG(ERR,
-			"async copy is not supported on packed queue or

non-inorder mode "

lib/librte_vhost/vhost.c +5 -1
+			"async copy is not supported on non-inorder mode "
 			"(vid %d, qid: %d)\n", vid, queue_id);
 		return -1;
 	}
@@ -1667,12 +1669,19 @@ int rte_vhost_async_channel_register(int vid,
 uint16_t queue_id,
 	vq->vec_pool = rte_malloc_socket(NULL,
 			VHOST_MAX_ASYNC_VEC * sizeof(struct iovec),
 			RTE_CACHE_LINE_SIZE, node);
-	vq->async_descs_split = rte_malloc_socket(NULL,
+	if (vq_is_packed(dev)) {
+		vq->async_buffers_packed = rte_malloc_socket(NULL,
+			vq->size * sizeof(struct vring_used_elem_packed),
+			RTE_CACHE_LINE_SIZE, node);

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, · line 1683

Quoted 10 lines
lib/librte_vhost/vhost.c +5 -3
+	} else {
+		vq->async_descs_split = rte_malloc_socket(NULL,
 			vq->size * sizeof(struct vring_used_elem),
 			RTE_CACHE_LINE_SIZE, node);
-	if (!vq->async_descs_split || !vq->async_pkts_info ||
-		!vq->it_pool || !vq->vec_pool) {
-		vhost_free_async_mem(vq);
+	}
+
+	if (!vq->async_buffers_packed || !vq->async_descs_split ||

async_buffers_packed and async_descs_split are two members of a union. Like the way processed in vhost_free_async_mem(), do you think it's better to check if they are NULL in if-else respectively?

Quoted 154 lines
lib/librte_vhost/vhost.c +2 -0
+		!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) {
+		vhost_free_async_mem(dev, vq);
 		VHOST_LOG_CONFIG(ERR,
 				"async register failed: cannot allocate

memory for vq data "

lib/librte_vhost/vhost.c +1 -1
 				"(vid %d, qid: %d)\n", vid, queue_id);
@@ -1728,7 +1737,7 @@ int rte_vhost_async_channel_unregister(int vid,
 uint16_t queue_id)
 		goto out;
 	}
 
-	vhost_free_async_mem(vq);
+	vhost_free_async_mem(dev, vq);
 
 	vq->async_ops.transfer_data = NULL;
lib/librte_vhost/vhost.c +0 -0
 	vq->async_ops.check_completed_copies = NULL;
lib/librte_vhost/vhost.h +12 -3
index f628714c2..673335217 100644
@@ -201,9 +201,18 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
-	uint16_t async_desc_idx;
-	uint16_t last_async_desc_idx;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
+	union {
+		uint16_t async_desc_idx;
+		uint16_t async_packed_buffer_idx;
+	};
+	union {
+		uint16_t last_async_desc_idx;
+		uint16_t last_async_buffer_idx;
+	};
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +5 -5
index 438bdafd1..54e11e3a5 100644
@@ -363,14 +363,14 @@
 vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue *vq,
 }
 
 static __rte_always_inline void
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-				   struct vhost_virtqueue *vq,
-				   uint32_t len[],
-				   uint16_t id[],
-				   uint16_t count[],
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
 				   uint16_t num_buffers)
 {
 	uint16_t i;
+
 	for (i = 0; i < num_buffers; i++) {
 		/* enqueue shadow flush action aligned with batch num */
lib/librte_vhost/virtio_net.c +11 -0
 		if (!vq->shadow_used_idx)
@@ -382,6 +382,17 @@ vhost_shadow_enqueue_single_packed(struct
 virtio_net *dev,
 		vq->shadow_aligned_idx += count[i];
 		vq->shadow_used_idx++;
 	}
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+				   struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
+				   uint16_t num_buffers)
+{
+	vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 	if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {
lib/librte_vhost/virtio_net.c +0 -0
 		do_data_copy_enqueue(dev, vq);
@@ -1474,6 +1485,23 @@ store_dma_desc_info_split(struct

vring_used_elem *s_ring, struct vring_used_elem

lib/librte_vhost/virtio_net.c +4 -0
 	}
 }
 
+static __rte_always_inline void
+store_dma_desc_info_packed(struct vring_used_elem_packed *s_ring,
+		struct vring_used_elem_packed *d_ring,
+		uint16_t ring_size, uint16_t s_idx, uint16_t d_idx, uint16_t

count)

lib/librte_vhost/virtio_net.c +5 -0
+{
+	uint16_t elem_size = sizeof(struct vring_used_elem_packed);
+
+	if (d_idx + count <= ring_size) {
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, count *

elem_size);

lib/librte_vhost/virtio_net.c +5 -0
+	} else {
+		uint16_t size = ring_size - d_idx;
+
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, size * elem_size);
+		rte_memcpy(d_ring, s_ring + s_idx + size, (count - size) *

elem_size);

lib/librte_vhost/virtio_net.c +38 -0
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1641,6 +1669,330 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+vhost_update_used_packed(struct vhost_virtqueue *vq,
+			struct vring_used_elem_packed *shadow_ring,
+			uint16_t count)
+{
+	int i;
+	uint16_t used_idx = vq->last_used_idx;
+	uint16_t head_idx = vq->last_used_idx;
+	uint16_t head_flags = 0;
+
+	if (count == 0)
+		return;
+
+	/* Split loop in two to save memory barriers */
+	for (i = 0; i < count; i++) {
+		vq->desc_packed[used_idx].id = shadow_ring[i].id;
+		vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+		used_idx += shadow_ring[i].count;
+		if (used_idx >= vq->size)
+			used_idx -= vq->size;
+	}
+
+	/* The ordering for storing desc flags needs to be enforced. */
+	rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+	for (i = 0; i < count; i++) {
+		uint16_t flags;
+
+		if (vq->shadow_used_packed[i].len)
+			flags = VRING_DESC_F_WRITE;
+		else
+			flags = 0;
+
+		if (vq->used_wrap_counter) {

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct · line 1717

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+			flags |= VRING_DESC_F_USED;
+			flags |= VRING_DESC_F_AVAIL;
+		} else {
+			flags &= ~VRING_DESC_F_USED;
+			flags &= ~VRING_DESC_F_AVAIL;
+		}
+
+		if (i > 0) {
+			vq->desc_packed[vq->last_used_idx].flags = flags;
+

No need a blank line above.

Quoted 134 lines
lib/librte_vhost/virtio_net.c +51 -0
+		} else {
+			head_idx = vq->last_used_idx;
+			head_flags = flags;
+		}
+
+		vq_inc_last_used_packed(vq, shadow_ring[i].count);
+	}
+
+	vq->desc_packed[head_idx].flags = head_flags;
+}
+
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    struct buf_vector *buf_vec,
+			    uint16_t *nr_descs,
+			    uint16_t *nr_buffers,
+			    struct vring_packed_desc *async_descs,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it)
+{
+	uint16_t nr_vec = 0;
+	uint16_t avail_idx = vq->last_avail_idx;
+	uint16_t max_tries, tries = 0;
+	uint16_t buf_id = 0;
+	uint32_t len = 0;
+	uint16_t desc_count = 0;
+	uint32_t size = pkt->pkt_len + sizeof(struct
 virtio_net_hdr_mrg_rxbuf);
+	uint32_t buffer_len[vq->size];
+	uint16_t buffer_buf_id[vq->size];
+	uint16_t buffer_desc_count[vq->size];
+	*nr_buffers = 0;
+
+	if (rxvq_is_mergeable(dev))
+		max_tries = vq->size - 1;
+	else
+		max_tries = 1;
+
+	while (size > 0) {
+		/*
+		 * if we tried all available ring items, and still
+		 * can't get enough buf, it means something abnormal
+		 * happened.
+		 */
+		if (unlikely(++tries > max_tries))
+			return -1;
+
+		if (unlikely(fill_vec_buf_packed(dev, vq, avail_idx,
 &desc_count, buf_vec, &nr_vec,
+						&buf_id, &len,

VHOST_ACCESS_RW) < 0))

lib/librte_vhost/virtio_net.c +17 -0
+			return -1;
+
+		len = RTE_MIN(len, size);
+		size -= len;
+
+		buffer_len[*nr_buffers] = len;
+		buffer_buf_id[*nr_buffers] = buf_id;
+		buffer_desc_count[*nr_buffers] = desc_count;
+		*nr_buffers += 1;
+
+		*nr_descs += desc_count;
+		avail_idx += desc_count;
+		if (avail_idx >= vq->size)
+			avail_idx -= vq->size;
+	}
+
+	if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,

src_iovec, dst_iovec,

lib/librte_vhost/virtio_net.c +5 -0
+			src_it, dst_it) < 0)
+		return -1;
+	/* store descriptors for DMA */
+	if (avail_idx >= *nr_descs) {
+		rte_memcpy(async_descs, &vq->desc_packed[vq-
Quoted 1 line

last_avail_idx],

lib/librte_vhost/virtio_net.c +4 -0
+			*nr_descs * sizeof(struct vring_packed_desc));
+	} else {
+		uint16_t nr_copy = vq->size - vq->last_avail_idx;
+		rte_memcpy(async_descs, &vq->desc_packed[vq-
Quoted 1 line

last_avail_idx],

lib/librte_vhost/virtio_net.c +3 -0
+			nr_copy * sizeof(struct vring_packed_desc));
+		rte_memcpy(async_descs + nr_copy, vq->desc_packed,
+			(*nr_descs - nr_copy) * sizeof(struct

vring_packed_desc));

lib/librte_vhost/virtio_net.c +3 -0
+	}
+
+	vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,

buffer_desc_count, *nr_buffers);

lib/librte_vhost/virtio_net.c +6 -0
+
+	return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev, struct

vhost_virtqueue *vq,

lib/librte_vhost/virtio_net.c +1 -0
+			    struct rte_mbuf *pkt, uint16_t *nr_descs, uint16_t

*nr_buffers,

lib/librte_vhost/virtio_net.c +3 -0
+			    struct vring_packed_desc *async_descs,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it, struct

rte_vhost_iov_iter *dst_it)

lib/librte_vhost/virtio_net.c +6 -0
+{
+	struct buf_vector buf_vec[BUF_VECTOR_MAX];
+	*nr_descs = 0;
+	*nr_buffers = 0;
+
+	if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,

buf_vec, nr_descs, nr_buffers,

lib/librte_vhost/virtio_net.c +1 -0
+						 async_descs, src_iovec,

dst_iovec,

lib/librte_vhost/virtio_net.c +2 -0
+						 src_it, dst_it) < 0)) {
+		VHOST_LOG_DATA(DEBUG, "(%d) failed to get enough desc

from vring\n", dev->vid);

lib/librte_vhost/virtio_net.c +4 -0
+		return -1;
+	}
+
+	VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

lib/librte_vhost/virtio_net.c +1 -0
+			dev->vid, vq->last_avail_idx, vq->last_avail_idx +

*nr_descs);

lib/librte_vhost/virtio_net.c +6 -0
+
+	return 0;
+}
+
+static __rte_always_inline void
+dma_error_handler_packed(struct vhost_virtqueue *vq, struct

vring_packed_desc *async_descs,

lib/librte_vhost/virtio_net.c +1 -0
+			uint16_t async_descs_idx, uint16_t slot_idx, uint32_t

nr_err,

lib/librte_vhost/virtio_net.c +1 -0
+			uint32_t *pkt_idx, uint32_t *num_async_pkts,

uint32_t *num_done_pkts)

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1841

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+{
+	uint16_t descs_err = 0;
+	uint16_t buffers_err = 0;
+	struct async_inflight_info *pkts_info = vq->async_pkts_info;
+
+	*num_async_pkts -= nr_err;
+	*pkt_idx -= nr_err;
+	/* calculate the sum of buffers and descs of DMA-error packets. */
+	while (nr_err-- > 0) {
+		descs_err += pkts_info[slot_idx % vq->size].descs;

I notice there are several parts using "%" to wrap around index, but existed code uses "& (vq->size - 1)" instead. I think it's better to keep it consistent.

Thanks,
Jiayu

#38 lore raw

Hi Jiayu,

Quoted 117 lines

-----Original Message-----
From: Hu, Jiayu <[email protected]>
Sent: Thursday, April 15, 2021 10:03 AM
To: Jiang, Cheng1 <[email protected]>; [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>
Subject: RE: [PATCH v7 2/4] vhost: add support for packed ring in async vhost

Hi Cheng,

Quoted 86 lines

-----Original Message-----
From: Jiang, Cheng1 <[email protected]>
Sent: Wednesday, April 14, 2021 2:14 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [PATCH v7 2/4] vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  49 ++--
 lib/librte_vhost/vhost.h           |  15 +-
 lib/librte_vhost/virtio_net.c      | 432 +++++++++++++++++++++++++++--
 4 files changed, 456 insertions(+), 41 deletions(-)
lib/librte_vhost/rte_vhost_async.h +0 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {  struct

async_inflight_info { struct rte_mbuf *mbuf; uint16_t descs; /* num of descs inflight */

lib/librte_vhost/rte_vhost_async.h +1 -0
+uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c index renamed from lib/librte_vhost/vhost.c +0 -0

a70fe01d8..f509186c6 100644

lib/librte_vhost/vhost.c +16 -11
@@ -338,19 +338,22 @@ cleanup_device(struct virtio_net *dev, int
 destroy)  }
 
 static void
-vhost_free_async_mem(struct vhost_virtqueue *vq)
+vhost_free_async_mem(struct virtio_net *dev, struct vhost_virtqueue
+*vq)
 {
-if (vq->async_pkts_info)
-rte_free(vq->async_pkts_info);
-if (vq->async_descs_split)
+rte_free(vq->async_pkts_info);
+
+if (vq_is_packed(dev)) {
+rte_free(vq->async_buffers_packed);
+vq->async_buffers_packed = NULL;
+} else {
 rte_free(vq->async_descs_split);
-if (vq->it_pool)
-rte_free(vq->it_pool);
-if (vq->vec_pool)
-rte_free(vq->vec_pool);
+vq->async_descs_split = NULL;
+}
+
+rte_free(vq->it_pool);
+rte_free(vq->vec_pool);
 
 vq->async_pkts_info = NULL;
-vq->async_descs_split = NULL;
 vq->it_pool = NULL;
 vq->vec_pool = NULL;
 }
@@ -360,10 +363,10 @@ free_vq(struct virtio_net *dev, struct
 vhost_virtqueue *vq)  {  if (vq_is_packed(dev))
 rte_free(vq->shadow_used_packed); -else {
+else
 rte_free(vq->shadow_used_split);
-vhost_free_async_mem(vq);
-}
+
+vhost_free_async_mem(dev, vq);
 rte_free(vq->batch_copy_elems);
 if (vq->iotlb_pool)
 rte_mempool_free(vq->iotlb_pool);
@@ -1626,10 +1629,9 @@ int rte_vhost_async_channel_register(int vid,
 uint16_t queue_id,  if (unlikely(vq == NULL || !dev->async_copy))
 return -1;

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, · line 1637

Quoted 10 lines
lib/librte_vhost/vhost.c +3 -3
 
-/* packed queue is not supported */
-if (unlikely(vq_is_packed(dev) || !f.async_inorder)) {
+if (unlikely(!f.async_inorder)) {
 VHOST_LOG_CONFIG(ERR,
-"async copy is not supported on packed queue or non-inorder mode "
+"async copy is not supported on non-inorder mode "
 "(vid %d, qid: %d)\n", vid, queue_id);  return -1;  } @@ -1667,12
+1669,19 @@ int rte_vhost_async_channel_register(int vid, uint16_t
 queue_id,  vq->vec_pool = rte_malloc_socket(NULL,

VHOST_MAX_ASYNC_VEC

Quoted 9 lines

* sizeof(struct iovec), RTE_CACHE_LINE_SIZE, node);

lib/librte_vhost/vhost.c +6 -1
-vq->async_descs_split = rte_malloc_socket(NULL,
+if (vq_is_packed(dev)) {
+vq->async_buffers_packed = rte_malloc_socket(NULL, size *
+vq->sizeof(struct vring_used_elem_packed),
+RTE_CACHE_LINE_SIZE, node);
+} else {
+vq->async_descs_split = rte_malloc_socket(NULL,
 vq->size * sizeof(struct vring_used_elem),  RTE_CACHE_LINE_SIZE,
Quoted 8 lines

lib/librte_vhost/vhost.c int rte_vhost_async_channel_register(int vid, uint16_t queue_id, · line 1682

Quoted 5 lines

node); -if (!vq->async_descs_split || !vq->async_pkts_info || -!vq->it_pool || !vq->vec_pool) { -vhost_free_async_mem(vq);

lib/librte_vhost/vhost.c +3 -0
+}
+
+if (!vq->async_buffers_packed || !vq->async_descs_split ||

async_buffers_packed and async_descs_split are two members of a union. Like the way processed in vhost_free_async_mem(), do you think it's better to check if they are NULL in if-else respectively?

Sure, make sense to me. I'll fix them together with other async mem check.

Quoted 140 lines
Quoted 35 lines

+!vq->async_pkts_info || !vq->it_pool || !vq->vec_pool) { +vhost_free_async_mem(dev, vq);
VHOST_LOG_CONFIG(ERR,
"async register failed: cannot allocate memory for vq data " "(vid %d, qid: %d)\n", vid, queue_id); @@ -1728,7 +1737,7 @@ int
rte_vhost_async_channel_unregister(int vid, uint16_t queue_id) goto out; }

lib/librte_vhost/vhost.c +1 -1
-vhost_free_async_mem(vq);
+vhost_free_async_mem(dev, vq);
 
 vq->async_ops.transfer_data = NULL;
 vq->async_ops.check_completed_copies = NULL; diff --git

a/lib/librte_vhost/vhost.h b/lib/librte_vhost/vhost.h index f628714c2..673335217 100644

lib/librte_vhost/vhost.h +0 -0
@@ -201,9 +201,18 @@ struct vhost_virtqueue {  uint16_tasync_pkts_idx;

uint16_tasync_pkts_inflight_n; uint16_tasync_last_pkts_n; -struct vring_used_elem *async_descs_split; -uint16_t async_desc_idx;

lib/librte_vhost/vhost.h +5 -1
-uint16_t last_async_desc_idx;
+union {
+struct vring_used_elem  *async_descs_split; struct
+vring_used_elem_packed *async_buffers_packed; }; union { uint16_t
+async_desc_idx; uint16_t async_packed_buffer_idx; }; union { uint16_t
+last_async_desc_idx; uint16_t last_async_buffer_idx; };
 
 /* vq async features */
 boolasync_inorder;
(quoted diff) +0 -0

b/lib/librte_vhost/virtio_net.c index 438bdafd1..54e11e3a5 100644

lib/librte_vhost/virtio_net.c +0 -0
@@ -363,14 +363,14 @@
Quoted 1 line

vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue

*vq,

Quoted 103 lines

}

static __rte_always_inline void

lib/librte_vhost/virtio_net.c +5 -5
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-   struct vhost_virtqueue *vq,
-   uint32_t len[],
-   uint16_t id[],
-   uint16_t count[],
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
+   uint32_t *len,
+   uint16_t *id,
+   uint16_t *count,
    uint16_t num_buffers)
 {
 uint16_t i;
+
 for (i = 0; i < num_buffers; i++) {
 /* enqueue shadow flush action aligned with batch num */  if

(!vq->shadow_used_idx) @@ -382,6 +382,17 @@
vhost_shadow_enqueue_single_packed(struct
virtio_net *dev,
vq->shadow_aligned_idx += count[i];
vq->shadow_used_idx++;
}

lib/librte_vhost/virtio_net.c +11 -0
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+   struct vhost_virtqueue *vq,
+   uint32_t *len,
+   uint16_t *id,
+   uint16_t *count,
+   uint16_t num_buffers)
+{
+vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {

do_data_copy_enqueue(dev, vq); @@ -1474,6 +1485,23 @@ store_dma_desc_info_split(struct vring_used_elem *s_ring, struct vring_used_elem } }

lib/librte_vhost/virtio_net.c +4 -0
+static __rte_always_inline void
+store_dma_desc_info_packed(struct vring_used_elem_packed *s_ring,
+struct vring_used_elem_packed *d_ring, uint16_t ring_size, uint16_t
+s_idx, uint16_t d_idx, uint16_t

count)

lib/librte_vhost/virtio_net.c +5 -0
+{
+uint16_t elem_size = sizeof(struct vring_used_elem_packed);
+
+if (d_idx + count <= ring_size) {
+rte_memcpy(d_ring + d_idx, s_ring + s_idx, count *

elem_size);

lib/librte_vhost/virtio_net.c +5 -0
+} else {
+uint16_t size = ring_size - d_idx;
+
+rte_memcpy(d_ring + d_idx, s_ring + s_idx, size * elem_size);
+rte_memcpy(d_ring, s_ring + s_idx + size, (count - size) *

elem_size);

lib/librte_vhost/virtio_net.c +3 -0
+}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,  struct

vhost_virtqueue *vq, uint16_t queue_id, @@ -1641,6 +1669,330 @@ virtio_dev_rx_async_submit_split(struct
virtio_net *dev,
return pkt_idx;
}

lib/librte_vhost/virtio_net.c +34 -0
+static __rte_always_inline void
+vhost_update_used_packed(struct vhost_virtqueue *vq, struct
+vring_used_elem_packed *shadow_ring, uint16_t count) { int i;
+uint16_t used_idx = vq->last_used_idx; uint16_t head_idx =
+vq->last_used_idx; uint16_t head_flags = 0;
+
+if (count == 0)
+return;
+
+/* Split loop in two to save memory barriers */ for (i = 0; i <
+count; i++) {
+vq->desc_packed[used_idx].id = shadow_ring[i].id;
+vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+used_idx += shadow_ring[i].count;
+if (used_idx >= vq->size)
+used_idx -= vq->size;
+}
+
+/* The ordering for storing desc flags needs to be enforced. */
+rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+for (i = 0; i < count; i++) {
+uint16_t flags;
+
+if (vq->shadow_used_packed[i].len)
+flags = VRING_DESC_F_WRITE;
+else
+flags = 0;
+
+if (vq->used_wrap_counter) {
+flags |= VRING_DESC_F_USED;
+flags |= VRING_DESC_F_AVAIL;
+} else {
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1716

Quoted 7 lines
lib/librte_vhost/virtio_net.c +7 -0
+flags &= ~VRING_DESC_F_USED;
+flags &= ~VRING_DESC_F_AVAIL;
+}
+
+if (i > 0) {
+vq->desc_packed[vq->last_used_idx].flags = flags;
+

No need a blank line above.

Sure, thanks.

Quoted 129 lines
Quoted 95 lines
lib/librte_vhost/virtio_net.c +24 -0
+} else {
+head_idx = vq->last_used_idx;
+head_flags = flags;
+}
+
+vq_inc_last_used_packed(vq, shadow_ring[i].count); }
+
+vq->desc_packed[head_idx].flags = head_flags;
+}
+
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+    struct vhost_virtqueue *vq,
+    struct rte_mbuf *pkt,
+    struct buf_vector *buf_vec,
+    uint16_t *nr_descs,
+    uint16_t *nr_buffers,
+    struct vring_packed_desc *async_descs,
+    struct iovec *src_iovec, struct iovec *dst_iovec,
+    struct rte_vhost_iov_iter *src_it,
+    struct rte_vhost_iov_iter *dst_it) { uint16_t nr_vec = 0;
+uint16_t avail_idx = vq->last_avail_idx; uint16_t max_tries, tries =
+0; uint16_t buf_id = 0; uint32_t len = 0; uint16_t desc_count = 0;
+uint32_t size = pkt->pkt_len + sizeof(struct

virtio_net_hdr_mrg_rxbuf);

lib/librte_vhost/virtio_net.c +19 -0
+uint32_t buffer_len[vq->size];
+uint16_t buffer_buf_id[vq->size];
+uint16_t buffer_desc_count[vq->size]; *nr_buffers = 0;
+
+if (rxvq_is_mergeable(dev))
+max_tries = vq->size - 1;
+else
+max_tries = 1;
+
+while (size > 0) {
+/*
+ * if we tried all available ring items, and still
+ * can't get enough buf, it means something abnormal
+ * happened.
+ */
+if (unlikely(++tries > max_tries))
+return -1;
+
+if (unlikely(fill_vec_buf_packed(dev, vq, avail_idx,

&desc_count, buf_vec, &nr_vec,
+&buf_id, &len,
VHOST_ACCESS_RW) < 0))

lib/librte_vhost/virtio_net.c +16 -0
+return -1;
+
+len = RTE_MIN(len, size);
+size -= len;
+
+buffer_len[*nr_buffers] = len;
+buffer_buf_id[*nr_buffers] = buf_id;
+buffer_desc_count[*nr_buffers] = desc_count; *nr_buffers += 1;
+
+*nr_descs += desc_count;
+avail_idx += desc_count;
+if (avail_idx >= vq->size)
+avail_idx -= vq->size;
+}
+
+if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,

src_iovec, dst_iovec,

lib/librte_vhost/virtio_net.c +5 -0
+src_it, dst_it) < 0)
+return -1;
+/* store descriptors for DMA */
+if (avail_idx >= *nr_descs) {
+rte_memcpy(async_descs, &vq->desc_packed[vq-
Quoted 1 line

last_avail_idx],

lib/librte_vhost/virtio_net.c +3 -0
+*nr_descs * sizeof(struct vring_packed_desc)); } else { uint16_t
+nr_copy = vq->size - vq->last_avail_idx; rte_memcpy(async_descs,
+&vq->desc_packed[vq-
Quoted 1 line

last_avail_idx],

lib/librte_vhost/virtio_net.c +2 -0
+nr_copy * sizeof(struct vring_packed_desc)); rte_memcpy(async_descs +
+nr_copy, vq->desc_packed, (*nr_descs - nr_copy) * sizeof(struct

vring_packed_desc));

lib/librte_vhost/virtio_net.c +3 -0
+}
+
+vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,

buffer_desc_count, *nr_buffers);

lib/librte_vhost/virtio_net.c +6 -0
+
+return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev, struct

vhost_virtqueue *vq,
+ struct rte_mbuf *pkt, uint16_t *nr_descs, uint16_t
*nr_buffers,

lib/librte_vhost/virtio_net.c +3 -0
+    struct vring_packed_desc *async_descs,
+    struct iovec *src_iovec, struct iovec *dst_iovec,
+    struct rte_vhost_iov_iter *src_it, struct

rte_vhost_iov_iter *dst_it)

lib/librte_vhost/virtio_net.c

Quoted 2 lines
lib/librte_vhost/virtio_net.c +2 -0
+{
+struct buf_vector buf_vec[BUF_VECTOR_MAX]; *nr_descs = 0;

*nr_buffers

Quoted 31 lines

+= 0;

lib/librte_vhost/virtio_net.c +2 -0
+
+if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,

buf_vec, nr_descs, nr_buffers,
+ async_descs, src_iovec,
dst_iovec,
+ src_it, dst_it) < 0)) { +VHOST_LOG_DATA(DEBUG, "(%d) failed to get enough desc from vring\n", dev->vid);

lib/librte_vhost/virtio_net.c +4 -0
+return -1;
+}
+
+VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",
+dev->vid, vq->last_avail_idx, vq->last_avail_idx + *nr_descs);

lib/librte_vhost/virtio_net.c +6 -0
+
+return 0;
+}
+
+static __rte_always_inline void
+dma_error_handler_packed(struct vhost_virtqueue *vq, struct

vring_packed_desc *async_descs,
+uint16_t async_descs_idx, uint16_t slot_idx, uint32_t nr_err,
+uint32_t *pkt_idx, uint32_t *num_async_pkts, uint32_t *num_done_pkts)

lib/librte_vhost/virtio_net.c +4 -0
+{
+uint16_t descs_err = 0;
+uint16_t buffers_err = 0;
+struct async_inflight_info *pkts_info = vq->async_pkts_info;
Quoted 8 lines

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 6 lines
lib/librte_vhost/virtio_net.c +6 -0
+
+*num_async_pkts -= nr_err;
+*pkt_idx -= nr_err;
+/* calculate the sum of buffers and descs of DMA-error packets. */
+while (nr_err-- > 0) { descs_err += pkts_info[slot_idx %
+vq->size].descs;

I notice there are several parts using "%" to wrap around index, but existed code uses "& (vq->size - 1)" instead. I think it's better to keep it consistent.

Unlike split ring, packed ring size is not necessarily a power of 2. So I can't use "& (vq->size - 1) in packed ring.

Thanks,
Cheng

Quoted 3 lines

Thanks,
Jiayu

#39 lore raw

PATCH v7 3/4 vhost: add batch datapath for async vhost packed ring

In reply to Cheng Jiang

Add batch datapath for async vhost packed ring to improve the performance of small packet processing.

---
 lib/librte_vhost/virtio_net.c | 41 +++++++++++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 4 deletions(-)
lib/librte_vhost/virtio_net.c +37 -4
index 54e11e3a5..7ba186585 100644
@@ -1725,6 +1725,29 @@ vhost_update_used_packed(struct vhost_virtqueue *vq,
 	vq->desc_packed[head_idx].flags = head_flags;
 }
 
+static __rte_always_inline int
+virtio_dev_rx_async_batch_packed(struct virtio_net *dev,
+			   struct vhost_virtqueue *vq,
+			   struct rte_mbuf **pkts,
+			   struct rte_mbuf **comp_pkts, uint32_t *pkt_done)
+{
+	uint16_t i;
+	uint32_t cpy_threshold = vq->async_threshold;
+
+	vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE) {
+		if (unlikely(pkts[i]->pkt_len >= cpy_threshold))
+			return -1;
+	}
+	if (!virtio_dev_rx_batch_packed(dev, vq, pkts)) {
+		vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE)
+			comp_pkts[(*pkt_done)++] = pkts[i];
+
+		return 0;
+	}
+
+	return -1;
+}
+
 static __rte_always_inline int
 vhost_enqueue_async_single_packed(struct virtio_net *dev,
 			    struct vhost_virtqueue *vq,
@@ -1875,6 +1898,7 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	struct rte_mbuf **comp_pkts, uint32_t *comp_count)
 {
 	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint32_t remained = count;
 	uint16_t async_descs_idx = 0;
 	uint16_t num_buffers;
 	uint16_t num_desc;
@@ -1892,9 +1916,17 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
 	struct vring_packed_desc async_descs[vq->size];
 
-	rte_prefetch0(&vq->desc_packed[vq->last_avail_idx]);
+	do {
+		rte_prefetch0(&vq->desc_packed[vq->last_avail_idx]);
+		if (remained >= PACKED_BATCH_SIZE) {
+			if (!virtio_dev_rx_async_batch_packed(dev, vq,
+				&pkts[pkt_idx], comp_pkts, &num_done_pkts)) {
+				pkt_idx += PACKED_BATCH_SIZE;
+				remained -= PACKED_BATCH_SIZE;
+				continue;
+			}
+		}
 
-	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
 		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq, pkts[pkt_idx],
 						&num_desc, &num_buffers,
 						&async_descs[async_descs_idx],
@@ -1937,6 +1969,8 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
 		}
 
+		pkt_idx++;
+		remained--;
 		vq_inc_last_avail_packed(vq, num_desc);
 
 		/*
@@ -1961,13 +1995,12 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 				 */
 				pkt_err = pkt_burst_idx - n_pkts;
 				pkt_burst_idx = 0;
-				pkt_idx++;
 				break;
 			}
 
 			pkt_burst_idx = 0;
 		}
-	}
+	} while (pkt_idx < count);
 
 	if (pkt_burst_idx) {
 		n_pkts = vq->async_ops.transfer_data(dev->vid, queue_id, tdes, 0, pkt_burst_idx);
Signature · 1 line
2.29.2
#40 lore raw

PATCH v7 4/4 doc: add release note for vhost async packed ring

In reply to Cheng Jiang

Add release note for the support of vhost async packed ring.

---
 doc/guides/rel_notes/release_21_05.rst | 4 ++++
 1 file changed, 4 insertions(+)
doc/guides/rel_notes/release_21_05.rst +4 -0
index 374d6d98e..eb5200669 100644
@@ -131,6 +131,10 @@ New Features
   * Added command to display Rx queue used descriptor count.
     ``show port (port_id) rxq (queue_id) desc used count``
 
+* **Added support for vhost async packed ring data path.**
+
+  Added packed ring support for async vhost.
+
 
 Removed Items
 -------------
Signature · 1 line
2.29.2
#41 lore raw

v8 cover add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch set cleans async split ring codes and enables packed ring in async vhost data path. Batch datapath is also enabled in async vhost packed ring.

v8:
* fix some variable names for consistency
* clean codes
v7:
* fix compile issues
* add argument *dev in vhost_free_async_mem() for ring type decision
v6:
* fix some typos in commit log
* improve index usage
* remove shadow_ring_store()
* add store_dma_desc_info_split() store_dma_desc_info_packed()
* remove some checks in vhost_free_async_mem()
* change index calculation since the size isn't necessarily a power of 2
* move error handling in a dedicated function
* clean codes
v5:
* clean some codes for packed ring datapath
* fix an index error in shadow_ring_store()
v4:
* change the patch structure
* clean code for async split ring
* reuse some code from split ring
* change the error handler for DMA-copy packet
* add check for malloc
* remove useless code
* add doc update
v3:
* fix error handler for DMA-copy packet
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

Cheng Jiang (4):
vhost: abstract and reorganize async split ring code
vhost: add support for packed ring in async vhost
vhost: add batch datapath for async vhost packed ring
doc: add release note for vhost async packed ring

 doc/guides/rel_notes/release_21_05.rst |   4 +
 lib/librte_vhost/rte_vhost_async.h     |   1 +
 lib/librte_vhost/vhost.c               |  79 +++-
 lib/librte_vhost/vhost.h               |  15 +-
 lib/librte_vhost/virtio_net.c          | 598 +++++++++++++++++++++----
 5 files changed, 587 insertions(+), 110 deletions(-)

--
2.29.2

#42 lore raw

PATCH v8 1/4 vhost: abstract and reorganize async split ring code

This patch puts some codes of async vhost split ring into inline functions to improve the readability of the code. And, it changes the pointer index style of iterator to make the code more concise.

---
 lib/librte_vhost/virtio_net.c | 132 +++++++++++++++++-----------------
 1 file changed, 66 insertions(+), 66 deletions(-)
lib/librte_vhost/virtio_net.c +66 -66
index ff3987860..438bdafd1 100644
@@ -1458,6 +1458,22 @@ virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+store_dma_desc_info_split(struct vring_used_elem *s_ring, struct vring_used_elem *d_ring,
+		uint16_t ring_size, uint16_t s_idx, uint16_t d_idx, uint16_t count)
+{
+	uint16_t elem_size = sizeof(struct vring_used_elem);
+
+	if (d_idx + count <= ring_size) {
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, count * elem_size);
+	} else {
+		uint16_t size = ring_size - d_idx;
+
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, size * elem_size);
+		rte_memcpy(d_ring, s_ring + s_idx + size, (count - size) * elem_size);
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1474,10 +1490,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
 	struct iovec *src_iovec = vec_pool;
 	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-	struct rte_vhost_iov_iter *src_it = it_pool;
-	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
@@ -1511,29 +1526,30 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			dev->vid, vq->last_avail_idx,
 			vq->last_avail_idx + num_buffers);
 
-		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
-				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx], buf_vec, nr_vec, num_buffers,
+				&src_iovec[iovec_idx], &dst_iovec[iovec_idx],
+				&it_pool[it_idx], &it_pool[it_idx + 1]) < 0) {
 			vq->shadow_used_idx -= num_buffers;
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (it_pool[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&it_pool[it_idx], &it_pool[it_idx + 1]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += it_pool[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += it_pool[it_idx].nr_segs;
 
 			/**
 			 * recover shadow used ring and keep DMA-occupied
@@ -1541,23 +1557,10 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						num_buffers *
-						sizeof(struct vring_used_elem));
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->async_descs_split,
-						&vq->shadow_used_split[from +
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			store_dma_desc_info_split(vq->shadow_used_split,
+					vq->async_descs_split, vq->size, from, to, num_buffers);
+
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1578,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
 			vq->async_pkts_inflight_n += n_pkts;
 
@@ -1639,6 +1641,36 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)
+{
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left : vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to], &vq->async_descs_split[from],
+					nr_copy * sizeof(struct vring_used_elem));
+		} else {
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to], &vq->async_descs_split[from],
+					size * sizeof(struct vring_used_elem));
+			rte_memcpy(&vq->used->ring[0], &vq->async_descs_split[from + size],
+					(nr_copy - size) * sizeof(struct vring_used_elem));
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
@@ -1695,39 +1727,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						nr_copy *
-						sizeof(struct vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from +
-						size], (nr_copy - size) *
-						sizeof(struct vring_used_elem));
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
 		vhost_vring_call_split(dev, vq);

--
2.29.2

Reviewed-by on v8 1/4
#43 lore raw

Re: [dpdk-dev] [PATCH v8 1/4] vhost: abstract and reorganize async split ring code

Quoted 253 lines

-----Original Message-----
From: Jiang, Cheng1 <[email protected]>
Sent: Monday, April 19, 2021 4:51 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [PATCH v8 1/4] vhost: abstract and reorganize async split ring code

This patch puts some codes of async vhost split ring into inline functions to improve the readability of the code. And, it changes the pointer index style of iterator to make the code more concise.

---
 lib/librte_vhost/virtio_net.c | 132 +++++++++++++++++-----------------
 1 file changed, 66 insertions(+), 66 deletions(-)
lib/librte_vhost/virtio_net.c +8 -0
index ff3987860..438bdafd1 100644
@@ -1458,6 +1458,22 @@ virtio_dev_rx_async_get_info_idx(uint16_t
 pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+store_dma_desc_info_split(struct vring_used_elem *s_ring, struct
 vring_used_elem *d_ring,
+		uint16_t ring_size, uint16_t s_idx, uint16_t d_idx, uint16_t
 count)
+{
+	uint16_t elem_size = sizeof(struct vring_used_elem);
+
+	if (d_idx + count <= ring_size) {
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, count *

elem_size);

lib/librte_vhost/virtio_net.c +5 -0
+	} else {
+		uint16_t size = ring_size - d_idx;
+
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, size * elem_size);
+		rte_memcpy(d_ring, s_ring + s_idx + size, (count - size) *

elem_size);

lib/librte_vhost/virtio_net.c +4 -2
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1474,10 +1490,9 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
 	struct iovec *src_iovec = vec_pool;
 	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-	struct rte_vhost_iov_iter *src_it = it_pool;
-	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
lib/librte_vhost/virtio_net.c +11 -10
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
@@ -1511,29 +1526,30 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 			dev->vid, vq->last_avail_idx,
 			vq->last_avail_idx + num_buffers);
 
-		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
-				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx], buf_vec,
 nr_vec, num_buffers,
+				&src_iovec[iovec_idx], &dst_iovec[iovec_idx],
+				&it_pool[it_idx], &it_pool[it_idx + 1]) < 0) {
 			vq->shadow_used_idx -= num_buffers;
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (it_pool[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&it_pool[it_idx], &it_pool[it_idx + 1]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += it_pool[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += it_pool[it_idx].nr_segs;
 
lib/librte_vhost/virtio_net.c +0 -14
 			/**
 			 * recover shadow used ring and keep DMA-occupied
@@ -1541,23 +1557,10 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq-
 >shadow_used_split[from],
-						num_buffers *
-						sizeof(struct
 vring_used_elem));
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq-
 >shadow_used_split[from],
-						size *
-						sizeof(struct
 vring_used_elem));
-				rte_memcpy(vq->async_descs_split,
-						&vq-
Quoted 1 line

shadow_used_split[from +

lib/librte_vhost/virtio_net.c +3 -3
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			store_dma_desc_info_split(vq->shadow_used_split,
+					vq->async_descs_split, vq->size, from,

to, num_buffers);

lib/librte_vhost/virtio_net.c +4 -4
+
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1578,9 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >>
 1);
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
lib/librte_vhost/virtio_net.c +15 -0
 			vq->async_pkts_inflight_n += n_pkts;
@@ -1639,6 +1641,36 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t
 n_descs)
+{
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left : vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to], &vq-
 >async_descs_split[from],
+					nr_copy * sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +4 -0
+		} else {
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to], &vq-
Quoted 1 line

async_descs_split[from],

lib/librte_vhost/virtio_net.c +2 -0
+					size * sizeof(struct vring_used_elem));
+			rte_memcpy(&vq->used->ring[0], &vq-
Quoted 1 line

async_descs_split[from + size],

lib/librte_vhost/virtio_net.c +1 -0
+					(nr_copy - size) * sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +8 -23
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
@@ -1695,39 +1727,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int
 vid, uint16_t queue_id,
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq-
 >async_descs_split[from],
-						nr_copy *
-						sizeof(struct
 vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq-
 >async_descs_split[from],
-						size *
-						sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +1 -4
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from
+
-						size], (nr_copy - size) *
-						sizeof(struct

vring_used_elem));

lib/librte_vhost/virtio_net.c +1 -6
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs,

__ATOMIC_RELEASE);
vhost_vring_call_split(dev, vq);
--
2.29.2

#44 lore raw

PATCH v8 2/4 vhost: add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  79 ++++--
 lib/librte_vhost/vhost.h           |  15 +-
 lib/librte_vhost/virtio_net.c      | 441 +++++++++++++++++++++++++++--
 4 files changed, 488 insertions(+), 48 deletions(-)
lib/librte_vhost/rte_vhost_async.h +1 -0
index c855ff875..6faa31f5a 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/librte_vhost/vhost.c +57 -22

118 lines not on this page · raw message

lib/librte_vhost/vhost.h +12 -3
index f628714c2..b30363564 100644
@@ -201,9 +201,18 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
-	uint16_t async_desc_idx;
-	uint16_t last_async_desc_idx;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
+	union {
+		uint16_t async_desc_idx_split;
+		uint16_t async_buffer_idx_packed;
+	};
+	union {
+		uint16_t last_async_desc_idx_split;
+		uint16_t last_async_buffer_idx_packed;
+	};
 
 	/* vq async features */
 	bool		async_inorder;
lib/librte_vhost/virtio_net.c +418 -23

531 lines not on this page · raw message

Signature · 1 line
2.29.2
#45 lore raw

Hi Cheng,

Some comments are inline.

Quoted 196 lines

-----Original Message-----
From: Jiang, Cheng1 <[email protected]>
Sent: Monday, April 19, 2021 4:51 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [PATCH v8 2/4] vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  79 ++++--
 lib/librte_vhost/vhost.h           |  15 +-
 lib/librte_vhost/virtio_net.c      | 441 +++++++++++++++++++++++++++--
 4 files changed, 488 insertions(+), 48 deletions(-)
lib/librte_vhost/virtio_net.c +5 -5
index 438bdafd1..ce88ad3c0 100644
@@ -363,14 +363,14 @@
 vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue *vq,
 }
 
 static __rte_always_inline void
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-				   struct vhost_virtqueue *vq,
-				   uint32_t len[],
-				   uint16_t id[],
-				   uint16_t count[],
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
 				   uint16_t num_buffers)
 {
 	uint16_t i;
+
 	for (i = 0; i < num_buffers; i++) {
 		/* enqueue shadow flush action aligned with batch num */
lib/librte_vhost/virtio_net.c +11 -0
 		if (!vq->shadow_used_idx)
@@ -382,6 +382,17 @@ vhost_shadow_enqueue_single_packed(struct
 virtio_net *dev,
 		vq->shadow_aligned_idx += count[i];
 		vq->shadow_used_idx++;
 	}
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+				   struct vhost_virtqueue *vq,
+				   uint32_t *len,
+				   uint16_t *id,
+				   uint16_t *count,
+				   uint16_t num_buffers)
+{
+	vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 	if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {
lib/librte_vhost/virtio_net.c +0 -0
 		do_data_copy_enqueue(dev, vq);
@@ -1474,6 +1485,23 @@ store_dma_desc_info_split(struct

vring_used_elem *s_ring, struct vring_used_elem

lib/librte_vhost/virtio_net.c +4 -0
 	}
 }
 
+static __rte_always_inline void
+store_dma_desc_info_packed(struct vring_used_elem_packed *s_ring,
+		struct vring_used_elem_packed *d_ring,
+		uint16_t ring_size, uint16_t s_idx, uint16_t d_idx, uint16_t

count)

lib/librte_vhost/virtio_net.c +5 -0
+{
+	uint16_t elem_size = sizeof(struct vring_used_elem_packed);
+
+	if (d_idx + count <= ring_size) {
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, count *

elem_size);

lib/librte_vhost/virtio_net.c +5 -0
+	} else {
+		uint16_t size = ring_size - d_idx;
+
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, size * elem_size);
+		rte_memcpy(d_ring, s_ring + s_idx + size, (count - size) *

elem_size);

lib/librte_vhost/virtio_net.c +5 -2
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1556,12 +1584,12 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 			 * descriptors.
 			 */
 			from = vq->shadow_used_idx - num_buffers;
-			to = vq->async_desc_idx & (vq->size - 1);
+			to = vq->async_desc_idx_split & (vq->size - 1);
 
 			store_dma_desc_info_split(vq->shadow_used_split,
 					vq->async_descs_split, vq->size, from,
 to, num_buffers);
 
-			vq->async_desc_idx += num_buffers;
+			vq->async_desc_idx_split += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
lib/librte_vhost/virtio_net.c +1 -1
 		} else
 			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
@@ -1619,7 +1647,7 @@ virtio_dev_rx_async_submit_split(struct virtio_net
 *dev,
 			num_descs += pkts_info[slot_idx & (vq->size -
 1)].descs;
 			slot_idx--;
 		}
-		vq->async_desc_idx -= num_descs;
+		vq->async_desc_idx_split -= num_descs;
 		/* recover shadow used ring and available ring */

vq->shadow_used_idx -= (vq->last_avail_idx -

async_pkts_log[num_async_pkts].last_avail_idx -

lib/librte_vhost/virtio_net.c +69 -0
@@ -1641,6 +1669,329 @@ virtio_dev_rx_async_submit_split(struct
 virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+vhost_update_used_packed(struct vhost_virtqueue *vq,
+			struct vring_used_elem_packed *shadow_ring,
+			uint16_t count)
+{
+	int i;
+	uint16_t used_idx = vq->last_used_idx;
+	uint16_t head_idx = vq->last_used_idx;
+	uint16_t head_flags = 0;
+
+	if (count == 0)
+		return;
+
+	/* Split loop in two to save memory barriers */
+	for (i = 0; i < count; i++) {
+		vq->desc_packed[used_idx].id = shadow_ring[i].id;
+		vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+		used_idx += shadow_ring[i].count;
+		if (used_idx >= vq->size)
+			used_idx -= vq->size;
+	}
+
+	/* The ordering for storing desc flags needs to be enforced. */
+	rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+	for (i = 0; i < count; i++) {
+		uint16_t flags;
+
+		if (vq->shadow_used_packed[i].len)
+			flags = VRING_DESC_F_WRITE;
+		else
+			flags = 0;
+
+		if (vq->used_wrap_counter) {
+			flags |= VRING_DESC_F_USED;
+			flags |= VRING_DESC_F_AVAIL;
+		} else {
+			flags &= ~VRING_DESC_F_USED;
+			flags &= ~VRING_DESC_F_AVAIL;
+		}
+
+		if (i > 0) {
+			vq->desc_packed[vq->last_used_idx].flags = flags;
+		} else {
+			head_idx = vq->last_used_idx;
+			head_flags = flags;
+		}
+
+		vq_inc_last_used_packed(vq, shadow_ring[i].count);
+	}
+
+	vq->desc_packed[head_idx].flags = head_flags;
+}
+
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+			    struct vhost_virtqueue *vq,
+			    struct rte_mbuf *pkt,
+			    struct buf_vector *buf_vec,
+			    uint16_t *nr_descs,
+			    uint16_t *nr_buffers,
+			    struct vring_packed_desc *async_descs,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it,
+			    struct rte_vhost_iov_iter *dst_it)
+{
+	uint16_t nr_vec = 0;
+	uint16_t avail_idx = vq->last_avail_idx;

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct · line 1751

Quoted 10 lines
lib/librte_vhost/virtio_net.c +9 -0
+	uint16_t max_tries, tries = 0;
+	uint16_t buf_id = 0;
+	uint32_t len = 0;
+	uint16_t desc_count = 0;
+	uint32_t size = pkt->pkt_len + sizeof(struct
 virtio_net_hdr_mrg_rxbuf);
+	uint32_t buffer_len[vq->size];
+	uint16_t buffer_buf_id[vq->size];
+	uint16_t buffer_desc_count[vq->size];
+	*nr_buffers = 0;

nr_buffers and nr_descs are pointers of num_buffers and num_desc in virtio_dev_rx_async_submit_packed(), and num_buffers and num_desc don't have init values. I think you need to init them before pass their pointers to another function, as it will read/update values pointed by the pointers.

In addition, *nr_buffers is set to 0, but *nr_descs is not, and both of them are set to 0 in virtio_dev_rx_async_single_packed(). It looks strange.

Quoted 43 lines
lib/librte_vhost/virtio_net.c +17 -0
+
+	if (rxvq_is_mergeable(dev))
+		max_tries = vq->size - 1;
+	else
+		max_tries = 1;
+
+	while (size > 0) {
+		/*
+		 * if we tried all available ring items, and still
+		 * can't get enough buf, it means something abnormal
+		 * happened.
+		 */
+		if (unlikely(++tries > max_tries))
+			return -1;
+
+		if (unlikely(fill_vec_buf_packed(dev, vq, avail_idx,
 &desc_count, buf_vec, &nr_vec,
+						&buf_id, &len,

VHOST_ACCESS_RW) < 0))

lib/librte_vhost/virtio_net.c +17 -0
+			return -1;
+
+		len = RTE_MIN(len, size);
+		size -= len;
+
+		buffer_len[*nr_buffers] = len;
+		buffer_buf_id[*nr_buffers] = buf_id;
+		buffer_desc_count[*nr_buffers] = desc_count;
+		*nr_buffers += 1;
+
+		*nr_descs += desc_count;
+		avail_idx += desc_count;
+		if (avail_idx >= vq->size)
+			avail_idx -= vq->size;
+	}
+
+	if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,

src_iovec, dst_iovec,

lib/librte_vhost/virtio_net.c +5 -0
+			src_it, dst_it) < 0)
+		return -1;
+	/* store descriptors for DMA */
+	if (avail_idx >= *nr_descs) {
+		rte_memcpy(async_descs, &vq->desc_packed[vq-
Quoted 1 line

last_avail_idx],

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1791

Quoted 3 lines
lib/librte_vhost/virtio_net.c +3 -0
+			*nr_descs * sizeof(struct vring_packed_desc));
+	} else {
+		uint16_t nr_copy = vq->size - vq->last_avail_idx;

It needs a blank line.

Quoted 93 lines
lib/librte_vhost/virtio_net.c +1 -0
+		rte_memcpy(async_descs, &vq->desc_packed[vq-
Quoted 1 line

last_avail_idx],

lib/librte_vhost/virtio_net.c +3 -0
+			nr_copy * sizeof(struct vring_packed_desc));
+		rte_memcpy(async_descs + nr_copy, vq->desc_packed,
+			(*nr_descs - nr_copy) * sizeof(struct

vring_packed_desc));

lib/librte_vhost/virtio_net.c +3 -0
+	}
+
+	vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,

buffer_desc_count, *nr_buffers);

lib/librte_vhost/virtio_net.c +6 -0
+
+	return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev, struct

vhost_virtqueue *vq,

lib/librte_vhost/virtio_net.c +1 -0
+			    struct rte_mbuf *pkt, uint16_t *nr_descs, uint16_t

*nr_buffers,

lib/librte_vhost/virtio_net.c +3 -0
+			    struct vring_packed_desc *async_descs,
+			    struct iovec *src_iovec, struct iovec *dst_iovec,
+			    struct rte_vhost_iov_iter *src_it, struct

rte_vhost_iov_iter *dst_it)

lib/librte_vhost/virtio_net.c +6 -0
+{
+	struct buf_vector buf_vec[BUF_VECTOR_MAX];
+	*nr_descs = 0;
+	*nr_buffers = 0;
+
+	if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,

buf_vec, nr_descs, nr_buffers,

lib/librte_vhost/virtio_net.c +1 -0
+						 async_descs, src_iovec,

dst_iovec,

lib/librte_vhost/virtio_net.c +2 -0
+						 src_it, dst_it) < 0)) {
+		VHOST_LOG_DATA(DEBUG, "(%d) failed to get enough desc

from vring\n", dev->vid);

lib/librte_vhost/virtio_net.c +4 -0
+		return -1;
+	}
+
+	VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",

lib/librte_vhost/virtio_net.c +1 -0
+			dev->vid, vq->last_avail_idx, vq->last_avail_idx +

*nr_descs);

lib/librte_vhost/virtio_net.c +6 -0
+
+	return 0;
+}
+
+static __rte_always_inline void
+dma_error_handler_packed(struct vhost_virtqueue *vq, struct

vring_packed_desc *async_descs,

lib/librte_vhost/virtio_net.c +1 -0
+			uint16_t async_descs_idx, uint16_t slot_idx, uint32_t

nr_err,

lib/librte_vhost/virtio_net.c +1 -0
+			uint32_t *pkt_idx, uint32_t *num_async_pkts,

uint32_t *num_done_pkts)

lib/librte_vhost/virtio_net.c +32 -0
+{
+	uint16_t descs_err = 0;
+	uint16_t buffers_err = 0;
+	struct async_inflight_info *pkts_info = vq->async_pkts_info;
+
+	*num_async_pkts -= nr_err;
+	*pkt_idx -= nr_err;
+	/* calculate the sum of buffers and descs of DMA-error packets. */
+	while (nr_err-- > 0) {
+		descs_err += pkts_info[slot_idx % vq->size].descs;
+		buffers_err += pkts_info[slot_idx % vq->size].nr_buffers;
+		slot_idx--;
+	}
+
+	vq->async_buffer_idx_packed -= buffers_err;
+
+	if (vq->last_avail_idx >= descs_err) {
+		vq->last_avail_idx -= descs_err;
+
+		rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+			&async_descs[async_descs_idx - descs_err],
+			descs_err * sizeof(struct vring_packed_desc));
+	} else {
+		uint16_t nr_copy;
+
+		vq->last_avail_idx = vq->last_avail_idx + vq->size - descs_err;
+		nr_copy = vq->size - vq->last_avail_idx;
+		rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+			&async_descs[async_descs_idx - descs_err],
+			nr_copy * sizeof(struct vring_packed_desc));
+		descs_err -= nr_copy;
+		rte_memcpy(&vq->desc_packed[0],

&async_descs[async_descs_idx - descs_err],

lib/librte_vhost/virtio_net.c +7 -0
+			descs_err * sizeof(struct vring_packed_desc));
+		vq->avail_wrap_counter ^= 1;
+	}
+
+	*num_done_pkts = *pkt_idx - *num_async_pkts;
+}
+

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev, · line 1879

Quoted 10 lines
lib/librte_vhost/virtio_net.c +10 -0
+static __rte_noinline uint32_t
+virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
+	struct vhost_virtqueue *vq, uint16_t queue_id,
+	struct rte_mbuf **pkts, uint32_t count,
+	struct rte_mbuf **comp_pkts, uint32_t *comp_count)
+{
+	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint16_t async_descs_idx = 0;
+	uint16_t num_buffers;
+	uint16_t num_desc;

How about using "num_descs" to make naming consist with "num_buffers"?

Thanks,
Jiayu

#46 lore raw

Hi Jiayu,

Quoted 208 lines

-----Original Message-----
From: Hu, Jiayu <[email protected]>
Sent: Tuesday, April 27, 2021 1:16 PM
To: Jiang, Cheng1 <[email protected]>; [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>
Subject: RE: [PATCH v8 2/4] vhost: add support for packed ring in async vhost

Hi Cheng,

Some comments are inline.

Quoted 27 lines

-----Original Message-----
From: Jiang, Cheng1 <[email protected]>
Sent: Monday, April 19, 2021 4:51 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [PATCH v8 2/4] vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/librte_vhost/rte_vhost_async.h |   1 +
 lib/librte_vhost/vhost.c           |  79 ++++--
 lib/librte_vhost/vhost.h           |  15 +-
 lib/librte_vhost/virtio_net.c      | 441 +++++++++++++++++++++++++++--
 4 files changed, 488 insertions(+), 48 deletions(-)
(quoted diff) +0 -0

b/lib/librte_vhost/virtio_net.c index 438bdafd1..ce88ad3c0 100644

lib/librte_vhost/virtio_net.c +0 -0
@@ -363,14 +363,14 @@
Quoted 1 line

vhost_shadow_dequeue_single_packed_inorder(struct vhost_virtqueue

*vq,

Quoted 158 lines

}

static __rte_always_inline void

lib/librte_vhost/virtio_net.c +5 -5
-vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
-   struct vhost_virtqueue *vq,
-   uint32_t len[],
-   uint16_t id[],
-   uint16_t count[],
+vhost_shadow_enqueue_packed(struct vhost_virtqueue *vq,
+   uint32_t *len,
+   uint16_t *id,
+   uint16_t *count,
    uint16_t num_buffers)
 {
 uint16_t i;
+
 for (i = 0; i < num_buffers; i++) {
 /* enqueue shadow flush action aligned with batch num */  if

(!vq->shadow_used_idx) @@ -382,6 +382,17 @@
vhost_shadow_enqueue_single_packed(struct
virtio_net *dev,
vq->shadow_aligned_idx += count[i];
vq->shadow_used_idx++;
}

lib/librte_vhost/virtio_net.c +11 -0
+}
+
+static __rte_always_inline void
+vhost_shadow_enqueue_single_packed(struct virtio_net *dev,
+   struct vhost_virtqueue *vq,
+   uint32_t *len,
+   uint16_t *id,
+   uint16_t *count,
+   uint16_t num_buffers)
+{
+vhost_shadow_enqueue_packed(vq, len, id, count, num_buffers);
 
 if (vq->shadow_aligned_idx >= PACKED_BATCH_SIZE) {

do_data_copy_enqueue(dev, vq); @@ -1474,6 +1485,23 @@ store_dma_desc_info_split(struct vring_used_elem *s_ring, struct vring_used_elem } }

lib/librte_vhost/virtio_net.c +4 -0
+static __rte_always_inline void
+store_dma_desc_info_packed(struct vring_used_elem_packed *s_ring,
+struct vring_used_elem_packed *d_ring, uint16_t ring_size, uint16_t
+s_idx, uint16_t d_idx, uint16_t

count)

lib/librte_vhost/virtio_net.c +5 -0
+{
+uint16_t elem_size = sizeof(struct vring_used_elem_packed);
+
+if (d_idx + count <= ring_size) {
+rte_memcpy(d_ring + d_idx, s_ring + s_idx, count *

elem_size);

lib/librte_vhost/virtio_net.c +5 -0
+} else {
+uint16_t size = ring_size - d_idx;
+
+rte_memcpy(d_ring + d_idx, s_ring + s_idx, size * elem_size);
+rte_memcpy(d_ring, s_ring + s_idx + size, (count - size) *

elem_size);

lib/librte_vhost/virtio_net.c +3 -0
+}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,  struct

vhost_virtqueue *vq, uint16_t queue_id, @@ -1556,12 +1584,12 @@ virtio_dev_rx_async_submit_split(struct
virtio_net *dev,
* descriptors.
*/
from = vq->shadow_used_idx - num_buffers; -to = vq->async_desc_idx &
(vq->size - 1);

lib/librte_vhost/virtio_net.c +2 -1
+to = vq->async_desc_idx_split & (vq->size - 1);
 
 store_dma_desc_info_split(vq->shadow_used_split,
 vq->async_descs_split, vq->size, from, to, num_buffers);
 
-vq->async_desc_idx += num_buffers;
+vq->async_desc_idx_split += num_buffers;
 vq->shadow_used_idx -= num_buffers;
 } else
 comp_pkts[num_done_pkts++] = pkts[pkt_idx]; @@ -1619,7 +1647,7 @@

virtio_dev_rx_async_submit_split(struct virtio_net *dev, num_descs += pkts_info[slot_idx & (vq->size - 1)].descs; slot_idx--; }

lib/librte_vhost/virtio_net.c +1 -1
-vq->async_desc_idx -= num_descs;
+vq->async_desc_idx_split -= num_descs;
 /* recover shadow used ring and available ring */

vq->shadow_used_idx -= (vq->last_avail_idx -

async_pkts_log[num_async_pkts].last_avail_idx - @@ -1641,6 +1669,329 @@ virtio_dev_rx_async_submit_split(struct
virtio_net *dev,
return pkt_idx;
}

lib/librte_vhost/virtio_net.c +64 -0
+static __rte_always_inline void
+vhost_update_used_packed(struct vhost_virtqueue *vq, struct
+vring_used_elem_packed *shadow_ring, uint16_t count) { int i;
+uint16_t used_idx = vq->last_used_idx; uint16_t head_idx =
+vq->last_used_idx; uint16_t head_flags = 0;
+
+if (count == 0)
+return;
+
+/* Split loop in two to save memory barriers */ for (i = 0; i <
+count; i++) {
+vq->desc_packed[used_idx].id = shadow_ring[i].id;
+vq->desc_packed[used_idx].len = shadow_ring[i].len;
+
+used_idx += shadow_ring[i].count;
+if (used_idx >= vq->size)
+used_idx -= vq->size;
+}
+
+/* The ordering for storing desc flags needs to be enforced. */
+rte_atomic_thread_fence(__ATOMIC_RELEASE);
+
+for (i = 0; i < count; i++) {
+uint16_t flags;
+
+if (vq->shadow_used_packed[i].len)
+flags = VRING_DESC_F_WRITE;
+else
+flags = 0;
+
+if (vq->used_wrap_counter) {
+flags |= VRING_DESC_F_USED;
+flags |= VRING_DESC_F_AVAIL;
+} else {
+flags &= ~VRING_DESC_F_USED;
+flags &= ~VRING_DESC_F_AVAIL;
+}
+
+if (i > 0) {
+vq->desc_packed[vq->last_used_idx].flags = flags;
+} else {
+head_idx = vq->last_used_idx;
+head_flags = flags;
+}
+
+vq_inc_last_used_packed(vq, shadow_ring[i].count); }
+
+vq->desc_packed[head_idx].flags = head_flags;
+}
+
+static __rte_always_inline int
+vhost_enqueue_async_single_packed(struct virtio_net *dev,
+    struct vhost_virtqueue *vq,
+    struct rte_mbuf *pkt,
+    struct buf_vector *buf_vec,
+    uint16_t *nr_descs,
+    uint16_t *nr_buffers,
+    struct vring_packed_desc *async_descs,
+    struct iovec *src_iovec, struct iovec *dst_iovec,
+    struct rte_vhost_iov_iter *src_it,
+    struct rte_vhost_iov_iter *dst_it) { uint16_t nr_vec = 0;
+uint16_t avail_idx = vq->last_avail_idx; uint16_t max_tries, tries =
+0; uint16_t buf_id = 0; uint32_t len = 0; uint16_t desc_count = 0;
+uint32_t size = pkt->pkt_len + sizeof(struct

virtio_net_hdr_mrg_rxbuf);

lib/librte_vhost/virtio_net.c virtio_dev_rx_async_submit_split(struct virtio_net *dev,

Quoted 3 lines
lib/librte_vhost/virtio_net.c +3 -0
+uint32_t buffer_len[vq->size];
+uint16_t buffer_buf_id[vq->size];
+uint16_t buffer_desc_count[vq->size]; *nr_buffers = 0;

nr_buffers and nr_descs are pointers of num_buffers and num_desc in virtio_dev_rx_async_submit_packed(), and num_buffers and num_desc don't have init values. I think you need to init them before pass their pointers to another function, as it will read/update values pointed by the pointers.

Quoted 3 lines

In addition, *nr_buffers is set to 0, but *nr_descs is not, and both of them are set to
0 in virtio_dev_rx_async_single_packed(). It looks strange.

Sure, I'll init them in virtio_dev_rx_async_submit_packed().

Quoted 42 lines
Quoted 42 lines
lib/librte_vhost/virtio_net.c +16 -0
+
+if (rxvq_is_mergeable(dev))
+max_tries = vq->size - 1;
+else
+max_tries = 1;
+
+while (size > 0) {
+/*
+ * if we tried all available ring items, and still
+ * can't get enough buf, it means something abnormal
+ * happened.
+ */
+if (unlikely(++tries > max_tries))
+return -1;
+
+if (unlikely(fill_vec_buf_packed(dev, vq, avail_idx,

&desc_count, buf_vec, &nr_vec,
+&buf_id, &len,
VHOST_ACCESS_RW) < 0))

lib/librte_vhost/virtio_net.c +16 -0
+return -1;
+
+len = RTE_MIN(len, size);
+size -= len;
+
+buffer_len[*nr_buffers] = len;
+buffer_buf_id[*nr_buffers] = buf_id;
+buffer_desc_count[*nr_buffers] = desc_count; *nr_buffers += 1;
+
+*nr_descs += desc_count;
+avail_idx += desc_count;
+if (avail_idx >= vq->size)
+avail_idx -= vq->size;
+}
+
+if (async_mbuf_to_desc(dev, vq, pkt, buf_vec, nr_vec, *nr_buffers,

src_iovec, dst_iovec,

lib/librte_vhost/virtio_net.c +5 -0
+src_it, dst_it) < 0)
+return -1;
+/* store descriptors for DMA */
+if (avail_idx >= *nr_descs) {
+rte_memcpy(async_descs, &vq->desc_packed[vq-
Quoted 5 lines

lib/librte_vhost/virtio_net.c

Quoted 3 lines
Quoted 1 line

last_avail_idx],

lib/librte_vhost/virtio_net.c +2 -0
+*nr_descs * sizeof(struct vring_packed_desc)); } else { uint16_t
+nr_copy = vq->size - vq->last_avail_idx;

It needs a blank line.

OK.

Quoted 86 lines
Quoted 23 lines

+rte_memcpy(async_descs, &vq->desc_packed[vq-

Quoted 1 line

last_avail_idx],

lib/librte_vhost/virtio_net.c +2 -0
+nr_copy * sizeof(struct vring_packed_desc)); rte_memcpy(async_descs +
+nr_copy, vq->desc_packed, (*nr_descs - nr_copy) * sizeof(struct

vring_packed_desc));

lib/librte_vhost/virtio_net.c +3 -0
+}
+
+vhost_shadow_enqueue_packed(vq, buffer_len, buffer_buf_id,

buffer_desc_count, *nr_buffers);

lib/librte_vhost/virtio_net.c +6 -0
+
+return 0;
+}
+
+static __rte_always_inline int16_t
+virtio_dev_rx_async_single_packed(struct virtio_net *dev, struct

vhost_virtqueue *vq,
+ struct rte_mbuf *pkt, uint16_t *nr_descs, uint16_t
*nr_buffers,

lib/librte_vhost/virtio_net.c +3 -0
+    struct vring_packed_desc *async_descs,
+    struct iovec *src_iovec, struct iovec *dst_iovec,
+    struct rte_vhost_iov_iter *src_it, struct

rte_vhost_iov_iter *dst_it)

lib/librte_vhost/virtio_net.c

Quoted 2 lines
lib/librte_vhost/virtio_net.c +2 -0
+{
+struct buf_vector buf_vec[BUF_VECTOR_MAX]; *nr_descs = 0;

*nr_buffers

Quoted 60 lines

+= 0;

lib/librte_vhost/virtio_net.c +2 -0
+
+if (unlikely(vhost_enqueue_async_single_packed(dev, vq, pkt,

buf_vec, nr_descs, nr_buffers,
+ async_descs, src_iovec,
dst_iovec,
+ src_it, dst_it) < 0)) { +VHOST_LOG_DATA(DEBUG, "(%d) failed to get enough desc from vring\n", dev->vid);

lib/librte_vhost/virtio_net.c +4 -0
+return -1;
+}
+
+VHOST_LOG_DATA(DEBUG, "(%d) current index %d | end

index %d\n",
+dev->vid, vq->last_avail_idx, vq->last_avail_idx + *nr_descs);

lib/librte_vhost/virtio_net.c +6 -0
+
+return 0;
+}
+
+static __rte_always_inline void
+dma_error_handler_packed(struct vhost_virtqueue *vq, struct

vring_packed_desc *async_descs,
+uint16_t async_descs_idx, uint16_t slot_idx, uint32_t nr_err,
+uint32_t *pkt_idx, uint32_t *num_async_pkts, uint32_t *num_done_pkts)

lib/librte_vhost/virtio_net.c +27 -0
+{
+uint16_t descs_err = 0;
+uint16_t buffers_err = 0;
+struct async_inflight_info *pkts_info = vq->async_pkts_info;
+
+*num_async_pkts -= nr_err;
+*pkt_idx -= nr_err;
+/* calculate the sum of buffers and descs of DMA-error packets. */
+while (nr_err-- > 0) { descs_err += pkts_info[slot_idx %
+vq->size].descs; buffers_err += pkts_info[slot_idx %
+vq->size].nr_buffers; slot_idx--; }
+
+vq->async_buffer_idx_packed -= buffers_err;
+
+if (vq->last_avail_idx >= descs_err) {
+vq->last_avail_idx -= descs_err;
+
+rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+&async_descs[async_descs_idx - descs_err], descs_err * sizeof(struct
+vring_packed_desc)); } else { uint16_t nr_copy;
+
+vq->last_avail_idx = vq->last_avail_idx + vq->size - descs_err;
+nr_copy = vq->size - vq->last_avail_idx;
+rte_memcpy(&vq->desc_packed[vq->last_avail_idx],
+&async_descs[async_descs_idx - descs_err], nr_copy * sizeof(struct
+vring_packed_desc)); descs_err -= nr_copy;
+rte_memcpy(&vq->desc_packed[0],

&async_descs[async_descs_idx - descs_err],

lib/librte_vhost/virtio_net.c +5 -0
+descs_err * sizeof(struct vring_packed_desc));
+vq->avail_wrap_counter ^= 1;
+}
+
+*num_done_pkts = *pkt_idx - *num_async_pkts; }
Quoted 8 lines

lib/librte_vhost/virtio_net.c

Quoted 7 lines
lib/librte_vhost/virtio_net.c +7 -0
+
+static __rte_noinline uint32_t
+virtio_dev_rx_async_submit_packed(struct virtio_net *dev, struct
+vhost_virtqueue *vq, uint16_t queue_id, struct rte_mbuf **pkts,
+uint32_t count, struct rte_mbuf **comp_pkts, uint32_t *comp_count) {
+uint32_t pkt_idx = 0, pkt_burst_idx = 0; uint16_t async_descs_idx =
+0; uint16_t num_buffers; uint16_t num_desc;

How about using "num_descs" to make naming consist with "num_buffers"?

Sure, that make sense, thanks a lot.
Cheng

Quoted 3 lines

Thanks,
Jiayu

#47 lore raw

PATCH v8 3/4 vhost: add batch datapath for async vhost packed ring

In reply to Cheng Jiang

Add batch datapath for async vhost packed ring to improve the performance of small packet processing.

---
 lib/librte_vhost/virtio_net.c | 41 +++++++++++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 4 deletions(-)
lib/librte_vhost/virtio_net.c +37 -4
index ce88ad3c0..0ad289e6e 100644
@@ -1724,6 +1724,29 @@ vhost_update_used_packed(struct vhost_virtqueue *vq,
 	vq->desc_packed[head_idx].flags = head_flags;
 }
 
+static __rte_always_inline int
+virtio_dev_rx_async_batch_packed(struct virtio_net *dev,
+			   struct vhost_virtqueue *vq,
+			   struct rte_mbuf **pkts,
+			   struct rte_mbuf **comp_pkts, uint32_t *pkt_done)
+{
+	uint16_t i;
+	uint32_t cpy_threshold = vq->async_threshold;
+
+	vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE) {
+		if (unlikely(pkts[i]->pkt_len >= cpy_threshold))
+			return -1;
+	}
+	if (!virtio_dev_rx_batch_packed(dev, vq, pkts)) {
+		vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE)
+			comp_pkts[(*pkt_done)++] = pkts[i];
+
+		return 0;
+	}
+
+	return -1;
+}
+
 static __rte_always_inline int
 vhost_enqueue_async_single_packed(struct virtio_net *dev,
 			    struct vhost_virtqueue *vq,
@@ -1874,6 +1897,7 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	struct rte_mbuf **comp_pkts, uint32_t *comp_count)
 {
 	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint32_t remained = count;
 	uint16_t async_descs_idx = 0;
 	uint16_t num_buffers;
 	uint16_t num_desc;
@@ -1891,9 +1915,17 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
 	struct vring_packed_desc async_descs[vq->size];
 
-	rte_prefetch0(&vq->desc_packed[vq->last_avail_idx]);
+	do {
+		rte_prefetch0(&vq->desc_packed[vq->last_avail_idx]);
+		if (remained >= PACKED_BATCH_SIZE) {
+			if (!virtio_dev_rx_async_batch_packed(dev, vq,
+				&pkts[pkt_idx], comp_pkts, &num_done_pkts)) {
+				pkt_idx += PACKED_BATCH_SIZE;
+				remained -= PACKED_BATCH_SIZE;
+				continue;
+			}
+		}
 
-	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
 		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq, pkts[pkt_idx],
 						&num_desc, &num_buffers,
 						&async_descs[async_descs_idx],
@@ -1936,6 +1968,8 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
 		}
 
+		pkt_idx++;
+		remained--;
 		vq_inc_last_avail_packed(vq, num_desc);
 
 		/*
@@ -1960,13 +1994,12 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 				 */
 				pkt_err = pkt_burst_idx - n_pkts;
 				pkt_burst_idx = 0;
-				pkt_idx++;
 				break;
 			}
 
 			pkt_burst_idx = 0;
 		}
-	}
+	} while (pkt_idx < count);
 
 	if (pkt_burst_idx) {
 		n_pkts = vq->async_ops.transfer_data(dev->vid, queue_id, tdes, 0, pkt_burst_idx);
Signature · 1 line
2.29.2
#48 lore raw

PATCH v8 4/4 doc: add release note for vhost async packed ring

In reply to Cheng Jiang

Add release note for the support of vhost async packed ring.

---
 doc/guides/rel_notes/release_21_05.rst | 4 ++++
 1 file changed, 4 insertions(+)
doc/guides/rel_notes/release_21_05.rst +4 -0
index 374d6d98e..eb5200669 100644
@@ -131,6 +131,10 @@ New Features
   * Added command to display Rx queue used descriptor count.
     ``show port (port_id) rxq (queue_id) desc used count``
 
+* **Added support for vhost async packed ring data path.**
+
+  Added packed ring support for async vhost.
+
 
 Removed Items
 -------------
Signature · 1 line
2.29.2
#49 lore raw

v9 cover add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch set cleans async split ring codes and enables packed ring in async vhost data path. Batch datapath is also enabled in async vhost packed ring.

v9:
* improve some variable initiation
* fix some variable names for consistency
* rebase on the latest code
v8:
* fix some variable names for consistency
* clean codes
v7:
* fix compile issues
* add argument *dev in vhost_free_async_mem() for ring type decision
v6:
* fix some typos in commit log
* improve index usage
* remove shadow_ring_store()
* add store_dma_desc_info_split() store_dma_desc_info_packed()
* remove some checks in vhost_free_async_mem()
* change index calculation since the size isn't necessarily a power of 2
* move error handling in a dedicated function
* clean codes
v5:
* clean some codes for packed ring datapath
* fix an index error in shadow_ring_store()
v4:
* change the patch structure
* clean code for async split ring
* reuse some code from split ring
* change the error handler for DMA-copy packet
* add check for malloc
* remove useless code
* add doc update
v3:
* fix error handler for DMA-copy packet
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

Cheng Jiang (4):
vhost: abstract and reorganize async split ring code
vhost: add support for packed ring in async vhost
vhost: add batch datapath for async vhost packed ring
doc: add release note for vhost async packed ring

 doc/guides/rel_notes/release_21_05.rst |   4 +
 lib/vhost/rte_vhost_async.h            |   1 +
 lib/vhost/vhost.c                      |  79 +++-
 lib/vhost/vhost.h                      |  15 +-
 lib/vhost/virtio_net.c                 | 598 +++++++++++++++++++++----
 5 files changed, 587 insertions(+), 110 deletions(-)

--
2.29.2

#50 lore raw

PATCH v9 1/4 vhost: abstract and reorganize async split ring code

This patch puts some codes of async vhost split ring into inline functions to improve the readability of the code. And, it changes the pointer index style of iterator to make the code more concise.

---
 lib/vhost/virtio_net.c | 132 ++++++++++++++++++++---------------------
 1 file changed, 66 insertions(+), 66 deletions(-)
lib/vhost/virtio_net.c +66 -66
index ff39878609..438bdafd14 100644
@@ -1458,6 +1458,22 @@ virtio_dev_rx_async_get_info_idx(uint16_t pkts_idx,
 		(vq_size - n_inflight + pkts_idx) & (vq_size - 1);
 }
 
+static __rte_always_inline void
+store_dma_desc_info_split(struct vring_used_elem *s_ring, struct vring_used_elem *d_ring,
+		uint16_t ring_size, uint16_t s_idx, uint16_t d_idx, uint16_t count)
+{
+	uint16_t elem_size = sizeof(struct vring_used_elem);
+
+	if (d_idx + count <= ring_size) {
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, count * elem_size);
+	} else {
+		uint16_t size = ring_size - d_idx;
+
+		rte_memcpy(d_ring + d_idx, s_ring + s_idx, size * elem_size);
+		rte_memcpy(d_ring, s_ring + s_idx + size, (count - size) * elem_size);
+	}
+}
+
 static __rte_noinline uint32_t
 virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct vhost_virtqueue *vq, uint16_t queue_id,
@@ -1474,10 +1490,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	struct rte_vhost_async_desc tdes[MAX_PKT_BURST];
 	struct iovec *src_iovec = vec_pool;
 	struct iovec *dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-	struct rte_vhost_iov_iter *src_it = it_pool;
-	struct rte_vhost_iov_iter *dst_it = it_pool + 1;
 	uint16_t slot_idx = 0;
 	uint16_t segs_await = 0;
+	uint16_t iovec_idx = 0, it_idx = 0;
 	struct async_inflight_info *pkts_info = vq->async_pkts_info;
 	uint32_t n_pkts = 0, pkt_err = 0;
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
@@ -1511,29 +1526,30 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			dev->vid, vq->last_avail_idx,
 			vq->last_avail_idx + num_buffers);
 
-		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx],
-				buf_vec, nr_vec, num_buffers,
-				src_iovec, dst_iovec, src_it, dst_it) < 0) {
+		if (async_mbuf_to_desc(dev, vq, pkts[pkt_idx], buf_vec, nr_vec, num_buffers,
+				&src_iovec[iovec_idx], &dst_iovec[iovec_idx],
+				&it_pool[it_idx], &it_pool[it_idx + 1]) < 0) {
 			vq->shadow_used_idx -= num_buffers;
 			break;
 		}
 
 		slot_idx = (vq->async_pkts_idx + num_async_pkts) &
 			(vq->size - 1);
-		if (src_it->count) {
+		if (it_pool[it_idx].count) {
 			uint16_t from, to;
 
-			async_fill_desc(&tdes[pkt_burst_idx++], src_it, dst_it);
+			async_fill_desc(&tdes[pkt_burst_idx++],
+				&it_pool[it_idx], &it_pool[it_idx + 1]);
 			pkts_info[slot_idx].descs = num_buffers;
 			pkts_info[slot_idx].mbuf = pkts[pkt_idx];
 			async_pkts_log[num_async_pkts].pkt_idx = pkt_idx;
 			async_pkts_log[num_async_pkts++].last_avail_idx =
 				vq->last_avail_idx;
-			src_iovec += src_it->nr_segs;
-			dst_iovec += dst_it->nr_segs;
-			src_it += 2;
-			dst_it += 2;
-			segs_await += src_it->nr_segs;
+
+			iovec_idx += it_pool[it_idx].nr_segs;
+			it_idx += 2;
+
+			segs_await += it_pool[it_idx].nr_segs;
 
 			/**
 			 * recover shadow used ring and keep DMA-occupied
@@ -1541,23 +1557,10 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			 */
 			from = vq->shadow_used_idx - num_buffers;
 			to = vq->async_desc_idx & (vq->size - 1);
-			if (num_buffers + to <= vq->size) {
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						num_buffers *
-						sizeof(struct vring_used_elem));
-			} else {
-				int size = vq->size - to;
-
-				rte_memcpy(&vq->async_descs_split[to],
-						&vq->shadow_used_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->async_descs_split,
-						&vq->shadow_used_split[from +
-						size], (num_buffers - size) *
-					   sizeof(struct vring_used_elem));
-			}
+
+			store_dma_desc_info_split(vq->shadow_used_split,
+					vq->async_descs_split, vq->size, from, to, num_buffers);
+
 			vq->async_desc_idx += num_buffers;
 			vq->shadow_used_idx -= num_buffers;
 		} else
@@ -1575,10 +1578,9 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 			BUF_VECTOR_MAX))) {
 			n_pkts = vq->async_ops.transfer_data(dev->vid,
 					queue_id, tdes, 0, pkt_burst_idx);
-			src_iovec = vec_pool;
-			dst_iovec = vec_pool + (VHOST_MAX_ASYNC_VEC >> 1);
-			src_it = it_pool;
-			dst_it = it_pool + 1;
+			iovec_idx = 0;
+			it_idx = 0;
+
 			segs_await = 0;
 			vq->async_pkts_inflight_n += n_pkts;
 
@@ -1639,6 +1641,36 @@ virtio_dev_rx_async_submit_split(struct virtio_net *dev,
 	return pkt_idx;
 }
 
+static __rte_always_inline void
+write_back_completed_descs_split(struct vhost_virtqueue *vq, uint16_t n_descs)
+{
+	uint16_t nr_left = n_descs;
+	uint16_t nr_copy;
+	uint16_t to, from;
+
+	do {
+		from = vq->last_async_desc_idx & (vq->size - 1);
+		nr_copy = nr_left + from <= vq->size ? nr_left : vq->size - from;
+		to = vq->last_used_idx & (vq->size - 1);
+
+		if (to + nr_copy <= vq->size) {
+			rte_memcpy(&vq->used->ring[to], &vq->async_descs_split[from],
+					nr_copy * sizeof(struct vring_used_elem));
+		} else {
+			uint16_t size = vq->size - to;
+
+			rte_memcpy(&vq->used->ring[to], &vq->async_descs_split[from],
+					size * sizeof(struct vring_used_elem));
+			rte_memcpy(&vq->used->ring[0], &vq->async_descs_split[from + size],
+					(nr_copy - size) * sizeof(struct vring_used_elem));
+		}
+
+		vq->last_async_desc_idx += nr_copy;
+		vq->last_used_idx += nr_copy;
+		nr_left -= nr_copy;
+	} while (nr_left > 0);
+}
+
 uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 		struct rte_mbuf **pkts, uint16_t count)
 {
@@ -1695,39 +1727,7 @@ uint16_t rte_vhost_poll_enqueue_completed(int vid, uint16_t queue_id,
 	vq->async_pkts_inflight_n -= n_pkts_put;
 
 	if (likely(vq->enabled && vq->access_ok)) {
-		uint16_t nr_left = n_descs;
-		uint16_t nr_copy;
-		uint16_t to;
-
-		/* write back completed descriptors to used ring */
-		do {
-			from = vq->last_async_desc_idx & (vq->size - 1);
-			nr_copy = nr_left + from <= vq->size ? nr_left :
-				vq->size - from;
-			to = vq->last_used_idx & (vq->size - 1);
-
-			if (to + nr_copy <= vq->size) {
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						nr_copy *
-						sizeof(struct vring_used_elem));
-			} else {
-				uint16_t size = vq->size - to;
-
-				rte_memcpy(&vq->used->ring[to],
-						&vq->async_descs_split[from],
-						size *
-						sizeof(struct vring_used_elem));
-				rte_memcpy(vq->used->ring,
-						&vq->async_descs_split[from +
-						size], (nr_copy - size) *
-						sizeof(struct vring_used_elem));
-			}
-
-			vq->last_async_desc_idx += nr_copy;
-			vq->last_used_idx += nr_copy;
-			nr_left -= nr_copy;
-		} while (nr_left > 0);
+		write_back_completed_descs_split(vq, n_descs);
 
 		__atomic_add_fetch(&vq->used->idx, n_descs, __ATOMIC_RELEASE);
 		vhost_vring_call_split(dev, vq);
Signature · 1 line
2.29.2
#51 lore raw

PATCH v9 2/4 vhost: add support for packed ring in async vhost

In reply to Cheng Jiang

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/vhost/rte_vhost_async.h |   1 +
 lib/vhost/vhost.c           |  79 +++++--
 lib/vhost/vhost.h           |  15 +-
 lib/vhost/virtio_net.c      | 442 ++++++++++++++++++++++++++++++++++--
 4 files changed, 489 insertions(+), 48 deletions(-)
lib/vhost/rte_vhost_async.h +1 -0
index c855ff875e..6faa31f5ad 100644
@@ -89,6 +89,7 @@ struct rte_vhost_async_channel_ops {
 struct async_inflight_info {
 	struct rte_mbuf *mbuf;
 	uint16_t descs; /* num of descs inflight */
+	uint16_t nr_buffers; /* num of buffers inflight for packed ring */
 };
 
 /**
lib/vhost/vhost.c +57 -22

118 lines not on this page · raw message

lib/vhost/vhost.h +12 -3
index f628714c24..b303635645 100644
@@ -201,9 +201,18 @@ struct vhost_virtqueue {
 	uint16_t	async_pkts_idx;
 	uint16_t	async_pkts_inflight_n;
 	uint16_t	async_last_pkts_n;
-	struct vring_used_elem  *async_descs_split;
-	uint16_t async_desc_idx;
-	uint16_t last_async_desc_idx;
+	union {
+		struct vring_used_elem  *async_descs_split;
+		struct vring_used_elem_packed *async_buffers_packed;
+	};
+	union {
+		uint16_t async_desc_idx_split;
+		uint16_t async_buffer_idx_packed;
+	};
+	union {
+		uint16_t last_async_desc_idx_split;
+		uint16_t last_async_buffer_idx_packed;
+	};
 
 	/* vq async features */
 	bool		async_inorder;
lib/vhost/virtio_net.c +419 -23

532 lines not on this page · raw message

Signature · 1 line
2.29.2
Reviewed-by on v9 2/4
#52 lore raw
Quoted 21 lines

-----Original Message-----
From: Jiang, Cheng1 <[email protected]>
Sent: Tuesday, April 27, 2021 4:04 PM
To: [email protected]; Xia, Chenbo <[email protected]>
Cc: [email protected]; Hu, Jiayu <[email protected]>; Yang, YvonneX <[email protected]>; Wang, Yinan <[email protected]>; Liu, Yong <[email protected]>; Jiang, Cheng1 <[email protected]>
Subject: [PATCH v9 2/4] vhost: add support for packed ring in async vhost

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

---
 lib/vhost/rte_vhost_async.h |   1 +
 lib/vhost/vhost.c           |  79 +++++--
 lib/vhost/vhost.h           |  15 +-
 lib/vhost/virtio_net.c      | 442 ++++++++++++++++++++++++++++++++++--
 4 files changed, 489 insertions(+), 48 deletions(-)
Reviewed-by on v9 2/4
#53 lore raw
In reply to Cheng Jiang
Quoted 4 lines · Cheng Jiang

For now async vhost data path only supports split ring. This patch enables packed ring in async vhost data path to make async vhost compatible with virtio 1.1 spec.

Quoted 8 lines
---
 lib/vhost/rte_vhost_async.h |   1 +
 lib/vhost/vhost.c           |  79 +++++--
 lib/vhost/vhost.h           |  15 +-
 lib/vhost/virtio_net.c      | 442 ++++++++++++++++++++++++++++++++++--
 4 files changed, 489 insertions(+), 48 deletions(-)

Thanks,
Maxime

#54 lore raw

PATCH v9 3/4 vhost: add batch datapath for async vhost packed ring

In reply to Cheng Jiang

Add batch datapath for async vhost packed ring to improve the performance of small packet processing.

---
 lib/vhost/virtio_net.c | 42 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 37 insertions(+), 5 deletions(-)
lib/vhost/virtio_net.c +37 -5
index 5d540e5599..f60f97ec72 100644
@@ -1724,6 +1724,29 @@ vhost_update_used_packed(struct vhost_virtqueue *vq,
 	vq->desc_packed[head_idx].flags = head_flags;
 }
 
+static __rte_always_inline int
+virtio_dev_rx_async_batch_packed(struct virtio_net *dev,
+			   struct vhost_virtqueue *vq,
+			   struct rte_mbuf **pkts,
+			   struct rte_mbuf **comp_pkts, uint32_t *pkt_done)
+{
+	uint16_t i;
+	uint32_t cpy_threshold = vq->async_threshold;
+
+	vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE) {
+		if (unlikely(pkts[i]->pkt_len >= cpy_threshold))
+			return -1;
+	}
+	if (!virtio_dev_rx_batch_packed(dev, vq, pkts)) {
+		vhost_for_each_try_unroll(i, 0, PACKED_BATCH_SIZE)
+			comp_pkts[(*pkt_done)++] = pkts[i];
+
+		return 0;
+	}
+
+	return -1;
+}
+
 static __rte_always_inline int
 vhost_enqueue_async_single_packed(struct virtio_net *dev,
 			    struct vhost_virtqueue *vq,
@@ -1872,6 +1895,7 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	struct rte_mbuf **comp_pkts, uint32_t *comp_count)
 {
 	uint32_t pkt_idx = 0, pkt_burst_idx = 0;
+	uint32_t remained = count;
 	uint16_t async_descs_idx = 0;
 	uint16_t num_buffers;
 	uint16_t num_descs;
@@ -1889,12 +1913,19 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 	uint32_t num_async_pkts = 0, num_done_pkts = 0;
 	struct vring_packed_desc async_descs[vq->size];
 
-	rte_prefetch0(&vq->desc_packed[vq->last_avail_idx]);
+	do {
+		rte_prefetch0(&vq->desc_packed[vq->last_avail_idx]);
+		if (remained >= PACKED_BATCH_SIZE) {
+			if (!virtio_dev_rx_async_batch_packed(dev, vq,
+				&pkts[pkt_idx], comp_pkts, &num_done_pkts)) {
+				pkt_idx += PACKED_BATCH_SIZE;
+				remained -= PACKED_BATCH_SIZE;
+				continue;
+			}
+		}
 
-	for (pkt_idx = 0; pkt_idx < count; pkt_idx++) {
 		num_buffers = 0;
 		num_descs = 0;
-
 		if (unlikely(virtio_dev_rx_async_single_packed(dev, vq, pkts[pkt_idx],
 						&num_descs, &num_buffers,
 						&async_descs[async_descs_idx],
@@ -1937,6 +1968,8 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 			comp_pkts[num_done_pkts++] = pkts[pkt_idx];
 		}
 
+		pkt_idx++;
+		remained--;
 		vq_inc_last_avail_packed(vq, num_descs);
 
 		/*
@@ -1961,13 +1994,12 @@ virtio_dev_rx_async_submit_packed(struct virtio_net *dev,
 				 */
 				pkt_err = pkt_burst_idx - n_pkts;
 				pkt_burst_idx = 0;
-				pkt_idx++;
 				break;
 			}
 
 			pkt_burst_idx = 0;
 		}
-	}
+	} while (pkt_idx < count);
 
 	if (pkt_burst_idx) {
 		n_pkts = vq->async_ops.transfer_data(dev->vid, queue_id, tdes, 0, pkt_burst_idx);
Signature · 1 line
2.29.2
Reviewed-by on v9 3/4
#55 lore raw

Re: [dpdk-dev] [PATCH v9 3/4] vhost: add batch datapath for async vhost packed ring

Quoted 8 lines · Cheng Jiang

Add batch datapath for async vhost packed ring to improve the performance of small packet processing.

---
 lib/vhost/virtio_net.c | 42 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 37 insertions(+), 5 deletions(-)

Thanks,
Maxime

#56 lore raw

PATCH v9 4/4 doc: add release note for vhost async packed ring

In reply to Cheng Jiang

Add release note for the support of vhost async packed ring.

---
 doc/guides/rel_notes/release_21_05.rst | 4 ++++
 1 file changed, 4 insertions(+)
doc/guides/rel_notes/release_21_05.rst +4 -0
index b3224dc332..aec020d558 100644
@@ -271,6 +271,10 @@ New Features
   * Added support for crypto adapter forward mode in octeontx2 event and crypto
     device driver.
 
+* **Added support for vhost async packed ring data path.**
+
+  Added packed ring support for async vhost.
+
 
 Removed Items
 -------------
Signature · 1 line
2.29.2
Reviewed-by on v9 4/4
#57 lore raw

Re: [dpdk-dev] [PATCH v9 4/4] doc: add release note for vhost async packed ring

Quoted 13 lines · Cheng Jiang

Add release note for the support of vhost async packed ring.

---
 doc/guides/rel_notes/release_21_05.rst | 4 ++++
 1 file changed, 4 insertions(+)
doc/guides/rel_notes/release_21_05.rst +0 -0
index b3224dc332..aec020d558 100644

doc/guides/rel_notes/release_21_05.rst New Features · line 280

Quoted 10 lines
doc/guides/rel_notes/release_21_05.rst +4 -0
@@ -271,6 +271,10 @@ New Features
   * Added support for crypto adapter forward mode in octeontx2 event and crypto
     device driver.
 
+* **Added support for vhost async packed ring data path.**
+
+  Added packed ring support for async vhost.
+
 
 Removed Items
 -------------

Thanks,
Maxime

#59 lore raw

Re: [dpdk-dev] [PATCH v9 0/4] add support for packed ring in async vhost

In reply to Cheng Jiang
Quoted 55 lines · Cheng Jiang

For now async vhost data path only supports split ring structure. In order to make async vhost compatible with virtio 1.1 spec this patch set cleans async split ring codes and enables packed ring in async vhost data path. Batch datapath is also enabled in async vhost packed ring.

v9:
* improve some variable initiation
* fix some variable names for consistency
* rebase on the latest code
v8:
* fix some variable names for consistency
* clean codes
v7:
* fix compile issues
* add argument *dev in vhost_free_async_mem() for ring type decision
v6:
* fix some typos in commit log
* improve index usage
* remove shadow_ring_store()
* add store_dma_desc_info_split() store_dma_desc_info_packed()
* remove some checks in vhost_free_async_mem()
* change index calculation since the size isn't necessarily a power of 2
* move error handling in a dedicated function
* clean codes
v5:
* clean some codes for packed ring datapath
* fix an index error in shadow_ring_store()
v4:
* change the patch structure
* clean code for async split ring
* reuse some code from split ring
* change the error handler for DMA-copy packet
* add check for malloc
* remove useless code
* add doc update
v3:
* fix error handler for DMA-copy packet
v2:
* fix wrong buffer index in rte_vhost_poll_enqueue_completed()
* add async_buffers_packed memory free in vhost_free_async_mem()

Cheng Jiang (4):
vhost: abstract and reorganize async split ring code
vhost: add support for packed ring in async vhost
vhost: add batch datapath for async vhost packed ring
doc: add release note for vhost async packed ring

 doc/guides/rel_notes/release_21_05.rst |   4 +
 lib/vhost/rte_vhost_async.h            |   1 +
 lib/vhost/vhost.c                      |  79 +++-
 lib/vhost/vhost.h                      |  15 +-
 lib/vhost/virtio_net.c                 | 598 +++++++++++++++++++++----
 5 files changed, 587 insertions(+), 110 deletions(-)
Quoted 3 lines

--
2.29.2

Applied to dpdk-next-virtio/main.

Thanks,
Maxime