* [PATCH v14 0/5] Support add/remove memory region and get-max-slots
From: pravin.bathija @ 2026-05-20 2:20 UTC (permalink / raw)
To: dev, fengchengwen, stephen, maxime.coquelin; +Cc: pravin.bathija, thomas
From: Pravin M Bathija <pravin.bathija@dell.com>
This is version v14 of the patchset and it incorporates the
recomendations made by Stephen Hemminger and Fengcheng Wen.
Changes from Fengcheng Wen review:
Patch 3/5
- Moved free_all_mem_regions() call sites in vhost_user_set_mem_table()
from patch 4/5 to patch 3/5 so each commit compiles independently
Patch 4/5
- Renamed _dev_invalidate_vrings() to vhost_user_invalidate_vrings() to
follow vhost naming convention
- Added comment explaining *pdev propagation through
translate_ring_addresses / numa_realloc()
- Reordered local variables in vhost_user_add_mem_reg() and
vhost_user_rem_mem_reg() by descending line length
- Shortened overlap check variable names (current_region_guest_start/end
--> cur_start/end, proposed_region_guest_start/end -> new_start/end)
- Fixed DMA error path in vhost_user_add_mem_reg(): added
free_new_region_no_dma label so async_dma_map_region(false) is not
called when the map itself failed.
Changes from Stephen Hemminger review:
Patch 4/5
- vhost_user_add_mem_reg() now constructs a reply with the back-end's
host mapping address in userspace_addr and returns
RTE_VHOST_MSG_RESULT_REPLY per the vhost-user spec
- Added validate_msg_fds(dev, ctx, 0) in vhost_user_rem_mem_reg() to
reject malformed messages with unexpected file descriptors
- Dropped unnecessary (uint64_t) cast in vhost_user_get_max_mem_slots()
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 v14 (Current version): Incorporate code review suggestions from
Stephen Hemminger and Fengcheng Wen as described above.
Version v13: Incorporate code review suggestions from Fengcheng Wen
Patch 2/5
Renamed VhostUserSingleMemReg to VhostUserMemRegMsg and memory_single
to memreg
Patches 3/5 and 4/5
Relocated function remove_guest_pages from patch 3/5 to 4/5
Version v12: Incorporate code review suggestions from Maxime Coquelin
and ai-code-review.
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.
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.
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.
Patch 3/5
Restored max_guest_pages initial value to hardcoded 8 instead of
VHOST_MEMORY_MAX_NREGIONS, matching upstream semantics.
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: header defines for add/rem mem region
vhost: refactor memory helper functions
vhost: add mem region add/remove handlers
vhost: enable configure memory slots
.mailmap | 1 +
lib/vhost/rte_vhost.h | 4 +
lib/vhost/vhost_user.c | 427 +++++++++++++++++++++++++++++++++++------
lib/vhost/vhost_user.h | 10 +
4 files changed, 380 insertions(+), 62 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH v14 1/5] vhost: add user to mailmap and define to vhost hdr
From: pravin.bathija @ 2026-05-20 2:20 UTC (permalink / raw)
To: dev, fengchengwen, stephen, maxime.coquelin
Cc: pravin.bathija, thomas, Stephen Hemminger
In-Reply-To: <20260520022012.243619-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>
Acked-by: Fengchengwen <fengchengwen@huawei.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.com>
Acked-by: Maxime Coquelin <maxime.coquelin@redhat.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 v14 3/5] vhost: refactor memory helper functions
From: pravin.bathija @ 2026-05-20 2:20 UTC (permalink / raw)
To: dev, fengchengwen, stephen, maxime.coquelin
Cc: pravin.bathija, thomas, Stephen Hemminger
In-Reply-To: <20260520022012.243619-1-pravin.bathija@dell.com>
From: Pravin M Bathija <pravin.bathija@dell.com>
- Extract reusable helper routines for vhost-user backend memory
operations.
- Split DMA map/unmap into per-region logic.
- Decouple and rework memory region free routines.
- Iterate over VHOST_MEMORY_MAX_NREGIONS uniformly
across related functions to simplify code reuse
Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
Acked-by: Fengchengwen <fengchengwen@huawei.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.com>
Acked-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
lib/vhost/vhost_user.c | 172 ++++++++++++++++++++++++++---------------
1 file changed, 110 insertions(+), 62 deletions(-)
diff --git a/lib/vhost/vhost_user.c b/lib/vhost/vhost_user.c
index 4bfb13fb98..94fca8b589 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;
- if (do_map) {
- for (i = 0; i < dev->nr_guest_pages; i++) {
- page = &dev->guest_pages[i];
+ 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) {
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);
@@ -1382,6 +1413,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,
@@ -1390,7 +1467,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;
@@ -1435,39 +1511,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];
@@ -1531,11 +1581,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:
--
2.43.0
^ permalink raw reply related
* [PATCH v14 2/5] vhost: header defines for add/rem mem region
From: pravin.bathija @ 2026-05-20 2:20 UTC (permalink / raw)
To: dev, fengchengwen, stephen, maxime.coquelin
Cc: pravin.bathija, thomas, Stephen Hemminger
In-Reply-To: <20260520022012.243619-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>
Acked-by: Fengchengwen <fengchengwen@huawei.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.com>
Acked-by: Maxime Coquelin <maxime.coquelin@redhat.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 v14 4/5] vhost: add mem region add/remove handlers
From: pravin.bathija @ 2026-05-20 2:20 UTC (permalink / raw)
To: dev, fengchengwen, stephen, maxime.coquelin
Cc: pravin.bathija, thomas, Stephen Hemminger
In-Reply-To: <20260520022012.243619-1-pravin.bathija@dell.com>
From: Pravin M Bathija <pravin.bathija@dell.com>
Add support for VHOST_USER_ADD_MEM_REG, VHOST_USER_REM_MEM_REG and
VHOST_USER_GET_MAX_MEM_SLOTS. Refactor memory initialization into
common helper and add supporting functions for dynamic memory management.
Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
Acked-by: Fengchengwen <fengchengwen@huawei.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.com>
Acked-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
lib/vhost/vhost_user.c | 255 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 255 insertions(+)
diff --git a/lib/vhost/vhost_user.c b/lib/vhost/vhost_user.c
index 94fca8b589..522ba1db82 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
@@ -1591,6 +1612,240 @@ 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 = max_mem_slots;
+ ctx->msg.size = sizeof(ctx->msg.payload.u64);
+ ctx->fd_num = 0;
+
+ return RTE_VHOST_MSG_RESULT_REPLY;
+}
+
+/*
+ * Invalidate and re-translate all vring addresses after the memory table
+ * has been modified (add/remove region).
+ *
+ * translate_ring_addresses() may call numa_realloc(), which can reallocate
+ * the device structure. The updated pointer is written back through *pdev
+ * so callers must refresh their local "dev" afterwards: dev = *pdev.
+ */
+static void
+vhost_user_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);
+
+ 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"); \
+ vhost_user_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)
+{
+ struct VhostUserMemoryRegion *region = &ctx->msg.payload.memreg.region;
+ struct virtio_net *dev = *pdev;
+ uint32_t i;
+
+ /* 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 *cur = &dev->mem->regions[i];
+ uint64_t cur_start = cur->guest_user_addr;
+ uint64_t cur_end = cur_start + cur->size - 1;
+ uint64_t new_start = region->userspace_addr;
+ uint64_t new_end = new_start + region->memory_size - 1;
+
+ if (new_end >= cur_start && new_start <= cur_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,
+ cur->guest_user_addr);
+ VHOST_CONFIG_LOG(dev->ifname, ERR,
+ "\tOverlapping region size:0x%" PRIx64,
+ cur->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_no_dma;
+ }
+
+ 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);
+
+ /* Reply with the back-end's mapping address per vhost-user spec */
+ ctx->msg.payload.memreg.region.userspace_addr = reg->host_user_addr;
+ ctx->msg.size = sizeof(ctx->msg.payload.memreg);
+ ctx->fd_num = 0;
+
+ return RTE_VHOST_MSG_RESULT_REPLY;
+
+free_new_region:
+ if (dev->async_copy && rte_vfio_is_enabled("vfio"))
+ async_dma_map_region(dev, reg, false);
+free_new_region_no_dma:
+ 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)
+{
+ struct VhostUserMemoryRegion *region = &ctx->msg.payload.memreg.region;
+ struct virtio_net *dev = *pdev;
+ uint32_t i;
+
+ 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;
+ }
+
+ if (validate_msg_fds(dev, ctx, 0) != 0)
+ 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 v14 5/5] vhost: enable configure memory slots
From: pravin.bathija @ 2026-05-20 2:20 UTC (permalink / raw)
To: dev, fengchengwen, stephen, maxime.coquelin
Cc: pravin.bathija, thomas, Stephen Hemminger
In-Reply-To: <20260520022012.243619-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>
Acked-by: Fengchengwen <fengchengwen@huawei.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.com>
Acked-by: Maxime Coquelin <maxime.coquelin@redhat.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
* RE: [PATCH v13 1/5] vhost: add user to mailmap and define to vhost hdr
From: Bathija, Pravin @ 2026-05-20 2:21 UTC (permalink / raw)
To: fengchengwen, dev@dpdk.org, stephen@networkplumber.org,
maxime.coquelin@redhat.com
Cc: thomas@monjalon.net
In-Reply-To: <b4e44ed7-7902-4369-a4e4-509ed3addf91@huawei.com>
Internal Use - Confidential
> -----Original Message-----
> From: fengchengwen <fengchengwen@huawei.com>
> Sent: Thursday, May 14, 2026 5:21 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 v13 1/5] vhost: add user to mailmap and define to vhost
> hdr
>
>
> [EXTERNAL EMAIL]
>
> On 5/15/2026 6:46 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>
>
> Please attach reviewed/acked-by of this commit in new version
done
>
>
^ permalink raw reply
* RE: [PATCH v13 3/5] vhost_user: support function defines for back-end
From: Bathija, Pravin @ 2026-05-20 2:23 UTC (permalink / raw)
To: fengchengwen, dev@dpdk.org, stephen@networkplumber.org,
maxime.coquelin@redhat.com
Cc: thomas@monjalon.net
In-Reply-To: <4663f567-0167-415d-ba5a-c1133ae0f7f5@huawei.com>
Internal Use - Confidential
> -----Original Message-----
> From: fengchengwen <fengchengwen@huawei.com>
> Sent: Thursday, May 14, 2026 5:33 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 v13 3/5] vhost_user: support function defines for back-end
>
>
> [EXTERNAL EMAIL]
>
> please use vhost as the commit title prefix (please use git blame to refer), the
> same as other commit in this patchset.
>
> How about: vhost: refactor memory helper functions
>
> On 5/15/2026 6:46 AM, pravin.bathija@dell.com wrote:
> > 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.
>
> Two much detail, how about:
>
> Extract reusable helper routines for vhost-user backend memory operations:
> split DMA map/unmap into per-region logic, decouple and rework memory
> region free routines, and iterate over VHOST_MEMORY_MAX_NREGIONS
> uniformly across related functions to simplify code reuse.
>
> As above fixed:
> Acked-by: Chengwen Feng <fengchengwen@huawei.com>
>
Made the changes as suggested in patch-set v14.
> >
> > 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);
^ permalink raw reply
* RE: [PATCH v13 4/5] vhost_user: Function defs for add/rem mem regions
From: Bathija, Pravin @ 2026-05-20 2:30 UTC (permalink / raw)
To: fengchengwen, dev@dpdk.org, stephen@networkplumber.org,
maxime.coquelin@redhat.com
Cc: thomas@monjalon.net
In-Reply-To: <71c3a27c-a76f-4339-a4f5-997cd1d98286@huawei.com>
Hi Fengcheng,
Thank you for your feedback. I have submitted patch-set v14 incorporating your suggestions. The responses to your comments are inline.
Internal Use - Confidential
> -----Original Message-----
> From: fengchengwen <fengchengwen@huawei.com>
> Sent: Thursday, May 14, 2026 6:05 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 v13 4/5] vhost_user: Function defs for add/rem mem
> regions
>
>
> [EXTERNAL EMAIL]
>
> On 5/15/2026 6:46 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.
> > The helper function remove_guest_pages is also defined here which is
> > called from vhost_user_add_mem_reg.
>
> Two much detail which provide noisy infomation I think, how about:
>
> vhost: add mem region add/remove handlers
>
> Add support for VHOST_USER_ADD_MEM_REG,
> VHOST_USER_REM_MEM_REG and VHOST_USER_GET_MAX_MEM_SLOTS.
> Refactor memory initialization into common helper and add supporting
> functions for dynamic memory management.
Made the changes as suggested in v14.
>
> Signed-off-by: Pravin M Bathija <pravin.bathija@dell.com>
>
> >
> > 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);
>
> This should be done in commit 3/5, I suspect that 3/5 of the code may fail to be
> compiled.
>
> Please make sure each commit should compile OK, so that git commit binary
> search and troubleshooting could work.
Agreed, moved to patch 3/5 to ensure each commit compiles independently.
>
> > 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);
>
> Same, it should be done in commit 3/5
done
>
> > 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)
>
> It seems that there is no such naming convention in vhost.
Renamed to vhost_user_invalidate_vrings() to follow vhost naming convention.
>
> > +{
> > + 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;
>
> why do this?
Added comment -- translate_ring_addresses() may call numa_realloc() which can reallocate the device, so the pointer must propagate back through *pdev.
>
> > +}
> > +
> > +/*
> > + * 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;
>
> Local variables should be arranged in descending order of length.
Reordered by descending line length in both add_mem_reg and rem_mem_reg.
>
> struct VhostUserMemoryRegion *region = &ctx->msg.payload.memreg.region;
> struct virtio_net *dev = *pdev; uint32_t i;
>
> > +
> > + /* 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;
>
> why not use short name?
Shortened to cur_start/end and new_start/end
>
> > +
> > + 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;
>
> I point it out in v12, maybe not so clear, so again:
> the goto will invoke async_dma_map_region(dev, reg, false), it should not
> invoke in this branch.
Fixed, added free_new_region_no_dma label that skips the DMA unmap.
>
> > + }
> > +
> > + 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;
>
> What the meaning? the dev already set *pdev in the beginning.
> I also point it out in v12, I don't know what happening.
Added comment -- translate_ring_addresses() may call numa_realloc() which can reallocate the device, so the pointer must propagate back through *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;
>
> I still don't know what the assignment meaning/function?
Added comment -- translate_ring_addresses() may call numa_realloc() which can reallocate the device, so the pointer must propagate back through *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) {
^ permalink raw reply
* RE: [PATCH v13 0/5] Support add/remove memory region and get-max-slots
From: Bathija, Pravin @ 2026-05-20 2:36 UTC (permalink / raw)
To: Stephen Hemminger
Cc: dev@dpdk.org, fengchengwen@huawei.com, maxime.coquelin@redhat.com,
thomas@monjalon.net
In-Reply-To: <20260518101344.7a1de0ef@phoenix.local>
Hi Stephen,
Thank you for your feedback on the patch-set. I have submitted v14 incorporating the changes you suggested. The responses to your comments are inline.
Internal Use - Confidential
> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Monday, May 18, 2026 10:14 AM
> To: Bathija, Pravin <Pravin.Bathija@dell.com>
> Cc: dev@dpdk.org; fengchengwen@huawei.com;
> maxime.coquelin@redhat.com; thomas@monjalon.net
> Subject: Re: [PATCH v13 0/5] Support add/remove memory region and get-max-
> slots
>
>
> [EXTERNAL EMAIL]
>
> On Thu, 14 May 2026 02:01:52 +0000
> <pravin.bathija@dell.com> wrote:
>
> > 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(-)
> >
>
> Some useful AI feedback
>
> Review of [PATCH v13 0-5/5] vhost: configure memory slots support
> Author: Pravin M Bathija <pravin.bathija@dell.com>
>
> This revision addresses essentially every correctness issue raised in the v7-v12
> reviews:
>
> - ctx->fds[0] = -1 ownership transfer is now done before mmap, and
> the mmap-failure path closes reg->fd explicitly when mmap never
> set reg->mmap_addr.
> - _dev_invalidate_vrings now takes struct virtio_net **pdev and
> writes back *pdev = dev at the end, so a numa_realloc inside
> translate_ring_addresses propagates correctly. Both call sites
> refresh "dev = *pdev;" afterwards.
> - The dev_invalidate_vrings() macro now takes the message id and
> uses static_assert(id ## _LOCK_ALL_QPS, ...), matching the
> existing VHOST_USER_ASSERT_LOCK pattern. Works for both
> VHOST_USER_ADD_MEM_REG and VHOST_USER_REM_MEM_REG call sites.
> - Overlap check in vhost_user_add_mem_reg uses guest address
> space (guest_user_addr, size / userspace_addr, memory_size),
> no longer mmap_size.
> - free_new_region undoes only the failed region: async DMA unmap,
> remove_guest_pages, free_mem_region(reg), nregions--.
> - async_dma_map_region iterates dev->nr_guest_pages and filters
> by [reg_start, reg_end), eliminating the prior reg_size
> underflow loop.
> - The regions array is kept contiguous via memmove on REM_MEM_REG,
> so existing iterators that walk mem->nregions remain correct.
> - max_guest_pages is back to 8 in vhost_user_initialize_memory.
>
> One protocol-level issue remains worth raising.
>
>
> Patch 4/5 -- vhost_user: Function defs for add/rem mem regions
> --------------------------------------------------------------------
>
> Warning: ADD_MEM_REG does not send the host_user_addr reply
>
> Per the vhost-user spec for VHOST_USER_ADD_MEM_REG, the back-end
> is expected to reply with the same message format and the
> userspace_addr field replaced by the host userspace address that
> the region was mapped into. The handler returns
> RTE_VHOST_MSG_RESULT_OK with no reply constructed, so the
> dispatcher does not call send_vhost_reply().
>
> For postcopy migration this matters in particular: the original
> vhost_user_postcopy_register() does two things -- exchange the
> host_user_addr with the front-end and wait for an ack, then
> register the regions with userfaultfd. The patch only does the
> userfaultfd registration via vhost_user_postcopy_region_register().
> The in-code comment notes the payload-layout mismatch with
> vhost_user_postcopy_register() but stops there.
>
> Without the address reply, QEMU will not know the back-end's
> mapping for regions added via ADD_MEM_REG, so the userfaultfd
> handling on the QEMU side cannot resolve faults in those
> regions. Postcopy migration combined with the
> CONFIGURE_MEM_SLOTS feature will not work.
>
> Suggested fix: construct a memreg-payload reply with
> region->userspace_addr replaced by reg->host_user_addr and
> return RTE_VHOST_MSG_RESULT_REPLY. At minimum, refuse
> ADD_MEM_REG when dev->postcopy_listening is set, so that the
> combination fails cleanly rather than silently mis-mapping.
Fixed, now constructs a memreg reply with host_user_addr and returns RTE_VHOST_MSG_RESULT_REPLY.
>
>
> Info: vhost_user_rem_mem_reg does not validate ctx->fd_num
>
> The handler is registered with accepts_fd = true and does not
> call validate_msg_fds(). The trailing close_msg_fds(ctx) cleans
> up whatever fds were passed, so this is not a leak, but a
> malformed message with an unexpected fd count is silently
> accepted. The other accepts_fd handlers in this file validate
> fd_num explicitly.
Fixed, added validate_msg_fds(dev, ctx, 0).
>
>
> Info: vhost_user_get_max_mem_slots cast is unnecessary
>
> ctx->msg.payload.u64 = (uint64_t)max_mem_slots;
>
> max_mem_slots is uint32_t and the assignment widens
> automatically; the cast can be dropped. Minor.
Dropped the cast.
>
>
> Reviewed-by would be appropriate once the postcopy reply is addressed (or the
> combination is rejected). The rest of the series looks correct.
Added the Reviewed-by line in v14
^ permalink raw reply
* [PATCH 2/2] ethdev: include device name in telemetry list output
From: Chengwen Feng @ 2026-05-20 3:56 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev
In-Reply-To: <20260520035641.50555-1-fengchengwen@huawei.com>
The /ethdev/list telemetry endpoint currently returns only port IDs
as integers. Enhance it to return strings in "ID NAME" format so
users can identify ports by name directly from the telemetry output.
Original:
{
"/ethdev/list": [
0,
1
]
}
After this commit:
{
"/ethdev/list": [
"0 0000:7d:00.0",
"1 0000:7d:00.1"
]
}
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
lib/ethdev/rte_ethdev_telemetry.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/lib/ethdev/rte_ethdev_telemetry.c b/lib/ethdev/rte_ethdev_telemetry.c
index a910864bc5..f82b174110 100644
--- a/lib/ethdev/rte_ethdev_telemetry.c
+++ b/lib/ethdev/rte_ethdev_telemetry.c
@@ -53,11 +53,17 @@ eth_dev_handle_port_list(const char *cmd __rte_unused,
const char *params __rte_unused,
struct rte_tel_data *d)
{
+ char id_name[RTE_TEL_MAX_STRING_LEN];
+ struct rte_eth_dev *dev;
int port_id;
- rte_tel_data_start_array(d, RTE_TEL_INT_VAL);
- RTE_ETH_FOREACH_DEV(port_id)
- rte_tel_data_add_array_int(d, port_id);
+ rte_tel_data_start_array(d, RTE_TEL_STRING_VAL);
+ RTE_ETH_FOREACH_DEV(port_id) {
+ dev = &rte_eth_devices[port_id];
+ memset(id_name, 0, sizeof(id_name));
+ sprintf(id_name, "%d %s", port_id, dev->data->name);
+ rte_tel_data_add_array_string(d, id_name);
+ }
return 0;
}
--
2.17.1
^ permalink raw reply related
* [PATCH 1/2] dmadev: include device name in telemetry list output
From: Chengwen Feng @ 2026-05-20 3:56 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev
In-Reply-To: <20260520035641.50555-1-fengchengwen@huawei.com>
The /dmadev/list telemetry endpoint currently returns only device IDs
as integers. Enhance it to return strings in "ID NAME" format so
users can identify devices by name directly from the telemetry output.
Original:
{
"/dmadev/list": [
0,
1
]
}
After this commit:
{
"/dmadev/list": [
"0 hisi_sec2-0-dma0",
"1 hisi_sec2-0-dma1"
]
}
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
lib/dmadev/rte_dmadev.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/lib/dmadev/rte_dmadev.c b/lib/dmadev/rte_dmadev.c
index b75b4f9bd1..084e06a3da 100644
--- a/lib/dmadev/rte_dmadev.c
+++ b/lib/dmadev/rte_dmadev.c
@@ -1147,12 +1147,19 @@ dmadev_handle_dev_list(const char *cmd __rte_unused,
const char *params __rte_unused,
struct rte_tel_data *d)
{
+ char id_name[RTE_TEL_MAX_STRING_LEN];
+ struct rte_dma_dev *dev;
int dev_id;
- rte_tel_data_start_array(d, RTE_TEL_INT_VAL);
- for (dev_id = 0; dev_id < dma_devices_max; dev_id++)
- if (rte_dma_is_valid(dev_id))
- rte_tel_data_add_array_int(d, dev_id);
+ rte_tel_data_start_array(d, RTE_TEL_STRING_VAL);
+ for (dev_id = 0; dev_id < dma_devices_max; dev_id++) {
+ if (!rte_dma_is_valid(dev_id))
+ continue;
+ dev = &rte_dma_devices[dev_id];
+ memset(id_name, 0, sizeof(id_name));
+ sprintf(id_name, "%d %s", dev_id, dev->data->dev_name);
+ rte_tel_data_add_array_string(d, id_name);
+ }
return 0;
}
@@ -1308,7 +1315,7 @@ dmadev_handle_dev_dump(const char *cmd __rte_unused,
RTE_INIT(dmadev_init_telemetry)
{
rte_telemetry_register_cmd("/dmadev/list", dmadev_handle_dev_list,
- "Returns list of available dmadev devices by IDs. No parameters.");
+ "Returns list of available dmadev devices by ID-NAMEs. No parameters.");
rte_telemetry_register_cmd("/dmadev/info", dmadev_handle_dev_info,
"Returns information for a dmadev. Parameters: int dev_id");
rte_telemetry_register_cmd("/dmadev/stats", dmadev_handle_dev_stats,
--
2.17.1
^ permalink raw reply related
* [PATCH 0/2] enhance telemetry list endpoint with device name
From: Chengwen Feng @ 2026-05-20 3:56 UTC (permalink / raw)
To: thomas, stephen; +Cc: dev
Currently, the /dmadev/list and /ethdev/list telemetry endpoints return
only integer IDs, making it hard to identify devices. This series changes
both to output strings in "ID NAME" format for better usability.
Chengwen Feng (2):
dmadev: include device name in telemetry list output
ethdev: include device name in telemetry list output
lib/dmadev/rte_dmadev.c | 17 ++++++++++++-----
lib/ethdev/rte_ethdev_telemetry.c | 12 +++++++++---
2 files changed, 21 insertions(+), 8 deletions(-)
--
2.17.1
^ permalink raw reply
* RE: [PATCH 0/2] enhance telemetry list endpoint with device name
From: Morten Brørup @ 2026-05-20 5:40 UTC (permalink / raw)
To: Chengwen Feng, thomas, stephen; +Cc: dev
In-Reply-To: <20260520035641.50555-1-fengchengwen@huawei.com>
> From: Chengwen Feng [mailto:fengchengwen@huawei.com]
> Sent: Wednesday, 20 May 2026 05.57
>
> Currently, the /dmadev/list and /ethdev/list telemetry endpoints return
> only integer IDs, making it hard to identify devices. This series
> changes
> both to output strings in "ID NAME" format for better usability.
For machine reading of the JSON output, it would be better returning an object with an integer and a string field, {ID, "NAME"}.
^ permalink raw reply
* [PATCH v5 1/2] net/iavf: reuse device alarm for PHC sync
From: Soumyadeep Hore @ 2026-05-20 18:43 UTC (permalink / raw)
To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev; +Cc: stable
In-Reply-To: <20260520184350.81934-1-soumyadeep.hore@intel.com>
Reuse existing iavf device alarm cadence to drive periodic PHC sync
instead of a dedicated PHC alarm callback.
Keep PHC start/stop hooks as pause/resume controls around queue
reconfiguration and device lifecycle paths, including WB_ON_ITR mode.
Refresh PHC state from the first active RX queue and propagate it to
all active RX queues so per-queue timestamp conversion stays aligned.
Bugzilla ID: 1898
Fixes: 61b6874b9224 ("net/iavf: support Rx timestamp offload on AVX512")
Fixes: 6ad2944f4e82 ("net/iavf: support Rx timestamp offload on AVX2")
Fixes: 33db16136e55 ("net/iavf: improve performance of Rx timestamp offload")
Cc: stable@dpdk.org
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/iavf/iavf.h | 6 +
drivers/net/intel/iavf/iavf_ethdev.c | 168 ++++++++++++++++++++++++---
drivers/net/intel/iavf/iavf_vchnl.c | 4 +
3 files changed, 164 insertions(+), 14 deletions(-)
diff --git a/drivers/net/intel/iavf/iavf.h b/drivers/net/intel/iavf/iavf.h
index 403c61e2e8..9aad077621 100644
--- a/drivers/net/intel/iavf/iavf.h
+++ b/drivers/net/intel/iavf/iavf.h
@@ -76,6 +76,7 @@
#define IAVF_QUEUE_ITR_INTERVAL_MAX 8160 /* 8160 us */
#define IAVF_ALARM_INTERVAL 50000 /* us */
+#define IAVF_PHC_SYNC_ALARM_INTERVAL_US 200000
/* The overhead from MTU to max frame size.
* Considering QinQ packet, the VLAN tag needs to be counted twice.
@@ -383,6 +384,9 @@ struct iavf_adapter {
enum iavf_rx_func_type rx_func_type;
enum iavf_tx_func_type tx_func_type;
uint16_t fdir_ref_cnt;
+ rte_spinlock_t phc_sync_lock;
+ uint8_t phc_sync_ticks;
+ bool phc_sync_paused;
struct iavf_devargs devargs;
bool mac_primary_set;
};
@@ -517,6 +521,8 @@ void iavf_add_del_all_mac_addr(struct iavf_adapter *adapter, bool add);
int iavf_dev_link_update(struct rte_eth_dev *dev,
__rte_unused int wait_to_complete);
void iavf_dev_alarm_handler(void *param);
+void iavf_phc_sync_alarm_start(struct rte_eth_dev *dev);
+void iavf_phc_sync_alarm_stop(struct rte_eth_dev *dev);
int iavf_query_stats(struct iavf_adapter *adapter,
struct virtchnl_eth_stats **pstats);
int iavf_config_promisc(struct iavf_adapter *adapter, bool enable_unicast,
diff --git a/drivers/net/intel/iavf/iavf_ethdev.c b/drivers/net/intel/iavf/iavf_ethdev.c
index 1eca20bc9a..3f49804aca 100644
--- a/drivers/net/intel/iavf/iavf_ethdev.c
+++ b/drivers/net/intel/iavf/iavf_ethdev.c
@@ -21,6 +21,7 @@
#include <rte_pci.h>
#include <rte_alarm.h>
#include <rte_atomic.h>
+#include <rte_cycles.h>
#include <rte_eal.h>
#include <rte_ether.h>
#include <ethdev_driver.h>
@@ -145,6 +146,12 @@ static int iavf_dev_rx_queue_intr_disable(struct rte_eth_dev *dev,
uint16_t queue_id);
static void iavf_dev_interrupt_handler(void *param);
static void iavf_disable_irq0(struct iavf_hw *hw);
+static struct ci_rx_queue *iavf_phc_sync_rxq_get(struct rte_eth_dev *dev);
+static void iavf_phc_sync_update_all_rxq(struct rte_eth_dev *dev,
+ uint64_t phc_time,
+ uint64_t sw_cur_time);
+static bool iavf_phc_sync_alarm_needed(struct rte_eth_dev *dev);
+static void iavf_phc_sync_tick(struct rte_eth_dev *dev);
static int iavf_dev_flow_ops_get(struct rte_eth_dev *dev,
const struct rte_flow_ops **ops);
static int iavf_set_mc_addr_list(struct rte_eth_dev *dev,
@@ -1056,6 +1063,8 @@ iavf_dev_start(struct rte_eth_dev *dev)
goto error;
}
+ iavf_phc_sync_alarm_start(dev);
+
return 0;
error:
@@ -1082,6 +1091,8 @@ iavf_dev_stop(struct rte_eth_dev *dev)
if (adapter->stopped == 1)
return 0;
+ iavf_phc_sync_alarm_stop(dev);
+
/* Disable the interrupt for Rx */
rte_intr_efd_disable(intr_handle);
/* Rx interrupt vector mapping free */
@@ -2701,32 +2712,160 @@ iavf_dev_interrupt_handler(void *param)
iavf_enable_irq0(hw);
}
+static struct ci_rx_queue *
+iavf_phc_sync_rxq_get(struct rte_eth_dev *dev)
+{
+ struct ci_rx_queue *rxq;
+ uint16_t i;
+
+ for (i = 0; i < dev->data->nb_rx_queues; i++) {
+ rxq = dev->data->rx_queues[i];
+ if (rxq != NULL)
+ return rxq;
+ }
+
+ return NULL;
+}
+
+static void
+iavf_phc_sync_update_all_rxq(struct rte_eth_dev *dev,
+ uint64_t phc_time,
+ uint64_t sw_cur_time)
+{
+ struct ci_rx_queue *rxq;
+ uint16_t i;
+
+ for (i = 0; i < dev->data->nb_rx_queues; i++) {
+ rxq = dev->data->rx_queues[i];
+ if (rxq == NULL)
+ continue;
+
+ rxq->phc_time = phc_time;
+ rxq->hw_time_update = sw_cur_time;
+ }
+}
+
+static void
+iavf_phc_sync_tick(struct rte_eth_dev *dev)
+{
+ struct iavf_adapter *adapter;
+ const uint16_t phc_sync_ticks_max = RTE_MAX((uint16_t)1,
+ (uint16_t)(IAVF_PHC_SYNC_ALARM_INTERVAL_US / IAVF_ALARM_INTERVAL));
+ struct ci_rx_queue *sync_rxq;
+ uint64_t sw_cur_time;
+
+ adapter = IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+
+ rte_spinlock_lock(&adapter->phc_sync_lock);
+ if (adapter->phc_sync_paused || !iavf_phc_sync_alarm_needed(dev)) {
+ adapter->phc_sync_ticks = 0;
+ goto unlock;
+ }
+
+ if (++adapter->phc_sync_ticks < phc_sync_ticks_max)
+ goto unlock;
+
+ adapter->phc_sync_ticks = 0;
+ sync_rxq = iavf_phc_sync_rxq_get(dev);
+ if (sync_rxq == NULL)
+ goto unlock;
+
+ if (iavf_get_phc_time(sync_rxq) != 0) {
+ PMD_DRV_LOG(ERR, "get physical time failed");
+ goto unlock;
+ }
+
+ sw_cur_time = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
+ iavf_phc_sync_update_all_rxq(dev, sync_rxq->phc_time, sw_cur_time);
+
+unlock:
+ rte_spinlock_unlock(&adapter->phc_sync_lock);
+}
+
void
iavf_dev_alarm_handler(void *param)
{
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
+ struct iavf_info *vf;
if (dev == NULL || dev->data == NULL || dev->data->dev_private == NULL)
return;
+ vf = IAVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
struct iavf_hw *hw = IAVF_DEV_PRIVATE_TO_HW(dev->data->dev_private);
uint32_t icr0;
- iavf_disable_irq0(hw);
+ if (!(vf->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR)) {
+ iavf_disable_irq0(hw);
+
+ /* read out interrupt causes */
+ icr0 = IAVF_READ_REG(hw, IAVF_VFINT_ICR01);
- /* read out interrupt causes */
- icr0 = IAVF_READ_REG(hw, IAVF_VFINT_ICR01);
+ if (icr0 & IAVF_VFINT_ICR01_ADMINQ_MASK) {
+ PMD_DRV_LOG(DEBUG, "ICR01_ADMINQ is reported");
+ iavf_handle_virtchnl_msg(dev);
+ }
- if (icr0 & IAVF_VFINT_ICR01_ADMINQ_MASK) {
- PMD_DRV_LOG(DEBUG, "ICR01_ADMINQ is reported");
- iavf_handle_virtchnl_msg(dev);
+ iavf_enable_irq0(hw);
}
- iavf_enable_irq0(hw);
+ iavf_phc_sync_tick(dev);
rte_eal_alarm_set(IAVF_ALARM_INTERVAL,
iavf_dev_alarm_handler, dev);
}
+static bool
+iavf_phc_sync_alarm_needed(struct rte_eth_dev *dev)
+{
+ struct iavf_adapter *adapter;
+
+ adapter = IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+
+ if (adapter->closed || adapter->stopped)
+ return false;
+
+ if (!(dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_TIMESTAMP))
+ return false;
+
+ if (dev->data->nb_rx_queues == 0)
+ return false;
+
+ if (iavf_phc_sync_rxq_get(dev) == NULL)
+ return false;
+
+ return true;
+}
+
+void
+iavf_phc_sync_alarm_start(struct rte_eth_dev *dev)
+{
+ struct iavf_adapter *adapter;
+
+ if (!iavf_phc_sync_alarm_needed(dev))
+ return;
+
+ adapter = IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+ rte_spinlock_lock(&adapter->phc_sync_lock);
+ adapter->phc_sync_paused = false;
+ adapter->phc_sync_ticks = 0;
+ rte_spinlock_unlock(&adapter->phc_sync_lock);
+}
+
+void
+iavf_phc_sync_alarm_stop(struct rte_eth_dev *dev)
+{
+ struct iavf_adapter *adapter;
+
+ if (dev == NULL || dev->data == NULL || dev->data->dev_private == NULL)
+ return;
+
+ adapter = IAVF_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+ rte_spinlock_lock(&adapter->phc_sync_lock);
+ adapter->phc_sync_paused = true;
+ adapter->phc_sync_ticks = 0;
+ rte_spinlock_unlock(&adapter->phc_sync_lock);
+}
+
static int
iavf_dev_flow_ops_get(struct rte_eth_dev *dev,
const struct rte_flow_ops **ops)
@@ -2808,6 +2947,7 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
adapter->dev_data = eth_dev->data;
adapter->stopped = 1;
adapter->mac_primary_set = false;
+ rte_spinlock_init(&adapter->phc_sync_lock);
if (iavf_dev_event_handler_init())
goto init_vf_err;
@@ -2847,9 +2987,9 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
/* enable uio intr after callback register */
rte_intr_enable(pci_dev->intr_handle);
- else
- rte_eal_alarm_set(IAVF_ALARM_INTERVAL,
- iavf_dev_alarm_handler, eth_dev);
+
+ rte_eal_alarm_set(IAVF_ALARM_INTERVAL,
+ iavf_dev_alarm_handler, eth_dev);
/* configure and enable device interrupt */
iavf_enable_irq0(hw);
@@ -2909,9 +3049,9 @@ iavf_dev_init(struct rte_eth_dev *eth_dev)
/* unregister callback func from eal lib */
rte_intr_callback_unregister(pci_dev->intr_handle,
iavf_dev_interrupt_handler, eth_dev);
- } else {
- rte_eal_alarm_cancel(iavf_dev_alarm_handler, eth_dev);
}
+ iavf_phc_sync_alarm_stop(eth_dev);
+ rte_eal_alarm_cancel(iavf_dev_alarm_handler, eth_dev);
rte_free(eth_dev->data->mac_addrs);
eth_dev->data->mac_addrs = NULL;
@@ -2983,9 +3123,9 @@ iavf_dev_close(struct rte_eth_dev *dev)
/* unregister callback func from eal lib */
rte_intr_callback_unregister(intr_handle,
iavf_dev_interrupt_handler, dev);
- } else {
- rte_eal_alarm_cancel(iavf_dev_alarm_handler, dev);
}
+ iavf_phc_sync_alarm_stop(dev);
+ rte_eal_alarm_cancel(iavf_dev_alarm_handler, dev);
iavf_disable_irq0(hw);
if (vf->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_QOS)
diff --git a/drivers/net/intel/iavf/iavf_vchnl.c b/drivers/net/intel/iavf/iavf_vchnl.c
index 08dd6f2d7f..79ef4cec56 100644
--- a/drivers/net/intel/iavf/iavf_vchnl.c
+++ b/drivers/net/intel/iavf/iavf_vchnl.c
@@ -2133,12 +2133,16 @@ iavf_request_queues(struct rte_eth_dev *dev, uint16_t num)
args.out_size = IAVF_AQ_BUF_SZ;
if (vf->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) {
+ iavf_phc_sync_alarm_stop(dev);
err = iavf_execute_vf_cmd_safe(adapter, &args, 0);
+ iavf_phc_sync_alarm_start(dev);
} else {
+ iavf_phc_sync_alarm_stop(dev);
rte_eal_alarm_cancel(iavf_dev_alarm_handler, dev);
err = iavf_execute_vf_cmd_safe(adapter, &args, 0);
rte_eal_alarm_set(IAVF_ALARM_INTERVAL,
iavf_dev_alarm_handler, dev);
+ iavf_phc_sync_alarm_start(dev);
}
if (err) {
--
2.47.1
^ permalink raw reply related
* [PATCH v5 0/2] Update Rx Timestamp in IAVF PMD
From: Soumyadeep Hore @ 2026-05-20 18:43 UTC (permalink / raw)
To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev
In-Reply-To: <20260509101447.42093-3-soumyadeep.hore@intel.com>
PHC Polling from Rx Datapath is removed and existing alarm handlers are
used to fix latency issues in IAVF PMD.
---
v5:
- Addressed Bruce's comments
---
v4:
- Addressed Bruce's comments
---
v3:
- Addressed AI code reviews
---
v2:
- Fixed patch apply issues
---
Soumyadeep Hore (2):
net/iavf: reuse device alarm for PHC sync
net/iavf: remove PHC polling from Rx datapath
drivers/net/intel/iavf/iavf.h | 6 +
drivers/net/intel/iavf/iavf_ethdev.c | 168 ++++++++++++++++--
drivers/net/intel/iavf/iavf_rxtx.c | 34 ----
drivers/net/intel/iavf/iavf_rxtx_vec_avx2.c | 16 +-
drivers/net/intel/iavf/iavf_rxtx_vec_avx512.c | 16 +-
drivers/net/intel/iavf/iavf_vchnl.c | 4 +
6 files changed, 168 insertions(+), 76 deletions(-)
--
2.47.1
^ permalink raw reply
* [PATCH v5 2/2] net/iavf: remove PHC polling from Rx datapath
From: Soumyadeep Hore @ 2026-05-20 18:43 UTC (permalink / raw)
To: bruce.richardson, manoj.kumar.subbarao, aman.deep.singh, dev; +Cc: stable
In-Reply-To: <20260520184350.81934-1-soumyadeep.hore@intel.com>
Remove periodic PHC read/update checks from scalar and vector flex
RX paths, keeping timestamp conversion based on queue PHC state.
With control-path PHC sync already in place, this avoids hot-path PHC
polling overhead while preserving RX timestamp correctness.
Bugzilla ID: 1898
Fixes: 61b6874b9224 ("net/iavf: support Rx timestamp offload on AVX512")
Fixes: 6ad2944f4e82 ("net/iavf: support Rx timestamp offload on AVX2")
Fixes: 33db16136e55 ("net/iavf: improve performance of Rx timestamp offload")
Cc: stable@dpdk.org
Signed-off-by: Soumyadeep Hore <soumyadeep.hore@intel.com>
---
drivers/net/intel/iavf/iavf_rxtx.c | 34 -------------------
drivers/net/intel/iavf/iavf_rxtx_vec_avx2.c | 16 ++-------
drivers/net/intel/iavf/iavf_rxtx_vec_avx512.c | 16 ++-------
3 files changed, 4 insertions(+), 62 deletions(-)
diff --git a/drivers/net/intel/iavf/iavf_rxtx.c b/drivers/net/intel/iavf/iavf_rxtx.c
index 4ff6c18dc4..fabccc89bf 100644
--- a/drivers/net/intel/iavf/iavf_rxtx.c
+++ b/drivers/net/intel/iavf/iavf_rxtx.c
@@ -1507,16 +1507,6 @@ iavf_recv_pkts_flex_rxd(void *rx_queue,
rx_ring = rxq->rx_flex_ring;
ptype_tbl = rxq->iavf_vsi->adapter->ptype_tbl;
- if (rxq->offloads & RTE_ETH_RX_OFFLOAD_TIMESTAMP) {
- uint64_t sw_cur_time = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
-
- if (sw_cur_time - rxq->hw_time_update > 4) {
- if (iavf_get_phc_time(rxq))
- PMD_DRV_LOG(ERR, "get physical time failed");
- rxq->hw_time_update = sw_cur_time;
- }
- }
-
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_stat_err0 = rte_le_to_cpu_16(rxdp->wb.status_error0);
@@ -1585,7 +1575,6 @@ iavf_recv_pkts_flex_rxd(void *rx_queue,
rte_le_to_cpu_32(rxd.wb.flex_ts.ts_high));
rxq->phc_time = ts_ns;
- rxq->hw_time_update = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(rxm,
iavf_timestamp_dynfield_offset,
@@ -1627,16 +1616,6 @@ iavf_recv_scattered_pkts_flex_rxd(void *rx_queue, struct rte_mbuf **rx_pkts,
volatile union ci_rx_flex_desc *rxdp;
const uint32_t *ptype_tbl = rxq->iavf_vsi->adapter->ptype_tbl;
- if (rxq->offloads & RTE_ETH_RX_OFFLOAD_TIMESTAMP) {
- uint64_t sw_cur_time = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
-
- if (sw_cur_time - rxq->hw_time_update > 4) {
- if (iavf_get_phc_time(rxq))
- PMD_DRV_LOG(ERR, "get physical time failed");
- rxq->hw_time_update = sw_cur_time;
- }
- }
-
while (nb_rx < nb_pkts) {
rxdp = &rx_ring[rx_id];
rx_stat_err0 = rte_le_to_cpu_16(rxdp->wb.status_error0);
@@ -1755,7 +1734,6 @@ iavf_recv_scattered_pkts_flex_rxd(void *rx_queue, struct rte_mbuf **rx_pkts,
rte_le_to_cpu_32(rxd.wb.flex_ts.ts_high));
rxq->phc_time = ts_ns;
- rxq->hw_time_update = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(first_seg,
iavf_timestamp_dynfield_offset,
@@ -1969,16 +1947,6 @@ iavf_rx_scan_hw_ring_flex_rxd(struct ci_rx_queue *rxq,
if (!(stat_err0 & (1 << IAVF_RX_FLEX_DESC_STATUS0_DD_S)))
return 0;
- if (rxq->offloads & RTE_ETH_RX_OFFLOAD_TIMESTAMP) {
- uint64_t sw_cur_time = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
-
- if (sw_cur_time - rxq->hw_time_update > 4) {
- if (iavf_get_phc_time(rxq))
- PMD_DRV_LOG(ERR, "get physical time failed");
- rxq->hw_time_update = sw_cur_time;
- }
- }
-
/* Scan LOOK_AHEAD descriptors at a time to determine which
* descriptors reference packets that are ready to be received.
*/
@@ -2041,8 +2009,6 @@ iavf_rx_scan_hw_ring_flex_rxd(struct ci_rx_queue *rxq,
rte_le_to_cpu_32(rxdp[j].wb.flex_ts.ts_high));
rxq->phc_time = ts_ns;
- rxq->hw_time_update = rte_get_timer_cycles() /
- (rte_get_timer_hz() / 1000);
*RTE_MBUF_DYNFIELD(mb,
iavf_timestamp_dynfield_offset,
diff --git a/drivers/net/intel/iavf/iavf_rxtx_vec_avx2.c b/drivers/net/intel/iavf/iavf_rxtx_vec_avx2.c
index db0462f0f5..9349646d55 100644
--- a/drivers/net/intel/iavf/iavf_rxtx_vec_avx2.c
+++ b/drivers/net/intel/iavf/iavf_rxtx_vec_avx2.c
@@ -514,18 +514,10 @@ _iavf_recv_raw_pkts_vec_avx2_flex_rxd(struct ci_rx_queue *rxq,
if (!(rxdp->wb.status_error0 &
rte_cpu_to_le_32(1 << IAVF_RX_FLEX_DESC_STATUS0_DD_S)))
return 0;
- bool is_tsinit = false;
uint8_t inflection_point = 0;
__m256i hw_low_last = _mm256_set_epi32(0, 0, 0, 0, 0, 0, 0, rxq->phc_time);
if (rxq->offloads & RTE_ETH_RX_OFFLOAD_TIMESTAMP) {
- uint64_t sw_cur_time = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
-
- if (unlikely(sw_cur_time - rxq->hw_time_update > 4)) {
- hw_low_last = _mm256_setzero_si256();
- is_tsinit = 1;
- } else {
- hw_low_last = _mm256_set_epi32(0, 0, 0, 0, 0, 0, 0, rxq->phc_time);
- }
+ hw_low_last = _mm256_set_epi32(0, 0, 0, 0, 0, 0, 0, rxq->phc_time);
}
/* constants used in processing loop */
@@ -1152,10 +1144,8 @@ _iavf_recv_raw_pkts_vec_avx2_flex_rxd(struct ci_rx_queue *rxq,
*RTE_MBUF_DYNFIELD(rx_pkts[i + 7],
iavf_timestamp_dynfield_offset, uint32_t *) = _mm256_extract_epi32(ts_low1, 7);
- if (unlikely(is_tsinit)) {
+ {
uint32_t in_timestamp;
- if (iavf_get_phc_time(rxq))
- PMD_DRV_LOG(ERR, "get physical time failed");
in_timestamp = *RTE_MBUF_DYNFIELD(rx_pkts[i + 0],
iavf_timestamp_dynfield_offset, uint32_t *);
rxq->phc_time = iavf_tstamp_convert_32b_64b(rxq->phc_time, in_timestamp);
@@ -1388,8 +1378,6 @@ _iavf_recv_raw_pkts_vec_avx2_flex_rxd(struct ci_rx_queue *rxq,
PMD_DRV_LOG(ERR, "invalid inflection point for rx timestamp");
break;
}
-
- rxq->hw_time_update = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
}
if (burst != IAVF_VPMD_DESCS_PER_LOOP_WIDE)
break;
diff --git a/drivers/net/intel/iavf/iavf_rxtx_vec_avx512.c b/drivers/net/intel/iavf/iavf_rxtx_vec_avx512.c
index 4e8bf94fa0..1bb3e9746b 100644
--- a/drivers/net/intel/iavf/iavf_rxtx_vec_avx512.c
+++ b/drivers/net/intel/iavf/iavf_rxtx_vec_avx512.c
@@ -615,18 +615,10 @@ _iavf_recv_raw_pkts_vec_avx512_flex_rxd(struct ci_rx_queue *rxq,
#ifdef IAVF_RX_TS_OFFLOAD
uint8_t inflection_point = 0;
- bool is_tsinit = false;
__m256i hw_low_last = _mm256_set_epi32(0, 0, 0, 0, 0, 0, 0, (uint32_t)rxq->phc_time);
if (rxq->offloads & RTE_ETH_RX_OFFLOAD_TIMESTAMP) {
- uint64_t sw_cur_time = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
-
- if (unlikely(sw_cur_time - rxq->hw_time_update > 4)) {
- hw_low_last = _mm256_setzero_si256();
- is_tsinit = 1;
- } else {
- hw_low_last = _mm256_set_epi32(0, 0, 0, 0, 0, 0, 0, (uint32_t)rxq->phc_time);
- }
+ hw_low_last = _mm256_set_epi32(0, 0, 0, 0, 0, 0, 0, (uint32_t)rxq->phc_time);
}
#endif
@@ -1343,11 +1335,9 @@ _iavf_recv_raw_pkts_vec_avx512_flex_rxd(struct ci_rx_queue *rxq,
*RTE_MBUF_DYNFIELD(rx_pkts[i + 7],
iavf_timestamp_dynfield_offset, uint32_t *) = _mm256_extract_epi32(ts_low1, 7);
- if (unlikely(is_tsinit)) {
+ {
uint32_t in_timestamp;
- if (iavf_get_phc_time(rxq))
- PMD_DRV_LOG(ERR, "get physical time failed");
in_timestamp = *RTE_MBUF_DYNFIELD(rx_pkts[i + 0],
iavf_timestamp_dynfield_offset, uint32_t *);
rxq->phc_time = iavf_tstamp_convert_32b_64b(rxq->phc_time, in_timestamp);
@@ -1584,8 +1574,6 @@ _iavf_recv_raw_pkts_vec_avx512_flex_rxd(struct ci_rx_queue *rxq,
PMD_DRV_LOG(ERR, "invalid inflection point for rx timestamp");
break;
}
-
- rxq->hw_time_update = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
}
#endif
if (burst != IAVF_VPMD_DESCS_PER_LOOP_WIDE)
--
2.47.1
^ permalink raw reply related
* RE: [EXTERNAL] Re: [PATCH v2 1/1] net/mana: add device reset support
From: Wei Hu @ 2026-05-20 6:32 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: Long Li, dpdk, Wei Hu
In-Reply-To: <SA1PR21MB66836A17CA584763D4D3F8A6CE002@SA1PR21MB6683.namprd21.prod.outlook.com>
Hi Stephen,
Many thanks for the review. I will send out a v3 to address most of them.
In the meantime, I would like to discuss further about two points from the
review comments. Please see my input below.
> -----Original Message-----
> 3. Spinlock held across blocking operations.
>
> priv->reset_ops_lock is rte_spinlock_t and is held across:
>
> - mana_reset_enter: mana_dev_stop, mana_dev_close,
> ibv_close_device,
> mana_mp_req_on_rxtx (5s IPC timeout)
> - mana_reset_exit_delay: ibv_close_device, mana_pci_probe,
> mana_mp_req_on_rxtx, mana_dev_start
>
> IPC with a 5-second timeout and device probe under a spinlock is
> not acceptable. Use a sleeping mutex (pthread_mutex_t initialized
> with PTHREAD_PROCESS_SHARED, since priv is in shared memory), or
> split the lock so the long operations run outside it.
>
The concern about holding rte_spinlock_t across blocking operations
is well noted. However, in the current design this works correctly, for
the following reasons:
1. mana_reset_enter runs on the EAL interrupt thread in response
to IBV_EVENT_DEVICE_FATAL. The device is already in a fatal
error state - the blocking IB verbs calls (ibv_close_device,
ibv_dealloc_pd) and IPC (mana_mp_req_on_rxtx) are unavoidable
teardown steps that must complete before recovery can begin.
2. The ethdev operations (configure, queue_setup, rss_hash_update,
etc.) all use rte_spinlock_trylock and return -EBUSY immediately
if the lock is held. They never spin-wait on the lock during
reset, so there is no starvation or latency impact on the
application threads.
3. dev_stop_lock and dev_close_lock use blocking rte_spinlock_lock,
but they first join the reset thread (which releases the lock
when done), so by the time they attempt to acquire the lock,
the reset path has already released it. IMHO, the actual spin-wait
is negligible.
4. The mana_reset_exit_delay path (re-probe, re-start) also holds
the lock, but this runs on a dedicated control thread where
blocking is acceptable. No data path or application thread is
affected.
The net effect is that the spinlock is held for an extended period
only on the interrupt thread and the reset control thread - neither
of which are latency sensitive. Application threads never block on
the spinlock.
That said, switching to pthread_mutex_t with PTHREAD_PROCESS_SHARED
is a valid improvement that would make the locking more conventional
and avoid any theoretical busy-wait concerns. I can make this change if
it is still desirable. I would like to know your thoughts on this.
> Warnings
> --------
>
> 5. Secondary data path has no synchronization with the doorbell unmap.
>
> MANA_MP_REQ_RESET_ENTER causes the secondary MP handler to
> munmap
> proc_priv->db_page and set it to NULL. The secondary's rx_burst /
> tx_burst protect themselves only with an atomic state check:
>
> rte_rcu_qsbr_thread_online(dstate_qsv, tid);
> if (state != MANA_DEV_ACTIVE || !db_page) { ... return 0; }
>
> But the qsbr only has primary threads registered (registration
> happens in mana_dev_configure, which never runs in secondary), so
> thread_online/offline in secondary do not block the primary's
> qsbr_check. The MP handler in secondary therefore unmaps db_page
> while a peer secondary lcore can still be inside rx/tx_burst with
> a stale pointer. Result: SIGSEGV on the next doorbell write.
>
> The secondary needs its own quiescence mechanism (a per-process
> qsbr or a reader-side rwlock around the data path that the MP
> handler acquires before unmap).
>
The concern is based on the assumption that "the qsbr only has
primary threads registered (registration happens in
mana_dev_configure, which never runs in secondary)". This seems
incorrect.
My understanding is when the primary starts, the total number of queues
for primary and all secondaries is already known, which is stored in
priv->num_queues by the time mana_dev_congiure() is called.
The RCU thread IDs are per-queue, not per-process. In mana_dev_configure,
I am assigning tids used in rte_rcu_qsbr_thread_online/offline as:
- RX path: tid = rxq->rxq_idx (0 to num_queues-1)
- TX path: tid = num_queues + txq_idx (num_queues to 2*num_queues-1)
These queue structures (rxq, txq) live in shared hugepage memory
via dev->data->rx_queues[] and dev->data->tx_queues[]. The QSV
itself (priv->dev_state_qsv) is also in shared hugepage memory
(allocated with rte_zmalloc_socket). So both primary and secondary
processes operate on the same QSV with the same thread IDs.
The rte_rcu_qsbr_thread_register call in mana_dev_configure
registers IDs 0..2*num_queues-1, which covers every queue
regardless of which process polls it. I think in DPDK
a given queue is polled by only one lcore at a time, so whether
that lcore is in the primary or a secondary process, it uses the
same tid for the same queue.
The reset synchronization flow is:
1. mana_reset_enter sets dev_state = MANA_DEV_RESET_ENTER
2. Calls rte_rcu_qsbr_check to wait for all registered threads
3. Secondary lcores polling rx/tx_burst see dev_state != ACTIVE,
call rte_rcu_qsbr_thread_offline, and return 0
4. Only after ALL threads (primary and secondary) are quiescent
does rte_rcu_qsbr_check return success
5. Then mana_reset_enter proceeds to stop queues and send
MANA_MP_REQ_RESET_ENTER to unmap secondary doorbells
At step 5, no secondary lcore can be inside the data path with
a stale doorbell pointer - the RCU quiescence check at step 2-4
guarantees this. The secondary MP handler unmaps db_page only
after all secondary data path threads have exited.
So, in my opinion, this works correctly for secondaries as well.
Thanks,
Wei
^ permalink raw reply
* [V0 1/1] Add VXLAN TSO function
From: Feifei Wang @ 2026-05-20 6:41 UTC (permalink / raw)
To: dev; +Cc: Feifei Wang
In-Reply-To: <20260520064112.705-1-wff_light@vip.163.com>
The RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO flag is added to support the VXLAN TSO function
---
drivers/net/hinic3/hinic3_ethdev.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/hinic3/hinic3_ethdev.c b/drivers/net/hinic3/hinic3_ethdev.c
index f4eb788..fbadb1e 100644
--- a/drivers/net/hinic3/hinic3_ethdev.c
+++ b/drivers/net/hinic3/hinic3_ethdev.c
@@ -696,7 +696,7 @@ hinic3_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
RTE_ETH_TX_OFFLOAD_VLAN_INSERT | RTE_ETH_TX_OFFLOAD_IPV4_CKSUM |
RTE_ETH_TX_OFFLOAD_UDP_CKSUM | RTE_ETH_TX_OFFLOAD_TCP_CKSUM |
RTE_ETH_TX_OFFLOAD_SCTP_CKSUM |
- RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM |
+ RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM | RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO |
RTE_ETH_TX_OFFLOAD_TCP_TSO | RTE_ETH_TX_OFFLOAD_MULTI_SEGS;
if (nic_dev->feature_cap & NIC_F_HTN_CMDQ)
hinic3_dev_tnl_tso_support(info, nic_dev);
--
2.47.0.windows.2
^ permalink raw reply related
* [V0 0/1] Add VXLAN TSO function
From: Feifei Wang @ 2026-05-20 6:41 UTC (permalink / raw)
To: dev
V0:The RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO flag is added to support the VXLAN TSO function
Feifei Wang (1):
Add VXLAN TSO function
drivers/net/hinic3/hinic3_ethdev.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--
2.47.0.windows.2
^ permalink raw reply
* [V1 0/1] Add VXLAN TSO function
From: Feifei Wang @ 2026-05-20 6:58 UTC (permalink / raw)
To: dev
V0: The RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO flag is added to support the VXLAN TSO function
V1: Add sign-off in the commit message
Feifei Wang (1):
net/hinic3: Add VXLAN TSO function
drivers/net/hinic3/hinic3_ethdev.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--
2.47.0.windows.2
^ permalink raw reply
* [V1 1/1] net/hinic3: Add VXLAN TSO function
From: Feifei Wang @ 2026-05-20 6:58 UTC (permalink / raw)
To: dev; +Cc: Feifei Wang
In-Reply-To: <20260520065817.931-1-wff_light@vip.163.com>
The RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO flag is added to support the VXLAN TSO function
Signed-off-by: Feifei Wang <wangfeifei40@huawei.com>
---
drivers/net/hinic3/hinic3_ethdev.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/hinic3/hinic3_ethdev.c b/drivers/net/hinic3/hinic3_ethdev.c
index f4eb788..fbadb1e 100644
--- a/drivers/net/hinic3/hinic3_ethdev.c
+++ b/drivers/net/hinic3/hinic3_ethdev.c
@@ -696,7 +696,7 @@ hinic3_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
RTE_ETH_TX_OFFLOAD_VLAN_INSERT | RTE_ETH_TX_OFFLOAD_IPV4_CKSUM |
RTE_ETH_TX_OFFLOAD_UDP_CKSUM | RTE_ETH_TX_OFFLOAD_TCP_CKSUM |
RTE_ETH_TX_OFFLOAD_SCTP_CKSUM |
- RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM |
+ RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM | RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO |
RTE_ETH_TX_OFFLOAD_TCP_TSO | RTE_ETH_TX_OFFLOAD_MULTI_SEGS;
if (nic_dev->feature_cap & NIC_F_HTN_CMDQ)
hinic3_dev_tnl_tso_support(info, nic_dev);
--
2.47.0.windows.2
^ permalink raw reply related
* Re: [PATCH 0/2] enhance telemetry list endpoint with device name
From: fengchengwen @ 2026-05-20 7:31 UTC (permalink / raw)
To: Morten Brørup, thomas, stephen; +Cc: dev
In-Reply-To: <98CBD80474FA8B44BF855DF32C47DC35F65885@smartserver.smartshare.dk>
On 5/20/2026 1:40 PM, Morten Brørup wrote:
>> From: Chengwen Feng [mailto:fengchengwen@huawei.com]
>> Sent: Wednesday, 20 May 2026 05.57
>>
>> Currently, the /dmadev/list and /ethdev/list telemetry endpoints return
>> only integer IDs, making it hard to identify devices. This series
>> changes
>> both to output strings in "ID NAME" format for better usability.
>
> For machine reading of the JSON output, it would be better returning an object with an integer and a string field, {ID, "NAME"}.
The TEL_DICT could do {"ID", "NAME"}, which like:
"/ethdev/list": {
"0": "0000:7d:00.0",
"1": "0000:7d:00.1"
}
Maybe we could add one TEL_INT_DICT which is int-value pairs, we may get:
"/ethdev/list": {
0: "0000:7d:00.0",
1: "0000:7d:00.1"
}
I prefer the first one, However, the capacity is reduced from 512 (RTE_TEL_MAX_ARRAY_ENTRIES) to 256 (RTE_TEL_MAX_DICT_ENTRIES), but I think it is enough.
What's your opinion?
>
>
^ permalink raw reply
* Re: [RFC PATCH 0/8] remove use of rte_memcpy from net/intel
From: Bruce Richardson @ 2026-05-20 7:44 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev
In-Reply-To: <20260519124332.5e68fd8f@phoenix.local>
On Tue, May 19, 2026 at 12:43:32PM -0700, Stephen Hemminger wrote:
> On Tue, 19 May 2026 16:05:57 +0000
> Bruce Richardson <bruce.richardson@intel.com> wrote:
>
> > This RFC proposed to replace all instances of rte_memcpy in Intel
> > (and former-Intel) net drivers with just regular memcpy. This is
> > done on the basis that the memcpy use is not datapath, but is used
> > for flow configuration, virt-channel (to firmware or PF) messaging
> > and other control path functions.
>
> Makes sense. You might also want to look for where structure
> assignment can be used instead of memcpy. Keeping data types
> is a good thing.
Yes, it would be nice to use in places. However, it's not a mechanical
change so I've not taken the time to do the analysis. Just a quick set of
sed replacements for this RFC.
/Bruce
^ permalink raw reply
* Re: [PATCH 0/2] enhance telemetry list endpoint with device name
From: Bruce Richardson @ 2026-05-20 7:49 UTC (permalink / raw)
To: fengchengwen; +Cc: Morten Brørup, thomas, stephen, dev
In-Reply-To: <689a32bf-b6e2-4646-8073-cd1f895b692b@huawei.com>
On Wed, May 20, 2026 at 03:31:57PM +0800, fengchengwen wrote:
> On 5/20/2026 1:40 PM, Morten Brørup wrote:
> >> From: Chengwen Feng [mailto:fengchengwen@huawei.com]
> >> Sent: Wednesday, 20 May 2026 05.57
> >>
> >> Currently, the /dmadev/list and /ethdev/list telemetry endpoints return
> >> only integer IDs, making it hard to identify devices. This series
> >> changes
> >> both to output strings in "ID NAME" format for better usability.
> >
> > For machine reading of the JSON output, it would be better returning an object with an integer and a string field, {ID, "NAME"}.
>
> The TEL_DICT could do {"ID", "NAME"}, which like:
> "/ethdev/list": {
> "0": "0000:7d:00.0",
> "1": "0000:7d:00.1"
> }
>
> Maybe we could add one TEL_INT_DICT which is int-value pairs, we may get:
> "/ethdev/list": {
> 0: "0000:7d:00.0",
> 1: "0000:7d:00.1"
> }
>
> I prefer the first one, However, the capacity is reduced from 512 (RTE_TEL_MAX_ARRAY_ENTRIES) to 256 (RTE_TEL_MAX_DICT_ENTRIES), but I think it is enough.
>
> What's your opinion?
>
I'm not sure about this change at all. This change is only relevant for
those using the script interactively, for any other use, I would expect the
the /ethdev/list call would be followed by the /ethdev/info calls for each
port to get the name. That was the basic design in mind for this, the list
call was purely to provide the ids, any other info you make separate calls
for.
Also, while not officially part of the ABI of DPDK, I think it would be
wrong to go changing the types of the returned data from this /ethdev/list
call. Any user-written interfaces to telemetry will be relying on the
current behaviour to list and query ports. If you really want to have an
easy way to get the names of the ports, I suggest adding instead an
"/ethdev/list_names" API, which can either return the objects above, or
else simply an array of names.
/Bruce
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox