DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* RE: [PATCH v12 2/5] vhost_user: header defines for add/rem mem region
From: Bathija, Pravin @ 2026-05-14  2:05 UTC (permalink / raw)
  To: fengchengwen, dev@dpdk.org, stephen@networkplumber.org,
	maxime.coquelin@redhat.com
  Cc: thomas@monjalon.net
In-Reply-To: <ef49146b-429f-484a-83b9-0ab3de0a28f1@huawei.com>

Hi Fengcheng,

Responses inline. I have made the suggested changes and submitted version 13 of the patch-set. Please review.


Internal Use - Confidential
> -----Original Message-----
> From: fengchengwen <fengchengwen@huawei.com>
> Sent: Tuesday, May 12, 2026 7:30 PM
> To: Bathija, Pravin <Pravin.Bathija@dell.com>; dev@dpdk.org;
> stephen@networkplumber.org; maxime.coquelin@redhat.com
> Cc: thomas@monjalon.net
> Subject: Re: [PATCH v12 2/5] vhost_user: header defines for add/rem mem
> region
>
>
> [EXTERNAL EMAIL]
>
> On 5/6/2026 11:37 AM, pravin.bathija@dell.com wrote:
> > From: Pravin M Bathija <pravin.bathija@dell.com>
> >
> > The changes in this file cover the enum message requests for
> > supporting add/remove memory regions. The front-end vhost-user client
> > sends messages like get max memory slots, add memory region and remove
> > memory region which are covered in these changes which are on the
> > vhost-user back-end. The changes also include data structure
> > definition of memory region to be added/removed. The data structure
> > VhostUserMsg has been changed to include the memory region.
> >
> > Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
> > ---
> >  lib/vhost/vhost_user.h | 9 +++++++++
> >  1 file changed, 9 insertions(+)
> >
> > diff --git a/lib/vhost/vhost_user.h b/lib/vhost/vhost_user.h index
> > ef486545ba..f8d921f7f1 100644
> > --- a/lib/vhost/vhost_user.h
> > +++ b/lib/vhost/vhost_user.h
> > @@ -67,6 +67,9 @@ typedef enum VhostUserRequest {
> >     VHOST_USER_POSTCOPY_END = 30,
> >     VHOST_USER_GET_INFLIGHT_FD = 31,
> >     VHOST_USER_SET_INFLIGHT_FD = 32,
> > +   VHOST_USER_GET_MAX_MEM_SLOTS = 36,
> > +   VHOST_USER_ADD_MEM_REG = 37,
> > +   VHOST_USER_REM_MEM_REG = 38,
> >     VHOST_USER_SET_STATUS = 39,
> >     VHOST_USER_GET_STATUS = 40,
> >  } VhostUserRequest;
> > @@ -91,6 +94,11 @@ typedef struct VhostUserMemory {
> >     VhostUserMemoryRegion
> regions[VHOST_MEMORY_MAX_NREGIONS];
> >  } VhostUserMemory;
> >
> > +typedef struct VhostUserSingleMemReg {
> > +   uint64_t padding;
> > +   VhostUserMemoryRegion region;
> > +} VhostUserSingleMemReg;
>
> How about the same as qemu vhost define: VhostUserMemRegMsg

Sure. Made the change as suggested.
>
> > +
> >  typedef struct VhostUserLog {
> >     uint64_t mmap_size;
> >     uint64_t mmap_offset;
> > @@ -186,6 +194,7 @@ typedef struct __rte_packed_begin VhostUserMsg {
> >             struct vhost_vring_state state;
> >             struct vhost_vring_addr addr;
> >             VhostUserMemory memory;
> > +           VhostUserSingleMemReg memory_single;
>
> Suggest the same as qemu define: VhostUserMemRegMsg memreg;

Sure. Made the change as suggested.

>
> >             VhostUserLog    log;
> >             struct vhost_iotlb_msg iotlb;
> >             VhostUserCryptoSessionParam crypto_session;
>
> With above fix
> Acked-by: Chengwen Feng <fengchengwen@huawei.com>


^ permalink raw reply

* [PATCH v13 5/5] vhost_user: enable configure memory slots
From: pravin.bathija @ 2026-05-14  2:01 UTC (permalink / raw)
  To: dev, fengchengwen, stephen, maxime.coquelin; +Cc: pravin.bathija, thomas
In-Reply-To: <20260514020157.1937404-1-pravin.bathija@dell.com>

From: Pravin M Bathija <pravin.bathija@dell.com>

This patch enables configure memory slots in the header define
VHOST_USER_PROTOCOL_FEATURES.

Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
---
 lib/vhost/vhost_user.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lib/vhost/vhost_user.h b/lib/vhost/vhost_user.h
index 6435816534..732aa4dc02 100644
--- a/lib/vhost/vhost_user.h
+++ b/lib/vhost/vhost_user.h
@@ -32,6 +32,7 @@
 					 (1ULL << VHOST_USER_PROTOCOL_F_BACKEND_SEND_FD) | \
 					 (1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER) | \
 					 (1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT) | \
+					 (1ULL << VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS) | \
 					 (1ULL << VHOST_USER_PROTOCOL_F_STATUS))
 
 typedef enum VhostUserRequest {
-- 
2.43.0


^ permalink raw reply related

* [PATCH v13 4/5] vhost_user: Function defs for add/rem mem regions
From: pravin.bathija @ 2026-05-14  2:01 UTC (permalink / raw)
  To: dev, fengchengwen, stephen, maxime.coquelin; +Cc: pravin.bathija, thomas
In-Reply-To: <20260514020157.1937404-1-pravin.bathija@dell.com>

From: Pravin M Bathija <pravin.bathija@dell.com>

These changes cover the function definition for add/remove memory
region calls which are invoked on receiving vhost user message from
vhost user front-end (e.g. Qemu). In our case, in addition to testing
with qemu front-end, the testing has also been performed with libblkio
front-end and spdk/dpdk back-end. We did I/O using libblkio based device
driver, to spdk based drives.
There are also changes for set_mem_table and new definition for get memory
slots. Our changes optimize the set memory table call to use common support
functions. A new vhost_user_initialize_memory() function is introduced to
factor out the common memory initialization logic from the function
vhost_user_set_mem_table(), which is now called from both the SET_MEM_TABLE
message handler and the ADD_MEM_REG handler (for the first region).
Message get memory slots is how the vhost-user front-end queries the
vhost-user back-end about the number of memory slots available to be
registered by the back-end. In addition support function to invalidate
vring is also defined which is used in add/remove memory region functions.
The helper function remove_guest_pages is also defined here which is called
from vhost_user_add_mem_reg.

Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
---
 lib/vhost/vhost_user.c | 329 ++++++++++++++++++++++++++++++++++++-----
 1 file changed, 296 insertions(+), 33 deletions(-)

diff --git a/lib/vhost/vhost_user.c b/lib/vhost/vhost_user.c
index 0ee3fe7a5e..fdcb7e0158 100644
--- a/lib/vhost/vhost_user.c
+++ b/lib/vhost/vhost_user.c
@@ -71,6 +71,9 @@ VHOST_MESSAGE_HANDLER(VHOST_USER_SET_FEATURES, vhost_user_set_features, false, t
 VHOST_MESSAGE_HANDLER(VHOST_USER_SET_OWNER, vhost_user_set_owner, false, true) \
 VHOST_MESSAGE_HANDLER(VHOST_USER_RESET_OWNER, vhost_user_reset_owner, false, false) \
 VHOST_MESSAGE_HANDLER(VHOST_USER_SET_MEM_TABLE, vhost_user_set_mem_table, true, true) \
+VHOST_MESSAGE_HANDLER(VHOST_USER_GET_MAX_MEM_SLOTS, vhost_user_get_max_mem_slots, false, false) \
+VHOST_MESSAGE_HANDLER(VHOST_USER_ADD_MEM_REG, vhost_user_add_mem_reg, true, true) \
+VHOST_MESSAGE_HANDLER(VHOST_USER_REM_MEM_REG, vhost_user_rem_mem_reg, true, true) \
 VHOST_MESSAGE_HANDLER(VHOST_USER_SET_LOG_BASE, vhost_user_set_log_base, true, true) \
 VHOST_MESSAGE_HANDLER(VHOST_USER_SET_LOG_FD, vhost_user_set_log_fd, true, true) \
 VHOST_MESSAGE_HANDLER(VHOST_USER_SET_VRING_NUM, vhost_user_set_vring_num, false, true) \
@@ -1167,6 +1170,24 @@ add_guest_pages(struct virtio_net *dev, struct rte_vhost_mem_region *reg,
 	return 0;
 }
 
+static void
+remove_guest_pages(struct virtio_net *dev, struct rte_vhost_mem_region *reg)
+{
+	uint64_t reg_start = reg->host_user_addr;
+	uint64_t reg_end = reg_start + reg->size;
+	uint32_t i, j = 0;
+
+	for (i = 0; i < dev->nr_guest_pages; i++) {
+		if (dev->guest_pages[i].host_user_addr >= reg_start &&
+		    dev->guest_pages[i].host_user_addr < reg_end)
+			continue;
+		if (j != i)
+			dev->guest_pages[j] = dev->guest_pages[i];
+		j++;
+	}
+	dev->nr_guest_pages = j;
+}
+
 #ifdef RTE_LIBRTE_VHOST_DEBUG
 /* TODO: enable it only in debug mode? */
 static void
@@ -1413,6 +1434,52 @@ vhost_user_mmap_region(struct virtio_net *dev,
 	return 0;
 }
 
+static int
+vhost_user_initialize_memory(struct virtio_net **pdev)
+{
+	struct virtio_net *dev = *pdev;
+	int numa_node = SOCKET_ID_ANY;
+
+	if (dev->mem != NULL) {
+		VHOST_CONFIG_LOG(dev->ifname, ERR,
+			"memory already initialized, free it first");
+		return -1;
+	}
+
+	/*
+	 * If VQ 0 has already been allocated, try to allocate on the same
+	 * NUMA node. It can be reallocated later in numa_realloc().
+	 */
+	if (dev->nr_vring > 0)
+		numa_node = dev->virtqueue[0]->numa_node;
+
+	dev->nr_guest_pages = 0;
+	if (dev->guest_pages == NULL) {
+		dev->max_guest_pages = 8;
+		dev->guest_pages = rte_zmalloc_socket(NULL,
+					dev->max_guest_pages *
+					sizeof(struct guest_page),
+					RTE_CACHE_LINE_SIZE,
+					numa_node);
+		if (dev->guest_pages == NULL) {
+			VHOST_CONFIG_LOG(dev->ifname, ERR,
+				"failed to allocate memory for dev->guest_pages");
+			return -1;
+		}
+	}
+
+	dev->mem = rte_zmalloc_socket("vhost-mem-table", sizeof(struct rte_vhost_memory) +
+		sizeof(struct rte_vhost_mem_region) * VHOST_MEMORY_MAX_NREGIONS, 0, numa_node);
+	if (dev->mem == NULL) {
+		VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to allocate memory for dev->mem");
+		rte_free(dev->guest_pages);
+		dev->guest_pages = NULL;
+		return -1;
+	}
+
+	return 0;
+}
+
 static int
 vhost_user_set_mem_table(struct virtio_net **pdev,
 			struct vhu_msg_context *ctx,
@@ -1421,7 +1488,6 @@ vhost_user_set_mem_table(struct virtio_net **pdev,
 	struct virtio_net *dev = *pdev;
 	struct VhostUserMemory *memory = &ctx->msg.payload.memory;
 	struct rte_vhost_mem_region *reg;
-	int numa_node = SOCKET_ID_ANY;
 	uint64_t mmap_offset;
 	uint32_t i;
 	bool async_notify = false;
@@ -1466,39 +1532,13 @@ vhost_user_set_mem_table(struct virtio_net **pdev,
 		if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
 			vhost_user_iotlb_flush_all(dev);
 
-		free_mem_region(dev);
+		free_all_mem_regions(dev);
 		rte_free(dev->mem);
 		dev->mem = NULL;
 	}
 
-	/*
-	 * If VQ 0 has already been allocated, try to allocate on the same
-	 * NUMA node. It can be reallocated later in numa_realloc().
-	 */
-	if (dev->nr_vring > 0)
-		numa_node = dev->virtqueue[0]->numa_node;
-
-	dev->nr_guest_pages = 0;
-	if (dev->guest_pages == NULL) {
-		dev->max_guest_pages = 8;
-		dev->guest_pages = rte_zmalloc_socket(NULL,
-					dev->max_guest_pages *
-					sizeof(struct guest_page),
-					RTE_CACHE_LINE_SIZE,
-					numa_node);
-		if (dev->guest_pages == NULL) {
-			VHOST_CONFIG_LOG(dev->ifname, ERR,
-				"failed to allocate memory for dev->guest_pages");
-			goto close_msg_fds;
-		}
-	}
-
-	dev->mem = rte_zmalloc_socket("vhost-mem-table", sizeof(struct rte_vhost_memory) +
-		sizeof(struct rte_vhost_mem_region) * memory->nregions, 0, numa_node);
-	if (dev->mem == NULL) {
-		VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to allocate memory for dev->mem");
-		goto free_guest_pages;
-	}
+	if (vhost_user_initialize_memory(pdev) < 0)
+		goto close_msg_fds;
 
 	for (i = 0; i < memory->nregions; i++) {
 		reg = &dev->mem->regions[i];
@@ -1562,11 +1602,9 @@ vhost_user_set_mem_table(struct virtio_net **pdev,
 	return RTE_VHOST_MSG_RESULT_OK;
 
 free_mem_table:
-	free_mem_region(dev);
+	free_all_mem_regions(dev);
 	rte_free(dev->mem);
 	dev->mem = NULL;
-
-free_guest_pages:
 	rte_free(dev->guest_pages);
 	dev->guest_pages = NULL;
 close_msg_fds:
@@ -1574,6 +1612,231 @@ vhost_user_set_mem_table(struct virtio_net **pdev,
 	return RTE_VHOST_MSG_RESULT_ERR;
 }
 
+
+static int
+vhost_user_get_max_mem_slots(struct virtio_net **pdev __rte_unused,
+			struct vhu_msg_context *ctx,
+			int main_fd __rte_unused)
+{
+	uint32_t max_mem_slots = VHOST_MEMORY_MAX_NREGIONS;
+
+	ctx->msg.payload.u64 = (uint64_t)max_mem_slots;
+	ctx->msg.size = sizeof(ctx->msg.payload.u64);
+	ctx->fd_num = 0;
+
+	return RTE_VHOST_MSG_RESULT_REPLY;
+}
+
+static void
+_dev_invalidate_vrings(struct virtio_net **pdev)
+{
+	struct virtio_net *dev = *pdev;
+	uint32_t i;
+
+	for (i = 0; i < dev->nr_vring; i++) {
+		struct vhost_virtqueue *vq = dev->virtqueue[i];
+
+		if (!vq)
+			continue;
+
+		if (vq->desc || vq->avail || vq->used) {
+			vq_assert_lock(dev, vq);
+
+			/*
+			 * If the memory table got updated, the ring addresses
+			 * need to be translated again as virtual addresses have
+			 * changed.
+			 */
+			vring_invalidate(dev, vq);
+
+			translate_ring_addresses(&dev, &vq);
+		}
+	}
+
+	*pdev = dev;
+}
+
+/*
+ * Macro wrapper that performs the compile-time lock assertion with the
+ * correct message ID at the call site, then calls the implementation.
+ */
+#define dev_invalidate_vrings(pdev, id) do { \
+	static_assert(id ## _LOCK_ALL_QPS, \
+		#id " handler is not declared as locking all queue pairs"); \
+	_dev_invalidate_vrings(pdev); \
+} while (0)
+
+static int
+vhost_user_add_mem_reg(struct virtio_net **pdev,
+			struct vhu_msg_context *ctx,
+			int main_fd __rte_unused)
+{
+	uint32_t i;
+	struct virtio_net *dev = *pdev;
+	struct VhostUserMemoryRegion *region = &ctx->msg.payload.memreg.region;
+
+	/* convert first region add to normal memory table set */
+	if (dev->mem == NULL) {
+		if (vhost_user_initialize_memory(pdev) < 0)
+			goto close_msg_fds;
+	}
+
+	/* make sure new region will fit */
+	if (dev->mem->nregions >= VHOST_MEMORY_MAX_NREGIONS) {
+		VHOST_CONFIG_LOG(dev->ifname, ERR, "too many memory regions already (%u)",
+									dev->mem->nregions);
+		goto close_msg_fds;
+	}
+
+	/* make sure supplied memory fd present */
+	if (ctx->fd_num != 1) {
+		VHOST_CONFIG_LOG(dev->ifname, ERR, "fd count makes no sense (%u)", ctx->fd_num);
+		goto close_msg_fds;
+	}
+
+	/* Make sure no overlap in guest virtual address space */
+	for (i = 0; i < dev->mem->nregions; i++) {
+		struct rte_vhost_mem_region *current_region = &dev->mem->regions[i];
+		uint64_t current_region_guest_start = current_region->guest_user_addr;
+		uint64_t current_region_guest_end = current_region_guest_start
+							+ current_region->size - 1;
+		uint64_t proposed_region_guest_start = region->userspace_addr;
+		uint64_t proposed_region_guest_end = proposed_region_guest_start
+							+ region->memory_size - 1;
+
+		if (!((proposed_region_guest_end < current_region_guest_start) ||
+			(proposed_region_guest_start > current_region_guest_end))) {
+			VHOST_CONFIG_LOG(dev->ifname, ERR,
+				"requested memory region overlaps with another region");
+			VHOST_CONFIG_LOG(dev->ifname, ERR,
+				"\tRequested region address:0x%" PRIx64,
+				region->userspace_addr);
+			VHOST_CONFIG_LOG(dev->ifname, ERR,
+				"\tRequested region size:0x%" PRIx64,
+				region->memory_size);
+			VHOST_CONFIG_LOG(dev->ifname, ERR,
+				"\tOverlapping region address:0x%" PRIx64,
+				current_region->guest_user_addr);
+			VHOST_CONFIG_LOG(dev->ifname, ERR,
+				"\tOverlapping region size:0x%" PRIx64,
+				current_region->size);
+			goto close_msg_fds;
+		}
+	}
+
+	/* New region goes at the end of the contiguous array */
+	struct rte_vhost_mem_region *reg = &dev->mem->regions[dev->mem->nregions];
+
+	reg->guest_phys_addr = region->guest_phys_addr;
+	reg->guest_user_addr = region->userspace_addr;
+	reg->size            = region->memory_size;
+	reg->fd              = ctx->fds[0];
+	ctx->fds[0]          = -1;
+
+	if (vhost_user_mmap_region(dev, reg, region->mmap_offset) < 0) {
+		VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to mmap region");
+		if (reg->mmap_addr) {
+			/* mmap succeeded but a later step (e.g. add_guest_pages)
+			 * failed; undo the mapping and any guest-page entries.
+			 */
+			remove_guest_pages(dev, reg);
+			free_mem_region(reg);
+		} else {
+			close(reg->fd);
+			reg->fd = -1;
+		}
+		goto close_msg_fds;
+	}
+
+	dev->mem->nregions++;
+
+	if (dev->async_copy && rte_vfio_is_enabled("vfio")) {
+		if (async_dma_map_region(dev, reg, true) < 0)
+			goto free_new_region;
+	}
+
+	if (dev->postcopy_listening) {
+		/*
+		 * Cannot use vhost_user_postcopy_register() here because it
+		 * reads ctx->msg.payload.memory (SET_MEM_TABLE layout), but
+		 * ADD_MEM_REG uses the memreg payload.  Register the
+		 * single new region directly instead.
+		 */
+		if (vhost_user_postcopy_region_register(dev, reg) < 0)
+			goto free_new_region;
+	}
+
+	dev_invalidate_vrings(pdev, VHOST_USER_ADD_MEM_REG);
+	dev = *pdev;
+	dump_guest_pages(dev);
+
+	return RTE_VHOST_MSG_RESULT_OK;
+
+free_new_region:
+	if (dev->async_copy && rte_vfio_is_enabled("vfio"))
+		async_dma_map_region(dev, reg, false);
+	remove_guest_pages(dev, reg);
+	free_mem_region(reg);
+	dev->mem->nregions--;
+close_msg_fds:
+	close_msg_fds(ctx);
+	return RTE_VHOST_MSG_RESULT_ERR;
+}
+
+static int
+vhost_user_rem_mem_reg(struct virtio_net **pdev,
+			struct vhu_msg_context *ctx,
+			int main_fd __rte_unused)
+{
+	uint32_t i;
+	struct virtio_net *dev = *pdev;
+	struct VhostUserMemoryRegion *region = &ctx->msg.payload.memreg.region;
+
+	if (dev->mem == NULL || dev->mem->nregions == 0) {
+		VHOST_CONFIG_LOG(dev->ifname, ERR, "no memory regions to remove");
+		close_msg_fds(ctx);
+		return RTE_VHOST_MSG_RESULT_ERR;
+	}
+
+	for (i = 0; i < dev->mem->nregions; i++) {
+		struct rte_vhost_mem_region *current_region = &dev->mem->regions[i];
+
+		/*
+		 * According to the vhost-user specification:
+		 * The memory region to be removed is identified by its GPA,
+		 * user address and size. The mmap offset is ignored.
+		 */
+		if (region->userspace_addr == current_region->guest_user_addr
+			&& region->guest_phys_addr == current_region->guest_phys_addr
+			&& region->memory_size == current_region->size) {
+			if (dev->async_copy && rte_vfio_is_enabled("vfio"))
+				async_dma_map_region(dev, current_region, false);
+			remove_guest_pages(dev, current_region);
+			free_mem_region(current_region);
+
+			/* Compact the regions array to keep it contiguous */
+			if (i < dev->mem->nregions - 1) {
+				memmove(&dev->mem->regions[i],
+					&dev->mem->regions[i + 1],
+					(dev->mem->nregions - 1 - i) *
+					sizeof(struct rte_vhost_mem_region));
+				memset(&dev->mem->regions[dev->mem->nregions - 1],
+					0, sizeof(struct rte_vhost_mem_region));
+			}
+
+			dev->mem->nregions--;
+			dev_invalidate_vrings(pdev, VHOST_USER_REM_MEM_REG);
+			dev = *pdev;
+			close_msg_fds(ctx);
+			return RTE_VHOST_MSG_RESULT_OK;
+		}
+	}
+
+	VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to find region");
+	close_msg_fds(ctx);
+	return RTE_VHOST_MSG_RESULT_ERR;
+}
+
 static bool
 vq_is_ready(struct virtio_net *dev, struct vhost_virtqueue *vq)
 {
-- 
2.43.0


^ permalink raw reply related

* [PATCH v13 3/5] vhost_user: support function defines for back-end
From: pravin.bathija @ 2026-05-14  2:01 UTC (permalink / raw)
  To: dev, fengchengwen, stephen, maxime.coquelin; +Cc: pravin.bathija, thomas
In-Reply-To: <20260514020157.1937404-1-pravin.bathija@dell.com>

From: Pravin M Bathija <pravin.bathija@dell.com>

Here we define support functions which are called from the various
vhost-user back-end message functions like set memory table, get
memory slots, add memory region, remove memory region.  These are
essentially common functions to unmap a set of memory regions,
perform register copy, align memory addresses and dma map/unmap a
single memory region.

Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
---
 lib/vhost/vhost_user.c | 89 ++++++++++++++++++++++++++++--------------
 1 file changed, 60 insertions(+), 29 deletions(-)

diff --git a/lib/vhost/vhost_user.c b/lib/vhost/vhost_user.c
index 4bfb13fb98..0ee3fe7a5e 100644
--- a/lib/vhost/vhost_user.c
+++ b/lib/vhost/vhost_user.c
@@ -171,20 +171,27 @@ get_blk_size(int fd)
 	return ret == -1 ? (uint64_t)-1 : (uint64_t)stat.st_blksize;
 }
 
-static void
-async_dma_map(struct virtio_net *dev, bool do_map)
+static int
+async_dma_map_region(struct virtio_net *dev, struct rte_vhost_mem_region *reg, bool do_map)
 {
-	int ret = 0;
 	uint32_t i;
-	struct guest_page *page;
+	int ret;
+	uint64_t reg_start = reg->host_user_addr;
+	uint64_t reg_end = reg_start + reg->size;
+
+	for (i = 0; i < dev->nr_guest_pages; i++) {
+		struct guest_page *page = &dev->guest_pages[i];
+
+		/* Only process pages belonging to this region */
+		if (page->host_user_addr < reg_start ||
+		    page->host_user_addr >= reg_end)
+			continue;
 
-	if (do_map) {
-		for (i = 0; i < dev->nr_guest_pages; i++) {
-			page = &dev->guest_pages[i];
+		if (do_map) {
 			ret = rte_vfio_container_dma_map(RTE_VFIO_DEFAULT_CONTAINER_FD,
-							 page->host_user_addr,
-							 page->host_iova,
-							 page->size);
+					page->host_user_addr,
+					page->host_iova,
+					page->size);
 			if (ret) {
 				/*
 				 * DMA device may bind with kernel driver, in this case,
@@ -199,33 +206,57 @@ async_dma_map(struct virtio_net *dev, bool do_map)
 				 * normal case in async path. This is a workaround.
 				 */
 				if (rte_errno == ENODEV)
-					return;
+					return 0;
 
 				/* DMA mapping errors won't stop VHOST_USER_SET_MEM_TABLE. */
 				VHOST_CONFIG_LOG(dev->ifname, ERR, "DMA engine map failed");
+				return -1;
 			}
-		}
-
-	} else {
-		for (i = 0; i < dev->nr_guest_pages; i++) {
-			page = &dev->guest_pages[i];
+		} else {
 			ret = rte_vfio_container_dma_unmap(RTE_VFIO_DEFAULT_CONTAINER_FD,
-							   page->host_user_addr,
-							   page->host_iova,
-							   page->size);
+					page->host_user_addr,
+					page->host_iova,
+					page->size);
 			if (ret) {
 				/* like DMA map, ignore the kernel driver case when unmap. */
 				if (rte_errno == EINVAL)
-					return;
+					return 0;
 
 				VHOST_CONFIG_LOG(dev->ifname, ERR, "DMA engine unmap failed");
+				return -1;
 			}
 		}
 	}
+
+	return 0;
+}
+
+static void
+async_dma_map(struct virtio_net *dev, bool do_map)
+{
+	uint32_t i;
+	struct rte_vhost_mem_region *reg;
+
+	for (i = 0; i < VHOST_MEMORY_MAX_NREGIONS; i++) {
+		reg = &dev->mem->regions[i];
+		if (reg->host_user_addr == 0)
+			continue;
+		async_dma_map_region(dev, reg, do_map);
+	}
 }
 
 static void
-free_mem_region(struct virtio_net *dev)
+free_mem_region(struct rte_vhost_mem_region *reg)
+{
+	if (reg != NULL && reg->mmap_addr) {
+		munmap(reg->mmap_addr, reg->mmap_size);
+		close(reg->fd);
+		memset(reg, 0, sizeof(struct rte_vhost_mem_region));
+	}
+}
+
+static void
+free_all_mem_regions(struct virtio_net *dev)
 {
 	uint32_t i;
 	struct rte_vhost_mem_region *reg;
@@ -236,12 +267,10 @@ free_mem_region(struct virtio_net *dev)
 	if (dev->async_copy && rte_vfio_is_enabled("vfio"))
 		async_dma_map(dev, false);
 
-	for (i = 0; i < dev->mem->nregions; i++) {
+	for (i = 0; i < VHOST_MEMORY_MAX_NREGIONS; i++) {
 		reg = &dev->mem->regions[i];
-		if (reg->host_user_addr) {
-			munmap(reg->mmap_addr, reg->mmap_size);
-			close(reg->fd);
-		}
+		if (reg->mmap_addr)
+			free_mem_region(reg);
 	}
 }
 
@@ -255,7 +284,7 @@ vhost_backend_cleanup(struct virtio_net *dev)
 		vdpa_dev->ops->dev_cleanup(dev->vid);
 
 	if (dev->mem) {
-		free_mem_region(dev);
+		free_all_mem_regions(dev);
 		rte_free(dev->mem);
 		dev->mem = NULL;
 	}
@@ -704,7 +733,7 @@ numa_realloc(struct virtio_net **pdev, struct vhost_virtqueue **pvq)
 	vhost_devices[dev->vid] = dev;
 
 	mem_size = sizeof(struct rte_vhost_memory) +
-		sizeof(struct rte_vhost_mem_region) * dev->mem->nregions;
+		sizeof(struct rte_vhost_mem_region) * VHOST_MEMORY_MAX_NREGIONS;
 	mem = rte_realloc_socket(dev->mem, mem_size, 0, node);
 	if (!mem) {
 		VHOST_CONFIG_LOG(dev->ifname, ERR,
@@ -808,8 +837,10 @@ hua_to_alignment(struct rte_vhost_memory *mem, void *ptr)
 	uint32_t i;
 	uintptr_t hua = (uintptr_t)ptr;
 
-	for (i = 0; i < mem->nregions; i++) {
+	for (i = 0; i < VHOST_MEMORY_MAX_NREGIONS; i++) {
 		r = &mem->regions[i];
+		if (r->host_user_addr == 0)
+			continue;
 		if (hua >= r->host_user_addr &&
 			hua < r->host_user_addr + r->size) {
 			return get_blk_size(r->fd);
-- 
2.43.0


^ permalink raw reply related

* [PATCH v13 2/5] vhost_user: header defines for add/rem mem region
From: pravin.bathija @ 2026-05-14  2:01 UTC (permalink / raw)
  To: dev, fengchengwen, stephen, maxime.coquelin; +Cc: pravin.bathija, thomas
In-Reply-To: <20260514020157.1937404-1-pravin.bathija@dell.com>

From: Pravin M Bathija <pravin.bathija@dell.com>

The changes in this file cover the enum message requests for
supporting add/remove memory regions. The front-end vhost-user
client sends messages like get max memory slots, add memory region
and remove memory region which are covered in these changes which
are on the vhost-user back-end. The changes also include data structure
definition of memory region to be added/removed. The data structure
VhostUserMsg has been changed to include the memory region.

Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
---
 lib/vhost/vhost_user.h | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/lib/vhost/vhost_user.h b/lib/vhost/vhost_user.h
index ef486545ba..6435816534 100644
--- a/lib/vhost/vhost_user.h
+++ b/lib/vhost/vhost_user.h
@@ -67,6 +67,9 @@ typedef enum VhostUserRequest {
 	VHOST_USER_POSTCOPY_END = 30,
 	VHOST_USER_GET_INFLIGHT_FD = 31,
 	VHOST_USER_SET_INFLIGHT_FD = 32,
+	VHOST_USER_GET_MAX_MEM_SLOTS = 36,
+	VHOST_USER_ADD_MEM_REG = 37,
+	VHOST_USER_REM_MEM_REG = 38,
 	VHOST_USER_SET_STATUS = 39,
 	VHOST_USER_GET_STATUS = 40,
 } VhostUserRequest;
@@ -91,6 +94,11 @@ typedef struct VhostUserMemory {
 	VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
 } VhostUserMemory;
 
+typedef struct VhostUserMemRegMsg {
+	uint64_t padding;
+	VhostUserMemoryRegion region;
+} VhostUserMemRegMsg;
+
 typedef struct VhostUserLog {
 	uint64_t mmap_size;
 	uint64_t mmap_offset;
@@ -186,6 +194,7 @@ typedef struct __rte_packed_begin VhostUserMsg {
 		struct vhost_vring_state state;
 		struct vhost_vring_addr addr;
 		VhostUserMemory memory;
+		VhostUserMemRegMsg memreg;
 		VhostUserLog    log;
 		struct vhost_iotlb_msg iotlb;
 		VhostUserCryptoSessionParam crypto_session;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v13 1/5] vhost: add user to mailmap and define to vhost hdr
From: pravin.bathija @ 2026-05-14  2:01 UTC (permalink / raw)
  To: dev, fengchengwen, stephen, maxime.coquelin; +Cc: pravin.bathija, thomas
In-Reply-To: <20260514020157.1937404-1-pravin.bathija@dell.com>

From: Pravin M Bathija <pravin.bathija@dell.com>

- add user to mailmap file.
- define a bit-field called VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS
  that depicts if the feature/capability to add/remove memory regions
  is supported. This is a part of the overall support for add/remove
  memory region feature in this patchset.

Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
---
 .mailmap              | 1 +
 lib/vhost/rte_vhost.h | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/.mailmap b/.mailmap
index 0e0d83e1c6..cc44e27036 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1295,6 +1295,7 @@ Prateek Agarwal <prateekag@cse.iitb.ac.in>
 Prathisna Padmasanan <prathisna.padmasanan@intel.com>
 Praveen Kaligineedi <pkaligineedi@google.com>
 Praveen Shetty <praveen.shetty@intel.com>
+Pravin M Bathija <pravin.bathija@dell.com>
 Pravin Pathak <pravin.pathak.dev@gmail.com> <pravin.pathak@intel.com>
 Prince Takkar <ptakkar@marvell.com>
 Priyalee Kushwaha <priyalee.kushwaha@intel.com>
diff --git a/lib/vhost/rte_vhost.h b/lib/vhost/rte_vhost.h
index 2f7c4c0080..a7f9700538 100644
--- a/lib/vhost/rte_vhost.h
+++ b/lib/vhost/rte_vhost.h
@@ -109,6 +109,10 @@ extern "C" {
 #define VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD 12
 #endif
 
+#ifndef VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS
+#define VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS 15
+#endif
+
 #ifndef VHOST_USER_PROTOCOL_F_STATUS
 #define VHOST_USER_PROTOCOL_F_STATUS 16
 #endif
-- 
2.43.0


^ permalink raw reply related

* [PATCH v13 0/5] Support add/remove memory region and get-max-slots
From: pravin.bathija @ 2026-05-14  2:01 UTC (permalink / raw)
  To: dev, fengchengwen, stephen, maxime.coquelin; +Cc: pravin.bathija, thomas

From: Pravin M Bathija <pravin.bathija@dell.com>

This is version v13 of the patchset and it incorporates the
recommendations made by Fengcheng Wen.

Changes made to patch 3/5 and 4/5
* Relocated function remove_guest_pages from patch 3/5 to 4/5.
* Renamed VhostUserSingleMemReg to VhostUserMemRegMsg and memory_single
  to memreg.

This implementation has been extensively tested by doing Read/Write I/O
from multiple instances of fio + libblkio (front-end) talking to
spdk/dpdk (back-end) based drives. Tested with qemu front-end talking to
dpdk testpmd (back-end) performing add/removal of memory regions. Also
tested post-copy live migration after doing add_memory_region.

Version Log:
Version v13 (Current version): Incorporate code review suggestions from
Fengcheng Wen as described above.
Version v12: Incorporate code review suggestions from Maxime Coquelin
and ai-code-review.
Changes made to patch 3/5
Refactored async_dma_map() to delegate to async_dma_map_region(),
eliminating code duplication between the two functions.
Restored original comments in async_dma_map_region() explaining why
ENODEV and EINVAL errors are ignored (these were stripped in v10)
Reverted unnecessary changes to vhost_user_postcopy_register() --
removed the host_user_addr == 0 checks and reg_msg_index indirection
that were added in  v10, since this function is only called from
vhost_user_set_mem_table() where regions are always contiguous.

Version v11: Incorporate code review suggestions from Stephen Hemminger.
Change made to patch 4/5
Fix incomplete cleanup in vhost_user_add_mem_reg() when
vhost_user_mmap_region() fails after the mmap succeeds (e.g.
add_guest_pages() realloc failure) realloc failure). The error path now
calls remove_guest_pages() and free_mem_region() to undo the mapping
and stale guest-page entries, preventing a leaked mmap and slot reuse
corruption. The plain close(fd) path is kept for pre-mmap failures.

Version v10: Incorporate code review suggestions from Stephen Hemminger.
Change made to patch 4/5
Moved dev_invalidate_vrings after free_mem_region, array compaction, and
nregions decrement. This ensures translate_ring_addresses only sees
surviving memory regions, preventing vring pointers from resolving into
a region that is about to be unmapped.

Version v9: Incorporate code review suggestions from Stephen Hemminger.
Changes made to patch 3/5
Restored max_guest_pages initial value to hardcoded 8 instead of
VHOST_MEMORY_MAX_NREGIONS, matching upstream semantics.
Changes made to patch 4/5
Added close(reg->fd) and reg->fd = -1 before goto close_msg_fds in the
mmap failure path to fix fd leak after fd was moved from ctx->fds[0].
Converted dev_invalidate_vrings from a plain function to a macro +
implementation function pair, accepting message ID as a parameter so
the static_assert reports the correct handler at each call site.
Updated dev_invalidate_vrings call in add_mem_reg to pass
VHOST_USER_ADD_MEM_REG as message ID.
Updated dev_invalidate_vrings call in rem_mem_reg to pass
VHOST_USER_REM_MEM_REG as message ID.

Version v8:  Incorporate code review suggestions from Stephen Hemminger.
rewrite async_dma_map_region function to iterate guest pages by host
address range matching
change function dev_invalidate_vrings to accept a double pointer to
propagate pointer updates
new function remove_guest_pages was added
add_mem_reg error path was narrowed to only clean up the single failed
region instead of destroting all existing regions

Version v7: Incorporate code review suggestions from Maxime Coquelin.
Add debug messages to vhost_postcopy_register function.

Version v6: Added the enablement of this feature as a final patch in
this patch-set and other code optimizations as suggested by Maxime
Coquelin.

Version v5: removed the patch that increased the number of memory regions
from 8 to 128. This will be submitted as a separate feature at a later
point after incorporating additional optimizations. Also includes code
optimizations as suggested by Feng Cheng Wen.

Version v4: code optimizations as suggested by Feng Cheng Wen.

Version v3: code optimizations as suggested by Maxime Coquelin
and Thomas Monjalon.

Version v2: code optimizations as suggested by Maxime Coquelin.

Version v1: Initial patch set.

Pravin M Bathija (5):
  vhost: add user to mailmap and define to vhost hdr
  vhost_user: header defines for add/rem mem region
  vhost_user: support function defines for back-end
  vhost_user: Function defs for add/rem mem regions
  vhost_user: enable configure memory slots

 .mailmap               |   1 +
 lib/vhost/rte_vhost.h  |   4 +
 lib/vhost/vhost_user.c | 418 +++++++++++++++++++++++++++++++++++------
 lib/vhost/vhost_user.h |  10 +
 4 files changed, 371 insertions(+), 62 deletions(-)

-- 
2.43.0


^ permalink raw reply

* [PATCH] Windows: fix core count on NUMA with more than 64 cores per node
From: Andre Muezerie @ 2026-05-13 23:55 UTC (permalink / raw)
  To: Dmitry Kozlyuk; +Cc: dev, Gena Tertychnyi, Andre Muezerie

From: Gena Tertychnyi <genter@microsoft.com>

Fix specific to Windows NUMA machines with more than 64 cores per node
(e.g. 2 NUMAs with 128 cores each):

- NumaNode.GroupMasks[] array is used instead of NumaNode.GroupMask.
- RelationAll is used instead of RelationNumaNode when calling
GetLogicalProcessorInformationEx because RelationAll returns the full
multi-group NUMA affinity as RelationNumaNode returns only the NUMA
node's primary group.

Signed-off-by: Gena Tertychnyi <genter@microsoft.com>
Signed-off-by: Andre Muezerie <andremue@linux.microsoft.com>
---
 lib/eal/windows/eal_lcore.c | 68 +++++++++++++++++++++++++++----------
 1 file changed, 51 insertions(+), 17 deletions(-)

diff --git a/lib/eal/windows/eal_lcore.c b/lib/eal/windows/eal_lcore.c
index a498044620..d2a2c2d091 100644
--- a/lib/eal/windows/eal_lcore.c
+++ b/lib/eal/windows/eal_lcore.c
@@ -17,13 +17,27 @@
 /** Number of logical processors (cores) in a processor group (32 or 64). */
 #define EAL_PROCESSOR_GROUP_SIZE (sizeof(KAFFINITY) * CHAR_BIT)
 
+/*
+ * NUMA_NODE_RELATIONSHIP layout differs:
+ *  - MSVC + modern SDK: GroupCount + GroupMasks[]
+ *  - MinGW-w64: only GroupMask (MinGW headers lag behind Windows SDK ABI changes)
+ */
+#ifdef RTE_TOOLCHAIN_GCC
+#define EAL_NUMA_GROUP_COUNT(numa) (RTE_SET_USED(numa), 1)
+#define EAL_NUMA_GROUP_MASKS(numa) (&((numa).GroupMask))
+#else
+#define EAL_NUMA_GROUP_COUNT(numa) ((numa).GroupCount)
+#define EAL_NUMA_GROUP_MASKS(numa) ((numa).GroupMasks)
+#endif
+
 struct lcore_map {
-	uint8_t socket_id;
-	uint8_t core_id;
+	unsigned int socket_id;
+	unsigned int core_id;
 };
 
 struct socket_map {
 	uint16_t node_id;
+	unsigned int lcore_count;
 };
 
 struct cpu_map {
@@ -112,11 +126,15 @@ static bool
 eal_create_lcore_map(const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *info)
 {
 	const unsigned int node_id = info->NumaNode.NodeNumber;
-	const GROUP_AFFINITY *cores = &info->NumaNode.GroupMask;
+	const GROUP_AFFINITY *group_masks = EAL_NUMA_GROUP_MASKS(info->NumaNode);
 	struct lcore_map *lcore;
 	unsigned int socket_id;
+	unsigned int group_count;
+	unsigned int group_no;
 	unsigned int i;
 
+	group_count = EAL_NUMA_GROUP_COUNT(info->NumaNode);
+
 	/*
 	 * NUMA node may be reported multiple times if it includes
 	 * cores from different processor groups, e. g. 80 cores
@@ -132,20 +150,33 @@ eal_create_lcore_map(const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *info)
 			return true;
 
 		cpu_map.sockets[socket_id].node_id = node_id;
+		cpu_map.sockets[socket_id].lcore_count = 0;
 		cpu_map.socket_count++;
 	}
 
-	for (i = 0; i < EAL_PROCESSOR_GROUP_SIZE; i++) {
-		if ((cores->Mask & ((KAFFINITY)1 << i)) == 0)
-			continue;
+	/* Old Windows versions report NUMA nodes with GroupCount == 0 */
+	if (group_count == 0) {
+		group_count = 1;
+		group_masks = &info->NumaNode.GroupMask;
+	}
 
-		if (cpu_map.lcore_count == RTE_DIM(cpu_map.lcores))
-			return true;
+	for (group_no = 0; group_no < group_count; group_no++) {
+		const GROUP_AFFINITY *cores = &group_masks[group_no];
+		for (i = 0; i < EAL_PROCESSOR_GROUP_SIZE; i++) {
+			if ((cores->Mask & ((KAFFINITY)1 << i)) == 0)
+				continue;
+
+			if (cpu_map.lcore_count == RTE_DIM(cpu_map.lcores))
+				return true;
 
-		lcore = &cpu_map.lcores[cpu_map.lcore_count];
-		lcore->socket_id = socket_id;
-		lcore->core_id = cores->Group * EAL_PROCESSOR_GROUP_SIZE + i;
-		cpu_map.lcore_count++;
+			lcore = &cpu_map.lcores[cpu_map.lcore_count];
+			lcore->socket_id = socket_id;
+
+			/* core_id within the socket */
+			lcore->core_id = cpu_map.sockets[socket_id].lcore_count;
+			cpu_map.sockets[socket_id].lcore_count++;
+			cpu_map.lcore_count++;
+		}
 	}
 	return false;
 }
@@ -160,8 +191,9 @@ eal_create_cpu_map(void)
 
 	infos = NULL;
 	infos_size = 0;
+	/* RelationAll is needed to get full multi-group NUMA affinity */
 	if (!GetLogicalProcessorInformationEx(
-			RelationNumaNode, NULL, &infos_size)) {
+			RelationAll, NULL, &infos_size)) {
 		DWORD error = GetLastError();
 		if (error != ERROR_INSUFFICIENT_BUFFER) {
 			log_early("Cannot get NUMA node info size, error %lu\n",
@@ -181,7 +213,7 @@ eal_create_cpu_map(void)
 	}
 
 	if (!GetLogicalProcessorInformationEx(
-			RelationNumaNode, infos, &infos_size)) {
+			RelationAll, infos, &infos_size)) {
 		log_early("Cannot get NUMA node information, error %lu\n",
 			GetLastError());
 		rte_errno = EINVAL;
@@ -191,9 +223,11 @@ eal_create_cpu_map(void)
 
 	info = infos;
 	while ((uint8_t *)info - (uint8_t *)infos < infos_size) {
-		if (eal_create_lcore_map(info)) {
-			full = true;
-			break;
+		if (info->Relationship == RelationNumaNode) {
+			if (eal_create_lcore_map(info)) {
+				full = true;
+				break;
+			}
 		}
 
 		info = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)(
-- 
2.53.0.vfs.0.7


^ permalink raw reply related

* Re: [PATCH v2 2/2] bpf/arm64: support packet data load instructions
From: Christophe Fontaine @ 2026-05-13 20:56 UTC (permalink / raw)
  To: Wathsala Vithanage; +Cc: dev, Konstantin Ananyev, Marat Khalili
In-Reply-To: <35f96fed-29d4-4a0b-b3a1-0f2c9fc6b60e@arm.com>

On Thu, Apr 9, 2026 at 7:24 PM Wathsala Vithanage
<wathsala.vithanage@arm.com> wrote:
>
> Hi Christophe,
>
> Thanks for doing this work. I saw that you intend to submit a v3 due to
> failure in Ubuntu. Please, see my comment regarding emit_b below.
>
> >> +       /* A64_R(0) = mbuf->buf_addr */
> >> +       emit_mov_imm(ctx, 1, tmp1, offsetof(struct rte_mbuf, buf_addr));
> >> +       emit_ldr(ctx, EBPF_DW, A64_R(0), r6, tmp1);
> >> +       /* tmp2 = * mbuf->data_off */
> >> +       emit_mov_imm(ctx, 1, tmp2, offsetof(struct rte_mbuf, data_off));
> >> +       emit_ldr(ctx, BPF_H, tmp2, r6, tmp2);
> >> +
> >> +       /* A64_R(0) += data_off + off */
> >> +       emit_add(ctx, 1, A64_R(0), tmp2);
> >> +       emit_add(ctx, 1, A64_R(0), A64_R(1));
> >> +
> >> +       /* End of Fast Path, skip slow path */
> >> +       emit_b(ctx, 4);
> >> +
> Followed by emit_b() above there is an emit_call(). It invokes
> emit_mov_imm() which could possibly generate different number of movk
> instructions depending on the address of the __rte_pktmbuf_read. So,
> emit_b(ctx, 4), may not  necessarily land at the intended location
> because there could be more than 4 instructions emitted by emit_call.

Hi
Very good point, this was probably the cause of the error on Ubuntu.
Sorry for the delay in responding to this patch, but I'm about to send
a new version, fixing (hopefully!) all the previous comments.
I'll add you in CC for the new version as well.

Christophe

> >> +       /* slow path, call __rte_pktmbuf_read */
> >> +       emit_call(ctx, tmp1, __rte_pktmbuf_read);
> >> +       /* check return value of __rte_pktmbuf_read */
> >> +       emit_return_zero_if_src_zero(ctx, 1, A64_R(0));
> >> +
> >> +       /* A64_R(0) points to the data, load 1/2/4 bytes into r0*/
> >> +       emit_ldr(ctx, opsz, r0, A64_R(0), A64_ZR);
> >> +       if (sz != sizeof(uint8_t))
> >> +               emit_be(ctx, r0, sz * CHAR_BIT);
> >> +}
> >> +
> >>
>
> --wathsala
>
>


^ permalink raw reply

* [DPDK/core Bug 1944] Crash due to corruption from data race in page prefault in alloc_seg()
From: bugzilla @ 2026-05-13 17:23 UTC (permalink / raw)
  To: dev

http://bugs.dpdk.org/show_bug.cgi?id=1944

            Bug ID: 1944
           Summary: Crash due to corruption from data race in page
                    prefault in alloc_seg()
           Product: DPDK
           Version: 24.11
          Hardware: All
                OS: Linux
            Status: UNCONFIRMED
          Severity: normal
          Priority: Normal
         Component: core
          Assignee: dev@dpdk.org
          Reporter: michal.sieron@nokia.com
  Target Milestone: ---

Hi, recently we caught a data corruption seemingly caused by a page prefault in
`alloc_seg()` function from `lib/eal/linux/eal_memalloc.c`:
> *(volatile int *)addr = *(volatile int *)addr;

Internal investigation showed that `rte_eal_init()` from a secondary process
was indirectly calling this function, which in rare cases led to data
corruption.
In our case it resulted in a scheduler crash:
> EAL: PANIC in eth_event_queue_callback():
> Cannot restart RxQ(p:1,q:0) after max trying times 4, abort!

The issue is reported for v24.11, but I don't think it is tied to this specific
version and is a general problem since that prefault was added in v18.05.

-- 
You are receiving this mail because:
You are the assignee for the bug.

^ permalink raw reply

* RE: [PATCH] Updated skeleton example code to better emit error
From: Marat Khalili @ 2026-05-13 16:30 UTC (permalink / raw)
  To: Arin Kharkar, dev@dpdk.org
In-Reply-To: <20260512192834.130651-1-arinkharkar@gmail.com>

> --- a/examples/skeleton/basicfwd.c
> +++ b/examples/skeleton/basicfwd.c
> @@ -188,11 +188,9 @@ main(int argc, char *argv[])
>  	/* Check that there is an even number of ports to send/receive on. */
>  	nb_ports = rte_eth_dev_count_avail();
>  	if (nb_ports < 2 || (nb_ports & 1))
> -		rte_exit(EXIT_FAILURE, "Error: number of ports must be even\n");
> +		rte_exit(EXIT_FAILURE, "Error: number of ports must be even, found %u ports\n", nb_ports);

Maybe "positive even"?

^ permalink raw reply

* [PATCH v3] dts: clean cryptodev environment after a test run
From: Andrew Bailey @ 2026-05-13 15:40 UTC (permalink / raw)
  To: luca.vizzarro, patrickrobb1997
  Cc: lylavoie, ahassick, knimoji, dev, Andrew Bailey
In-Reply-To: <20260507163659.40739-1-abailey@iol.unh.edu>

Prior to this patch, the virtual functions created by DTS for crypto
devices were not properly cleanedu up. This could lead to a system to be
left in a bad state after running the cryptodev throughput test suite.
This patch properly cleans up the environment after cryptodev testing.

Bugzilla ID: 1923
Fixes: 8ee2df9da125 ("dts: add cryptodev package")

Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
v3:
* Added a missing type hint.

 dts/framework/testbed_model/linux_session.py | 29 +++++++++++++++++++-
 dts/framework/testbed_model/os_session.py    |  8 ++++++
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index ee943462c2..aa53f1e5e2 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -181,6 +181,22 @@ def get_port_info(self, pci_address: str) -> PortInfo:

         return PortInfo(mac_address, logical_name, driver, is_link_up)

+    def unbind_ports(self, ports: list[Port]):
+        """Overrides :meth:`~.os_session.OSSession.unbind_ports`.
+
+        The :attr:`~.devbind_script_path` property must be setup in order to call this method.
+        """
+        ports_pci_addrs = " ".join(port.pci for port in ports)
+
+        self.send_command(
+            f"{self.devbind_script_path} -u --force {ports_pci_addrs}",
+            privileged=True,
+            verify=True,
+        )
+
+        if self._lshw_net_info:
+            del self._lshw_net_info
+
     def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
         """Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.

@@ -289,7 +305,18 @@ def create_vfs(self, pf_port: Port) -> None:
             self.refresh_lshw()

     def delete_crypto_vfs(self, pf_port: Port) -> None:
-        """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`."""
+        """Overrides :meth:`~.os_session.OSSession.delete_crypto_vfs`.
+
+        The :attr:`~.devbind_script_path` property must be setup in order to call this method.
+        """
+        if vfs := self.get_pci_addr_of_crypto_vfs(pf_port):
+            vf_pci_addrs = " ".join(vf for vf in vfs)
+            self.send_command(
+                f"{self.devbind_script_path} -u --force {vf_pci_addrs}",
+                privileged=True,
+                verify=True,
+            )
+        self.unbind_ports([pf_port])
         self.send_command(
             f"echo 1 | sudo tee /sys/bus/pci/devices/{pf_port.pci}/remove".replace(":", "\\:"),
             privileged=True,
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index 2c267afed1..f2dc9b20a9 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -573,6 +573,14 @@ def get_port_info(self, pci_address: str) -> PortInfo:
             ConfigurationError: If the port could not be found.
         """

+    @abstractmethod
+    def unbind_ports(self, ports: list[Port]) -> None:
+        """Unbind `ports` from any driver.
+
+        Args:
+            ports: The list of ports to unbind.
+        """
+
     @abstractmethod
     def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
         """Bind `ports` to the given `driver_name`.
--
2.50.1


^ permalink raw reply related

* [PATCH v1 2/2] dts: add latency coverage for cryptodev testing
From: Andrew Bailey @ 2026-05-13 15:27 UTC (permalink / raw)
  To: luca.vizzarro, patrickrobb1997
  Cc: dev, lylavoie, ahassick, knimoji, Andrew Bailey
In-Reply-To: <20260513152715.133381-1-abailey@iol.unh.edu>

Currently, next DTS only has cryptodev testing coverage for throughput
metrics. This patch adds a test suite to include latency testing for
crypto devices.

Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
 .../dts/tests.TestSuite_cryptodev_latency.rst |   8 +
 dts/tests/TestSuite_cryptodev_latency.py      | 695 ++++++++++++++++++
 2 files changed, 703 insertions(+)
 create mode 100644 doc/api/dts/tests.TestSuite_cryptodev_latency.rst
 create mode 100644 dts/tests/TestSuite_cryptodev_latency.py

diff --git a/doc/api/dts/tests.TestSuite_cryptodev_latency.rst b/doc/api/dts/tests.TestSuite_cryptodev_latency.rst
new file mode 100644
index 0000000000..1bf4a0f4b0
--- /dev/null
+++ b/doc/api/dts/tests.TestSuite_cryptodev_latency.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: BSD-3-Clause
+
+cryptodev_latency Test Suite
+============================
+
+.. automodule:: tests.TestSuite_cryptodev_latency
+   :members:
+   :show-inheritance:
diff --git a/dts/tests/TestSuite_cryptodev_latency.py b/dts/tests/TestSuite_cryptodev_latency.py
new file mode 100644
index 0000000000..54edd4e0a6
--- /dev/null
+++ b/dts/tests/TestSuite_cryptodev_latency.py
@@ -0,0 +1,695 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2026 University of New Hampshire
+
+"""DPDK cryptodev performance test suite.
+
+The main goal of this test suite is to utilize the dpdk-test-cryptodev application to gather
+performance metrics for various cryptographic operations supported by DPDK cryptodev-pmd.
+It will then compare the results against a predefined baseline given in the test_config file to
+ensure performance standards are met.
+"""
+
+from api.capabilities import (
+    LinkTopology,
+    requires_link_topology,
+)
+from api.cryptodev import Cryptodev
+from api.cryptodev.config import (
+    AeadAlgName,
+    AuthenticationAlgorithm,
+    AuthenticationOpMode,
+    CipherAlgorithm,
+    DeviceType,
+    EncryptDecryptSwitch,
+    ListWrapper,
+    OperationType,
+    TestType,
+    get_device_from_str,
+)
+from api.cryptodev.types import (
+    CryptodevResults,
+)
+from api.test import skip, verify
+from framework.context import get_ctx
+from framework.test_suite import BaseConfig, TestSuite, crypto_test
+from framework.testbed_model.virtual_device import VirtualDevice
+
+config_list: list[dict[str, int | float | str]] = [
+    {"buff_size": 64, "avg_cycles": 99_999.00, "avg_time_us": 9999.0},
+    {"buff_size": 512, "avg_cycles": 99_999.00, "avg_time_us": 9999.0},
+    {"buff_size": 2048, "avg_cycles": 99_999.00, "avg_time_us": 9999.0},
+]
+
+TOTAL_OPS = 10_000_000
+
+
+class Config(BaseConfig):
+    """Performance test metrics.
+
+    Attributes:
+        delta_tolerance: The allowed tolerance below a given baseline.
+        latency_test_parameters: The test parameters to use in the test suite.
+    """
+
+    delta_tolerance: float = 0.05
+
+    latency_test_parameters: dict[str, list[dict[str, int | float | str]]] = {
+        "aes_cbc": config_list,
+        "aes_cbc_sha1_hmac": config_list,
+        "aes_cbc_sha2_hmac": config_list,
+        "aes_docsisbpi": config_list,
+        "aes_gcm": config_list,
+        "kasumi_f8_kasumi_f9": config_list,
+        "snow3g_uea2_snow3g_uia2": config_list,
+        "zuc_eea3_zuc_eia3": config_list,
+        "aesni_gcm_vdev": config_list,
+        "aesni_mb_cipher_then_auth_vdev": config_list,
+        "aesni_mb_vdev": config_list,
+        "kasumi_vdev": config_list,
+        "open_ssl_vdev": config_list,
+        "snow3g_vdev": config_list,
+        "zuc_vdev": config_list,
+    }
+
+
+@requires_link_topology(LinkTopology.NO_LINK)
+class TestCryptodevLatency(TestSuite):
+    """DPDK Crypto Device Testing Suite."""
+
+    config: Config
+
+    def set_up_suite(self) -> None:
+        """Set up the test suite."""
+        self.latency_test_parameters: dict[str, list[dict[str, int | float | str]]] = (
+            self.config.latency_test_parameters
+        )
+        self.delta_tolerance: float = self.config.delta_tolerance
+        self.device_type: DeviceType | None = get_device_from_str(
+            str(get_ctx().sut_node.crypto_device_type)
+        )
+        self.buffer_sizes = {}
+
+        for k, v in self.latency_test_parameters.items():
+            self.buffer_sizes[k] = ListWrapper([int(run["buff_size"]) for run in v])
+
+    def _print_stats(self, test_vals: list[dict[str, int | float | str]]) -> None:
+        element_len = len("Avg Time us Target")
+        border_len = (element_len + 1) * (len(test_vals[0]))
+
+        print(f"{'Latency Results'.center(border_len)}\n{'=' * border_len}")
+        for k, v in test_vals[0].items():
+            print(f"|{k.title():<{element_len}}", end="")
+        print(f"|\n{'='*border_len}")
+
+        for test_val in test_vals:
+            for k, v in test_val.items():
+                print(f"|{v:<{element_len}}", end="")
+            print(f"|\n{'='*border_len}")
+
+    def _verify_latency(
+        self,
+        results: list[CryptodevResults],
+        key: str,
+    ) -> list[dict[str, int | float | str]]:
+        result_list: list[dict[str, int | float | str]] = []
+
+        for result in results:
+            # get the corresponding baseline for the current buffer size
+            parameters: dict[str, int | float | str] = list(
+                filter(
+                    lambda x: x["buff_size"] == result.buffer_size,
+                    self.latency_test_parameters[key],
+                )
+            )[0]
+            test_result = True
+            expected_cycles = parameters["avg_cycles"]
+            expected_time_us = parameters["avg_time_us"]
+            measured_time_delta = abs(
+                (getattr(result, "avg_time_us") - expected_time_us) / expected_time_us
+            )
+            measured_cycles_delta = abs(
+                (getattr(result, "avg_cycles") - expected_cycles) / expected_cycles
+            )
+
+            # result did not meet the given cycles parameter, check if within delta.
+            if getattr(result, "avg_cycles") > expected_cycles:
+                if self.delta_tolerance < measured_cycles_delta:
+                    test_result = False
+            # result did not meet the given time parameter, check if within delta.
+            if getattr(result, "avg_time_us") > expected_time_us:
+                if self.delta_tolerance < measured_time_delta:
+                    test_result = False
+            result_list.append(
+                {
+                    "Buffer Size": parameters["buff_size"],
+                    "delta tolerance": self.delta_tolerance,
+                    "cycles delta": round(measured_cycles_delta, 5),
+                    "Avg cycles": round(getattr(result, "avg_cycles"), 5),
+                    "Avg cycles target": expected_cycles,
+                    "time delta": round(measured_time_delta, 5),
+                    "Avg time us": round(getattr(result, "avg_time_us"), 5),
+                    "Avg time us target": expected_time_us,
+                    "passed": "PASS" if test_result else "FAIL",
+                }
+            )
+        return result_list
+
+    @crypto_test
+    def aes_cbc(self) -> None:
+        """aes_cbc latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "aes_cbc" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            devtype=self.device_type,
+            optype=OperationType.cipher_only,
+            cipher_algo=CipherAlgorithm.aes_cbc,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            cipher_iv_sz=16,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["aes_cbc"],
+        )
+        results = self._verify_latency(app.run_app(), "aes_cbc")
+        self._print_stats(results)
+        for result in results:
+            verify(
+                result["passed"] == "PASS",
+                "latency fell more than the delta tolerance below baseline",
+            )
+
+    @crypto_test
+    def aes_cbc_sha1_hmac(self) -> None:
+        """aes_cbc_sha1_hmac latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "aes_cbc_sha1_hmac" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            devtype=self.device_type,
+            optype=OperationType.cipher_then_auth,
+            cipher_algo=CipherAlgorithm.aes_cbc,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            auth_algo=AuthenticationAlgorithm.sha1_hmac,
+            auth_op=AuthenticationOpMode.generate,
+            auth_key_sz=64,
+            digest_sz=12,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["aes_cbc_sha1_hmac"],
+        )
+        results = self._verify_latency(app.run_app(), "aes_cbc_sha1_hmac")
+        self._print_stats(results)
+        for result in results:
+            verify(
+                result["passed"] == "PASS",
+                "latency fell more than the delta tolerance below baseline",
+            )
+
+    @crypto_test
+    def aes_cbc_sha2_hmac(self) -> None:
+        """aes_cbc_sha2_hmac latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "aes_cbc_sha2_hmac" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            devtype=self.device_type,
+            optype=OperationType.cipher_then_auth,
+            cipher_algo=CipherAlgorithm.aes_cbc,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            cipher_iv_sz=16,
+            auth_algo=AuthenticationAlgorithm.sha2_256_hmac,
+            auth_op=AuthenticationOpMode.generate,
+            auth_key_sz=64,
+            digest_sz=32,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["aes_cbc_sha2_hmac"],
+        )
+        results = self._verify_latency(app.run_app(), "aes_cbc_sha2_hmac")
+        self._print_stats(results)
+        for result in results:
+            verify(
+                result["passed"] == "PASS",
+                "latency fell more than the delta tolerance below baseline",
+            )
+
+    @crypto_test
+    def aes_docsisbpi(self) -> None:
+        """aes_docsisbpi latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "aes_docsisbpi" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            devtype=self.device_type,
+            optype=OperationType.cipher_only,
+            cipher_algo=CipherAlgorithm.aes_docsisbpi,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=32,
+            cipher_iv_sz=16,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["aes_docsisbpi"],
+        )
+        results = self._verify_latency(app.run_app(), "aes_docsisbpi")
+        self._print_stats(results)
+        for result in results:
+            verify(
+                result["passed"] == "PASS",
+                "latency fell more than the delta tolerance below baseline",
+            )
+
+    @crypto_test
+    def aes_gcm(self) -> None:
+        """aes_gcm latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "aes_gcm" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            devtype=self.device_type,
+            optype=OperationType.aead,
+            aead_algo=AeadAlgName.aes_gcm,
+            aead_op=EncryptDecryptSwitch.encrypt,
+            aead_key_sz=16,
+            aead_iv_sz=12,
+            aead_aad_sz=16,
+            digest_sz=16,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["aes_gcm"],
+        )
+        results = self._verify_latency(app.run_app(), "aes_gcm")
+        self._print_stats(results)
+        for result in results:
+            verify(
+                result["passed"] == "PASS",
+                "latency fell more than the delta tolerance below baseline",
+            )
+
+    @crypto_test
+    def kasumi_f8_kasumi_f9(self) -> None:
+        """kasumi_f8_kasumi_f9 latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "kasumi_f8_kasumi_f9" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            devtype=self.device_type,
+            optype=OperationType.cipher_then_auth,
+            cipher_algo=CipherAlgorithm.kasumi_f8,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            cipher_iv_sz=8,
+            auth_algo=AuthenticationAlgorithm.kasumi_f9,
+            auth_op=AuthenticationOpMode.generate,
+            auth_key_sz=16,
+            digest_sz=4,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["kasumi_f8_kasumi_f9"],
+        )
+        results = self._verify_latency(app.run_app(), "kasumi_f8_kasumi_f9")
+        self._print_stats(results)
+        for result in results:
+            verify(
+                result["passed"] == "PASS",
+                "latency fell more than the delta tolerance below baseline",
+            )
+
+    @crypto_test
+    def snow3g_uea2_snow3g_uia2(self) -> None:
+        """snow3g_uea2_snow3g_uia2 latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "snow3g_uea2_snow3g_uia2" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            devtype=self.device_type,
+            optype=OperationType.cipher_then_auth,
+            cipher_algo=CipherAlgorithm.snow3g_uea2,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            cipher_iv_sz=16,
+            auth_algo=AuthenticationAlgorithm.snow3g_uia2,
+            auth_op=AuthenticationOpMode.generate,
+            auth_key_sz=16,
+            auth_iv_sz=16,
+            digest_sz=4,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["snow3g_uea2_snow3g_uia2"],
+        )
+        results = self._verify_latency(app.run_app(), "snow3g_uea2_snow3g_uia2")
+        self._print_stats(results)
+        for result in results:
+            verify(
+                result["passed"] == "PASS",
+                "latency fell more than the delta tolerance below baseline",
+            )
+
+    @crypto_test
+    def zuc_eea3_zuc_eia3(self) -> None:
+        """zuc_eea3_zuc_eia3 vdev cipher and auth latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "zuc_eea3_zuc_eia3" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            devtype=self.device_type,
+            optype=OperationType.cipher_then_auth,
+            cipher_algo=CipherAlgorithm.zuc_eea3,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            cipher_iv_sz=16,
+            auth_algo=AuthenticationAlgorithm.zuc_eia3,
+            auth_op=AuthenticationOpMode.generate,
+            auth_key_sz=16,
+            auth_iv_sz=16,
+            digest_sz=4,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["zuc_eea3_zuc_eia3"],
+        )
+        results = self._verify_latency(app.run_app(), "zuc_eea3_zuc_eia3")
+        self._print_stats(results)
+        for result in results:
+            verify(
+                result["passed"] == "PASS",
+                "latency fell more than the delta tolerance below baseline",
+            )
+
+    # BEGIN VDEV TESTS
+
+    @crypto_test
+    def aesni_gcm_vdev(self) -> None:
+        """aesni_gcm virtual device latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "aesni_gcm_vdev" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            vdevs=[VirtualDevice("crypto_aesni_gcm0")],
+            devtype=DeviceType.crypto_aesni_gcm,
+            optype=OperationType.aead,
+            aead_op=EncryptDecryptSwitch.encrypt,
+            aead_key_sz=16,
+            aead_iv_sz=12,
+            aead_aad_sz=16,
+            digest_sz=16,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["aesni_gcm_vdev"],
+        )
+        results = self._verify_latency(app.run_app(), "aesni_gcm_vdev")
+        self._print_stats(results)
+        for result in results:
+            verify(result["passed"] == "PASS", "latency fell more than the delta tolerance")
+
+    @crypto_test
+    def aesni_mb_cipher_then_auth_vdev(self) -> None:
+        """aesni_mb vdev cipher and auth latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "aesni_mb_cipher_then_auth_vdev" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            vdevs=[VirtualDevice("crypto_aesni_mb0")],
+            devtype=DeviceType.crypto_aesni_mb,
+            optype=OperationType.cipher_then_auth,
+            cipher_algo=CipherAlgorithm.aes_cbc,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            auth_algo=AuthenticationAlgorithm.sha1_hmac,
+            auth_op=AuthenticationOpMode.generate,
+            auth_key_sz=64,
+            digest_sz=12,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["aesni_mb_cipher_then_auth_vdev"],
+        )
+        results = self._verify_latency(app.run_app(), "aesni_mb_cipher_then_auth_vdev")
+        self._print_stats(results)
+        for result in results:
+            verify(
+                result["passed"] == "PASS",
+                "latency fell more than the delta tolerance below baseline",
+            )
+
+    @crypto_test
+    def aesni_mb_vdev(self) -> None:
+        """aesni_mb vdev latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "aesni_mb_vdev" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            vdevs=[VirtualDevice("crypto_aesni_mb0")],
+            devtype=DeviceType.crypto_aesni_mb,
+            optype=OperationType.cipher_only,
+            cipher_algo=CipherAlgorithm.aes_cbc,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            cipher_iv_sz=16,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["aesni_mb_vdev"],
+        )
+        results = self._verify_latency(app.run_app(), "aesni_mb_vdev")
+        self._print_stats(results)
+        for result in results:
+            verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+    @crypto_test
+    def kasumi_vdev(self) -> None:
+        """Kasumi vdev latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "kasumi_vdev" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            vdevs=[VirtualDevice("crypto_kasumi0")],
+            devtype=DeviceType.crypto_kasumi,
+            optype=OperationType.cipher_then_auth,
+            cipher_algo=CipherAlgorithm.kasumi_f8,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            cipher_iv_sz=8,
+            auth_algo=AuthenticationAlgorithm.kasumi_f9,
+            auth_op=AuthenticationOpMode.generate,
+            auth_key_sz=16,
+            digest_sz=4,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["kasumi_vdev"],
+        )
+        results = self._verify_latency(app.run_app(), "kasmui_vdev")
+        self._print_stats(results)
+        for result in results:
+            verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+    @crypto_test
+    def open_ssl_vdev(self) -> None:
+        """open_ssl vdev latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "open_ssl_vdev" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            vdevs=[VirtualDevice("crypto_openssl0")],
+            devtype=DeviceType.crypto_openssl,
+            optype=OperationType.aead,
+            aead_algo=AeadAlgName.aes_gcm,
+            aead_op=EncryptDecryptSwitch.encrypt,
+            aead_key_sz=16,
+            aead_iv_sz=16,
+            aead_aad_sz=16,
+            digest_sz=16,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["open_ssl_vdev"],
+        )
+        results = self._verify_latency(app.run_app(), "open_ssl_vdev")
+        self._print_stats(results)
+        for result in results:
+            verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+    @crypto_test
+    def snow3g_vdev(self) -> None:
+        """snow3g vdev latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "snow3g_vdev" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            vdevs=[VirtualDevice("crypto_snow3g0")],
+            devtype=DeviceType.crypto_snow3g,
+            optype=OperationType.cipher_then_auth,
+            cipher_algo=CipherAlgorithm.snow3g_uea2,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            cipher_iv_sz=16,
+            auth_algo=AuthenticationAlgorithm.snow3g_uia2,
+            auth_op=AuthenticationOpMode.generate,
+            auth_key_sz=16,
+            auth_iv_sz=16,
+            digest_sz=16,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["open_ssl_vdev"],
+        )
+        results = self._verify_latency(app.run_app(), "open_ssl_vdev")
+        self._print_stats(results)
+        for result in results:
+            verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
+
+    @crypto_test
+    def zuc_vdev(self) -> None:
+        """Zuc vdev latency test.
+
+        Steps:
+            * Create a cryptodev instance with provided device type and buffer sizes.
+        Verify:
+            * The latency is below or within delta of provided baseline.
+
+        Raises:
+            SkippedTestException: When configuration is not provided.
+        """
+        if "zuc_vdev" not in self.latency_test_parameters:
+            skip("test not configured")
+        app = Cryptodev(
+            ptest=TestType.latency,
+            vdevs=[VirtualDevice("crypto_zuc0")],
+            devtype=DeviceType.crypto_zuc,
+            optype=OperationType.cipher_then_auth,
+            cipher_algo=CipherAlgorithm.zuc_eea3,
+            cipher_op=EncryptDecryptSwitch.encrypt,
+            cipher_key_sz=16,
+            cipher_iv_sz=16,
+            auth_algo=AuthenticationAlgorithm.zuc_eia3,
+            auth_op=AuthenticationOpMode.generate,
+            auth_key_sz=16,
+            auth_iv_sz=16,
+            digest_sz=4,
+            burst_sz=32,
+            total_ops=TOTAL_OPS,
+            buffer_sz=self.buffer_sizes["zuc_vdev"],
+        )
+        results = self._verify_latency(app.run_app(), "zuc_vdev")
+        self._print_stats(results)
+        for result in results:
+            verify(result["passed"] == "PASS", "Gbps fell below delta tolerance")
-- 
2.50.1


^ permalink raw reply related

* [PATCH v1 1/2] dts: update parsing for cryptodev latency
From: Andrew Bailey @ 2026-05-13 15:27 UTC (permalink / raw)
  To: luca.vizzarro, patrickrobb1997
  Cc: dev, lylavoie, ahassick, knimoji, Andrew Bailey
In-Reply-To: <20260428181501.72434-1-abailey@iol.unh.edu>

Previously, the parsing for cryptodev latency output would fail to parse
due to an incorrect regex. This commit updates this regex to properly
capture the burst size attribute for latency results.

Bugzilla ID: 1938
Fixes: 8ee2df9da125 ("dts: add cryptodev package")

Signed-off-by: Andrew Bailey <abailey@iol.unh.edu>
---
 dts/api/cryptodev/__init__.py | 3 +--
 dts/api/cryptodev/types.py    | 4 ++--
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/dts/api/cryptodev/__init__.py b/dts/api/cryptodev/__init__.py
index a4fafc3713..15cffcb409 100644
--- a/dts/api/cryptodev/__init__.py
+++ b/dts/api/cryptodev/__init__.py
@@ -117,7 +117,6 @@ def run_app(self, num_vfs: int = 1) -> list[CryptodevResults]:
                     f"dependencies missing for virtual device {self._app_params['vdevs'][0].name}"
                 )
             raise e
-
         regex = r"^\s+\d+.*$"
         parser_options = re.MULTILINE
         parser: type[CryptodevResults]
@@ -126,7 +125,7 @@ def run_app(self, num_vfs: int = 1) -> list[CryptodevResults]:
             case TestType.throughput:
                 parser = ThroughputResults
             case TestType.latency:
-                regex = r"total operations:.*time[^\n]*"
+                regex = r"total operations:.*?time[^\n]*"
                 parser_options |= re.DOTALL
                 parser = LatencyResults
             case TestType.pmd_cyclecount:
diff --git a/dts/api/cryptodev/types.py b/dts/api/cryptodev/types.py
index df73a86fa4..7b4fd17cb6 100644
--- a/dts/api/cryptodev/types.py
+++ b/dts/api/cryptodev/types.py
@@ -65,11 +65,11 @@ class LatencyResults(CryptodevResults):
 
     #: buffer size ran with app
     buffer_size: int = field(
-        metadata=TextParser.find_int(r"Buf(?:.*\n\s+\d+\s+)?(?:fer size:\s+)?(\d+)"),
+        metadata=TextParser.find_int(r"Buffer size:\s+(\d+)"),
     )
     #: burst size ran with app
     burst_size: int = field(
-        metadata=TextParser.find_int(rf"Burst(?:.*\n\s+\d+\s+){2}?(?: size:\s+)?(\d+)"),
+        metadata=TextParser.find_int(r"Burst size:\s+(\d+)"),
     )
     #: total operations ran
     total_ops: int = field(metadata=TextParser.find_int(r"total operations:\s+(\d+)"))
-- 
2.50.1


^ permalink raw reply related

* Re: [PATCH v13 00/10] net/sxe2: fix logic errors and address feedback
From: Stephen Hemminger @ 2026-05-13 14:45 UTC (permalink / raw)
  To: liujie5; +Cc: dev
In-Reply-To: <20260512113700.837744-1-liujie5@linkdatatechnology.com>

On Tue, 12 May 2026 19:36:49 +0800
liujie5@linkdatatechnology.com wrote:

> From: Jie Liu <liujie5@linkdatatechnology.com>
> 
> This patch set addresses the feedback received on the v10 submission
> for the sxe2 PMD. The primary focus is on fixing vector path selection,
> ensuring memory safety during mbuf initialization, and cleaning up
> redundant logic in the configuration functions.
> 
> v13 Changes:
> - Fixed vector Rx burst function being overwritten by scalar selection.
> - Refactored Rx/Tx mode set functions to seed flags from caps first,eliminating tautological checks.
> - Added memset for mbuf_def in vector init to avoid uninitialized reads.
> - Converted pci_map_addr_info to designated initializers.
> - Removed dead Windows-only code in meson.build.
> - Added NULL checks for mbuf free for driver-wide consistency.
> - Updated burst_mode_get to accurately report AVX paths.
> - Adjusted SXE2_ETH_OVERHEAD to match actual VLAN capabilities.
> 
> Jie Liu (10):
>   mailmap: add Jie Liu
>   doc: add sxe2 guide and release notes
>   common/sxe2: add sxe2 basic structures
>   drivers: add base driver skeleton
>   drivers: add base driver probe skeleton
>   drivers: support PCI BAR mapping
>   common/sxe2: add ioctl interface for DMA map and unmap
>   net/sxe2: support queue setup and control
>   drivers: add data path for Rx and Tx
>   net/sxe2: add vectorized Rx and Tx
> 
>  .mailmap                                   |   1 +
>  doc/guides/nics/features/sxe2.ini          |  30 +
>  doc/guides/nics/index.rst                  |   1 +
>  doc/guides/nics/sxe2.rst                   |  34 +
>  doc/guides/rel_notes/release_26_07.rst     |   4 +
>  drivers/common/sxe2/meson.build            |  15 +
>  drivers/common/sxe2/sxe2_common.c          | 685 +++++++++++++++
>  drivers/common/sxe2/sxe2_common.h          |  86 ++
>  drivers/common/sxe2/sxe2_common_log.h      |  83 ++
>  drivers/common/sxe2/sxe2_errno.h           | 110 +++
>  drivers/common/sxe2/sxe2_host_regs.h       | 707 +++++++++++++++
>  drivers/common/sxe2/sxe2_internal_ver.h    |  33 +
>  drivers/common/sxe2/sxe2_ioctl_chnl.c      | 326 +++++++
>  drivers/common/sxe2/sxe2_ioctl_chnl.h      | 141 +++
>  drivers/common/sxe2/sxe2_ioctl_chnl_func.h |  63 ++
>  drivers/common/sxe2/sxe2_osal.h            | 584 +++++++++++++
>  drivers/common/sxe2/sxe2_type.h            |  60 ++
>  drivers/meson.build                        |   1 +
>  drivers/net/meson.build                    |   1 +
>  drivers/net/sxe2/meson.build               |  32 +
>  drivers/net/sxe2/sxe2_cmd_chnl.c           | 319 +++++++
>  drivers/net/sxe2/sxe2_cmd_chnl.h           |  33 +
>  drivers/net/sxe2/sxe2_drv_cmd.h            | 389 +++++++++
>  drivers/net/sxe2/sxe2_ethdev.c             | 941 ++++++++++++++++++++
>  drivers/net/sxe2/sxe2_ethdev.h             | 315 +++++++
>  drivers/net/sxe2/sxe2_irq.h                |  49 ++
>  drivers/net/sxe2/sxe2_queue.c              |  67 ++
>  drivers/net/sxe2/sxe2_queue.h              | 194 +++++
>  drivers/net/sxe2/sxe2_rx.c                 | 579 +++++++++++++
>  drivers/net/sxe2/sxe2_rx.h                 |  34 +
>  drivers/net/sxe2/sxe2_tx.c                 | 447 ++++++++++
>  drivers/net/sxe2/sxe2_tx.h                 |  32 +
>  drivers/net/sxe2/sxe2_txrx.c               | 372 ++++++++
>  drivers/net/sxe2/sxe2_txrx.h               |  22 +
>  drivers/net/sxe2/sxe2_txrx_common.h        | 541 ++++++++++++
>  drivers/net/sxe2/sxe2_txrx_poll.c          | 945 +++++++++++++++++++++
>  drivers/net/sxe2/sxe2_txrx_poll.h          |  17 +
>  drivers/net/sxe2/sxe2_txrx_vec.c           | 197 +++++
>  drivers/net/sxe2/sxe2_txrx_vec.h           |  72 ++
>  drivers/net/sxe2/sxe2_txrx_vec_common.h    | 235 +++++
>  drivers/net/sxe2/sxe2_txrx_vec_sse.c       | 545 ++++++++++++
>  drivers/net/sxe2/sxe2_vsi.c                | 212 +++++
>  drivers/net/sxe2/sxe2_vsi.h                | 205 +++++
>  43 files changed, 9759 insertions(+)
>  create mode 100644 doc/guides/nics/features/sxe2.ini
>  create mode 100644 doc/guides/nics/sxe2.rst
>  create mode 100644 drivers/common/sxe2/meson.build
>  create mode 100644 drivers/common/sxe2/sxe2_common.c
>  create mode 100644 drivers/common/sxe2/sxe2_common.h
>  create mode 100644 drivers/common/sxe2/sxe2_common_log.h
>  create mode 100644 drivers/common/sxe2/sxe2_errno.h
>  create mode 100644 drivers/common/sxe2/sxe2_host_regs.h
>  create mode 100644 drivers/common/sxe2/sxe2_internal_ver.h
>  create mode 100644 drivers/common/sxe2/sxe2_ioctl_chnl.c
>  create mode 100644 drivers/common/sxe2/sxe2_ioctl_chnl.h
>  create mode 100644 drivers/common/sxe2/sxe2_ioctl_chnl_func.h
>  create mode 100644 drivers/common/sxe2/sxe2_osal.h
>  create mode 100644 drivers/common/sxe2/sxe2_type.h
>  create mode 100644 drivers/net/sxe2/meson.build
>  create mode 100644 drivers/net/sxe2/sxe2_cmd_chnl.c
>  create mode 100644 drivers/net/sxe2/sxe2_cmd_chnl.h
>  create mode 100644 drivers/net/sxe2/sxe2_drv_cmd.h
>  create mode 100644 drivers/net/sxe2/sxe2_ethdev.c
>  create mode 100644 drivers/net/sxe2/sxe2_ethdev.h
>  create mode 100644 drivers/net/sxe2/sxe2_irq.h
>  create mode 100644 drivers/net/sxe2/sxe2_queue.c
>  create mode 100644 drivers/net/sxe2/sxe2_queue.h
>  create mode 100644 drivers/net/sxe2/sxe2_rx.c
>  create mode 100644 drivers/net/sxe2/sxe2_rx.h
>  create mode 100644 drivers/net/sxe2/sxe2_tx.c
>  create mode 100644 drivers/net/sxe2/sxe2_tx.h
>  create mode 100644 drivers/net/sxe2/sxe2_txrx.c
>  create mode 100644 drivers/net/sxe2/sxe2_txrx.h
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_common.h
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_poll.c
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_poll.h
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_vec.c
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_vec.h
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_common.h
>  create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_sse.c
>  create mode 100644 drivers/net/sxe2/sxe2_vsi.c
>  create mode 100644 drivers/net/sxe2/sxe2_vsi.h
> 

Still lots of AI review feedback:

Summary of v13 sxe2 PMD review

Most of the mechanical issues from v10 (carried into v11/v12) have
been addressed in v13: duplicate tx_queue_offload_capa is gone,
RTE_LOG_REGISTER_SUFFIX no longer registers Tx as "rx", the
fopen("/var/log/...") path, SXE2_DPDK_DEBUG, FPGA_VER_ASIC and the
debug flags in meson are gone, rx_queue_release / tx_queue_release
are in dev_ops, and mmap() is correctly checked against MAP_FAILED.

What still needs to be fixed:

Errors
------

Patch 06/10 - Inverted error checks in sxe2_dev_pci_map_init().
sxe2_dev_pci_res_seg_map() returns 0 on success, negative on
failure (the definition is in this same patch). Five consecutive
call sites use "if (!ret)" which is true on success, then log
"Failed to map..." and goto cleanup, unmapping the BAR segment
just mapped and returning ret=0 so the caller thinks probe
succeeded. On a real failure, the check is false and the code
proceeds as if the mapping had succeeded. Same bug flagged in
v10, unchanged in v11/v12/v13.

Patch 04/10 - rte_ticketlock_t (busy-wait FIFO lock) held
across blocking ioctl() to the kernel driver in
sxe2_drv_cmd_exec(), sxe2_drv_dev_handshark(), and the DMA
map/unmap entry points added in patch 07. Other lcores trying
to acquire the lock burn CPU spinning until the kernel returns.
Use pthread_mutex_t; the lock is in process-private memory so
PTHREAD_PROCESS_SHARED is not needed.

Patch 06/10 - Non-ASCII characters in source comments. In
sxe2_net_map_addr_info_pf[]: "/* SXE2_PCI_MAP_RES_IRQ_ITR(默认使用ITR0) */".
DPDK source should be ASCII English.

Warnings
--------

Patch 02/10 - Feature matrix overclaims. sxe2.ini lists
"MTU update = Y" but no .mtu_set is registered, and
"Free Tx mbuf on demand = Y" but no .tx_done_cleanup is
registered. Either implement the callbacks or fix the matrix.

Patch 09/10 - Dead "if (ret != SXE2_SUCCESS)" check in the
secondary-process branch of sxe2_dev_init() after two calls to
void-returning functions (sxe2_rx_mode_func_set,
sxe2_tx_mode_func_set). ret cannot change, so the error log
never fires.

Patch 03/10 - Driver-private kernel-style aliases in
sxe2_type.h: u8/s8/u16/.../s64 typedefs, "#define STATIC static",
"#define __le16 u16", "#define __be16 u16". DPDK convention is
to use the standard names directly. The __le*/__be* defines
also erase the endianness annotation rather than preserving
it.

Patch 03/10 - sxe2_osal.h reinvents infrastructure that already
exists in DPDK: BIT/BIT_ULL/GENMASK/set_bit/test_bit/bitmap_weight
(use rte_bitops.h / rte_bitmap.h), LIST_FOR_EACH_ENTRY and
friends (use sys/queue.h TAILQ_* or rte_tailq), COMPILER_BARRIER
(use rte_compiler_barrier), sxe2_*_lock wrappers around
rte_spinlock_*, and udelay/mdelay/msleep aliases for
rte_delay_us. The kernel idioms __iomem and IS_ERR/IS_ERR_VALUE
do not belong in a userspace PMD.

Patch 03/10 - sxe2_errno.h defines a parallel SXE2_ERR_* namespace
that aliases every POSIX errno. The rest of the driver mixes
both spellings (-EFAULT and SXE2_ERR_PERM appear in the same
file). Pick one and use it everywhere.

Patch 08/10 - Redundant "queue_idx >= nb_*_queues" guards at
the top of sxe2_rx_queue_setup / sxe2_tx_queue_setup / queue_start
/ queue_stop. The ethdev layer validates queue_idx before
calling the PMD.

Patches 04 and 06 - Typos in identifiers and log strings:
sxe2_commoin_inited (commoin -> common), sxe2_drv_dev_handshark
(handshark -> handshake; the ioctl SXE2_COM_CMD_HANDSHAKE is
correct, only the wrapper is mistyped), "kernel reseted, need
restart app." (reseted -> was reset).

Info
----

Patch 09/10 - sxe2_rx_mode_func_set always picks a scattered Rx
burst (split or non-split) regardless of dev->data->scattered_rx.
There is no plain single-segment fast path.

Patch 10/10 - The vector capability probe in
sxe2_rx_mode_func_set is gated on RTE_PROC_PRIMARY, but a
secondary process calls the same function and lands on the
scalar path. Since rx_pkt_burst is per-rte_eth_dev and
re-assigned by whichever process calls last, the resulting
mode depends on attach ordering.

Patch 09/10 - Dead "goto l_end_of_tx;" immediately before the
"l_end_of_tx:" label in sxe2_tx_pkts().

Patch 10/10 - The "AVX512/AVX2 is not supported in build env"
log lines fire based on CPU capability, not on any build-time
absence, so the message is misleading.

The v10-flagged PCI map inverted-check bug is still present
after three respins. 

Longer report available if needed.

^ permalink raw reply

* Re: [PATCH 0/6] net/gve: add hardware timestamping support
From: Stephen Hemminger @ 2026-05-13 14:41 UTC (permalink / raw)
  To: Mark Blasko; +Cc: dev
In-Reply-To: <20260512005404.946979-1-blasko@google.com>

On Tue, 12 May 2026 00:53:47 +0000
Mark Blasko <blasko@google.com> wrote:

> This patch series introduces support for GVE hardware timestamping on DQO
> queues. To support concurrent access, a mutex lock is introduced to protect
> admin queue operations. A mechanism is then added to periodically synchronize
> the NIC clock via AdminQ, and support is introduced for the read_clock ethdev
> operation. Finally, the RX datapath is updated to reconstruct full 64-bit
> timestamps from the 32-bit values in DQO descriptors.
> 
> Mark Blasko (6):
>   net/gve: add thread safety to admin queue
>   net/gve: add device option support for HW timestamps
>   net/gve: add AdminQ command for NIC timestamps
>   net/gve: add periodic NIC clock synchronization
>   net/gve: support read clock ethdev op
>   net/gve: reconstruct HW timestamps from DQO
> 
>  .mailmap                               |   1 +
>  doc/guides/nics/features/gve.ini       |   1 +
>  doc/guides/nics/gve.rst                |  18 +++
>  doc/guides/rel_notes/release_26_07.rst |   3 +
>  drivers/net/gve/base/gve_adminq.c      | 127 +++++++++++++++++----
>  drivers/net/gve/base/gve_adminq.h      |  29 +++++
>  drivers/net/gve/base/gve_desc_dqo.h    |   8 +-
>  drivers/net/gve/gve_ethdev.c           | 148 ++++++++++++++++++++++++-
>  drivers/net/gve/gve_ethdev.h           |  39 +++++++
>  drivers/net/gve/gve_rx_dqo.c           |  26 +++++
>  10 files changed, 378 insertions(+), 22 deletions(-)
> 

Please use version when sending updated patches. See contribution docs.
I.e use -v2 --in-reply-to <mail message id>

AI review spotted some issues:

Patch 1: net/gve: add thread safety to admin queue

Info: The new adminq_lock is initialized with PTHREAD_MUTEX_ROBUST,
but pthread_mutex_lock() return values are not checked anywhere in
the patch. With robust attribute set, lock can return EOWNERDEAD if
a previous owner terminated while holding the mutex; the caller is
required to call pthread_mutex_consistent() in that case, otherwise
the mutex transitions to ENOTRECOVERABLE on subsequent locks. In a
single-process DPDK PMD a thread crash brings down the whole
process, so EOWNERDEAD is unreachable in practice and the robust
attribute is unnecessary. The existing flow_rule_lock in this
driver uses PROCESS_SHARED but not ROBUST. Drop
pthread_mutexattr_setrobust() to match.


Patch 5: net/gve: support read clock ethdev op

Warning: gve_read_clock and the periodic alarm callback
gve_read_nic_clock share a single DMA buffer (priv->nic_ts_report)
for the GVE_ADMINQ_REPORT_NIC_TIMESTAMP response. The adminq_lock
introduced in patch 1 serializes the adminq command exchange, but
is released inside gve_adminq_execute_cmd before either caller
reads the buffer:

    err = gve_adminq_report_nic_timestamp(priv,
                                priv->nic_ts_report_mz->iova);
    /* lock has already been released here */
    if (err != 0)
        return err;
    ts = be64_to_cpu(priv->nic_ts_report->nic_timestamp);

If the user thread (gve_read_clock) is preempted between returning
from gve_adminq_report_nic_timestamp and the be64_to_cpu read, the
alarm callback can run, memset() the buffer to zero, take the
adminq lock, issue its own command, and either leave its own
response in the buffer or be mid-DMA when the user thread reads.
The user thread will then read zero or a different command's
response.

The simplest fix is to return the cached
priv->last_read_nic_timestamp from gve_read_clock rather than
issuing a fresh adminq command. The periodic sync runs every
250ms, so the cached value is recent; the .read_clock semantics
do not require a brand new device read. Alternatively, use a
separate per-caller DMA buffer or extend the critical section
to cover the buffer read.


Patch 6: net/gve: reconstruct HW timestamps from DQO

Warning: RTE_ETH_RX_OFFLOAD_TIMESTAMP is added to
dev_info->rx_offload_capa whenever priv->nic_timestamp_supported
is true, but per-packet timestamp delivery is only implemented in
the DQO RX path (gve_rx_burst_dqo). For GQI devices,
rxq->timestamp_enabled is never set (gve_rx_queue_setup_dqo is
the only setup path that consults the offload flag), and the GQI
burst function does not touch the timestamp fields. A GQI user
that enables RX_OFFLOAD_TIMESTAMP will see the configure call
succeed and no timestamps appear in any mbuf - the offload is
silently a no-op.

The release notes for this series ("Added hardware timestamping
support on DQO queues") already scope the feature to DQO. The
capability advertisement should match:

    if (!gve_is_gqi(priv) && priv->nic_timestamp_supported)
        dev_info->rx_offload_capa |= RTE_ETH_RX_OFFLOAD_TIMESTAMP;

Info: doc/guides/nics/gve.rst's new "Timestamp Offload" section
should mention that the per-packet RX timestamp requires DQO
queue format. The .read_clock op works regardless of queue
format; only the per-packet path is DQO-only.

^ permalink raw reply

* [PATCH] FIX: excessive logging in I40E driver
From: Arin Kharkar @ 2026-05-12 23:42 UTC (permalink / raw)
  To: dev; +Cc: Arin Kharkar

Resolved bug #1936 by changing the log level in i40e_set_tx_function from NOTICE to DEBUG
---
 drivers/net/intel/i40e/i40e_rxtx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/intel/i40e/i40e_rxtx.c b/drivers/net/intel/i40e/i40e_rxtx.c
index c5ac75e0f0..bc48303a25 100644
--- a/drivers/net/intel/i40e/i40e_rxtx.c
+++ b/drivers/net/intel/i40e/i40e_rxtx.c
@@ -3124,7 +3124,7 @@ i40e_set_tx_function(struct rte_eth_dev *dev)
 					 i40e_tx_path_infos[ad->tx_func_type].pkt_burst;
 	dev->tx_pkt_prepare = i40e_tx_path_infos[ad->tx_func_type].pkt_prep;
 
-	PMD_DRV_LOG(NOTICE, "Using %s (port %d).",
+	PMD_DRV_LOG(DEBUG, "Using %s (port %d).",
 		i40e_tx_path_infos[ad->tx_func_type].info, dev->data->port_id);
 
 	if (ad->tx_func_type == I40E_TX_SCALAR_SIMPLE ||
-- 
2.43.0


^ permalink raw reply related

* [PATCH] Updated skeleton example code to better emit error
From: Arin Kharkar @ 2026-05-12 19:28 UTC (permalink / raw)
  To: dev; +Cc: Arin Kharkar

Previously, the program would check if the number of ports is even and if not, would exit with an error stating "the number of ports must be even". To provide more information, the program now states how many ports were found. This is especially helpful if 0 ports were found, as it explicitly states DPDK wasn't able to find any ports, rather than making the user troubleshoot this.
---
 examples/skeleton/basicfwd.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/examples/skeleton/basicfwd.c b/examples/skeleton/basicfwd.c
index 133293cf15..381e4d4273 100644
--- a/examples/skeleton/basicfwd.c
+++ b/examples/skeleton/basicfwd.c
@@ -188,11 +188,9 @@ main(int argc, char *argv[])
 	/* Check that there is an even number of ports to send/receive on. */
 	nb_ports = rte_eth_dev_count_avail();
 	if (nb_ports < 2 || (nb_ports & 1))
-		rte_exit(EXIT_FAILURE, "Error: number of ports must be even\n");
+		rte_exit(EXIT_FAILURE, "Error: number of ports must be even, found %u ports\n", nb_ports);
 
-	/* Creates a new mempool in memory to hold the mbufs. */
-
-	/* Allocates mempool to hold the mbufs. 8< */
+	/* Creates and allocates a mempool to hold the mbufs. 8< */
 	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
 		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
 	/* >8 End of allocating mempool to hold mbuf. */
-- 
2.43.0


^ permalink raw reply related

* [PATCH] Remove redundant simpler equivalent section
From: Arin Kharkar @ 2026-05-12 19:16 UTC (permalink / raw)
  To: dev; +Cc: Arin Kharkar
In-Reply-To: <CAJFAV8zBGUVp-Df3aqY_pUw-JZpVqJfXERXsw4oQ+k2_TJbu2Q@mail.gmail.com>

Fixed outdated documentation on the hello world doc, which incorrectly stated an easier way to launch the lcore_hello function. The example program uses the simpler version and the documentation stating it as an "equivalent and simpler" alternative is moot. It is now the default.

Signed-off-by: Arin Kharkar <arinkharkar@gmail.com>
---
 doc/guides/sample_app_ug/hello_world.rst | 8 --------
 examples/helloworld/main.c               | 4 +---
 2 files changed, 1 insertion(+), 11 deletions(-)

diff --git a/doc/guides/sample_app_ug/hello_world.rst b/doc/guides/sample_app_ug/hello_world.rst
index 5bfd4b3fda..b7167aa345 100644
--- a/doc/guides/sample_app_ug/hello_world.rst
+++ b/doc/guides/sample_app_ug/hello_world.rst
@@ -70,12 +70,4 @@ The code that launches the function on each lcore is as follows:
     :end-before: >8 End of launching the function on each lcore.
     :dedent: 1
 
-The following code is equivalent and simpler:
-
-.. literalinclude:: ../../../examples/helloworld/main.c
-    :language: c
-    :start-after: Simpler equivalent. 8<
-    :end-before: >8 End of simpler equivalent.
-    :dedent: 2
-
 Refer to the *DPDK API Reference* for detailed information on the rte_eal_mp_remote_launch() function.
diff --git a/examples/helloworld/main.c b/examples/helloworld/main.c
index af509138da..8d1a959c58 100644
--- a/examples/helloworld/main.c
+++ b/examples/helloworld/main.c
@@ -40,13 +40,11 @@ main(int argc, char **argv)
 
 	/* Launches the function on each lcore. 8< */
 	RTE_LCORE_FOREACH_WORKER(lcore_id) {
-		/* Simpler equivalent. 8< */
 		rte_eal_remote_launch(lcore_hello, NULL, lcore_id);
-		/* >8 End of simpler equivalent. */
 	}
 
 	/* call it on main lcore too */
-	lcore_hello(NULL);
+	 lcore_hello(NULL);
 	/* >8 End of launching the function on each lcore. */
 
 	rte_eal_mp_wait_lcore();
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH v12 4/5] vhost_user: Function defs for add/rem mem regions
From: fengchengwen @ 2026-05-13  4:16 UTC (permalink / raw)
  To: pravin.bathija, dev, stephen, maxime.coquelin; +Cc: thomas
In-Reply-To: <20260506033733.972952-5-pravin.bathija@dell.com>

The 3/5 commit is helper function, I doubt it could compile, why not merge with this commit.

I suggest placing the refactored code on 3/5, but it should be self-contained and not include
functions that will not be used in 3/5 (e.g. remove_guest_pages).

On 5/6/2026 11:37 AM, pravin.bathija@dell.com wrote:
> From: Pravin M Bathija <pravin.bathija@dell.com>
> 
> These changes cover the function definition for add/remove memory
> region calls which are invoked on receiving vhost user message from
> vhost user front-end (e.g. Qemu). In our case, in addition to testing
> with qemu front-end, the testing has also been performed with libblkio
> front-end and spdk/dpdk back-end. We did I/O using libblkio based device
> driver, to spdk based drives.
> There are also changes for set_mem_table and new definition for get memory
> slots. Our changes optimize the set memory table call to use common support
> functions. A new vhost_user_initialize_memory() function is introduced to
> factor out the common memory initialization logic from the function
> vhost_user_set_mem_table(), which is now called from both the SET_MEM_TABLE
> message handler and the ADD_MEM_REG handler (for the first region).
> Message get memory slots is how the vhost-user front-end queries the
> vhost-user back-end about the number of memory slots available to be
> registered by the back-end. In addition support function to invalidate
> vring is also defined which is used in add/remove memory region functions.
> 
> Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
> ---
>  lib/vhost/vhost_user.c | 311 ++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 278 insertions(+), 33 deletions(-)
> 
> diff --git a/lib/vhost/vhost_user.c b/lib/vhost/vhost_user.c
> index 9f1abb1842..c6b404da75 100644
> --- a/lib/vhost/vhost_user.c
> +++ b/lib/vhost/vhost_user.c
> @@ -71,6 +71,9 @@ VHOST_MESSAGE_HANDLER(VHOST_USER_SET_FEATURES, vhost_user_set_features, false, t
>  VHOST_MESSAGE_HANDLER(VHOST_USER_SET_OWNER, vhost_user_set_owner, false, true) \
>  VHOST_MESSAGE_HANDLER(VHOST_USER_RESET_OWNER, vhost_user_reset_owner, false, false) \
>  VHOST_MESSAGE_HANDLER(VHOST_USER_SET_MEM_TABLE, vhost_user_set_mem_table, true, true) \
> +VHOST_MESSAGE_HANDLER(VHOST_USER_GET_MAX_MEM_SLOTS, vhost_user_get_max_mem_slots, false, false) \
> +VHOST_MESSAGE_HANDLER(VHOST_USER_ADD_MEM_REG, vhost_user_add_mem_reg, true, true) \
> +VHOST_MESSAGE_HANDLER(VHOST_USER_REM_MEM_REG, vhost_user_rem_mem_reg, true, true) \
>  VHOST_MESSAGE_HANDLER(VHOST_USER_SET_LOG_BASE, vhost_user_set_log_base, true, true) \
>  VHOST_MESSAGE_HANDLER(VHOST_USER_SET_LOG_FD, vhost_user_set_log_fd, true, true) \
>  VHOST_MESSAGE_HANDLER(VHOST_USER_SET_VRING_NUM, vhost_user_set_vring_num, false, true) \
> @@ -1431,6 +1434,52 @@ vhost_user_mmap_region(struct virtio_net *dev,
>  	return 0;
>  }
>  
> +static int
> +vhost_user_initialize_memory(struct virtio_net **pdev)
> +{
> +	struct virtio_net *dev = *pdev;
> +	int numa_node = SOCKET_ID_ANY;
> +
> +	if (dev->mem != NULL) {
> +		VHOST_CONFIG_LOG(dev->ifname, ERR,
> +			"memory already initialized, free it first");
> +		return -1;
> +	}
> +
> +	/*
> +	 * If VQ 0 has already been allocated, try to allocate on the same
> +	 * NUMA node. It can be reallocated later in numa_realloc().
> +	 */
> +	if (dev->nr_vring > 0)
> +		numa_node = dev->virtqueue[0]->numa_node;
> +
> +	dev->nr_guest_pages = 0;
> +	if (dev->guest_pages == NULL) {
> +		dev->max_guest_pages = 8;
> +		dev->guest_pages = rte_zmalloc_socket(NULL,
> +					dev->max_guest_pages *
> +					sizeof(struct guest_page),
> +					RTE_CACHE_LINE_SIZE,
> +					numa_node);
> +		if (dev->guest_pages == NULL) {
> +			VHOST_CONFIG_LOG(dev->ifname, ERR,
> +				"failed to allocate memory for dev->guest_pages");
> +			return -1;
> +		}
> +	}
> +
> +	dev->mem = rte_zmalloc_socket("vhost-mem-table", sizeof(struct rte_vhost_memory) +
> +		sizeof(struct rte_vhost_mem_region) * VHOST_MEMORY_MAX_NREGIONS, 0, numa_node);
> +	if (dev->mem == NULL) {
> +		VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to allocate memory for dev->mem");
> +		rte_free(dev->guest_pages);
> +		dev->guest_pages = NULL;
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
>  static int
>  vhost_user_set_mem_table(struct virtio_net **pdev,
>  			struct vhu_msg_context *ctx,
> @@ -1439,7 +1488,6 @@ vhost_user_set_mem_table(struct virtio_net **pdev,
>  	struct virtio_net *dev = *pdev;
>  	struct VhostUserMemory *memory = &ctx->msg.payload.memory;
>  	struct rte_vhost_mem_region *reg;
> -	int numa_node = SOCKET_ID_ANY;
>  	uint64_t mmap_offset;
>  	uint32_t i;
>  	bool async_notify = false;
> @@ -1484,39 +1532,13 @@ vhost_user_set_mem_table(struct virtio_net **pdev,
>  		if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM))
>  			vhost_user_iotlb_flush_all(dev);
>  
> -		free_mem_region(dev);
> +		free_all_mem_regions(dev);
>  		rte_free(dev->mem);
>  		dev->mem = NULL;
>  	}
>  
> -	/*
> -	 * If VQ 0 has already been allocated, try to allocate on the same
> -	 * NUMA node. It can be reallocated later in numa_realloc().
> -	 */
> -	if (dev->nr_vring > 0)
> -		numa_node = dev->virtqueue[0]->numa_node;
> -
> -	dev->nr_guest_pages = 0;
> -	if (dev->guest_pages == NULL) {
> -		dev->max_guest_pages = 8;
> -		dev->guest_pages = rte_zmalloc_socket(NULL,
> -					dev->max_guest_pages *
> -					sizeof(struct guest_page),
> -					RTE_CACHE_LINE_SIZE,
> -					numa_node);
> -		if (dev->guest_pages == NULL) {
> -			VHOST_CONFIG_LOG(dev->ifname, ERR,
> -				"failed to allocate memory for dev->guest_pages");
> -			goto close_msg_fds;
> -		}
> -	}
> -
> -	dev->mem = rte_zmalloc_socket("vhost-mem-table", sizeof(struct rte_vhost_memory) +
> -		sizeof(struct rte_vhost_mem_region) * memory->nregions, 0, numa_node);
> -	if (dev->mem == NULL) {
> -		VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to allocate memory for dev->mem");
> -		goto free_guest_pages;
> -	}
> +	if (vhost_user_initialize_memory(pdev) < 0)
> +		goto close_msg_fds;
>  
>  	for (i = 0; i < memory->nregions; i++) {
>  		reg = &dev->mem->regions[i];
> @@ -1580,11 +1602,9 @@ vhost_user_set_mem_table(struct virtio_net **pdev,
>  	return RTE_VHOST_MSG_RESULT_OK;
>  
>  free_mem_table:
> -	free_mem_region(dev);
> +	free_all_mem_regions(dev);
>  	rte_free(dev->mem);
>  	dev->mem = NULL;
> -
> -free_guest_pages:
>  	rte_free(dev->guest_pages);
>  	dev->guest_pages = NULL;
>  close_msg_fds:
> @@ -1592,6 +1612,231 @@ vhost_user_set_mem_table(struct virtio_net **pdev,
>  	return RTE_VHOST_MSG_RESULT_ERR;
>  }
>  
> +
> +static int
> +vhost_user_get_max_mem_slots(struct virtio_net **pdev __rte_unused,
> +			struct vhu_msg_context *ctx,
> +			int main_fd __rte_unused)
> +{
> +	uint32_t max_mem_slots = VHOST_MEMORY_MAX_NREGIONS;
> +
> +	ctx->msg.payload.u64 = (uint64_t)max_mem_slots;
> +	ctx->msg.size = sizeof(ctx->msg.payload.u64);
> +	ctx->fd_num = 0;
> +
> +	return RTE_VHOST_MSG_RESULT_REPLY;
> +}
> +
> +static void
> +_dev_invalidate_vrings(struct virtio_net **pdev)
> +{
> +	struct virtio_net *dev = *pdev;
> +	uint32_t i;
> +
> +	for (i = 0; i < dev->nr_vring; i++) {
> +		struct vhost_virtqueue *vq = dev->virtqueue[i];
> +
> +		if (!vq)
> +			continue;
> +
> +		if (vq->desc || vq->avail || vq->used) {
> +			vq_assert_lock(dev, vq);
> +
> +			/*
> +			 * If the memory table got updated, the ring addresses
> +			 * need to be translated again as virtual addresses have
> +			 * changed.
> +			 */
> +			vring_invalidate(dev, vq);
> +
> +			translate_ring_addresses(&dev, &vq);
> +		}
> +	}
> +
> +	*pdev = dev;
> +}
> +
> +/*
> + * Macro wrapper that performs the compile-time lock assertion with the
> + * correct message ID at the call site, then calls the implementation.
> + */
> +#define dev_invalidate_vrings(pdev, id) do { \
> +	static_assert(id ## _LOCK_ALL_QPS, \
> +		#id " handler is not declared as locking all queue pairs"); \
> +	_dev_invalidate_vrings(pdev); \
> +} while (0)
> +
> +static int
> +vhost_user_add_mem_reg(struct virtio_net **pdev,
> +			struct vhu_msg_context *ctx,
> +			int main_fd __rte_unused)
> +{
> +	uint32_t i;
> +	struct virtio_net *dev = *pdev;
> +	struct VhostUserMemoryRegion *region = &ctx->msg.payload.memory_single.region;
> +
> +	/* convert first region add to normal memory table set */
> +	if (dev->mem == NULL) {
> +		if (vhost_user_initialize_memory(pdev) < 0)
> +			goto close_msg_fds;
> +	}
> +
> +	/* make sure new region will fit */
> +	if (dev->mem->nregions >= VHOST_MEMORY_MAX_NREGIONS) {
> +		VHOST_CONFIG_LOG(dev->ifname, ERR, "too many memory regions already (%u)",
> +									dev->mem->nregions);
> +		goto close_msg_fds;
> +	}
> +
> +	/* make sure supplied memory fd present */
> +	if (ctx->fd_num != 1) {
> +		VHOST_CONFIG_LOG(dev->ifname, ERR, "fd count makes no sense (%u)", ctx->fd_num);
> +		goto close_msg_fds;
> +	}
> +
> +	/* Make sure no overlap in guest virtual address space */
> +	for (i = 0; i < dev->mem->nregions; i++) {
> +		struct rte_vhost_mem_region *current_region = &dev->mem->regions[i];
> +		uint64_t current_region_guest_start = current_region->guest_user_addr;
> +		uint64_t current_region_guest_end = current_region_guest_start
> +							+ current_region->size - 1;
> +		uint64_t proposed_region_guest_start = region->userspace_addr;
> +		uint64_t proposed_region_guest_end = proposed_region_guest_start
> +							+ region->memory_size - 1;



> +
> +		if (!((proposed_region_guest_end < current_region_guest_start) ||
> +			(proposed_region_guest_start > current_region_guest_end))) {
> +			VHOST_CONFIG_LOG(dev->ifname, ERR,
> +				"requested memory region overlaps with another region");
> +			VHOST_CONFIG_LOG(dev->ifname, ERR,
> +				"\tRequested region address:0x%" PRIx64,
> +				region->userspace_addr);
> +			VHOST_CONFIG_LOG(dev->ifname, ERR,
> +				"\tRequested region size:0x%" PRIx64,
> +				region->memory_size);
> +			VHOST_CONFIG_LOG(dev->ifname, ERR,
> +				"\tOverlapping region address:0x%" PRIx64,
> +				current_region->guest_user_addr);
> +			VHOST_CONFIG_LOG(dev->ifname, ERR,
> +				"\tOverlapping region size:0x%" PRIx64,
> +				current_region->size);
> +			goto close_msg_fds;
> +		}
> +	}
> +
> +	/* New region goes at the end of the contiguous array */
> +	struct rte_vhost_mem_region *reg = &dev->mem->regions[dev->mem->nregions];
> +
> +	reg->guest_phys_addr = region->guest_phys_addr;
> +	reg->guest_user_addr = region->userspace_addr;
> +	reg->size            = region->memory_size;
> +	reg->fd              = ctx->fds[0];
> +	ctx->fds[0]          = -1;
> +
> +	if (vhost_user_mmap_region(dev, reg, region->mmap_offset) < 0) {
> +		VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to mmap region");
> +		if (reg->mmap_addr) {
> +			/* mmap succeeded but a later step (e.g. add_guest_pages)
> +			 * failed; undo the mapping and any guest-page entries.
> +			 */
> +			remove_guest_pages(dev, reg);
> +			free_mem_region(reg);
> +		} else {
> +			close(reg->fd);
> +			reg->fd = -1;
> +		}
> +		goto close_msg_fds;
> +	}
> +
> +	dev->mem->nregions++;
> +
> +	if (dev->async_copy && rte_vfio_is_enabled("vfio")) {
> +		if (async_dma_map_region(dev, reg, true) < 0)
> +			goto free_new_region;

It will invoke in goto label

> +	}
> +
> +	if (dev->postcopy_listening) {
> +		/*
> +		 * Cannot use vhost_user_postcopy_register() here because it
> +		 * reads ctx->msg.payload.memory (SET_MEM_TABLE layout), but
> +		 * ADD_MEM_REG uses the memory_single payload.  Register the
> +		 * single new region directly instead.
> +		 */
> +		if (vhost_user_postcopy_region_register(dev, reg) < 0)
> +			goto free_new_region;
> +	}
> +
> +	dev_invalidate_vrings(pdev, VHOST_USER_ADD_MEM_REG);
> +	dev = *pdev;

no need to do this, there are: struct virtio_net *dev = *pdev; in beginning

> +	dump_guest_pages(dev);
> +
> +	return RTE_VHOST_MSG_RESULT_OK;
> +
> +free_new_region:
> +	if (dev->async_copy && rte_vfio_is_enabled("vfio"))
> +		async_dma_map_region(dev, reg, false);
> +	remove_guest_pages(dev, reg);
> +	free_mem_region(reg);
> +	dev->mem->nregions--;
> +close_msg_fds:
> +	close_msg_fds(ctx);
> +	return RTE_VHOST_MSG_RESULT_ERR;
> +}
> +
> +static int
> +vhost_user_rem_mem_reg(struct virtio_net **pdev,
> +			struct vhu_msg_context *ctx,
> +			int main_fd __rte_unused)
> +{
> +	uint32_t i;
> +	struct virtio_net *dev = *pdev;
> +	struct VhostUserMemoryRegion *region = &ctx->msg.payload.memory_single.region;
> +
> +	if (dev->mem == NULL || dev->mem->nregions == 0) {
> +		VHOST_CONFIG_LOG(dev->ifname, ERR, "no memory regions to remove");
> +		close_msg_fds(ctx);

no fd when remove, so please don't invoke close_msg_fds

> +		return RTE_VHOST_MSG_RESULT_ERR;
> +	}
> +
> +	for (i = 0; i < dev->mem->nregions; i++) {
> +		struct rte_vhost_mem_region *current_region = &dev->mem->regions[i];
> +
> +		/*
> +		 * According to the vhost-user specification:
> +		 * The memory region to be removed is identified by its GPA,
> +		 * user address and size. The mmap offset is ignored.
> +		 */
> +		if (region->userspace_addr == current_region->guest_user_addr
> +			&& region->guest_phys_addr == current_region->guest_phys_addr
> +			&& region->memory_size == current_region->size) {
> +			if (dev->async_copy && rte_vfio_is_enabled("vfio"))
> +				async_dma_map_region(dev, current_region, false);

I'm afaid it not enough, we should drain DMA's pending request. But I think the original
impl also has this problem.

> +			remove_guest_pages(dev, current_region);
> +			free_mem_region(current_region);
> +
> +			/* Compact the regions array to keep it contiguous */
> +			if (i < dev->mem->nregions - 1) {
> +				memmove(&dev->mem->regions[i],
> +					&dev->mem->regions[i + 1],
> +					(dev->mem->nregions - 1 - i) *
> +					sizeof(struct rte_vhost_mem_region));
> +				memset(&dev->mem->regions[dev->mem->nregions - 1],
> +					0, sizeof(struct rte_vhost_mem_region));
> +			}
> +
> +			dev->mem->nregions--;
> +			dev_invalidate_vrings(pdev, VHOST_USER_REM_MEM_REG);
> +			dev = *pdev;

no need to do this, there are: struct virtio_net *dev = *pdev; in beginning

> +			close_msg_fds(ctx);
> +			return RTE_VHOST_MSG_RESULT_OK;
> +		}
> +	}
> +
> +	VHOST_CONFIG_LOG(dev->ifname, ERR, "failed to find region");
> +	close_msg_fds(ctx);
> +	return RTE_VHOST_MSG_RESULT_ERR;
> +}
> +
>  static bool
>  vq_is_ready(struct virtio_net *dev, struct vhost_virtqueue *vq)
>  {


^ permalink raw reply

* Re: [PATCH v12 2/5] vhost_user: header defines for add/rem mem region
From: fengchengwen @ 2026-05-13  2:30 UTC (permalink / raw)
  To: pravin.bathija, dev, stephen, maxime.coquelin; +Cc: thomas
In-Reply-To: <20260506033733.972952-3-pravin.bathija@dell.com>

On 5/6/2026 11:37 AM, pravin.bathija@dell.com wrote:
> From: Pravin M Bathija <pravin.bathija@dell.com>
> 
> The changes in this file cover the enum message requests for
> supporting add/remove memory regions. The front-end vhost-user
> client sends messages like get max memory slots, add memory region
> and remove memory region which are covered in these changes which
> are on the vhost-user back-end. The changes also include data structure
> definition of memory region to be added/removed. The data structure
> VhostUserMsg has been changed to include the memory region.
> 
> Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
> ---
>  lib/vhost/vhost_user.h | 9 +++++++++
>  1 file changed, 9 insertions(+)
> 
> diff --git a/lib/vhost/vhost_user.h b/lib/vhost/vhost_user.h
> index ef486545ba..f8d921f7f1 100644
> --- a/lib/vhost/vhost_user.h
> +++ b/lib/vhost/vhost_user.h
> @@ -67,6 +67,9 @@ typedef enum VhostUserRequest {
>  	VHOST_USER_POSTCOPY_END = 30,
>  	VHOST_USER_GET_INFLIGHT_FD = 31,
>  	VHOST_USER_SET_INFLIGHT_FD = 32,
> +	VHOST_USER_GET_MAX_MEM_SLOTS = 36,
> +	VHOST_USER_ADD_MEM_REG = 37,
> +	VHOST_USER_REM_MEM_REG = 38,
>  	VHOST_USER_SET_STATUS = 39,
>  	VHOST_USER_GET_STATUS = 40,
>  } VhostUserRequest;
> @@ -91,6 +94,11 @@ typedef struct VhostUserMemory {
>  	VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS];
>  } VhostUserMemory;
>  
> +typedef struct VhostUserSingleMemReg {
> +	uint64_t padding;
> +	VhostUserMemoryRegion region;
> +} VhostUserSingleMemReg;

How about the same as qemu vhost define: VhostUserMemRegMsg

> +
>  typedef struct VhostUserLog {
>  	uint64_t mmap_size;
>  	uint64_t mmap_offset;
> @@ -186,6 +194,7 @@ typedef struct __rte_packed_begin VhostUserMsg {
>  		struct vhost_vring_state state;
>  		struct vhost_vring_addr addr;
>  		VhostUserMemory memory;
> +		VhostUserSingleMemReg memory_single;

Suggest the same as qemu define: VhostUserMemRegMsg memreg;

>  		VhostUserLog    log;
>  		struct vhost_iotlb_msg iotlb;
>  		VhostUserCryptoSessionParam crypto_session;

With above fix
Acked-by: Chengwen Feng <fengchengwen@huawei.com>


^ permalink raw reply

* Re: [PATCH v12 1/5] vhost: add user to mailmap and define to vhost hdr
From: fengchengwen @ 2026-05-13  2:22 UTC (permalink / raw)
  To: pravin.bathija, dev, stephen, maxime.coquelin; +Cc: thomas
In-Reply-To: <20260506033733.972952-2-pravin.bathija@dell.com>

Acked-by: Chengwen Feng <fengchengwen@huawei.com>

On 5/6/2026 11:37 AM, pravin.bathija@dell.com wrote:
> From: Pravin M Bathija <pravin.bathija@dell.com>
> 
> - add user to mailmap file.
> - define a bit-field called VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS
>   that depicts if the feature/capability to add/remove memory regions
>   is supported. This is a part of the overall support for add/remove
>   memory region feature in this patchset.
> 
> Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
> ---
>  .mailmap              | 1 +
>  lib/vhost/rte_vhost.h | 4 ++++
>  2 files changed, 5 insertions(+)
> 
> diff --git a/.mailmap b/.mailmap
> index 0e0d83e1c6..cc44e27036 100644
> --- a/.mailmap
> +++ b/.mailmap
> @@ -1295,6 +1295,7 @@ Prateek Agarwal <prateekag@cse.iitb.ac.in>
>  Prathisna Padmasanan <prathisna.padmasanan@intel.com>
>  Praveen Kaligineedi <pkaligineedi@google.com>
>  Praveen Shetty <praveen.shetty@intel.com>
> +Pravin M Bathija <pravin.bathija@dell.com>
>  Pravin Pathak <pravin.pathak.dev@gmail.com> <pravin.pathak@intel.com>
>  Prince Takkar <ptakkar@marvell.com>
>  Priyalee Kushwaha <priyalee.kushwaha@intel.com>
> diff --git a/lib/vhost/rte_vhost.h b/lib/vhost/rte_vhost.h
> index 2f7c4c0080..a7f9700538 100644
> --- a/lib/vhost/rte_vhost.h
> +++ b/lib/vhost/rte_vhost.h
> @@ -109,6 +109,10 @@ extern "C" {
>  #define VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD 12
>  #endif
>  
> +#ifndef VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS
> +#define VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS 15
> +#endif
> +
>  #ifndef VHOST_USER_PROTOCOL_F_STATUS
>  #define VHOST_USER_PROTOCOL_F_STATUS 16
>  #endif


^ permalink raw reply

* Re: [PATCH] Fixed outdated documentation on the hello world doc, which incorrectly stated an easier way to launch the lcore_hello function. The example program uses the simpler version and the documentation stating it as an "equivalent and simpler" alternative is moot. It is now the default.
From: David Marchand @ 2026-05-12 15:29 UTC (permalink / raw)
  To: Arin Kharkar; +Cc: dev
In-Reply-To: <20260511233350.79402-1-arinkharkar@gmail.com>

Hello Arin,

Thanks for the patch.

On Tue, 12 May 2026 at 09:57, Arin Kharkar <arinkharkar@gmail.com> wrote:

You'll have to add a title so that your commitlog does not end up as the title.

>
> Signed-off-by: Arin Kharkar <arinkharkar@gmail.com>
> ---
>  doc/guides/sample_app_ug/hello_world.rst | 8 --------
>  1 file changed, 8 deletions(-)
>
> diff --git a/doc/guides/sample_app_ug/hello_world.rst b/doc/guides/sample_app_ug/hello_world.rst
> index 5bfd4b3fda..b7167aa345 100644
> --- a/doc/guides/sample_app_ug/hello_world.rst
> +++ b/doc/guides/sample_app_ug/hello_world.rst
> @@ -70,12 +70,4 @@ The code that launches the function on each lcore is as follows:
>      :end-before: >8 End of launching the function on each lcore.
>      :dedent: 1
>
> -The following code is equivalent and simpler:
> -
> -.. literalinclude:: ../../../examples/helloworld/main.c
> -    :language: c
> -    :start-after: Simpler equivalent. 8<
> -    :end-before: >8 End of simpler equivalent.
> -    :dedent: 2
> -
>  Refer to the *DPDK API Reference* for detailed information on the rte_eal_mp_remote_launch() function.
> --
> 2.43.0
>

Please also drop the associated "Simpler equivalent. 8>" and ">8 End
of simpler equivalent." comments from the example source code.


-- 
David Marchand


^ permalink raw reply

* [PATCH dpdk v4] net/tap: use offsets provided by rte_net_get_ptype
From: Robin Jarry @ 2026-05-12 15:16 UTC (permalink / raw)
  To: dev, Stephen Hemminger
In-Reply-To: <20260422133615.680318-2-rjarry@redhat.com>

Instead of guessing what are the proper header lengths, pass
a rte_net_hdr_lens struct to rte_net_get_ptype and use it to get the
proper header lengths/offsets in tap_verify_csum.

This allows supporting stacked VLAN/QinQ tags and IPv6 extensions.

Signed-off-by: Robin Jarry <rjarry@redhat.com>
---

Notes:
    v4:
    
    * Fixed stale comments.
    
    v3:
    
    * Fixed double count for ipv6 extension headers.
    
    v2:
    
    * Restored packet length checks in IPv4/IPv6 before checking L4
      checksums.
    * Moved struct rte_net_hdr_lens variable next to rte_net_get_ptype call
      and initialize it to 0 instead of calling memset().

 drivers/net/tap/rte_eth_tap.c | 46 +++++++++++++----------------------
 1 file changed, 17 insertions(+), 29 deletions(-)

diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index a5d460a0b3cb..09cd252918e4 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -327,79 +327,66 @@ tun_alloc(struct pmd_internals *pmd, int is_keepalive, int persistent)
 }
 
 static void
-tap_verify_csum(struct rte_mbuf *mbuf)
+tap_verify_csum(struct rte_mbuf *mbuf, const struct rte_net_hdr_lens *hlen)
 {
-	uint32_t l2 = mbuf->packet_type & RTE_PTYPE_L2_MASK;
 	uint32_t l3 = mbuf->packet_type & RTE_PTYPE_L3_MASK;
 	uint32_t l4 = mbuf->packet_type & RTE_PTYPE_L4_MASK;
-	unsigned int l2_len = sizeof(struct rte_ether_hdr);
-	unsigned int l3_len;
+	uint32_t l4_off = hlen->l2_len + hlen->l3_len;
 	uint16_t cksum = 0;
 	void *l3_hdr;
 	void *l4_hdr;
-	struct rte_udp_hdr *udp_hdr;
 
-	if (l2 == RTE_PTYPE_L2_ETHER_VLAN)
-		l2_len += 4;
-	else if (l2 == RTE_PTYPE_L2_ETHER_QINQ)
-		l2_len += 8;
 	/* Don't verify checksum for packets with discontinuous L2 header */
-	if (unlikely(l2_len + sizeof(struct rte_ipv4_hdr) >
-		     rte_pktmbuf_data_len(mbuf)))
+	if (unlikely(l4_off > rte_pktmbuf_data_len(mbuf)))
 		return;
-	l3_hdr = rte_pktmbuf_mtod_offset(mbuf, void *, l2_len);
+
+	l3_hdr = rte_pktmbuf_mtod_offset(mbuf, void *, hlen->l2_len);
 	if (l3 == RTE_PTYPE_L3_IPV4 || l3 == RTE_PTYPE_L3_IPV4_EXT) {
 		struct rte_ipv4_hdr *iph = l3_hdr;
 
-		l3_len = rte_ipv4_hdr_len(iph);
-		if (unlikely(l2_len + l3_len > rte_pktmbuf_data_len(mbuf)))
-			return;
 		/* check that the total length reported by header is not
 		 * greater than the total received size
 		 */
-		if (l2_len + rte_be_to_cpu_16(iph->total_length) >
+		if (hlen->l2_len + rte_be_to_cpu_16(iph->total_length) >
 				rte_pktmbuf_data_len(mbuf))
 			return;
 
-		cksum = ~rte_raw_cksum(iph, l3_len);
+		cksum = ~rte_raw_cksum(iph, hlen->l3_len);
 		mbuf->ol_flags |= cksum ?
 			RTE_MBUF_F_RX_IP_CKSUM_BAD :
 			RTE_MBUF_F_RX_IP_CKSUM_GOOD;
-	} else if (l3 == RTE_PTYPE_L3_IPV6) {
+	} else if (l3 == RTE_PTYPE_L3_IPV6 || l3 == RTE_PTYPE_L3_IPV6_EXT) {
 		struct rte_ipv6_hdr *iph = l3_hdr;
 
-		l3_len = sizeof(struct rte_ipv6_hdr);
 		/* check that the total length reported by header is not
 		 * greater than the total received size
 		 */
-		if (l2_len + l3_len + rte_be_to_cpu_16(iph->payload_len) >
+		if (hlen->l2_len + sizeof(*iph) + rte_be_to_cpu_16(iph->payload_len) >
 				rte_pktmbuf_data_len(mbuf))
 			return;
 	} else {
 		/* - RTE_PTYPE_L3_IPV4_EXT_UNKNOWN cannot happen because
 		 *   mbuf->packet_type is filled by rte_net_get_ptype() which
 		 *   never returns this value.
-		 * - IPv6 extensions are not supported.
 		 */
 		return;
 	}
 
 	if (l4 == RTE_PTYPE_L4_UDP || l4 == RTE_PTYPE_L4_TCP) {
 		int cksum_ok;
-		const unsigned int l4_min_len = (l4 == RTE_PTYPE_L4_UDP)
-			? sizeof(struct rte_udp_hdr) : sizeof(struct rte_tcp_hdr);
 
 		/* Don't verify checksum if L4 header is truncated */
-		if (l2_len + l3_len + l4_min_len > rte_pktmbuf_data_len(mbuf))
+		if (l4_off + hlen->l4_len > rte_pktmbuf_data_len(mbuf))
 			return;
 
-		l4_hdr = rte_pktmbuf_mtod_offset(mbuf, void *, l2_len + l3_len);
 		/* Don't verify checksum for multi-segment packets. */
 		if (mbuf->nb_segs > 1)
 			return;
+
+		l4_hdr = rte_pktmbuf_mtod_offset(mbuf, void *, l4_off);
 		if (l3 == RTE_PTYPE_L3_IPV4 || l3 == RTE_PTYPE_L3_IPV4_EXT) {
 			if (l4 == RTE_PTYPE_L4_UDP) {
-				udp_hdr = (struct rte_udp_hdr *)l4_hdr;
+				struct rte_udp_hdr *udp_hdr = l4_hdr;
 				if (udp_hdr->dgram_cksum == 0) {
 					/*
 					 * For IPv4, a zero UDP checksum
@@ -412,7 +399,7 @@ tap_verify_csum(struct rte_mbuf *mbuf)
 			}
 			cksum_ok = !rte_ipv4_udptcp_cksum_verify(l3_hdr,
 								 l4_hdr);
-		} else { /* l3 == RTE_PTYPE_L3_IPV6, checked above */
+		} else { /* l3 == RTE_PTYPE_L3_IPV6{,_EXT}, checked above */
 			cksum_ok = !rte_ipv6_udptcp_cksum_verify(l3_hdr,
 								 l4_hdr);
 		}
@@ -561,10 +548,11 @@ pmd_rx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
 			continue;
 		}
 
-		mbuf->packet_type = rte_net_get_ptype(mbuf, NULL,
+		struct rte_net_hdr_lens hlen = {0};
+		mbuf->packet_type = rte_net_get_ptype(mbuf, &hlen,
 						      RTE_PTYPE_ALL_MASK);
 		if (rxq->rxmode->offloads & RTE_ETH_RX_OFFLOAD_CHECKSUM)
-			tap_verify_csum(mbuf);
+			tap_verify_csum(mbuf, &hlen);
 
 		/* account for the receive frame */
 		bufs[num_rx++] = mbuf;
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v3 4/5] net/iavf: accept up to 32k unicast MAC addresses
From: Stephen Hemminger @ 2026-05-12 14:41 UTC (permalink / raw)
  To: David Marchand; +Cc: dev, rjarry, cfontain, Vladimir Medvedkin
In-Reply-To: <20260510170306.3406045-5-david.marchand@redhat.com>

On Sun, 10 May 2026 19:03:04 +0200
David Marchand <david.marchand@redhat.com> wrote:

> E810 hardware provides 32k switch lookups.
> Thanks to this, it is possible to allow a lot more secondary mac
> addresses than what is possible today.
> 
> In practice, the maximum number of macs available per port may be lower
> and depends on usage by other (trusted?) VFs on the same PF.
> There is no way to figure out this limit but to try adding a mac address
> and get an error from the PF driver.
> 
> Mailbox exchanges are limited to IAVF_AQ_BUF_SZ, segment messages
> accordingly.
> 
> Signed-off

AI review feedback; nothing major but worth a look

Series: ethdev: VMDq cleanup and iavf: 32k MACs / duplicate install fix
Tree: dpdk main (DPDK 26.07)

Patch 2 - ethdev: skip VMDq pools unless configured
  Info
    Doxygen of rte_eth_dev_mac_addr_add() is not updated to describe the
    new constraint that pool must be 0 when VMDq is not configured. The
    function-level @param documentation and @return list should mention
    that -EINVAL is now returned when VMDq is disabled and pool != 0.

    Subtle behavior change: when VMDq is not configured, mac_pool_sel[index]
    is no longer set to RTE_BIT64(0) on add. Before this patch every added
    MAC ended up with bit 0 set; afterwards the array stays at 0. This is
    consistent with the new restore path (which now uses the VMDq flag
    rather than walking pool_mask in the !vmdq case), but the change in
    observable state of dev->data->mac_pool_sel[] could surprise an out-of-
    tree consumer. Worth a sentence in the API change note.

Patch 4 - net/iavf: accept up to 32k unicast MAC addresses
  Info
    Release notes wording: "secondary MAC addresses from 64 to 32k" is
    slightly imprecise. IAVF_NUM_MACADDR_MAX (64) was the total cap including
    the primary MAC; IAVF_UC_MACADDR_MAX (32768) is also a total cap
    including index 0. Either drop "secondary" or say "secondary MAC
    addresses from 63 to 32767".

    iavf_add_del_uc_addr_bulk() returns an int but the only caller
    (iavf_add_del_all_mac_addr) discards it. The pre-existing function was
    already void so this is no regression, but having the helper return an
    error code that is unconditionally dropped is misleading. Either
    propagate the error to the caller (and make iavf_add_del_all_mac_addr
    return int) or make the helper void.

    eth_dev_mac_restore() now iterates dev_info->max_mac_addrs which for
    iavf becomes 32768. With patch 5 applied this path is skipped via
    get_restore_flags, but any driver that increases max_mac_addrs into the
    thousands without setting RTE_ETH_RESTORE_MAC_ADDR off will pay a 32k
    rte_is_zero_ether_addr() scan on every dev_start. Not a bug here, but
    worth keeping in mind.

    sizeof(struct virtchnl_ether_addr_list) already accounts for the
    embedded list[1] slot, so
       in_args_size = sizeof(struct virtchnl_ether_addr_list) +
                      sizeof(struct virtchnl_ether_addr) * list->num_elements
    overcounts by sizeof(struct virtchnl_ether_addr). This matches what
    iavf_add_del_eth_addr() already does, and the buffer is sized to hold
    it, so it's not a bug -- noting it for completeness.

Patch 5 - net/iavf: fix duplicate MAC addresses install
  Info
    Recovery edge case: if iavf_add_del_eth_addr() for the primary fails
    during the first iavf_dev_start(), mac_primary_set stays false. On a
    subsequent VF reset, iavf_dev_start() (called from iavf_handle_hw_reset)
    will re-attempt the primary add, and the new
    iavf_add_del_all_mac_addr() call right after will add the primary again
    via VIRTCHNL_ETHER_ADDR_PRIMARY. This re-introduces a single-MAC
    double-install in a narrow failure path. Could be avoided by skipping
    the primary in iavf_add_del_all_mac_addr() when mac_primary_set is
    already true, or by having iavf_dev_start() skip the primary add when
    in_reset_recovery is set.

    No release notes entry. With Fixes: + Cc: stable this is acceptable as
    a bug fix, but the patch also introduces a new dev_op
    (get_restore_flags) for the driver and changes when MC addresses are
    re-pushed (only on VF reset, not on every dev_start). A one-line note
    in the iavf section of the release notes would help users tracking
    behaviour changes between stop/start cycles.

Patches 1 and 3 have no findings.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox