* [PATCH 0/1] mbuf: add optional dynfield3 storage
@ 2026-09-24 19:37 Randy L Tice
2026-09-24 19:37 ` [PATCH 1/1] " Randy L Tice
2026-09-25 19:31 ` [PATCH v2 0/1] " Randy L Tice
0 siblings, 2 replies; 24+ messages in thread
From: Randy L Tice @ 2026-09-24 19:37 UTC (permalink / raw)
To: dev; +Cc: mb, bruce.richardson
This patch adds build-time optional backing storage for the Dynamic
Mbuf Fields allocator.
The new mbuf_dynfield3_cnt Meson option reserves an additional
cache-line-aligned area at the end of struct rte_mbuf. The default value
is zero, so the default mbuf layout is unchanged. A non-zero value makes
the area available to the dynamic mbuf field allocator and to the normal
dynamic-field copy path.
The enabled configuration increases sizeof(struct rte_mbuf). The release
note calls out mempool/octeontx as a known fixed-size assumption that
needs follow-up before that driver can be built with a non-zero setting.
Validation:
- devtools/check-git-log.sh -n1
- devtools/checkpatches.sh -n1
- devtools/checkpatches.sh on exported patch
- devtools/get-maintainer.sh on exported patch
- Meson setup with default mbuf_dynfield3_cnt
- Meson setup with invalid -Dmbuf_dynfield3_cnt=1, which failed at
configure time with the expected cache-line multiple error
- native Meson/Ninja build with default mbuf_dynfield3_cnt
- native Meson/Ninja build with -Dmbuf_dynfield3_cnt=0
- native Meson/Ninja build with -Dmbuf_dynfield3_cnt=8 and
-Ddisable_drivers=mempool/octeontx
Randy Tice (1):
mbuf: add optional dynfield3 storage
app/test/test_mbuf.c | 5 +++--
config/meson.build | 10 ++++++++++
doc/guides/rel_notes/release_26_11.rst | 15 +++++++++++++++
lib/mbuf/rte_mbuf.h | 3 +++
lib/mbuf/rte_mbuf_core.h | 16 ++++++++++++++++
lib/mbuf/rte_mbuf_dyn.c | 3 +++
meson_options.txt | 2 ++
7 files changed, 52 insertions(+), 2 deletions(-)
--
2.35.6
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 1/1] mbuf: add optional dynfield3 storage
2026-09-24 19:37 [PATCH 0/1] mbuf: add optional dynfield3 storage Randy L Tice
@ 2026-09-24 19:37 ` Randy L Tice
2026-09-25 9:48 ` Morten Brørup
2026-09-25 19:31 ` [PATCH v2 0/1] " Randy L Tice
1 sibling, 1 reply; 24+ messages in thread
From: Randy L Tice @ 2026-09-24 19:37 UTC (permalink / raw)
To: dev; +Cc: mb, bruce.richardson
From: Randy L Tice <rtice@cisco.com>
Date: Thu, 03 Sep 2026 09:13:28 -0400
Add build-time support for optional cache-line-aligned dynamic-field
storage at the end of struct rte_mbuf.
The mbuf_dynfield3_cnt Meson option sets RTE_MBUF_DYNFIELD3_CNT in
rte_build_config.h. A non-zero value enables the extra area.
When enabled, dynfield3 is made available to the mbuf dynamic field
allocator and is copied by the generic mbuf dynamic-field copy helper.
This extends the existing Dynamic Mbuf Fields backing storage without
changing the default mbuf layout.
Validate that the configured element count is non-negative and that the
reserved space is a multiple of the cache line size.
Signed-off-by: Randy L Tice <rtice@cisco.com>
---
app/test/test_mbuf.c | 5 +++--
config/meson.build | 10 ++++++++++
doc/guides/rel_notes/release_26_11.rst | 15 +++++++++++++++
lib/mbuf/rte_mbuf.h | 3 +++
lib/mbuf/rte_mbuf_core.h | 16 ++++++++++++++++
lib/mbuf/rte_mbuf_dyn.c | 3 +++
meson_options.txt | 2 ++
7 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/app/test/test_mbuf.c b/app/test/test_mbuf.c
index db23259745..9f3b396a61 100644
--- a/app/test/test_mbuf.c
+++ b/app/test/test_mbuf.c
@@ -2776,8 +2776,9 @@ test_mbuf(void)
struct rte_mempool *pktmbuf_pool = NULL;
struct rte_mempool *pktmbuf_pool2 = NULL;
-
- RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) != RTE_CACHE_LINE_MIN_SIZE * 2);
+ RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) !=
+ RTE_CACHE_LINE_MIN_SIZE * 2 +
+ RTE_MBUF_DYNFIELD3_SIZE);
/* create pktmbuf pool if it does not exist */
pktmbuf_pool = rte_pktmbuf_pool_create("test_pktmbuf_pool",
diff --git a/config/meson.build b/config/meson.build
index 344f68822b..e500a7b075 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -384,6 +384,11 @@ dpdk_conf.set('RTE_LIBEAL_USE_HPET', get_option('use_hpet'))
dpdk_conf.set('RTE_ENABLE_STDATOMIC', get_option('enable_stdatomic'))
dpdk_conf.set('RTE_ENABLE_TRACE_FP', get_option('enable_trace_fp'))
dpdk_conf.set('RTE_PKTMBUF_HEADROOM', get_option('pkt_mbuf_headroom'))
+mbuf_dynfield3_cnt = get_option('mbuf_dynfield3_cnt')
+if mbuf_dynfield3_cnt < 0
+ error('mbuf_dynfield3_cnt must be greater than or equal to 0')
+endif
+dpdk_conf.set('RTE_MBUF_DYNFIELD3_CNT', mbuf_dynfield3_cnt)
# values which have defaults which may be overridden
dpdk_conf.set('RTE_MAX_VFIO_GROUPS', 64)
dpdk_conf.set('RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB', 64)
@@ -395,6 +400,11 @@ dpdk_conf.set10('RTE_IOVA_IN_MBUF', get_option('enable_iova_as_pa'))
compile_time_cpuflags = []
subdir(arch_subdir)
+mbuf_dynfield3_size = mbuf_dynfield3_cnt * cc.sizeof('uintptr_t',
+ prefix: '#include <stdint.h>')
+if mbuf_dynfield3_size % dpdk_conf.get('RTE_CACHE_LINE_SIZE') != 0
+ error('mbuf_dynfield3_cnt must reserve a multiple of RTE_CACHE_LINE_SIZE')
+endif
dpdk_conf.set('RTE_COMPILE_TIME_CPUFLAGS', ','.join(compile_time_cpuflags))
# apply cross-specific options
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index dec96ccbc7..abe82e7e3f 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -60,6 +60,13 @@ New Features
Added the experimental ``rte_cpu_socket_id()`` function
to map an OS logical CPU ID to the NUMA socket containing that CPU.
+* **Added optional extra mbuf dynamic-field storage.**
+
+ Added ``mbuf_dynfield3_cnt`` build option to reserve a
+ cache-line-aligned ``dynfield3`` area in ``struct rte_mbuf``.
+ The value is defined as ``RTE_MBUF_DYNFIELD3_CNT`` in
+ ``rte_build_config.h``; a non-zero value enables the extra area.
+
* **Added TPID support to VLAN tag insertion.**
Added ``rte_vlan_insert_tpid()`` to the net library.
@@ -338,6 +345,14 @@ Known Issues
Also, make sure to start the actual text at the margin.
=======================================================
+* **Some drivers may require changes for enlarged mbufs.**
+
+ Enabling ``mbuf_dynfield3_cnt`` with a non-zero value increases
+ ``sizeof(struct rte_mbuf)``. Drivers or applications that assume a
+ fixed mbuf size may require follow-up changes. The
+ ``mempool/octeontx`` driver currently asserts that
+ ``sizeof(struct rte_mbuf)`` does not exceed its fixed buffer offset.
+
Tested Platforms
----------------
diff --git a/lib/mbuf/rte_mbuf.h b/lib/mbuf/rte_mbuf.h
index 60ec8158cd..6a567a9e43 100644
--- a/lib/mbuf/rte_mbuf.h
+++ b/lib/mbuf/rte_mbuf.h
@@ -1231,6 +1231,9 @@ rte_mbuf_dynfield_copy(struct rte_mbuf *mdst, const struct rte_mbuf *msrc)
mdst->dynfield2 = msrc->dynfield2;
#endif
memcpy(&mdst->dynfield1, msrc->dynfield1, sizeof(mdst->dynfield1));
+#if RTE_MBUF_DYNFIELD3_CNT > 0
+ memcpy(&mdst->dynfield3, msrc->dynfield3, sizeof(mdst->dynfield3));
+#endif
}
/* internal */
diff --git a/lib/mbuf/rte_mbuf_core.h b/lib/mbuf/rte_mbuf_core.h
index 98b0bd9ca7..bb57397bb6 100644
--- a/lib/mbuf/rte_mbuf_core.h
+++ b/lib/mbuf/rte_mbuf_core.h
@@ -17,6 +17,7 @@
*/
#include <stdalign.h>
+#include <stddef.h>
#include <stdint.h>
#include <rte_byteorder.h>
@@ -686,8 +687,23 @@ struct __rte_cache_aligned rte_mbuf {
uint16_t timesync;
uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
+
+#if RTE_MBUF_DYNFIELD3_CNT > 0
+ alignas(RTE_CACHE_LINE_SIZE)
+ uintptr_t dynfield3[RTE_MBUF_DYNFIELD3_CNT];
+ /**< Reserved for dynamic fields. */
+#endif /* RTE_MBUF_DYNFIELD3_CNT > 0 */
};
+#define RTE_MBUF_DYNFIELD3_SIZE \
+ (RTE_MBUF_DYNFIELD3_CNT * sizeof(uintptr_t))
+#if RTE_MBUF_DYNFIELD3_CNT > 0
+#define RTE_MBUF_DYNFIELD3_OFFSET \
+ offsetof(struct rte_mbuf, dynfield3)
+#else
+#define RTE_MBUF_DYNFIELD3_OFFSET 0
+#endif
+
/**
* Function typedef of callback to free externally attached buffer.
*/
diff --git a/lib/mbuf/rte_mbuf_dyn.c b/lib/mbuf/rte_mbuf_dyn.c
index 5987c9dee8..dcbe0a8416 100644
--- a/lib/mbuf/rte_mbuf_dyn.c
+++ b/lib/mbuf/rte_mbuf_dyn.c
@@ -135,6 +135,9 @@ init_shared_mem(void)
#if !RTE_IOVA_IN_MBUF
mark_free(dynfield2);
#endif
+#if RTE_MBUF_DYNFIELD3_CNT > 0
+ mark_free(dynfield3);
+#endif
/* init free_flags */
for (mask = RTE_MBUF_F_FIRST_FREE; mask <= RTE_MBUF_F_LAST_FREE; mask <<= 1)
diff --git a/meson_options.txt b/meson_options.txt
index e28d24054c..337f9e5e49 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -44,6 +44,8 @@ option('max_numa_nodes', type: 'string', value: 'default', description:
'Set the highest NUMA node supported by EAL; "default" is different per-arch, "detect" detects the highest NUMA node on the build machine.')
option('enable_iova_as_pa', type: 'boolean', value: true, description:
'Support the use of physical addresses for IO addresses, such as used by UIO or VFIO in no-IOMMU mode. When disabled, DPDK can only run with IOMMU support for address mappings, but will have more space available in the mbuf structure.')
+option('mbuf_dynfield3_cnt', type: 'integer', value: 0, description:
+ 'Size of optional extra mbuf dynamic field area, in uintptr_t units.')
option('mbuf_refcnt_atomic', type: 'boolean', value: true, description:
'Atomically access the mbuf refcnt.')
option('platform', type: 'string', value: 'native', description:
--
2.35.6
^ permalink raw reply related [flat|nested] 24+ messages in thread
* RE: [PATCH 1/1] mbuf: add optional dynfield3 storage
2026-09-24 19:37 ` [PATCH 1/1] " Randy L Tice
@ 2026-09-25 9:48 ` Morten Brørup
0 siblings, 0 replies; 24+ messages in thread
From: Morten Brørup @ 2026-09-25 9:48 UTC (permalink / raw)
To: Randy L Tice, dev; +Cc: bruce.richardson
> From: Randy L Tice [mailto:rtice@cisco.com]
> Sent: Thursday, 24 September 2026 21.38
>
> Add build-time support for optional cache-line-aligned dynamic-field
> storage at the end of struct rte_mbuf.
>
> The mbuf_dynfield3_cnt Meson option sets RTE_MBUF_DYNFIELD3_CNT in
> rte_build_config.h. A non-zero value enables the extra area.
>
> When enabled, dynfield3 is made available to the mbuf dynamic field
> allocator and is copied by the generic mbuf dynamic-field copy helper.
> This extends the existing Dynamic Mbuf Fields backing storage without
> changing the default mbuf layout.
Sorry about not responding to your earlier email... it got drowned in my TODO list.
You are absolutely on the right track with this patch!
Two high-level comments:
1. Configuration of the field's size.
For 32/64 bit CPU architecture independence, please make it configurable by size (in bytes), instead of by count of pointers.
Or provide both configuration options (byte size, and pointer count), and calculate the actual size as the sum = sz + cnt * sizeof(uintptr_t).
IMHO, it is perfectly reasonable to require that the actual size (i.e. the sum) is a multiple of sizeof(uint64_t).
Instead of using the uintptr_t type in the mbuf structure:
uintptr_t dynfield3[RTE_MBUF_DYNFIELD3_CNT];
Please use the uint64_t type:
uint64_t dynfield3[RTE_MBUF_DYNFIELD3_CNT];
2. Don't copy dynfield3 on mbuf copy/clone. (Feature creep - optional improvement)
I recall you didn't want these dynamic fields to be copied on mbuf copy and clone.
You can implement that as follows:
Somewhere at the top of rte_mbuf_dyn.h, add:
/* Flags for use in struct rte_mbuf_dynfield. */
/**
* Isolate the dynamic field to each mbuf.
* Do not reset, copy or overwrite it by any operation performed by the mbuf library.
*/
#define RTE_MBUF_DYN_F_ISOLATE 1
And, in rte_mbuf_dyn.c, in the rte_mbuf_dynfield_register[_offset]() functions, if (params->flags & RTE_MBUF_DYN_F_ISOLATE) is set, use the dynfield3 region for the dynamic field being registered, otherwise use the dynfield1/dynfield2 region.
Keep the existing behavior (copy on copy/clone) for the existing "normal" dynamic fields stored in dynfield1/dynfield2 region, i.e. copy them on mbuf copy/clone operations.
And use the dynfield3 region exclusively for "isolated" dynamic fields; do not reset or copy the mbuf's dynfield3 array anywhere in the mbuf library.
By reserving fixed regions in the mbuf for respectively "normal" and "isolated" dynamic fields, the mbuf library can perform mbuf copy/clone operations without having to go through the list of dynamic fields to determine if each field must be copied or not.
If you implement this "isolation" detail, maybe you can come up with a more saying name than "dynfield3", e.g. "dynfield_isolated".
-Morten
>
> Validate that the configured element count is non-negative and that the
> reserved space is a multiple of the cache line size.
>
> Signed-off-by: Randy L Tice <rtice@cisco.com>
> ---
> app/test/test_mbuf.c | 5 +++--
> config/meson.build | 10 ++++++++++
> doc/guides/rel_notes/release_26_11.rst | 15 +++++++++++++++
> lib/mbuf/rte_mbuf.h | 3 +++
> lib/mbuf/rte_mbuf_core.h | 16 ++++++++++++++++
> lib/mbuf/rte_mbuf_dyn.c | 3 +++
> meson_options.txt | 2 ++
> 7 files changed, 52 insertions(+), 2 deletions(-)
>
> diff --git a/app/test/test_mbuf.c b/app/test/test_mbuf.c
> index db23259745..9f3b396a61 100644
> --- a/app/test/test_mbuf.c
> +++ b/app/test/test_mbuf.c
> @@ -2776,8 +2776,9 @@ test_mbuf(void)
> struct rte_mempool *pktmbuf_pool = NULL;
> struct rte_mempool *pktmbuf_pool2 = NULL;
>
> -
> - RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) !=
> RTE_CACHE_LINE_MIN_SIZE * 2);
> + RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) !=
> + RTE_CACHE_LINE_MIN_SIZE * 2 +
> + RTE_MBUF_DYNFIELD3_SIZE);
>
> /* create pktmbuf pool if it does not exist */
> pktmbuf_pool = rte_pktmbuf_pool_create("test_pktmbuf_pool",
> diff --git a/config/meson.build b/config/meson.build
> index 344f68822b..e500a7b075 100644
> --- a/config/meson.build
> +++ b/config/meson.build
> @@ -384,6 +384,11 @@ dpdk_conf.set('RTE_LIBEAL_USE_HPET',
> get_option('use_hpet'))
> dpdk_conf.set('RTE_ENABLE_STDATOMIC', get_option('enable_stdatomic'))
> dpdk_conf.set('RTE_ENABLE_TRACE_FP', get_option('enable_trace_fp'))
> dpdk_conf.set('RTE_PKTMBUF_HEADROOM', get_option('pkt_mbuf_headroom'))
> +mbuf_dynfield3_cnt = get_option('mbuf_dynfield3_cnt')
> +if mbuf_dynfield3_cnt < 0
> + error('mbuf_dynfield3_cnt must be greater than or equal to 0')
> +endif
> +dpdk_conf.set('RTE_MBUF_DYNFIELD3_CNT', mbuf_dynfield3_cnt)
> # values which have defaults which may be overridden
> dpdk_conf.set('RTE_MAX_VFIO_GROUPS', 64)
> dpdk_conf.set('RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB', 64)
> @@ -395,6 +400,11 @@ dpdk_conf.set10('RTE_IOVA_IN_MBUF',
> get_option('enable_iova_as_pa'))
>
> compile_time_cpuflags = []
> subdir(arch_subdir)
> +mbuf_dynfield3_size = mbuf_dynfield3_cnt * cc.sizeof('uintptr_t',
> + prefix: '#include <stdint.h>')
> +if mbuf_dynfield3_size % dpdk_conf.get('RTE_CACHE_LINE_SIZE') != 0
> + error('mbuf_dynfield3_cnt must reserve a multiple of
> RTE_CACHE_LINE_SIZE')
> +endif
> dpdk_conf.set('RTE_COMPILE_TIME_CPUFLAGS',
> ','.join(compile_time_cpuflags))
>
> # apply cross-specific options
> diff --git a/doc/guides/rel_notes/release_26_11.rst
> b/doc/guides/rel_notes/release_26_11.rst
> index dec96ccbc7..abe82e7e3f 100644
> --- a/doc/guides/rel_notes/release_26_11.rst
> +++ b/doc/guides/rel_notes/release_26_11.rst
> @@ -60,6 +60,13 @@ New Features
> Added the experimental ``rte_cpu_socket_id()`` function
> to map an OS logical CPU ID to the NUMA socket containing that CPU.
>
> +* **Added optional extra mbuf dynamic-field storage.**
> +
> + Added ``mbuf_dynfield3_cnt`` build option to reserve a
> + cache-line-aligned ``dynfield3`` area in ``struct rte_mbuf``.
> + The value is defined as ``RTE_MBUF_DYNFIELD3_CNT`` in
> + ``rte_build_config.h``; a non-zero value enables the extra area.
> +
> * **Added TPID support to VLAN tag insertion.**
>
> Added ``rte_vlan_insert_tpid()`` to the net library.
> @@ -338,6 +345,14 @@ Known Issues
> Also, make sure to start the actual text at the margin.
> =======================================================
>
> +* **Some drivers may require changes for enlarged mbufs.**
> +
> + Enabling ``mbuf_dynfield3_cnt`` with a non-zero value increases
> + ``sizeof(struct rte_mbuf)``. Drivers or applications that assume a
> + fixed mbuf size may require follow-up changes. The
> + ``mempool/octeontx`` driver currently asserts that
> + ``sizeof(struct rte_mbuf)`` does not exceed its fixed buffer offset.
> +
>
> Tested Platforms
> ----------------
> diff --git a/lib/mbuf/rte_mbuf.h b/lib/mbuf/rte_mbuf.h
> index 60ec8158cd..6a567a9e43 100644
> --- a/lib/mbuf/rte_mbuf.h
> +++ b/lib/mbuf/rte_mbuf.h
> @@ -1231,6 +1231,9 @@ rte_mbuf_dynfield_copy(struct rte_mbuf *mdst,
> const struct rte_mbuf *msrc)
> mdst->dynfield2 = msrc->dynfield2;
> #endif
> memcpy(&mdst->dynfield1, msrc->dynfield1, sizeof(mdst-
> >dynfield1));
> +#if RTE_MBUF_DYNFIELD3_CNT > 0
> + memcpy(&mdst->dynfield3, msrc->dynfield3, sizeof(mdst-
> >dynfield3));
> +#endif
> }
>
> /* internal */
> diff --git a/lib/mbuf/rte_mbuf_core.h b/lib/mbuf/rte_mbuf_core.h
> index 98b0bd9ca7..bb57397bb6 100644
> --- a/lib/mbuf/rte_mbuf_core.h
> +++ b/lib/mbuf/rte_mbuf_core.h
> @@ -17,6 +17,7 @@
> */
>
> #include <stdalign.h>
> +#include <stddef.h>
> #include <stdint.h>
>
> #include <rte_byteorder.h>
> @@ -686,8 +687,23 @@ struct __rte_cache_aligned rte_mbuf {
> uint16_t timesync;
>
> uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
> +
> +#if RTE_MBUF_DYNFIELD3_CNT > 0
> + alignas(RTE_CACHE_LINE_SIZE)
> + uintptr_t dynfield3[RTE_MBUF_DYNFIELD3_CNT];
> + /**< Reserved for dynamic fields. */
> +#endif /* RTE_MBUF_DYNFIELD3_CNT > 0 */
> };
>
> +#define RTE_MBUF_DYNFIELD3_SIZE \
> + (RTE_MBUF_DYNFIELD3_CNT * sizeof(uintptr_t))
> +#if RTE_MBUF_DYNFIELD3_CNT > 0
> +#define RTE_MBUF_DYNFIELD3_OFFSET \
> + offsetof(struct rte_mbuf, dynfield3)
> +#else
> +#define RTE_MBUF_DYNFIELD3_OFFSET 0
> +#endif
> +
> /**
> * Function typedef of callback to free externally attached buffer.
> */
> diff --git a/lib/mbuf/rte_mbuf_dyn.c b/lib/mbuf/rte_mbuf_dyn.c
> index 5987c9dee8..dcbe0a8416 100644
> --- a/lib/mbuf/rte_mbuf_dyn.c
> +++ b/lib/mbuf/rte_mbuf_dyn.c
> @@ -135,6 +135,9 @@ init_shared_mem(void)
> #if !RTE_IOVA_IN_MBUF
> mark_free(dynfield2);
> #endif
> +#if RTE_MBUF_DYNFIELD3_CNT > 0
> + mark_free(dynfield3);
> +#endif
>
> /* init free_flags */
> for (mask = RTE_MBUF_F_FIRST_FREE; mask <=
> RTE_MBUF_F_LAST_FREE; mask <<= 1)
> diff --git a/meson_options.txt b/meson_options.txt
> index e28d24054c..337f9e5e49 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -44,6 +44,8 @@ option('max_numa_nodes', type: 'string', value:
> 'default', description:
> 'Set the highest NUMA node supported by EAL; "default" is
> different per-arch, "detect" detects the highest NUMA node on the build
> machine.')
> option('enable_iova_as_pa', type: 'boolean', value: true, description:
> 'Support the use of physical addresses for IO addresses, such
> as used by UIO or VFIO in no-IOMMU mode. When disabled, DPDK can only
> run with IOMMU support for address mappings, but will have more space
> available in the mbuf structure.')
> +option('mbuf_dynfield3_cnt', type: 'integer', value: 0, description:
> + 'Size of optional extra mbuf dynamic field area, in uintptr_t
> units.')
> option('mbuf_refcnt_atomic', type: 'boolean', value: true,
> description:
> 'Atomically access the mbuf refcnt.')
> option('platform', type: 'string', value: 'native', description:
> --
> 2.35.6
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v2 0/1] mbuf: add optional dynfield3 storage
2026-09-24 19:37 [PATCH 0/1] mbuf: add optional dynfield3 storage Randy L Tice
2026-09-24 19:37 ` [PATCH 1/1] " Randy L Tice
@ 2026-09-25 19:31 ` Randy L Tice
2026-09-25 19:31 ` [PATCH v2 1/1] " Randy L Tice
2026-09-28 18:16 ` [PATCH v3 0/1] mbuf: add optional no-copy dynamic field storage Randy L Tice
1 sibling, 2 replies; 24+ messages in thread
From: Randy L Tice @ 2026-09-25 19:31 UTC (permalink / raw)
To: dev; +Cc: Morten Brørup, Bruce Richardson
This version updates the dynfield3 proposal based on review feedback from
Morten.
The optional area is now configured by byte size instead of pointer count,
so the configured value is independent of 32-bit or 64-bit builds. The
storage is represented as uint64_t elements, and the configured size must
be a multiple of sizeof(uint64_t) and reserve a multiple of the cache line
size.
The extra area remains part of the dynamic mbuf field allocator. To
preserve the historical behavior expected for dynamic fields while still
supporting per-mbuf private metadata use cases, copying this area is
controlled by a separate build option. The default does not copy dynfield3
in the generic mbuf copy/clone path; users that want the existing copied
dynamic-field semantics can opt in with mbuf_dynfield3_copy.
This version does not add a per-field isolate registration flag. The
immediate use case needs a configured per-mbuf metadata area whose contents
remain local to each mbuf by default, matching the downstream storage this
replaces. Keeping dynfield3 allocator-backed preserves the dynamic-field
extension model, while the build-time copy option lets deployments opt in
to copied dynamic-field semantics without imposing that behavior on private
metadata users.
Validation:
- devtools/check-git-log.sh -n1
- devtools/checkpatches.sh -n1
- default generic-all build
- build with mbuf_dynfield3_size=128 and mbuf_dynfield3_copy=true
- mbuf_autotest with default and enabled builds
Randy Tice (1):
mbuf: add optional dynfield3 storage
app/test/test_mbuf.c | 5 +++--
config/meson.build | 16 ++++++++++++++++
doc/guides/rel_notes/release_26_11.rst | 15 +++++++++++++++
lib/mbuf/rte_mbuf.h | 6 ++++++
lib/mbuf/rte_mbuf_core.h | 17 +++++++++++++++++
lib/mbuf/rte_mbuf_dyn.c | 3 +++
meson_options.txt | 4 ++++
7 files changed, 64 insertions(+), 2 deletions(-)
--
2.35.6
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v2 1/1] mbuf: add optional dynfield3 storage
2026-09-25 19:31 ` [PATCH v2 0/1] " Randy L Tice
@ 2026-09-25 19:31 ` Randy L Tice
2026-09-26 16:51 ` Stephen Hemminger
2026-09-28 18:16 ` [PATCH v3 0/1] mbuf: add optional no-copy dynamic field storage Randy L Tice
1 sibling, 1 reply; 24+ messages in thread
From: Randy L Tice @ 2026-09-25 19:31 UTC (permalink / raw)
To: dev; +Cc: Morten Brørup, Bruce Richardson
From: Randy L Tice <rtice@cisco.com>
Date: Thu, 03 Sep 2026 09:13:28 -0400
Add build-time support for optional cache-line-aligned dynamic-field
storage at the end of struct rte_mbuf.
The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE
in rte_build_config.h. A non-zero value enables the extra area. The
storage is represented as uint64_t elements for 32-bit and 64-bit
build consistency.
When enabled, dynfield3 is made available to the mbuf dynamic field
allocator. The mbuf_dynfield3_copy option controls whether the area is
copied by the generic mbuf dynamic-field copy helper, and defaults to
false.
Validate that the configured size is non-negative, is a multiple of
sizeof(uint64_t), and reserves a multiple of the cache line size.
Signed-off-by: Randy L Tice <rtice@cisco.com>
---
app/test/test_mbuf.c | 5 +++--
config/meson.build | 16 ++++++++++++++++
doc/guides/rel_notes/release_26_11.rst | 15 +++++++++++++++
lib/mbuf/rte_mbuf.h | 6 ++++++
lib/mbuf/rte_mbuf_core.h | 17 +++++++++++++++++
lib/mbuf/rte_mbuf_dyn.c | 3 +++
meson_options.txt | 4 ++++
7 files changed, 64 insertions(+), 2 deletions(-)
diff --git a/app/test/test_mbuf.c b/app/test/test_mbuf.c
index db23259745..9f3b396a61 100644
--- a/app/test/test_mbuf.c
+++ b/app/test/test_mbuf.c
@@ -2776,8 +2776,9 @@ test_mbuf(void)
struct rte_mempool *pktmbuf_pool = NULL;
struct rte_mempool *pktmbuf_pool2 = NULL;
-
- RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) != RTE_CACHE_LINE_MIN_SIZE * 2);
+ RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) !=
+ RTE_CACHE_LINE_MIN_SIZE * 2 +
+ RTE_MBUF_DYNFIELD3_SIZE);
/* create pktmbuf pool if it does not exist */
pktmbuf_pool = rte_pktmbuf_pool_create("test_pktmbuf_pool",
diff --git a/config/meson.build b/config/meson.build
index 344f68822b..dd857e065f 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -384,6 +384,19 @@ dpdk_conf.set('RTE_LIBEAL_USE_HPET', get_option('use_hpet'))
dpdk_conf.set('RTE_ENABLE_STDATOMIC', get_option('enable_stdatomic'))
dpdk_conf.set('RTE_ENABLE_TRACE_FP', get_option('enable_trace_fp'))
dpdk_conf.set('RTE_PKTMBUF_HEADROOM', get_option('pkt_mbuf_headroom'))
+mbuf_dynfield3_size = get_option('mbuf_dynfield3_size')
+if mbuf_dynfield3_size < 0
+ error('mbuf_dynfield3_size must be greater than or equal to 0')
+endif
+if mbuf_dynfield3_size % cc.sizeof('uint64_t', prefix: '#include <stdint.h>') != 0
+ error('mbuf_dynfield3_size must be a multiple of sizeof(uint64_t)')
+endif
+mbuf_dynfield3_copy = get_option('mbuf_dynfield3_copy')
+if mbuf_dynfield3_copy and mbuf_dynfield3_size == 0
+ error('mbuf_dynfield3_copy requires mbuf_dynfield3_size greater than 0')
+endif
+dpdk_conf.set('RTE_MBUF_DYNFIELD3_SIZE', mbuf_dynfield3_size)
+dpdk_conf.set10('RTE_MBUF_DYNFIELD3_COPY', mbuf_dynfield3_copy)
# values which have defaults which may be overridden
dpdk_conf.set('RTE_MAX_VFIO_GROUPS', 64)
dpdk_conf.set('RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB', 64)
@@ -395,6 +408,9 @@ dpdk_conf.set10('RTE_IOVA_IN_MBUF', get_option('enable_iova_as_pa'))
compile_time_cpuflags = []
subdir(arch_subdir)
+if mbuf_dynfield3_size % dpdk_conf.get('RTE_CACHE_LINE_SIZE') != 0
+ error('mbuf_dynfield3_size must be a multiple of RTE_CACHE_LINE_SIZE')
+endif
dpdk_conf.set('RTE_COMPILE_TIME_CPUFLAGS', ','.join(compile_time_cpuflags))
# apply cross-specific options
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index dec96ccbc7..2d0fb6660c 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -60,6 +60,15 @@ New Features
Added the experimental ``rte_cpu_socket_id()`` function
to map an OS logical CPU ID to the NUMA socket containing that CPU.
+* **Added optional extra mbuf dynamic field storage.**
+
+ Added ``mbuf_dynfield3_size`` build option to enable a
+ cache-line-aligned ``dynfield3`` area in ``struct rte_mbuf``.
+ The configured size is defined as ``RTE_MBUF_DYNFIELD3_SIZE``
+ in ``rte_build_config.h``.
+ The area is not copied by generic mbuf copy or clone operations
+ unless ``mbuf_dynfield3_copy`` is enabled.
+
* **Added TPID support to VLAN tag insertion.**
Added ``rte_vlan_insert_tpid()`` to the net library.
@@ -338,6 +347,12 @@ Known Issues
Also, make sure to start the actual text at the margin.
=======================================================
+* **Some drivers may require changes for enlarged mbufs.**
+
+ Enabling ``mbuf_dynfield3_size`` with a non-zero value increases
+ ``sizeof(struct rte_mbuf)``. Drivers or applications that assume a
+ fixed mbuf size may require follow-up changes.
+
Tested Platforms
----------------
diff --git a/lib/mbuf/rte_mbuf.h b/lib/mbuf/rte_mbuf.h
index 60ec8158cd..97360d6549 100644
--- a/lib/mbuf/rte_mbuf.h
+++ b/lib/mbuf/rte_mbuf.h
@@ -1231,6 +1231,12 @@ rte_mbuf_dynfield_copy(struct rte_mbuf *mdst, const struct rte_mbuf *msrc)
mdst->dynfield2 = msrc->dynfield2;
#endif
memcpy(&mdst->dynfield1, msrc->dynfield1, sizeof(mdst->dynfield1));
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ if (RTE_MBUF_DYNFIELD3_COPY)
+ memcpy(RTE_PTR_ADD(mdst, RTE_MBUF_DYNFIELD3_OFFSET),
+ RTE_PTR_ADD(msrc, RTE_MBUF_DYNFIELD3_OFFSET),
+ RTE_MBUF_DYNFIELD3_SIZE);
+#endif
}
/* internal */
diff --git a/lib/mbuf/rte_mbuf_core.h b/lib/mbuf/rte_mbuf_core.h
index 98b0bd9ca7..5069984269 100644
--- a/lib/mbuf/rte_mbuf_core.h
+++ b/lib/mbuf/rte_mbuf_core.h
@@ -17,6 +17,7 @@
*/
#include <stdalign.h>
+#include <stddef.h>
#include <stdint.h>
#include <rte_byteorder.h>
@@ -26,6 +27,9 @@
extern "C" {
#endif
+#define RTE_MBUF_DYNFIELD3_CNT \
+ (RTE_MBUF_DYNFIELD3_SIZE / sizeof(uint64_t))
+
/*
* Packet Offload Features Flags. It also carry packet type information.
* Critical resources. Both rx/tx shared these bits. Be cautious on any change
@@ -686,8 +690,21 @@ struct __rte_cache_aligned rte_mbuf {
uint16_t timesync;
uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
+
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ alignas(RTE_CACHE_LINE_SIZE)
+ uint64_t dynfield3[RTE_MBUF_DYNFIELD3_CNT];
+ /**< Reserved cache-line-aligned space for dynamic fields. */
+#endif /* RTE_MBUF_DYNFIELD3_SIZE > 0 */
};
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+#define RTE_MBUF_DYNFIELD3_OFFSET \
+ offsetof(struct rte_mbuf, dynfield3)
+#else
+#define RTE_MBUF_DYNFIELD3_OFFSET 0
+#endif
+
/**
* Function typedef of callback to free externally attached buffer.
*/
diff --git a/lib/mbuf/rte_mbuf_dyn.c b/lib/mbuf/rte_mbuf_dyn.c
index 5987c9dee8..b42fe6377d 100644
--- a/lib/mbuf/rte_mbuf_dyn.c
+++ b/lib/mbuf/rte_mbuf_dyn.c
@@ -135,6 +135,9 @@ init_shared_mem(void)
#if !RTE_IOVA_IN_MBUF
mark_free(dynfield2);
#endif
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ mark_free(dynfield3);
+#endif
/* init free_flags */
for (mask = RTE_MBUF_F_FIRST_FREE; mask <= RTE_MBUF_F_LAST_FREE; mask <<= 1)
diff --git a/meson_options.txt b/meson_options.txt
index e28d24054c..579c434b9a 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -44,6 +44,10 @@ option('max_numa_nodes', type: 'string', value: 'default', description:
'Set the highest NUMA node supported by EAL; "default" is different per-arch, "detect" detects the highest NUMA node on the build machine.')
option('enable_iova_as_pa', type: 'boolean', value: true, description:
'Support the use of physical addresses for IO addresses, such as used by UIO or VFIO in no-IOMMU mode. When disabled, DPDK can only run with IOMMU support for address mappings, but will have more space available in the mbuf structure.')
+option('mbuf_dynfield3_size', type: 'integer', value: 0, description:
+ 'Size of optional extra mbuf dynamic field area, in bytes.')
+option('mbuf_dynfield3_copy', type: 'boolean', value: false, description:
+ 'Copy optional extra mbuf dynamic field area during mbuf copy/clone.')
option('mbuf_refcnt_atomic', type: 'boolean', value: true, description:
'Atomically access the mbuf refcnt.')
option('platform', type: 'string', value: 'native', description:
--
2.35.6
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v2 1/1] mbuf: add optional dynfield3 storage
2026-09-25 19:31 ` [PATCH v2 1/1] " Randy L Tice
@ 2026-09-26 16:51 ` Stephen Hemminger
2026-09-28 14:45 ` Randy Tice (rtice)
0 siblings, 1 reply; 24+ messages in thread
From: Stephen Hemminger @ 2026-09-26 16:51 UTC (permalink / raw)
To: Randy L Tice; +Cc: dev, Morten Brørup, Bruce Richardson
On Fri, 25 Sep 2026 15:31:08 -0400
Randy L Tice <rtice@cisco.com> wrote:
> From: Randy L Tice <rtice@cisco.com>
> Date: Thu, 03 Sep 2026 09:13:28 -0400
>
> Add build-time support for optional cache-line-aligned dynamic-field
> storage at the end of struct rte_mbuf.
>
> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE
> in rte_build_config.h. A non-zero value enables the extra area. The
> storage is represented as uint64_t elements for 32-bit and 64-bit
> build consistency.
>
> When enabled, dynfield3 is made available to the mbuf dynamic field
> allocator. The mbuf_dynfield3_copy option controls whether the area is
> copied by the generic mbuf dynamic-field copy helper, and defaults to
> false.
>
> Validate that the configured size is non-negative, is a multiple of
> sizeof(uint64_t), and reserves a multiple of the cache line size.
>
> Signed-off-by: Randy L Tice <rtice@cisco.com>
> ---
Ran this through AI review with full model
Subject: Re: [PATCH v2 1/1] mbuf: add optional dynfield3 storage
Applied to main (4f795dd), built and tested on x86_64 with
-Dmbuf_dynfield3_size=256 and 512.
Errors
------
1. Enabling the option breaks the build.
drivers/mempool/octeontx is built on all 64-bit Linux targets,
including x86_64, and has:
RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) > OCTEONTX_FPAVF_BUF_OFFSET);
with OCTEONTX_FPAVF_BUF_OFFSET fixed at 128. Any non-zero
mbuf_dynfield3_size fails to compile with the default driver set.
The Known Issues entry is not a substitute. Drivers that depend on a
128 byte mbuf must be disabled at configure time when the option is
set, the same way require_iova_in_mbuf handles
enable_iova_as_pa=false. Please audit drivers that program
sizeof(struct rte_mbuf) or a fixed offset into hardware (octeontx
FPA buf_offset, cnxk first_skip) and state the result in the commit
message.
2. Fields can straddle dynfield1 and dynfield3; clone copies half.
On 64-bit targets dynfield1 ends at offset 128 and dynfield3 starts
at 128 (for both 64 and 128 byte cache lines), so init_shared_mem()
creates one contiguous free run. The best-fit allocator places a
field across the boundary, and with mbuf_dynfield3_copy=false (the
default) rte_mbuf_dynfield_copy() copies only the dynfield1 part.
Reproduced with size=512: register three 8 byte fields (96, 104,
112), then a 16 byte align 8 field. It lands at 120..135. Set it to
0xab and rte_pktmbuf_clone():
abababababababab0000000000000000
The second half is whatever the clone mbuf held before, not zero.
Even without straddling, whether a field is copied now depends on
registration order and on what other libraries and PMDs registered
first. The field owner cannot know or control this, and every
existing user of rte_mbuf_dynfield_register() assumes copy on clone.
3. free_space[] score overflows for sizes >= 384.
struct mbuf_dyn_shm keeps the score in uint8_t free_space[].
process_score() computes align = 256 for a free run of 256 bytes or
more at a 256 byte aligned offset; the store truncates it to 0,
which means occupied, permanently.
With size=512 (dynfield3 at 128..639), bytes 256..511 are never
allocatable: a 256 byte field fails with ENOENT, and only 288 bytes
of 8 byte fields can be registered in total.
Widen free_space[] or cap the option. Meson integer options take
min/max, which also replaces the explicit < 0 check:
option('mbuf_dynfield3_size', type: 'integer',
min: 0, max: 256, value: 0, description: ...)
Warnings
--------
4. mbuf_autotest fails with size >= 256.
test_mbuf_dyn() expects dynfield_fail_big (size 256, align 1) to be
rejected. With size=256 it registers at offset 92, spanning 92..347
across both areas (see 2). The "too big" case should use
sizeof(struct rte_mbuf).
5. Copy policy is at the wrong level.
One build-time switch for the whole area cannot be right for every
field placed there. struct rte_mbuf_dynfield has a flags member,
reserved and required to be 0 today. Keep dynfield3 out of the
default allocator and hand it out only to callers that ask for it
with a new flag. That makes the no-copy semantics explicit and per
field, fixes 2, and removes mbuf_dynfield3_copy.
6. No test or CI coverage.
The option defaults to 0, so CI compiles none of the new code. Add a
devtools/test-meson-builds.sh build with the option set (it would
have caught 1), and a test_mbuf.c case that checks placement and
clone behaviour of a field in dynfield3.
7. Missing documentation and rationale.
doc/guides/prog_guide/mbuf_lib.rst describes dynamic fields and
needs to cover the new area, its copy semantics, and that the mbuf
layout now depends on a build option: applications and secondary
processes must be built with the same value.
The commit message does not say why the existing per-mbuf private
area (priv_size in rte_pktmbuf_pool_create()) is not sufficient.
The cover letter does not go into git; the rationale belongs in the
commit message, along with the cost: every mbuf grows by at least
one cache line.
Info
----
8. RTE_MBUF_DYNFIELD3_CNT and RTE_MBUF_DYNFIELD3_OFFSET are not
needed, and defining the offset as 0 when disabled is misleading
(offset 0 is buf_addr). Use the member directly, like dynfield1:
memcpy(mdst->dynfield3, msrc->dynfield3,
sizeof(mdst->dynfield3));
and drop the <stddef.h> include.
9. The sizeof(uint64_t) check in config/meson.build is redundant; a
multiple of RTE_CACHE_LINE_SIZE is always a multiple of 8.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 1/1] mbuf: add optional dynfield3 storage
2026-09-26 16:51 ` Stephen Hemminger
@ 2026-09-28 14:45 ` Randy Tice (rtice)
0 siblings, 0 replies; 24+ messages in thread
From: Randy Tice (rtice) @ 2026-09-28 14:45 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev@dpdk.org, Morten Brørup, Bruce Richardson
[-- Attachment #1: Type: text/plain, Size: 8375 bytes --]
Hi Stephen,
Thanks for the detailed review. I agree v2 needs a design rework rather than
just patching the reported failures.
For the octeontx issue, I’ll handle this in v3 by disabling mempool/octeontx
at Meson configure time when mbuf_dynfield3_size != 0, with a clear reason
that it requires sizeof(struct rte_mbuf) <= 128. I don’t plan to change
OCTEONTX_FPAVF_BUF_OFFSET in this patch since that looks like hardware-
programmed layout behavior and should be owned/validated by the Marvell
maintainers.
For CN20K, that specific size-truncation problem has already been fixed upstream
by 570c0b273cde (“drivers: fix CN20K mbuf size truncation"), so I’m using that as
confirmation that the current tree no longer has the same CN20K blocker.
For the copy semantics, agreed. The build-wide mbuf_dynfield3_copy option is
the wrong abstraction. In v3 I’m dropping it and preserving the existing
default dynamic-field behavior: fields registered with flags = 0 are copied by
generic mbuf copy/clone paths. For deployments that need non-copy metadata,
I’m adding an explicit per-field flag, RTE_MBUF_DYNFIELD_F_NO_COPY. Fields
using that flag are restricted to the optional dynfield3 area, so the no-copy
behavior is explicit and cannot accidentally affect existing dynamic-field
users.
That also fixes the dynfield1/dynfield3 straddling problem. Normal fields keep
copy semantics, and no-copy fields are constrained to dynfield3. The copy path
tracks the registered copied portions of dynfield3 and only copies those
bytes, instead of relying on a global copy switch.
For the allocator score overflow, I’m widening free_space[] from uint8_t to
uint16_t.
For the mbuf autotest issue, I’ll change the “too big” negative case to use
sizeof(struct rte_mbuf) rather than a fixed 256-byte field, and add dynfield3-
specific coverage for placement and copy/no-copy behavior.
I’ll also add a devtools/test-meson-builds.sh build with
-Dmbuf_dynfield3_size=256, update the programmer guide and release notes, drop
the unused dynfield3 count/offset macros, and remove the redundant
sizeof(uint64_t) Meson check.
Thanks again — the v3 will be a more general-purpose API with default copy
semantics preserved, and Cisco’s no-copy use case handled explicitly through
the new field flag.
-rt
From: Stephen Hemminger <stephen@networkplumber.org>
Date: Saturday, September 26, 2026 at 12:51 PM
To: Randy Tice (rtice) <rtice@cisco.com>
Cc: dev@dpdk.org <dev@dpdk.org>; Morten Brørup <mb@smartsharesystems.com>; Bruce Richardson <bruce.richardson@intel.com>
Subject: Re: [PATCH v2 1/1] mbuf: add optional dynfield3 storage
On Fri, 25 Sep 2026 15:31:08 -0400
Randy L Tice <rtice@cisco.com> wrote:
> From: Randy L Tice <rtice@cisco.com>
> Date: Thu, 03 Sep 2026 09:13:28 -0400
>
> Add build-time support for optional cache-line-aligned dynamic-field
> storage at the end of struct rte_mbuf.
>
> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE
> in rte_build_config.h. A non-zero value enables the extra area. The
> storage is represented as uint64_t elements for 32-bit and 64-bit
> build consistency.
>
> When enabled, dynfield3 is made available to the mbuf dynamic field
> allocator. The mbuf_dynfield3_copy option controls whether the area is
> copied by the generic mbuf dynamic-field copy helper, and defaults to
> false.
>
> Validate that the configured size is non-negative, is a multiple of
> sizeof(uint64_t), and reserves a multiple of the cache line size.
>
> Signed-off-by: Randy L Tice <rtice@cisco.com>
> ---
Ran this through AI review with full model
Subject: Re: [PATCH v2 1/1] mbuf: add optional dynfield3 storage
Applied to main (4f795dd), built and tested on x86_64 with
-Dmbuf_dynfield3_size=256 and 512.
Errors
------
1. Enabling the option breaks the build.
drivers/mempool/octeontx is built on all 64-bit Linux targets,
including x86_64, and has:
RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) > OCTEONTX_FPAVF_BUF_OFFSET);
with OCTEONTX_FPAVF_BUF_OFFSET fixed at 128. Any non-zero
mbuf_dynfield3_size fails to compile with the default driver set.
The Known Issues entry is not a substitute. Drivers that depend on a
128 byte mbuf must be disabled at configure time when the option is
set, the same way require_iova_in_mbuf handles
enable_iova_as_pa=false. Please audit drivers that program
sizeof(struct rte_mbuf) or a fixed offset into hardware (octeontx
FPA buf_offset, cnxk first_skip) and state the result in the commit
message.
2. Fields can straddle dynfield1 and dynfield3; clone copies half.
On 64-bit targets dynfield1 ends at offset 128 and dynfield3 starts
at 128 (for both 64 and 128 byte cache lines), so init_shared_mem()
creates one contiguous free run. The best-fit allocator places a
field across the boundary, and with mbuf_dynfield3_copy=false (the
default) rte_mbuf_dynfield_copy() copies only the dynfield1 part.
Reproduced with size=512: register three 8 byte fields (96, 104,
112), then a 16 byte align 8 field. It lands at 120..135. Set it to
0xab and rte_pktmbuf_clone():
abababababababab0000000000000000
The second half is whatever the clone mbuf held before, not zero.
Even without straddling, whether a field is copied now depends on
registration order and on what other libraries and PMDs registered
first. The field owner cannot know or control this, and every
existing user of rte_mbuf_dynfield_register() assumes copy on clone.
3. free_space[] score overflows for sizes >= 384.
struct mbuf_dyn_shm keeps the score in uint8_t free_space[].
process_score() computes align = 256 for a free run of 256 bytes or
more at a 256 byte aligned offset; the store truncates it to 0,
which means occupied, permanently.
With size=512 (dynfield3 at 128..639), bytes 256..511 are never
allocatable: a 256 byte field fails with ENOENT, and only 288 bytes
of 8 byte fields can be registered in total.
Widen free_space[] or cap the option. Meson integer options take
min/max, which also replaces the explicit < 0 check:
option('mbuf_dynfield3_size', type: 'integer',
min: 0, max: 256, value: 0, description: ...)
Warnings
--------
4. mbuf_autotest fails with size >= 256.
test_mbuf_dyn() expects dynfield_fail_big (size 256, align 1) to be
rejected. With size=256 it registers at offset 92, spanning 92..347
across both areas (see 2). The "too big" case should use
sizeof(struct rte_mbuf).
5. Copy policy is at the wrong level.
One build-time switch for the whole area cannot be right for every
field placed there. struct rte_mbuf_dynfield has a flags member,
reserved and required to be 0 today. Keep dynfield3 out of the
default allocator and hand it out only to callers that ask for it
with a new flag. That makes the no-copy semantics explicit and per
field, fixes 2, and removes mbuf_dynfield3_copy.
6. No test or CI coverage.
The option defaults to 0, so CI compiles none of the new code. Add a
devtools/test-meson-builds.sh build with the option set (it would
have caught 1), and a test_mbuf.c case that checks placement and
clone behaviour of a field in dynfield3.
7. Missing documentation and rationale.
doc/guides/prog_guide/mbuf_lib.rst describes dynamic fields and
needs to cover the new area, its copy semantics, and that the mbuf
layout now depends on a build option: applications and secondary
processes must be built with the same value.
The commit message does not say why the existing per-mbuf private
area (priv_size in rte_pktmbuf_pool_create()) is not sufficient.
The cover letter does not go into git; the rationale belongs in the
commit message, along with the cost: every mbuf grows by at least
one cache line.
Info
----
8. RTE_MBUF_DYNFIELD3_CNT and RTE_MBUF_DYNFIELD3_OFFSET are not
needed, and defining the offset as 0 when disabled is misleading
(offset 0 is buf_addr). Use the member directly, like dynfield1:
memcpy(mdst->dynfield3, msrc->dynfield3,
sizeof(mdst->dynfield3));
and drop the <stddef.h> include.
9. The sizeof(uint64_t) check in config/meson.build is redundant; a
multiple of RTE_CACHE_LINE_SIZE is always a multiple of 8.
[-- Attachment #2: Type: text/html, Size: 15560 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v3 0/1] mbuf: add optional no-copy dynamic field storage
2026-09-25 19:31 ` [PATCH v2 0/1] " Randy L Tice
2026-09-25 19:31 ` [PATCH v2 1/1] " Randy L Tice
@ 2026-09-28 18:16 ` Randy L Tice
2026-09-28 18:16 ` [PATCH v3 1/1] " Randy L Tice
2026-10-06 15:54 ` [PATCH v4 0/1] mbuf: add runtime metadata dynamic-field storage Randy L Tice
1 sibling, 2 replies; 24+ messages in thread
From: Randy L Tice @ 2026-09-28 18:16 UTC (permalink / raw)
To: dev; +Cc: Morten Brørup, Bruce Richardson, Harman Kalra,
Stephen Hemminger
This revision reworks the optional dynfield3 area around explicit no-copy
semantics.
The area remains managed by the dynamic mbuf field registry, but is only
used by fields registered with RTE_MBUF_DYNFIELD_F_NO_COPY. Normal
fields, with flags set to 0, continue to use the existing copied dynamic
field storage and are prevented from overlapping dynfield3. The generic
mbuf dynamic-field copy helper remains unchanged and copies only the
existing copied storage.
This keeps the registry benefits for consumers that need additional mbuf
metadata, while making the non-copy behavior explicit per field and
avoiding a build-wide dynfield3 copy policy.
Randy Tice (1):
mbuf: add optional no-copy dynamic field storage
---
v3:
- Reserve dynfield3 only for fields registered with
RTE_MBUF_DYNFIELD_F_NO_COPY.
- Keep flags=0 dynamic fields in existing copied dynamic-field storage.
- Prevent copied fields from overlapping or straddling dynfield3.
- Drop the mbuf_dynfield3_copy build option and avoid changing
rte_mbuf_dynfield_copy().
- Widen dynamic-field allocator score storage to uint16_t.
- Disable mempool/octeontx when mbuf_dynfield3_size is non-zero.
- Add mbuf unit-test coverage for no-copy placement and copy behavior.
- Add devtools build coverage with mbuf_dynfield3_size enabled.
- Update programmer guide, release notes and commit rationale.
Validation:
- git diff --check HEAD~1..HEAD
- devtools/check-git-log.sh -n1
- devtools/checkpatches.sh -n1
- ninja -C build-v3-dynfield3
- build-v3-dynfield3/app/dpdk-test ... mbuf_autotest
- ninja -C build-generic
app/test/test_mbuf.c | 72 ++++++++++++++++++++++++--
config/meson.build | 5 ++
devtools/test-meson-builds.sh | 3 ++
doc/guides/prog_guide/mbuf_lib.rst | 9 ++++
doc/guides/rel_notes/release_26_11.rst | 17 +++++-
drivers/mempool/octeontx/meson.build | 5 ++
lib/mbuf/rte_mbuf_core.h | 6 +++
lib/mbuf/rte_mbuf_dyn.c | 54 ++++++++++++++++---
lib/mbuf/rte_mbuf_dyn.h | 11 +++-
meson_options.txt | 2 +
10 files changed, 172 insertions(+), 12 deletions(-)
--
2.35.6
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-28 18:16 ` [PATCH v3 0/1] mbuf: add optional no-copy dynamic field storage Randy L Tice
@ 2026-09-28 18:16 ` Randy L Tice
2026-09-29 7:15 ` Morten Brørup
2026-09-29 11:59 ` Konstantin Ananyev
2026-10-06 15:54 ` [PATCH v4 0/1] mbuf: add runtime metadata dynamic-field storage Randy L Tice
1 sibling, 2 replies; 24+ messages in thread
From: Randy L Tice @ 2026-09-28 18:16 UTC (permalink / raw)
To: dev; +Cc: Morten Brørup, Bruce Richardson, Harman Kalra,
Stephen Hemminger
From: Randy L Tice <rtice@cisco.com>
Date: Thu, 03 Sep 2026 09:13:28 -0400
Add build-time support for optional cache-line-aligned dynamic-field
storage at the end of struct rte_mbuf.
The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE in
rte_build_config.h. A non-zero value enables the extra area and grows
every mbuf by the configured amount.
When enabled, dynfield3 is reserved for dynamic fields registered with
RTE_MBUF_DYNFIELD_F_NO_COPY. Such fields are allocated from dynfield3
and are not copied by the generic mbuf dynamic-field copy helper used by
mbuf copy and clone operations. Dynamic fields registered without this
flag continue to use the existing copied dynamic-field storage and are
prevented from overlapping dynfield3.
Disable the octeontx mempool driver when the option is enabled because
it requires sizeof(struct rte_mbuf) to remain at most 128 bytes.
Signed-off-by: Randy L Tice <rtice@cisco.com>
---
app/test/test_mbuf.c | 72 ++++++++++++++++++++++++--
config/meson.build | 5 ++
devtools/test-meson-builds.sh | 3 ++
doc/guides/prog_guide/mbuf_lib.rst | 9 ++++
doc/guides/rel_notes/release_26_11.rst | 17 +++++-
drivers/mempool/octeontx/meson.build | 5 ++
lib/mbuf/rte_mbuf_core.h | 6 +++
lib/mbuf/rte_mbuf_dyn.c | 54 ++++++++++++++++---
lib/mbuf/rte_mbuf_dyn.h | 11 +++-
meson_options.txt | 2 +
10 files changed, 172 insertions(+), 12 deletions(-)
diff --git a/app/test/test_mbuf.c b/app/test/test_mbuf.c
index db23259745..c03878bd96 100644
--- a/app/test/test_mbuf.c
+++ b/app/test/test_mbuf.c
@@ -2569,7 +2569,7 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
};
const struct rte_mbuf_dynfield dynfield_fail_big = {
.name = "test-dynfield-fail-big",
- .size = 256,
+ .size = sizeof(struct rte_mbuf),
.align = 1,
.flags = 0,
};
@@ -2583,8 +2583,28 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
.name = "test-dynfield",
.size = sizeof(uint8_t),
.align = alignof(uint8_t),
- .flags = 1,
+ .flags = RTE_MBUF_DYNFIELD_F_NO_COPY << 1,
+ };
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ const struct rte_mbuf_dynfield dynfield3_no_copy = {
+ .name = "test-dynfield3-no-copy",
+ .size = sizeof(uint64_t),
+ .align = alignof(uint64_t),
+ .flags = RTE_MBUF_DYNFIELD_F_NO_COPY,
+ };
+ const struct rte_mbuf_dynfield dynfield_no_copy_bad_offset = {
+ .name = "test-dynfield-no-copy-bad-offset",
+ .size = sizeof(uint64_t),
+ .align = alignof(uint64_t),
+ .flags = RTE_MBUF_DYNFIELD_F_NO_COPY,
+ };
+ const struct rte_mbuf_dynfield dynfield_copy_bad_offset = {
+ .name = "test-dynfield-copy-bad-offset",
+ .size = 2 * sizeof(uint64_t),
+ .align = alignof(uint64_t),
+ .flags = 0,
};
+#endif
const struct rte_mbuf_dynflag dynflag_fail_flag = {
.name = "test-dynflag",
.flags = 1,
@@ -2602,7 +2622,11 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
.flags = 0,
};
struct rte_mbuf *m = NULL;
+ struct rte_mbuf *mc = NULL;
int offset, offset2, offset3;
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ int dynfield3_no_copy_offset;
+#endif
int flag, flag2, flag3;
int ret;
@@ -2654,6 +2678,29 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
if (ret != -1)
GOTO_FAIL("dynamic field creation should fail (invalid flag)");
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ dynfield3_no_copy_offset = rte_mbuf_dynfield_register_offset(&dynfield3_no_copy,
+ offsetof(struct rte_mbuf, dynfield3));
+ if (dynfield3_no_copy_offset != offsetof(struct rte_mbuf, dynfield3))
+ GOTO_FAIL("failed to register no-copy dynfield3 field, offset=%d: %s",
+ dynfield3_no_copy_offset, strerror(errno));
+
+ ret = rte_mbuf_dynfield_register_offset(&dynfield_no_copy_bad_offset,
+ offsetof(struct rte_mbuf, dynfield1[0]));
+ if (ret != -1)
+ GOTO_FAIL("no-copy dynamic field creation should fail outside dynfield3");
+
+ ret = rte_mbuf_dynfield_register_offset(&dynfield_copy_bad_offset,
+ offsetof(struct rte_mbuf, dynfield3));
+ if (ret != -1)
+ GOTO_FAIL("copied dynamic field creation should fail in dynfield3");
+
+ ret = rte_mbuf_dynfield_register_offset(&dynfield_copy_bad_offset,
+ offsetof(struct rte_mbuf, dynfield3) - sizeof(uint64_t));
+ if (ret != -1)
+ GOTO_FAIL("copied dynamic field creation should fail when straddling dynfield3");
+#endif
+
ret = rte_mbuf_dynflag_register(&dynflag_fail_flag);
if (ret != -1)
GOTO_FAIL("dynamic flag creation should fail (invalid flag)");
@@ -2693,13 +2740,29 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
if (*RTE_MBUF_DYNFIELD(m, offset2, uint16_t *) != 1000)
GOTO_FAIL("failed to read dynamic field");
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ *RTE_MBUF_DYNFIELD(m, dynfield3_no_copy_offset, uint64_t *) =
+ UINT64_C(0x8877665544332211);
+ mc = rte_pktmbuf_alloc(pktmbuf_pool);
+ if (mc == NULL)
+ GOTO_FAIL("Cannot allocate mbuf for dynamic field copy test");
+ *RTE_MBUF_DYNFIELD(mc, dynfield3_no_copy_offset, uint64_t *) =
+ UINT64_C(0xa5a5a5a5a5a5a5a5);
+ rte_mbuf_dynfield_copy(mc, m);
+ if (*RTE_MBUF_DYNFIELD(mc, dynfield3_no_copy_offset, uint64_t *) !=
+ UINT64_C(0xa5a5a5a5a5a5a5a5))
+ GOTO_FAIL("copied no-copy dynfield3 dynamic field");
+#endif
+
/* set a dynamic flag */
m->ol_flags |= (1ULL << flag);
rte_mbuf_dyn_dump(stdout);
+ rte_pktmbuf_free(mc);
rte_pktmbuf_free(m);
return 0;
fail:
+ rte_pktmbuf_free(mc);
rte_pktmbuf_free(m);
return -1;
}
@@ -2776,8 +2839,9 @@ test_mbuf(void)
struct rte_mempool *pktmbuf_pool = NULL;
struct rte_mempool *pktmbuf_pool2 = NULL;
-
- RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) != RTE_CACHE_LINE_MIN_SIZE * 2);
+ RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) !=
+ RTE_CACHE_LINE_MIN_SIZE * 2 +
+ RTE_MBUF_DYNFIELD3_SIZE);
/* create pktmbuf pool if it does not exist */
pktmbuf_pool = rte_pktmbuf_pool_create("test_pktmbuf_pool",
diff --git a/config/meson.build b/config/meson.build
index 344f68822b..a6bd25b9ac 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -384,6 +384,8 @@ dpdk_conf.set('RTE_LIBEAL_USE_HPET', get_option('use_hpet'))
dpdk_conf.set('RTE_ENABLE_STDATOMIC', get_option('enable_stdatomic'))
dpdk_conf.set('RTE_ENABLE_TRACE_FP', get_option('enable_trace_fp'))
dpdk_conf.set('RTE_PKTMBUF_HEADROOM', get_option('pkt_mbuf_headroom'))
+mbuf_dynfield3_size = get_option('mbuf_dynfield3_size')
+dpdk_conf.set('RTE_MBUF_DYNFIELD3_SIZE', mbuf_dynfield3_size)
# values which have defaults which may be overridden
dpdk_conf.set('RTE_MAX_VFIO_GROUPS', 64)
dpdk_conf.set('RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB', 64)
@@ -395,6 +397,9 @@ dpdk_conf.set10('RTE_IOVA_IN_MBUF', get_option('enable_iova_as_pa'))
compile_time_cpuflags = []
subdir(arch_subdir)
+if mbuf_dynfield3_size % dpdk_conf.get('RTE_CACHE_LINE_SIZE') != 0
+ error('mbuf_dynfield3_size must be a multiple of RTE_CACHE_LINE_SIZE')
+endif
dpdk_conf.set('RTE_COMPILE_TIME_CPUFLAGS', ','.join(compile_time_cpuflags))
# apply cross-specific options
diff --git a/devtools/test-meson-builds.sh b/devtools/test-meson-builds.sh
index 11e4be3f88..23115def8c 100755
--- a/devtools/test-meson-builds.sh
+++ b/devtools/test-meson-builds.sh
@@ -259,6 +259,9 @@ fi
build build-x86-generic cc skipABI --buildtype=debug -Dcheck_includes=true \
-Dlibdir=lib -Dcpu_instruction_set=$generic_isa $use_shared
+build build-mbuf-dynfield3 cc skipABI --buildtype=debug \
+ -Dmbuf_dynfield3_size=256 $use_shared
+
# 32-bit with default compiler
if check_cc_flags '-m32' ; then
target_override='i386-pc-linux-gnu'
diff --git a/doc/guides/prog_guide/mbuf_lib.rst b/doc/guides/prog_guide/mbuf_lib.rst
index cf64add109..ca0efb99c1 100644
--- a/doc/guides/prog_guide/mbuf_lib.rst
+++ b/doc/guides/prog_guide/mbuf_lib.rst
@@ -234,6 +234,15 @@ The dynamic fields and flags are managed with the functions ``rte_mbuf_dyn*``.
It is not possible to unregister fields or flags.
+The build option ``mbuf_dynfield3_size`` can add extra cache-line-aligned
+dynamic field storage to ``struct rte_mbuf``. This increases every mbuf by
+the configured amount and changes the mbuf layout, so applications and
+secondary processes must be built with the same value as the primary process.
+The option defaults to ``0``. The extra storage is reserved for dynamic
+fields registered with ``RTE_MBUF_DYNFIELD_F_NO_COPY``. These fields are not
+copied by mbuf copy and clone operations. Dynamic fields registered without
+this flag continue to use the existing copied dynamic-field storage.
+
.. _direct_indirect_buffer:
Direct and Indirect Buffers
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index dec96ccbc7..cff68d7034 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -60,6 +60,22 @@ New Features
Added the experimental ``rte_cpu_socket_id()`` function
to map an OS logical CPU ID to the NUMA socket containing that CPU.
+* **Added optional extra mbuf dynamic field storage.**
+
+ Added ``mbuf_dynfield3_size`` build option to enable a
+ cache-line-aligned ``dynfield3`` area in ``struct rte_mbuf``.
+ The configured size is defined as ``RTE_MBUF_DYNFIELD3_SIZE``
+ in ``rte_build_config.h``.
+ The extra storage is reserved for dynamic fields registered with
+ ``RTE_MBUF_DYNFIELD_F_NO_COPY``.
+ These fields are not copied by generic mbuf copy and clone operations.
+ Dynamic fields registered without this flag continue to use the existing
+ copied dynamic-field storage.
+ Applications and secondary processes must be built with the same
+ non-zero value as the primary process.
+ The ``mempool/octeontx`` driver is disabled when this option is
+ enabled because it requires a fixed 128-byte mbuf header.
+
* **Added TPID support to VLAN tag insertion.**
Added ``rte_vlan_insert_tpid()`` to the net library.
@@ -338,7 +354,6 @@ Known Issues
Also, make sure to start the actual text at the margin.
=======================================================
-
Tested Platforms
----------------
diff --git a/drivers/mempool/octeontx/meson.build b/drivers/mempool/octeontx/meson.build
index 3ccecac75d..01aece94be 100644
--- a/drivers/mempool/octeontx/meson.build
+++ b/drivers/mempool/octeontx/meson.build
@@ -6,6 +6,11 @@ if not is_linux or not dpdk_conf.get('RTE_ARCH_64')
reason = 'only supported on 64-bit Linux'
subdir_done()
endif
+if get_option('mbuf_dynfield3_size') != 0
+ build = false
+ reason = 'requires sizeof(struct rte_mbuf) <= 128'
+ subdir_done()
+endif
sources = files(
'octeontx_fpavf.c',
diff --git a/lib/mbuf/rte_mbuf_core.h b/lib/mbuf/rte_mbuf_core.h
index 98b0bd9ca7..b4f204b268 100644
--- a/lib/mbuf/rte_mbuf_core.h
+++ b/lib/mbuf/rte_mbuf_core.h
@@ -686,6 +686,12 @@ struct __rte_cache_aligned rte_mbuf {
uint16_t timesync;
uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
+
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ alignas(RTE_CACHE_LINE_SIZE)
+ uint64_t dynfield3[RTE_MBUF_DYNFIELD3_SIZE / sizeof(uint64_t)];
+ /**< Reserved cache-line-aligned space for dynamic fields. */
+#endif /* RTE_MBUF_DYNFIELD3_SIZE > 0 */
};
/**
diff --git a/lib/mbuf/rte_mbuf_dyn.c b/lib/mbuf/rte_mbuf_dyn.c
index 5987c9dee8..7cecbc9ee6 100644
--- a/lib/mbuf/rte_mbuf_dyn.c
+++ b/lib/mbuf/rte_mbuf_dyn.c
@@ -3,6 +3,7 @@
*/
#include <stdalign.h>
+#include <stddef.h>
#include <sys/queue.h>
#include <stdint.h>
#include <limits.h>
@@ -51,7 +52,7 @@ struct mbuf_dyn_shm {
* The value is the size of the biggest aligned element that
* can fit in the zone.
*/
- uint8_t free_space[sizeof(struct rte_mbuf)];
+ uint16_t free_space[sizeof(struct rte_mbuf)];
/** Bitfield of available flags. */
uint64_t free_flags;
};
@@ -135,6 +136,9 @@ init_shared_mem(void)
#if !RTE_IOVA_IN_MBUF
mark_free(dynfield2);
#endif
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ mark_free(dynfield3);
+#endif
/* init free_flags */
for (mask = RTE_MBUF_F_FIRST_FREE; mask <= RTE_MBUF_F_LAST_FREE; mask <<= 1)
@@ -147,11 +151,48 @@ init_shared_mem(void)
}
/* check if this offset can be used */
+static bool
+dynfield_in_dynfield3(size_t offset, size_t size)
+{
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ size_t dynfield3_offset = offsetof(struct rte_mbuf, dynfield3);
+
+ return offset >= dynfield3_offset &&
+ size <= sizeof(((struct rte_mbuf *)0)->dynfield3) &&
+ offset - dynfield3_offset <= sizeof(((struct rte_mbuf *)0)->dynfield3) - size;
+#else
+ RTE_SET_USED(offset);
+ RTE_SET_USED(size);
+ return false;
+#endif
+}
+
+static bool
+dynfield_overlaps_dynfield3(size_t offset, size_t size)
+{
+#if RTE_MBUF_DYNFIELD3_SIZE > 0
+ size_t dynfield3_offset = offsetof(struct rte_mbuf, dynfield3);
+ size_t dynfield3_end = dynfield3_offset + sizeof(((struct rte_mbuf *)0)->dynfield3);
+
+ return offset < dynfield3_end && offset + size > dynfield3_offset;
+#else
+ RTE_SET_USED(offset);
+ RTE_SET_USED(size);
+ return false;
+#endif
+}
+
static int
-check_offset(size_t offset, size_t size, size_t align)
+check_offset(size_t offset, size_t size, size_t align, unsigned int flags)
{
size_t i;
+ if ((flags & RTE_MBUF_DYNFIELD_F_NO_COPY) != 0 &&
+ !dynfield_in_dynfield3(offset, size))
+ return -1;
+ if ((flags & RTE_MBUF_DYNFIELD_F_NO_COPY) == 0 &&
+ dynfield_overlaps_dynfield3(offset, size))
+ return -1;
if ((offset & (align - 1)) != 0)
return -1;
if (offset + size > sizeof(struct rte_mbuf))
@@ -268,7 +309,7 @@ __rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
offset < sizeof(struct rte_mbuf);
offset++) {
if (check_offset(offset, params->size,
- params->align) == 0 &&
+ params->align, params->flags) == 0 &&
shm->free_space[offset] < best_zone) {
best_zone = shm->free_space[offset];
req = offset;
@@ -279,7 +320,8 @@ __rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
return -1;
}
} else {
- if (check_offset(req, params->size, params->align) < 0) {
+ if (check_offset(req, params->size, params->align,
+ params->flags) < 0) {
rte_errno = EBUSY;
return -1;
}
@@ -342,7 +384,7 @@ rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
rte_errno = EINVAL;
return -1;
}
- if (params->flags != 0) {
+ if ((params->flags & ~RTE_MBUF_DYNFIELD_F_NO_COPY) != 0) {
rte_errno = EINVAL;
return -1;
}
@@ -573,7 +615,7 @@ void rte_mbuf_dyn_dump(FILE *out)
for (i = 0; i < sizeof(struct rte_mbuf); i++) {
if ((i % 8) == 0)
fprintf(out, " %4.4zx: ", i);
- fprintf(out, "%2.2x%s", shm->free_space[i],
+ fprintf(out, "%4.4x%s", shm->free_space[i],
(i % 8 != 7) ? " " : "\n");
}
fprintf(out, "Free bit in mbuf->ol_flags (0 = occupied, 1 = free):\n");
diff --git a/lib/mbuf/rte_mbuf_dyn.h b/lib/mbuf/rte_mbuf_dyn.h
index 20ce505bb4..d9a46b3c33 100644
--- a/lib/mbuf/rte_mbuf_dyn.h
+++ b/lib/mbuf/rte_mbuf_dyn.h
@@ -69,6 +69,7 @@
#include <stdio.h>
#include <stdint.h>
+#include <rte_bitops.h>
#include <rte_stdatomic.h>
#ifdef __cplusplus
@@ -80,6 +81,14 @@ extern "C" {
*/
#define RTE_MBUF_DYN_NAMESIZE 64
+/**
+ * Do not copy this dynamic field during mbuf clone or copy.
+ *
+ * Fields using this flag are allocated from the optional dynfield3 area
+ * configured by the mbuf_dynfield3_size build option.
+ */
+#define RTE_MBUF_DYNFIELD_F_NO_COPY RTE_BIT32(0)
+
/**
* Structure describing the parameters of a mbuf dynamic field.
*/
@@ -87,7 +96,7 @@ struct rte_mbuf_dynfield {
char name[RTE_MBUF_DYN_NAMESIZE]; /**< Name of the field. */
size_t size; /**< The number of bytes to reserve. */
size_t align; /**< The alignment constraint (power of 2). */
- unsigned int flags; /**< Reserved for future use, must be 0. */
+ unsigned int flags; /**< Dynamic field flags. */
};
/**
diff --git a/meson_options.txt b/meson_options.txt
index e28d24054c..1248498a3d 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -44,6 +44,8 @@ option('max_numa_nodes', type: 'string', value: 'default', description:
'Set the highest NUMA node supported by EAL; "default" is different per-arch, "detect" detects the highest NUMA node on the build machine.')
option('enable_iova_as_pa', type: 'boolean', value: true, description:
'Support the use of physical addresses for IO addresses, such as used by UIO or VFIO in no-IOMMU mode. When disabled, DPDK can only run with IOMMU support for address mappings, but will have more space available in the mbuf structure.')
+option('mbuf_dynfield3_size', type: 'integer', min: 0, value: 0, description:
+ 'Size of optional extra mbuf dynamic field area, in bytes.')
option('mbuf_refcnt_atomic', type: 'boolean', value: true, description:
'Atomically access the mbuf refcnt.')
option('platform', type: 'string', value: 'native', description:
--
2.35.6
^ permalink raw reply related [flat|nested] 24+ messages in thread
* RE: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-28 18:16 ` [PATCH v3 1/1] " Randy L Tice
@ 2026-09-29 7:15 ` Morten Brørup
2026-09-29 11:59 ` Konstantin Ananyev
1 sibling, 0 replies; 24+ messages in thread
From: Morten Brørup @ 2026-09-29 7:15 UTC (permalink / raw)
To: Randy L Tice, dev; +Cc: Bruce Richardson, Harman Kalra, Stephen Hemminger
Yes, this is the solution I was looking for. Thank you.
Nitpicking...
Could you use with a more meaningful name than dynfield3, e.g. dynfield_nc or dynfield_no_copy.
A few minor clarifications suggested inline below.
With or without suggested changes,
Reviewed-by: Morten Brørup <mb@smartsharesystems.com>
> --- a/doc/guides/prog_guide/mbuf_lib.rst
> +++ b/doc/guides/prog_guide/mbuf_lib.rst
> @@ -234,6 +234,15 @@ The dynamic fields and flags are managed with the
> functions ``rte_mbuf_dyn*``.
>
> It is not possible to unregister fields or flags.
>
> +The build option ``mbuf_dynfield3_size`` can add extra cache-line-
> aligned
> +dynamic field storage to ``struct rte_mbuf``. This increases every
> mbuf by
"increases every mbuf" -> "increases the size of every mbuf"
> +the configured amount and changes the mbuf layout, so applications and
> +secondary processes must be built with the same value as the primary
> process.
> +The option defaults to ``0``. The extra storage is reserved for
> dynamic
> +fields registered with ``RTE_MBUF_DYNFIELD_F_NO_COPY``. These fields
> are not
> +copied by mbuf copy and clone operations. Dynamic fields registered
> without
> +this flag continue to use the existing copied dynamic-field storage.
> +
> .. _direct_indirect_buffer:
>
> Direct and Indirect Buffers
> diff --git a/doc/guides/rel_notes/release_26_11.rst
> b/doc/guides/rel_notes/release_26_11.rst
> index dec96ccbc7..cff68d7034 100644
> --- a/doc/guides/rel_notes/release_26_11.rst
> +++ b/doc/guides/rel_notes/release_26_11.rst
> @@ -60,6 +60,22 @@ New Features
> Added the experimental ``rte_cpu_socket_id()`` function
> to map an OS logical CPU ID to the NUMA socket containing that CPU.
>
> +* **Added optional extra mbuf dynamic field storage.**
> +
> + Added ``mbuf_dynfield3_size`` build option to enable a
> + cache-line-aligned ``dynfield3`` area in ``struct rte_mbuf``.
> + The configured size is defined as ``RTE_MBUF_DYNFIELD3_SIZE``
> + in ``rte_build_config.h``.
> + The extra storage is reserved for dynamic fields registered with
> + ``RTE_MBUF_DYNFIELD_F_NO_COPY``.
> + These fields are not copied by generic mbuf copy and clone
> operations.
> + Dynamic fields registered without this flag continue to use the
> existing
> + copied dynamic-field storage.
> + Applications and secondary processes must be built with the same
> + non-zero value as the primary process.
"with the same non-zero value" ->"with the same value"
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -44,6 +44,8 @@ option('max_numa_nodes', type: 'string', value:
> 'default', description:
> 'Set the highest NUMA node supported by EAL; "default" is
> different per-arch, "detect" detects the highest NUMA node on the build
> machine.')
> option('enable_iova_as_pa', type: 'boolean', value: true, description:
> 'Support the use of physical addresses for IO addresses, such
> as used by UIO or VFIO in no-IOMMU mode. When disabled, DPDK can only
> run with IOMMU support for address mappings, but will have more space
> available in the mbuf structure.')
> +option('mbuf_dynfield3_size', type: 'integer', min: 0, value: 0,
> description:
> + 'Size of optional extra mbuf dynamic field area, in bytes.')
The description of this option should mention that the area is for mbuf dynamic fields with the no-copy property.
> option('mbuf_refcnt_atomic', type: 'boolean', value: true,
> description:
> 'Atomically access the mbuf refcnt.')
> option('platform', type: 'string', value: 'native', description:
> --
> 2.35.6
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-28 18:16 ` [PATCH v3 1/1] " Randy L Tice
2026-09-29 7:15 ` Morten Brørup
@ 2026-09-29 11:59 ` Konstantin Ananyev
2026-09-29 12:44 ` Morten Brørup
1 sibling, 1 reply; 24+ messages in thread
From: Konstantin Ananyev @ 2026-09-29 11:59 UTC (permalink / raw)
To: Randy L Tice, dev
Cc: Morten Brørup, Bruce Richardson, Harman Kalra,
Stephen Hemminger
28.09.2026 19:16, Randy L Tice пишет:
> From: Randy L Tice <rtice@cisco.com>
> Date: Thu, 03 Sep 2026 09:13:28 -0400
>
> Add build-time support for optional cache-line-aligned dynamic-field
> storage at the end of struct rte_mbuf.
>
> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE in
> rte_build_config.h. A non-zero value enables the extra area and grows
> every mbuf by the configured amount.
I am strongly opposed to that patch.
Inside mbuf we already do have priv_size that allows user to store
his/her specific
data straight after rte_mbuf in adjacent manner.
It worked well so far for many use-cases (including VPP) and I don't see any
reason why this is not enough.
From other side - making size of core rte_mbuf configurable at run-time,
will affect DPDK ABI stability in a negative way.
Fro my perspective it is much plausible in terms of ABI stability and
predictability
to have just one fixed layout for the mbuf.
Konstantin
> When enabled, dynfield3 is reserved for dynamic fields registered with
> RTE_MBUF_DYNFIELD_F_NO_COPY. Such fields are allocated from dynfield3
> and are not copied by the generic mbuf dynamic-field copy helper used by
> mbuf copy and clone operations. Dynamic fields registered without this
> flag continue to use the existing copied dynamic-field storage and are
> prevented from overlapping dynfield3.
>
> Disable the octeontx mempool driver when the option is enabled because
> it requires sizeof(struct rte_mbuf) to remain at most 128 bytes.
>
> Signed-off-by: Randy L Tice <rtice@cisco.com>
> ---
> app/test/test_mbuf.c | 72 ++++++++++++++++++++++++--
> config/meson.build | 5 ++
> devtools/test-meson-builds.sh | 3 ++
> doc/guides/prog_guide/mbuf_lib.rst | 9 ++++
> doc/guides/rel_notes/release_26_11.rst | 17 +++++-
> drivers/mempool/octeontx/meson.build | 5 ++
> lib/mbuf/rte_mbuf_core.h | 6 +++
> lib/mbuf/rte_mbuf_dyn.c | 54 ++++++++++++++++---
> lib/mbuf/rte_mbuf_dyn.h | 11 +++-
> meson_options.txt | 2 +
> 10 files changed, 172 insertions(+), 12 deletions(-)
>
> diff --git a/app/test/test_mbuf.c b/app/test/test_mbuf.c
> index db23259745..c03878bd96 100644
> --- a/app/test/test_mbuf.c
> +++ b/app/test/test_mbuf.c
> @@ -2569,7 +2569,7 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
> };
> const struct rte_mbuf_dynfield dynfield_fail_big = {
> .name = "test-dynfield-fail-big",
> - .size = 256,
> + .size = sizeof(struct rte_mbuf),
> .align = 1,
> .flags = 0,
> };
> @@ -2583,8 +2583,28 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
> .name = "test-dynfield",
> .size = sizeof(uint8_t),
> .align = alignof(uint8_t),
> - .flags = 1,
> + .flags = RTE_MBUF_DYNFIELD_F_NO_COPY << 1,
> + };
> +#if RTE_MBUF_DYNFIELD3_SIZE > 0
> + const struct rte_mbuf_dynfield dynfield3_no_copy = {
> + .name = "test-dynfield3-no-copy",
> + .size = sizeof(uint64_t),
> + .align = alignof(uint64_t),
> + .flags = RTE_MBUF_DYNFIELD_F_NO_COPY,
> + };
> + const struct rte_mbuf_dynfield dynfield_no_copy_bad_offset = {
> + .name = "test-dynfield-no-copy-bad-offset",
> + .size = sizeof(uint64_t),
> + .align = alignof(uint64_t),
> + .flags = RTE_MBUF_DYNFIELD_F_NO_COPY,
> + };
> + const struct rte_mbuf_dynfield dynfield_copy_bad_offset = {
> + .name = "test-dynfield-copy-bad-offset",
> + .size = 2 * sizeof(uint64_t),
> + .align = alignof(uint64_t),
> + .flags = 0,
> };
> +#endif
> const struct rte_mbuf_dynflag dynflag_fail_flag = {
> .name = "test-dynflag",
> .flags = 1,
> @@ -2602,7 +2622,11 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
> .flags = 0,
> };
> struct rte_mbuf *m = NULL;
> + struct rte_mbuf *mc = NULL;
> int offset, offset2, offset3;
> +#if RTE_MBUF_DYNFIELD3_SIZE > 0
> + int dynfield3_no_copy_offset;
> +#endif
> int flag, flag2, flag3;
> int ret;
>
> @@ -2654,6 +2678,29 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
> if (ret != -1)
> GOTO_FAIL("dynamic field creation should fail (invalid flag)");
>
> +#if RTE_MBUF_DYNFIELD3_SIZE > 0
> + dynfield3_no_copy_offset = rte_mbuf_dynfield_register_offset(&dynfield3_no_copy,
> + offsetof(struct rte_mbuf, dynfield3));
> + if (dynfield3_no_copy_offset != offsetof(struct rte_mbuf, dynfield3))
> + GOTO_FAIL("failed to register no-copy dynfield3 field, offset=%d: %s",
> + dynfield3_no_copy_offset, strerror(errno));
> +
> + ret = rte_mbuf_dynfield_register_offset(&dynfield_no_copy_bad_offset,
> + offsetof(struct rte_mbuf, dynfield1[0]));
> + if (ret != -1)
> + GOTO_FAIL("no-copy dynamic field creation should fail outside dynfield3");
> +
> + ret = rte_mbuf_dynfield_register_offset(&dynfield_copy_bad_offset,
> + offsetof(struct rte_mbuf, dynfield3));
> + if (ret != -1)
> + GOTO_FAIL("copied dynamic field creation should fail in dynfield3");
> +
> + ret = rte_mbuf_dynfield_register_offset(&dynfield_copy_bad_offset,
> + offsetof(struct rte_mbuf, dynfield3) - sizeof(uint64_t));
> + if (ret != -1)
> + GOTO_FAIL("copied dynamic field creation should fail when straddling dynfield3");
> +#endif
> +
> ret = rte_mbuf_dynflag_register(&dynflag_fail_flag);
> if (ret != -1)
> GOTO_FAIL("dynamic flag creation should fail (invalid flag)");
> @@ -2693,13 +2740,29 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
> if (*RTE_MBUF_DYNFIELD(m, offset2, uint16_t *) != 1000)
> GOTO_FAIL("failed to read dynamic field");
>
> +#if RTE_MBUF_DYNFIELD3_SIZE > 0
> + *RTE_MBUF_DYNFIELD(m, dynfield3_no_copy_offset, uint64_t *) =
> + UINT64_C(0x8877665544332211);
> + mc = rte_pktmbuf_alloc(pktmbuf_pool);
> + if (mc == NULL)
> + GOTO_FAIL("Cannot allocate mbuf for dynamic field copy test");
> + *RTE_MBUF_DYNFIELD(mc, dynfield3_no_copy_offset, uint64_t *) =
> + UINT64_C(0xa5a5a5a5a5a5a5a5);
> + rte_mbuf_dynfield_copy(mc, m);
> + if (*RTE_MBUF_DYNFIELD(mc, dynfield3_no_copy_offset, uint64_t *) !=
> + UINT64_C(0xa5a5a5a5a5a5a5a5))
> + GOTO_FAIL("copied no-copy dynfield3 dynamic field");
> +#endif
> +
> /* set a dynamic flag */
> m->ol_flags |= (1ULL << flag);
>
> rte_mbuf_dyn_dump(stdout);
> + rte_pktmbuf_free(mc);
> rte_pktmbuf_free(m);
> return 0;
> fail:
> + rte_pktmbuf_free(mc);
> rte_pktmbuf_free(m);
> return -1;
> }
> @@ -2776,8 +2839,9 @@ test_mbuf(void)
> struct rte_mempool *pktmbuf_pool = NULL;
> struct rte_mempool *pktmbuf_pool2 = NULL;
>
> -
> - RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) != RTE_CACHE_LINE_MIN_SIZE * 2);
> + RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) !=
> + RTE_CACHE_LINE_MIN_SIZE * 2 +
> + RTE_MBUF_DYNFIELD3_SIZE);
>
> /* create pktmbuf pool if it does not exist */
> pktmbuf_pool = rte_pktmbuf_pool_create("test_pktmbuf_pool",
> diff --git a/config/meson.build b/config/meson.build
> index 344f68822b..a6bd25b9ac 100644
> --- a/config/meson.build
> +++ b/config/meson.build
> @@ -384,6 +384,8 @@ dpdk_conf.set('RTE_LIBEAL_USE_HPET', get_option('use_hpet'))
> dpdk_conf.set('RTE_ENABLE_STDATOMIC', get_option('enable_stdatomic'))
> dpdk_conf.set('RTE_ENABLE_TRACE_FP', get_option('enable_trace_fp'))
> dpdk_conf.set('RTE_PKTMBUF_HEADROOM', get_option('pkt_mbuf_headroom'))
> +mbuf_dynfield3_size = get_option('mbuf_dynfield3_size')
> +dpdk_conf.set('RTE_MBUF_DYNFIELD3_SIZE', mbuf_dynfield3_size)
> # values which have defaults which may be overridden
> dpdk_conf.set('RTE_MAX_VFIO_GROUPS', 64)
> dpdk_conf.set('RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB', 64)
> @@ -395,6 +397,9 @@ dpdk_conf.set10('RTE_IOVA_IN_MBUF', get_option('enable_iova_as_pa'))
>
> compile_time_cpuflags = []
> subdir(arch_subdir)
> +if mbuf_dynfield3_size % dpdk_conf.get('RTE_CACHE_LINE_SIZE') != 0
> + error('mbuf_dynfield3_size must be a multiple of RTE_CACHE_LINE_SIZE')
> +endif
> dpdk_conf.set('RTE_COMPILE_TIME_CPUFLAGS', ','.join(compile_time_cpuflags))
>
> # apply cross-specific options
> diff --git a/devtools/test-meson-builds.sh b/devtools/test-meson-builds.sh
> index 11e4be3f88..23115def8c 100755
> --- a/devtools/test-meson-builds.sh
> +++ b/devtools/test-meson-builds.sh
> @@ -259,6 +259,9 @@ fi
> build build-x86-generic cc skipABI --buildtype=debug -Dcheck_includes=true \
> -Dlibdir=lib -Dcpu_instruction_set=$generic_isa $use_shared
>
> +build build-mbuf-dynfield3 cc skipABI --buildtype=debug \
> + -Dmbuf_dynfield3_size=256 $use_shared
> +
> # 32-bit with default compiler
> if check_cc_flags '-m32' ; then
> target_override='i386-pc-linux-gnu'
> diff --git a/doc/guides/prog_guide/mbuf_lib.rst b/doc/guides/prog_guide/mbuf_lib.rst
> index cf64add109..ca0efb99c1 100644
> --- a/doc/guides/prog_guide/mbuf_lib.rst
> +++ b/doc/guides/prog_guide/mbuf_lib.rst
> @@ -234,6 +234,15 @@ The dynamic fields and flags are managed with the functions ``rte_mbuf_dyn*``.
>
> It is not possible to unregister fields or flags.
>
> +The build option ``mbuf_dynfield3_size`` can add extra cache-line-aligned
> +dynamic field storage to ``struct rte_mbuf``. This increases every mbuf by
> +the configured amount and changes the mbuf layout, so applications and
> +secondary processes must be built with the same value as the primary process.
> +The option defaults to ``0``. The extra storage is reserved for dynamic
> +fields registered with ``RTE_MBUF_DYNFIELD_F_NO_COPY``. These fields are not
> +copied by mbuf copy and clone operations. Dynamic fields registered without
> +this flag continue to use the existing copied dynamic-field storage.
> +
> .. _direct_indirect_buffer:
>
> Direct and Indirect Buffers
> diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
> index dec96ccbc7..cff68d7034 100644
> --- a/doc/guides/rel_notes/release_26_11.rst
> +++ b/doc/guides/rel_notes/release_26_11.rst
> @@ -60,6 +60,22 @@ New Features
> Added the experimental ``rte_cpu_socket_id()`` function
> to map an OS logical CPU ID to the NUMA socket containing that CPU.
>
> +* **Added optional extra mbuf dynamic field storage.**
> +
> + Added ``mbuf_dynfield3_size`` build option to enable a
> + cache-line-aligned ``dynfield3`` area in ``struct rte_mbuf``.
> + The configured size is defined as ``RTE_MBUF_DYNFIELD3_SIZE``
> + in ``rte_build_config.h``.
> + The extra storage is reserved for dynamic fields registered with
> + ``RTE_MBUF_DYNFIELD_F_NO_COPY``.
> + These fields are not copied by generic mbuf copy and clone operations.
> + Dynamic fields registered without this flag continue to use the existing
> + copied dynamic-field storage.
> + Applications and secondary processes must be built with the same
> + non-zero value as the primary process.
> + The ``mempool/octeontx`` driver is disabled when this option is
> + enabled because it requires a fixed 128-byte mbuf header.
> +
> * **Added TPID support to VLAN tag insertion.**
>
> Added ``rte_vlan_insert_tpid()`` to the net library.
> @@ -338,7 +354,6 @@ Known Issues
> Also, make sure to start the actual text at the margin.
> =======================================================
>
> -
> Tested Platforms
> ----------------
>
> diff --git a/drivers/mempool/octeontx/meson.build b/drivers/mempool/octeontx/meson.build
> index 3ccecac75d..01aece94be 100644
> --- a/drivers/mempool/octeontx/meson.build
> +++ b/drivers/mempool/octeontx/meson.build
> @@ -6,6 +6,11 @@ if not is_linux or not dpdk_conf.get('RTE_ARCH_64')
> reason = 'only supported on 64-bit Linux'
> subdir_done()
> endif
> +if get_option('mbuf_dynfield3_size') != 0
> + build = false
> + reason = 'requires sizeof(struct rte_mbuf) <= 128'
> + subdir_done()
> +endif
>
> sources = files(
> 'octeontx_fpavf.c',
> diff --git a/lib/mbuf/rte_mbuf_core.h b/lib/mbuf/rte_mbuf_core.h
> index 98b0bd9ca7..b4f204b268 100644
> --- a/lib/mbuf/rte_mbuf_core.h
> +++ b/lib/mbuf/rte_mbuf_core.h
> @@ -686,6 +686,12 @@ struct __rte_cache_aligned rte_mbuf {
> uint16_t timesync;
>
> uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
> +
> +#if RTE_MBUF_DYNFIELD3_SIZE > 0
> + alignas(RTE_CACHE_LINE_SIZE)
> + uint64_t dynfield3[RTE_MBUF_DYNFIELD3_SIZE / sizeof(uint64_t)];
> + /**< Reserved cache-line-aligned space for dynamic fields. */
> +#endif /* RTE_MBUF_DYNFIELD3_SIZE > 0 */
> };
>
> /**
> diff --git a/lib/mbuf/rte_mbuf_dyn.c b/lib/mbuf/rte_mbuf_dyn.c
> index 5987c9dee8..7cecbc9ee6 100644
> --- a/lib/mbuf/rte_mbuf_dyn.c
> +++ b/lib/mbuf/rte_mbuf_dyn.c
> @@ -3,6 +3,7 @@
> */
>
> #include <stdalign.h>
> +#include <stddef.h>
> #include <sys/queue.h>
> #include <stdint.h>
> #include <limits.h>
> @@ -51,7 +52,7 @@ struct mbuf_dyn_shm {
> * The value is the size of the biggest aligned element that
> * can fit in the zone.
> */
> - uint8_t free_space[sizeof(struct rte_mbuf)];
> + uint16_t free_space[sizeof(struct rte_mbuf)];
> /** Bitfield of available flags. */
> uint64_t free_flags;
> };
> @@ -135,6 +136,9 @@ init_shared_mem(void)
> #if !RTE_IOVA_IN_MBUF
> mark_free(dynfield2);
> #endif
> +#if RTE_MBUF_DYNFIELD3_SIZE > 0
> + mark_free(dynfield3);
> +#endif
>
> /* init free_flags */
> for (mask = RTE_MBUF_F_FIRST_FREE; mask <= RTE_MBUF_F_LAST_FREE; mask <<= 1)
> @@ -147,11 +151,48 @@ init_shared_mem(void)
> }
>
> /* check if this offset can be used */
> +static bool
> +dynfield_in_dynfield3(size_t offset, size_t size)
> +{
> +#if RTE_MBUF_DYNFIELD3_SIZE > 0
> + size_t dynfield3_offset = offsetof(struct rte_mbuf, dynfield3);
> +
> + return offset >= dynfield3_offset &&
> + size <= sizeof(((struct rte_mbuf *)0)->dynfield3) &&
> + offset - dynfield3_offset <= sizeof(((struct rte_mbuf *)0)->dynfield3) - size;
> +#else
> + RTE_SET_USED(offset);
> + RTE_SET_USED(size);
> + return false;
> +#endif
> +}
> +
> +static bool
> +dynfield_overlaps_dynfield3(size_t offset, size_t size)
> +{
> +#if RTE_MBUF_DYNFIELD3_SIZE > 0
> + size_t dynfield3_offset = offsetof(struct rte_mbuf, dynfield3);
> + size_t dynfield3_end = dynfield3_offset + sizeof(((struct rte_mbuf *)0)->dynfield3);
> +
> + return offset < dynfield3_end && offset + size > dynfield3_offset;
> +#else
> + RTE_SET_USED(offset);
> + RTE_SET_USED(size);
> + return false;
> +#endif
> +}
> +
> static int
> -check_offset(size_t offset, size_t size, size_t align)
> +check_offset(size_t offset, size_t size, size_t align, unsigned int flags)
> {
> size_t i;
>
> + if ((flags & RTE_MBUF_DYNFIELD_F_NO_COPY) != 0 &&
> + !dynfield_in_dynfield3(offset, size))
> + return -1;
> + if ((flags & RTE_MBUF_DYNFIELD_F_NO_COPY) == 0 &&
> + dynfield_overlaps_dynfield3(offset, size))
> + return -1;
> if ((offset & (align - 1)) != 0)
> return -1;
> if (offset + size > sizeof(struct rte_mbuf))
> @@ -268,7 +309,7 @@ __rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
> offset < sizeof(struct rte_mbuf);
> offset++) {
> if (check_offset(offset, params->size,
> - params->align) == 0 &&
> + params->align, params->flags) == 0 &&
> shm->free_space[offset] < best_zone) {
> best_zone = shm->free_space[offset];
> req = offset;
> @@ -279,7 +320,8 @@ __rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
> return -1;
> }
> } else {
> - if (check_offset(req, params->size, params->align) < 0) {
> + if (check_offset(req, params->size, params->align,
> + params->flags) < 0) {
> rte_errno = EBUSY;
> return -1;
> }
> @@ -342,7 +384,7 @@ rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
> rte_errno = EINVAL;
> return -1;
> }
> - if (params->flags != 0) {
> + if ((params->flags & ~RTE_MBUF_DYNFIELD_F_NO_COPY) != 0) {
> rte_errno = EINVAL;
> return -1;
> }
> @@ -573,7 +615,7 @@ void rte_mbuf_dyn_dump(FILE *out)
> for (i = 0; i < sizeof(struct rte_mbuf); i++) {
> if ((i % 8) == 0)
> fprintf(out, " %4.4zx: ", i);
> - fprintf(out, "%2.2x%s", shm->free_space[i],
> + fprintf(out, "%4.4x%s", shm->free_space[i],
> (i % 8 != 7) ? " " : "\n");
> }
> fprintf(out, "Free bit in mbuf->ol_flags (0 = occupied, 1 = free):\n");
> diff --git a/lib/mbuf/rte_mbuf_dyn.h b/lib/mbuf/rte_mbuf_dyn.h
> index 20ce505bb4..d9a46b3c33 100644
> --- a/lib/mbuf/rte_mbuf_dyn.h
> +++ b/lib/mbuf/rte_mbuf_dyn.h
> @@ -69,6 +69,7 @@
> #include <stdio.h>
> #include <stdint.h>
>
> +#include <rte_bitops.h>
> #include <rte_stdatomic.h>
>
> #ifdef __cplusplus
> @@ -80,6 +81,14 @@ extern "C" {
> */
> #define RTE_MBUF_DYN_NAMESIZE 64
>
> +/**
> + * Do not copy this dynamic field during mbuf clone or copy.
> + *
> + * Fields using this flag are allocated from the optional dynfield3 area
> + * configured by the mbuf_dynfield3_size build option.
> + */
> +#define RTE_MBUF_DYNFIELD_F_NO_COPY RTE_BIT32(0)
> +
> /**
> * Structure describing the parameters of a mbuf dynamic field.
> */
> @@ -87,7 +96,7 @@ struct rte_mbuf_dynfield {
> char name[RTE_MBUF_DYN_NAMESIZE]; /**< Name of the field. */
> size_t size; /**< The number of bytes to reserve. */
> size_t align; /**< The alignment constraint (power of 2). */
> - unsigned int flags; /**< Reserved for future use, must be 0. */
> + unsigned int flags; /**< Dynamic field flags. */
> };
>
> /**
> diff --git a/meson_options.txt b/meson_options.txt
> index e28d24054c..1248498a3d 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -44,6 +44,8 @@ option('max_numa_nodes', type: 'string', value: 'default', description:
> 'Set the highest NUMA node supported by EAL; "default" is different per-arch, "detect" detects the highest NUMA node on the build machine.')
> option('enable_iova_as_pa', type: 'boolean', value: true, description:
> 'Support the use of physical addresses for IO addresses, such as used by UIO or VFIO in no-IOMMU mode. When disabled, DPDK can only run with IOMMU support for address mappings, but will have more space available in the mbuf structure.')
> +option('mbuf_dynfield3_size', type: 'integer', min: 0, value: 0, description:
> + 'Size of optional extra mbuf dynamic field area, in bytes.')
> option('mbuf_refcnt_atomic', type: 'boolean', value: true, description:
> 'Atomically access the mbuf refcnt.')
> option('platform', type: 'string', value: 'native', description:
^ permalink raw reply [flat|nested] 24+ messages in thread
* RE: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-29 11:59 ` Konstantin Ananyev
@ 2026-09-29 12:44 ` Morten Brørup
2026-09-29 13:12 ` Konstantin Ananyev
0 siblings, 1 reply; 24+ messages in thread
From: Morten Brørup @ 2026-09-29 12:44 UTC (permalink / raw)
To: Konstantin Ananyev, Randy L Tice, dev
Cc: Bruce Richardson, Harman Kalra, Stephen Hemminger
> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
> Sent: Tuesday, 29 September 2026 14.00
>
> 28.09.2026 19:16, Randy L Tice пишет:
> > From: Randy L Tice <rtice@cisco.com>
> > Date: Thu, 03 Sep 2026 09:13:28 -0400
> >
> > Add build-time support for optional cache-line-aligned dynamic-field
> > storage at the end of struct rte_mbuf.
> >
> > The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE in
> > rte_build_config.h. A non-zero value enables the extra area and grows
> > every mbuf by the configured amount.
> I am strongly opposed to that patch.
> Inside mbuf we already do have priv_size that allows user to store
> his/her specific
> data straight after rte_mbuf in adjacent manner.
> It worked well so far for many use-cases (including VPP) and I don't
> see any
> reason why this is not enough.
> From other side - making size of core rte_mbuf configurable at run-
> time,
> will affect DPDK ABI stability in a negative way.
> Fro my perspective it is much plausible in terms of ABI stability and
> predictability
> to have just one fixed layout for the mbuf.
> Konstantin
The private data area (priv_size) is independent per mbuf pool, and selected at run-time when creating each pool. As Randy explained in the RFC, this is unavailable for mbuf pools created by other components.
Mbuf dynamic fields are shared across all mbuf pools, and serves the need with an existing API. So I am strongly in favor of using the mbuf dynamic fields API for this.
I agree with Konstantin that it would be optimal if the size of the added dynfields area was run-time configurable (as an EAL startup parameter).
However, such a modification to the mbuf library would also require that the performance cost in the dataplane is negligible. We don't want to compromise on mbuf performance for applications not using this new feature.
Randy,
Could you please explore such an approach?
-Morten
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-29 12:44 ` Morten Brørup
@ 2026-09-29 13:12 ` Konstantin Ananyev
2026-09-29 13:34 ` Morten Brørup
0 siblings, 1 reply; 24+ messages in thread
From: Konstantin Ananyev @ 2026-09-29 13:12 UTC (permalink / raw)
To: Morten Brørup, Randy L Tice, dev
Cc: Bruce Richardson, Harman Kalra, Stephen Hemminger
29.09.2026 13:44, Morten Brørup пишет:
>> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
>> Sent: Tuesday, 29 September 2026 14.00
>>
>> 28.09.2026 19:16, Randy L Tice пишет:
>>> From: Randy L Tice <rtice@cisco.com>
>>> Date: Thu, 03 Sep 2026 09:13:28 -0400
>>>
>>> Add build-time support for optional cache-line-aligned dynamic-field
>>> storage at the end of struct rte_mbuf.
>>>
>>> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE in
>>> rte_build_config.h. A non-zero value enables the extra area and grows
>>> every mbuf by the configured amount.
>> I am strongly opposed to that patch.
>> Inside mbuf we already do have priv_size that allows user to store
>> his/her specific
>> data straight after rte_mbuf in adjacent manner.
>> It worked well so far for many use-cases (including VPP) and I don't
>> see any
>> reason why this is not enough.
>> From other side - making size of core rte_mbuf configurable at run-
>> time,
>> will affect DPDK ABI stability in a negative way.
>> Fro my perspective it is much plausible in terms of ABI stability and
>> predictability
>> to have just one fixed layout for the mbuf.
>> Konstantin
> The private data area (priv_size) is independent per mbuf pool, and selected at run-time when creating each pool. As Randy explained in the RFC, this is unavailable for mbuf pools created by other components.
I think it should be trivial to enforce minimal priv_size across all
mbuf pools what will be obeyed by different components
(as long as they do use rte_pktmbuf_pool_create() and friends):
1) introduce new EAL parameter 'mbuf-min-priv-size' or so (keep default
as zero)
2) make rte_pktmbuf_pool_create_by_ops() and
rte_pktmbuf_pool_create_extbuf() to check that input paramter
'priv_size' GE then value specified by EAL parameter, if so then return
an error.
>
> Mbuf dynamic fields are shared across all mbuf pools, and serves the need with an existing API. So I am strongly in favor of using the mbuf dynamic fields API for this.
>
> I agree with Konstantin that it would be optimal if the size of the added dynfields area was run-time configurable (as an EAL startup parameter).
> However, such a modification to the mbuf library would also require that the performance cost in the dataplane is negligible. We don't want to compromise on mbuf performance for applications not using this new feature.
>
> Randy,
> Could you please explore such an approach?
>
> -Morten
^ permalink raw reply [flat|nested] 24+ messages in thread
* RE: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-29 13:12 ` Konstantin Ananyev
@ 2026-09-29 13:34 ` Morten Brørup
2026-09-29 14:14 ` Konstantin Ananyev
0 siblings, 1 reply; 24+ messages in thread
From: Morten Brørup @ 2026-09-29 13:34 UTC (permalink / raw)
To: Konstantin Ananyev, Randy L Tice, dev
Cc: Bruce Richardson, Harman Kalra, Stephen Hemminger
> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
> Sent: Tuesday, 29 September 2026 15.13
>
> 29.09.2026 13:44, Morten Brørup пишет:
> >> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
> >> Sent: Tuesday, 29 September 2026 14.00
> >>
> >> 28.09.2026 19:16, Randy L Tice пишет:
> >>> From: Randy L Tice <rtice@cisco.com>
> >>> Date: Thu, 03 Sep 2026 09:13:28 -0400
> >>>
> >>> Add build-time support for optional cache-line-aligned dynamic-
> field
> >>> storage at the end of struct rte_mbuf.
> >>>
> >>> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE
> in
> >>> rte_build_config.h. A non-zero value enables the extra area and
> grows
> >>> every mbuf by the configured amount.
> >> I am strongly opposed to that patch.
> >> Inside mbuf we already do have priv_size that allows user to store
> >> his/her specific
> >> data straight after rte_mbuf in adjacent manner.
> >> It worked well so far for many use-cases (including VPP) and I don't
> >> see any
> >> reason why this is not enough.
> >> From other side - making size of core rte_mbuf configurable at
> run-
> >> time,
> >> will affect DPDK ABI stability in a negative way.
> >> Fro my perspective it is much plausible in terms of ABI stability
> and
> >> predictability
> >> to have just one fixed layout for the mbuf.
> >> Konstantin
> > The private data area (priv_size) is independent per mbuf pool, and
> selected at run-time when creating each pool. As Randy explained in the
> RFC, this is unavailable for mbuf pools created by other components.
>
> I think it should be trivial to enforce minimal priv_size across all
> mbuf pools what will be obeyed by different components
> (as long as they do use rte_pktmbuf_pool_create() and friends):
> 1) introduce new EAL parameter 'mbuf-min-priv-size' or so (keep default
> as zero)
> 2) make rte_pktmbuf_pool_create_by_ops() and
> rte_pktmbuf_pool_create_extbuf() to check that input paramter
> 'priv_size' GE then value specified by EAL parameter, if so then return
> an error.
The private data area cannot be used.
Let's say one module creates an mbuf pool with priv_size of 8, and uses those 8 bytes,
and some second module creates an mbuf pool with priv_size of 16, and uses those 16 bytes.
How should a module (or the application) know at which offset to store its private data without overwriting the private data of other modules?
The mbuf dynamic field's registry manages centrally where each module should store its own data, and the data is even accessible by other modules (because they can fetch the offset to the data from the registry).
>
> >
> > Mbuf dynamic fields are shared across all mbuf pools, and serves the
> need with an existing API. So I am strongly in favor of using the mbuf
> dynamic fields API for this.
> >
> > I agree with Konstantin that it would be optimal if the size of the
> added dynfields area was run-time configurable (as an EAL startup
> parameter).
> > However, such a modification to the mbuf library would also require
> that the performance cost in the dataplane is negligible. We don't want
> to compromise on mbuf performance for applications not using this new
> feature.
> >
> > Randy,
> > Could you please explore such an approach?
> >
> > -Morten
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-29 13:34 ` Morten Brørup
@ 2026-09-29 14:14 ` Konstantin Ananyev
2026-09-29 14:44 ` Randy Tice (rtice)
0 siblings, 1 reply; 24+ messages in thread
From: Konstantin Ananyev @ 2026-09-29 14:14 UTC (permalink / raw)
To: Morten Brørup, Randy L Tice, dev
Cc: Bruce Richardson, Harman Kalra, Stephen Hemminger
>> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
>> Sent: Tuesday, 29 September 2026 15.13
>>
>> 29.09.2026 13:44, Morten Brørup пишет:
>>>> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
>>>> Sent: Tuesday, 29 September 2026 14.00
>>>>
>>>> 28.09.2026 19:16, Randy L Tice пишет:
>>>>> From: Randy L Tice <rtice@cisco.com>
>>>>> Date: Thu, 03 Sep 2026 09:13:28 -0400
>>>>>
>>>>> Add build-time support for optional cache-line-aligned dynamic-
>> field
>>>>> storage at the end of struct rte_mbuf.
>>>>>
>>>>> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE
>> in
>>>>> rte_build_config.h. A non-zero value enables the extra area and
>> grows
>>>>> every mbuf by the configured amount.
>>>> I am strongly opposed to that patch.
>>>> Inside mbuf we already do have priv_size that allows user to store
>>>> his/her specific
>>>> data straight after rte_mbuf in adjacent manner.
>>>> It worked well so far for many use-cases (including VPP) and I don't
>>>> see any
>>>> reason why this is not enough.
>>>> From other side - making size of core rte_mbuf configurable at
>> run-
>>>> time,
>>>> will affect DPDK ABI stability in a negative way.
>>>> Fro my perspective it is much plausible in terms of ABI stability
>> and
>>>> predictability
>>>> to have just one fixed layout for the mbuf.
>>>> Konstantin
>>> The private data area (priv_size) is independent per mbuf pool, and
>> selected at run-time when creating each pool. As Randy explained in the
>> RFC, this is unavailable for mbuf pools created by other components.
>>
>> I think it should be trivial to enforce minimal priv_size across all
>> mbuf pools what will be obeyed by different components
>> (as long as they do use rte_pktmbuf_pool_create() and friends):
>> 1) introduce new EAL parameter 'mbuf-min-priv-size' or so (keep default
>> as zero)
>> 2) make rte_pktmbuf_pool_create_by_ops() and
>> rte_pktmbuf_pool_create_extbuf() to check that input paramter
>> 'priv_size' GE then value specified by EAL parameter, if so then return
>> an error.
> The private data area cannot be used.
> Let's say one module creates an mbuf pool with priv_size of 8, and uses those 8 bytes,
> and some second module creates an mbuf pool with priv_size of 16, and uses those 16 bytes.
>
> How should a module (or the application) know at which offset to store its private data without overwriting the private data of other modules?
>
> The mbuf dynamic field's registry manages centrally where each module should store its own data, and the data is even accessible by other modules (because they can fetch the offset to the data from the registry)
ok, I see, you need an ability to register/unregister/query layout for
that private buffer (what we have now for dynfields).
Then yes, if we'll add an ability to expand mbuf dynfield[] buffer that
might be useful, and probably will become
more popular then current 'priv_size' apporach.
But I believe it shouldn't be a build time option.
>
>>> Mbuf dynamic fields are shared across all mbuf pools, and serves the
>> need with an existing API. So I am strongly in favor of using the mbuf
>> dynamic fields API for this.
>>> I agree with Konstantin that it would be optimal if the size of the
>> added dynfields area was run-time configurable (as an EAL startup
>> parameter).
>>> However, such a modification to the mbuf library would also require
>> that the performance cost in the dataplane is negligible. We don't want
>> to compromise on mbuf performance for applications not using this new
>> feature.
>>> Randy,
>>> Could you please explore such an approach?
>>>
>>> -Morten
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-29 14:14 ` Konstantin Ananyev
@ 2026-09-29 14:44 ` Randy Tice (rtice)
2026-09-29 15:20 ` Morten Brørup
2026-09-29 19:26 ` Konstantin Ananyev
0 siblings, 2 replies; 24+ messages in thread
From: Randy Tice (rtice) @ 2026-09-29 14:44 UTC (permalink / raw)
To: Konstantin Ananyev, Morten Brørup, dev@dpdk.org
Cc: Bruce Richardson, Harman Kalra, Stephen Hemminger
[-- Attachment #1: Type: text/plain, Size: 7067 bytes --]
Hi all,
Thanks for the discussion. We are now where I had hoped we’d get to during
RFC but we are here.
Konstantin, I understand your concern about making sizeof(struct rte_mbuf)
depend on a build-time option. That can create different mbuf layouts between
DPDK builds that otherwise present the same ABI/version, which is not a good
property for a core public structure.
After thinking through this again, I think the current patch may be trying too
hard to make this a dynamic-field allocator feature. The actual requirement is
simpler: a fixed global per-mbuf metadata area that is present in every
pktmbuf object, separate from ordinary application private data, and not
copied by mbuf copy/clone helpers.
The mbuf structure change would look roughly like this:
struct rte_mbuf {
...
uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
+
+ alignas(RTE_CACHE_LINE_SIZE)
+ uint8_t metadata[];
+ /**< Optional cache-line-aligned per-mbuf metadata area. */
};
Since this is a flexible array member, it does not change sizeof(struct
rte_mbuf). The object layout would become:
struct rte_mbuf fixed header
global per-mbuf metadata area
application private data
packet data buffer
With that layout, this could be sized at EAL init time rather than by a build
option, for example:
--mbuf-metadata-size=256
That avoids creating different DPDK builds with different mbuf struct sizes or
different build-time ABI expectations. The configured size would be part of
the process/runtime configuration instead of requiring applications,
libraries, and package providers to agree on a compile-time define.
The official mbuf helpers would account for this area before ordinary
priv_size, so application private data remains available and does not overlap
with the global metadata area.
This would also avoid changing the existing dynamic-field allocator and copy
semantics. The area would not be part of the dynamic-field registry; it would
be explicit per-mbuf metadata storage for applications that deliberately
enable it.
That seems to address the main concerns:
- sizeof(struct rte_mbuf) remains fixed for ABI purposes.
- the metadata area is globally present across pktmbuf pools when enabled.
- ordinary priv_size remains separate and available.
- dynamic-field allocator/copy behavior remains unchanged.
- users that do not enable the EAL option pay no extra per-mbuf storage cost.
- applications do not need to be built against a different mbuf-size define.
If this direction is acceptable, I can take a look at what it means in
practice for EAL configuration, mbuf layout helpers, pool constructors, and
places that currently do direct object-layout math.
Thanks,
-rt
From: Konstantin Ananyev <konstantin.v.ananyev@yandex.ru>
Date: Tuesday, September 29, 2026 at 10:14 AM
To: Morten Brørup <mb@smartsharesystems.com>; Randy Tice (rtice) <rtice@cisco.com>; dev@dpdk.org <dev@dpdk.org>
Cc: Bruce Richardson <bruce.richardson@intel.com>; Harman Kalra <hkalra@marvell.com>; Stephen Hemminger <stephen@networkplumber.org>
Subject: Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
>> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
>> Sent: Tuesday, 29 September 2026 15.13
>>
>> 29.09.2026 13:44, Morten Brørup пишет:
>>>> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
>>>> Sent: Tuesday, 29 September 2026 14.00
>>>>
>>>> 28.09.2026 19:16, Randy L Tice пишет:
>>>>> From: Randy L Tice <rtice@cisco.com>
>>>>> Date: Thu, 03 Sep 2026 09:13:28 -0400
>>>>>
>>>>> Add build-time support for optional cache-line-aligned dynamic-
>> field
>>>>> storage at the end of struct rte_mbuf.
>>>>>
>>>>> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE
>> in
>>>>> rte_build_config.h. A non-zero value enables the extra area and
>> grows
>>>>> every mbuf by the configured amount.
>>>> I am strongly opposed to that patch.
>>>> Inside mbuf we already do have priv_size that allows user to store
>>>> his/her specific
>>>> data straight after rte_mbuf in adjacent manner.
>>>> It worked well so far for many use-cases (including VPP) and I don't
>>>> see any
>>>> reason why this is not enough.
>>>> From other side - making size of core rte_mbuf configurable at
>> run-
>>>> time,
>>>> will affect DPDK ABI stability in a negative way.
>>>> Fro my perspective it is much plausible in terms of ABI stability
>> and
>>>> predictability
>>>> to have just one fixed layout for the mbuf.
>>>> Konstantin
>>> The private data area (priv_size) is independent per mbuf pool, and
>> selected at run-time when creating each pool. As Randy explained in the
>> RFC, this is unavailable for mbuf pools created by other components.
>>
>> I think it should be trivial to enforce minimal priv_size across all
>> mbuf pools what will be obeyed by different components
>> (as long as they do use rte_pktmbuf_pool_create() and friends):
>> 1) introduce new EAL parameter 'mbuf-min-priv-size' or so (keep default
>> as zero)
>> 2) make rte_pktmbuf_pool_create_by_ops() and
>> rte_pktmbuf_pool_create_extbuf() to check that input paramter
>> 'priv_size' GE then value specified by EAL parameter, if so then return
>> an error.
> The private data area cannot be used.
> Let's say one module creates an mbuf pool with priv_size of 8, and uses those 8 bytes,
> and some second module creates an mbuf pool with priv_size of 16, and uses those 16 bytes.
>
> How should a module (or the application) know at which offset to store its private data without overwriting the private data of other modules?
>
> The mbuf dynamic field's registry manages centrally where each module should store its own data, and the data is even accessible by other modules (because they can fetch the offset to the data from the registry)
ok, I see, you need an ability to register/unregister/query layout for
that private buffer (what we have now for dynfields).
Then yes, if we'll add an ability to expand mbuf dynfield[] buffer that
might be useful, and probably will become
more popular then current 'priv_size' apporach.
But I believe it shouldn't be a build time option.
>
>>> Mbuf dynamic fields are shared across all mbuf pools, and serves the
>> need with an existing API. So I am strongly in favor of using the mbuf
>> dynamic fields API for this.
>>> I agree with Konstantin that it would be optimal if the size of the
>> added dynfields area was run-time configurable (as an EAL startup
>> parameter).
>>> However, such a modification to the mbuf library would also require
>> that the performance cost in the dataplane is negligible. We don't want
>> to compromise on mbuf performance for applications not using this new
>> feature.
>>> Randy,
>>> Could you please explore such an approach?
>>>
>>> -Morten
[-- Attachment #2: Type: text/html, Size: 17323 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* RE: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-29 14:44 ` Randy Tice (rtice)
@ 2026-09-29 15:20 ` Morten Brørup
2026-09-29 19:03 ` Randy Tice (rtice)
2026-09-29 19:26 ` Konstantin Ananyev
1 sibling, 1 reply; 24+ messages in thread
From: Morten Brørup @ 2026-09-29 15:20 UTC (permalink / raw)
To: Randy Tice (rtice), Konstantin Ananyev, dev
Cc: Bruce Richardson, Harman Kalra, Stephen Hemminger
[-- Attachment #1: Type: text/plain, Size: 8518 bytes --]
Randy,
This is very close to what I suggested you explore.
But one piece is missing:
Registering fields in this metadata area should be managed through the dynamic mbuf fields API.
Without a central registry, only one module can use the new metadata area; it cannot be used by multiple modules without coordination.
And instead of rolling your own registry of fields in the metadata area, just reuse the dynamic mbuf fields machinery.
I agree with your proposed mbuf layout.
There will be a performance cost for accessing the mbuf private data: rte_mbuf_to_priv() will change from adding a simple constant offset (sizeof(struct rte_mbuf)) to adding the value of a global variable holding the offset, reflecting the startup-time configured metadata area size.
The global variable will be hot in the cache when working on mbuf bursts, so I think this performance cost will be insignificant.
Venlig hilsen / Kind regards,
-Morten Brørup
From: Randy Tice (rtice) [mailto:rtice@cisco.com]
Sent: Tuesday, 29 September 2026 16.45
To: Konstantin Ananyev; Morten Brørup; dev@dpdk.org
Cc: Bruce Richardson; Harman Kalra; Stephen Hemminger
Subject: Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
Hi all,
Thanks for the discussion. We are now where I had hoped we’d get to during
RFC but we are here.
Konstantin, I understand your concern about making sizeof(struct rte_mbuf)
depend on a build-time option. That can create different mbuf layouts between
DPDK builds that otherwise present the same ABI/version, which is not a good
property for a core public structure.
After thinking through this again, I think the current patch may be trying too
hard to make this a dynamic-field allocator feature. The actual requirement is
simpler: a fixed global per-mbuf metadata area that is present in every
pktmbuf object, separate from ordinary application private data, and not
copied by mbuf copy/clone helpers.
The mbuf structure change would look roughly like this:
struct rte_mbuf {
...
uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
+
+ alignas(RTE_CACHE_LINE_SIZE)
+ uint8_t metadata[];
+ /**< Optional cache-line-aligned per-mbuf metadata area. */
};
Since this is a flexible array member, it does not change sizeof(struct
rte_mbuf). The object layout would become:
struct rte_mbuf fixed header
global per-mbuf metadata area
application private data
packet data buffer
With that layout, this could be sized at EAL init time rather than by a build
option, for example:
--mbuf-metadata-size=256
That avoids creating different DPDK builds with different mbuf struct sizes or
different build-time ABI expectations. The configured size would be part of
the process/runtime configuration instead of requiring applications,
libraries, and package providers to agree on a compile-time define.
The official mbuf helpers would account for this area before ordinary
priv_size, so application private data remains available and does not overlap
with the global metadata area.
This would also avoid changing the existing dynamic-field allocator and copy
semantics. The area would not be part of the dynamic-field registry; it would
be explicit per-mbuf metadata storage for applications that deliberately
enable it.
That seems to address the main concerns:
- sizeof(struct rte_mbuf) remains fixed for ABI purposes.
- the metadata area is globally present across pktmbuf pools when enabled.
- ordinary priv_size remains separate and available.
- dynamic-field allocator/copy behavior remains unchanged.
- users that do not enable the EAL option pay no extra per-mbuf storage cost.
- applications do not need to be built against a different mbuf-size define.
If this direction is acceptable, I can take a look at what it means in
practice for EAL configuration, mbuf layout helpers, pool constructors, and
places that currently do direct object-layout math.
Thanks,
-rt
From: Konstantin Ananyev <konstantin.v.ananyev@yandex.ru>
Date: Tuesday, September 29, 2026 at 10:14 AM
To: Morten Brørup <mb@smartsharesystems.com>; Randy Tice (rtice) <rtice@cisco.com>; dev@dpdk.org <dev@dpdk.org>
Cc: Bruce Richardson <bruce.richardson@intel.com>; Harman Kalra <hkalra@marvell.com>; Stephen Hemminger <stephen@networkplumber.org>
Subject: Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
>> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
>> Sent: Tuesday, 29 September 2026 15.13
>>
>> 29.09.2026 13:44, Morten Brørup пишет:
>>>> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
>>>> Sent: Tuesday, 29 September 2026 14.00
>>>>
>>>> 28.09.2026 19:16, Randy L Tice пишет:
>>>>> From: Randy L Tice <rtice@cisco.com>
>>>>> Date: Thu, 03 Sep 2026 09:13:28 -0400
>>>>>
>>>>> Add build-time support for optional cache-line-aligned dynamic-
>> field
>>>>> storage at the end of struct rte_mbuf.
>>>>>
>>>>> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE
>> in
>>>>> rte_build_config.h. A non-zero value enables the extra area and
>> grows
>>>>> every mbuf by the configured amount.
>>>> I am strongly opposed to that patch.
>>>> Inside mbuf we already do have priv_size that allows user to store
>>>> his/her specific
>>>> data straight after rte_mbuf in adjacent manner.
>>>> It worked well so far for many use-cases (including VPP) and I don't
>>>> see any
>>>> reason why this is not enough.
>>>> From other side - making size of core rte_mbuf configurable at
>> run-
>>>> time,
>>>> will affect DPDK ABI stability in a negative way.
>>>> Fro my perspective it is much plausible in terms of ABI stability
>> and
>>>> predictability
>>>> to have just one fixed layout for the mbuf.
>>>> Konstantin
>>> The private data area (priv_size) is independent per mbuf pool, and
>> selected at run-time when creating each pool. As Randy explained in the
>> RFC, this is unavailable for mbuf pools created by other components.
>>
>> I think it should be trivial to enforce minimal priv_size across all
>> mbuf pools what will be obeyed by different components
>> (as long as they do use rte_pktmbuf_pool_create() and friends):
>> 1) introduce new EAL parameter 'mbuf-min-priv-size' or so (keep default
>> as zero)
>> 2) make rte_pktmbuf_pool_create_by_ops() and
>> rte_pktmbuf_pool_create_extbuf() to check that input paramter
>> 'priv_size' GE then value specified by EAL parameter, if so then return
>> an error.
> The private data area cannot be used.
> Let's say one module creates an mbuf pool with priv_size of 8, and uses those 8 bytes,
> and some second module creates an mbuf pool with priv_size of 16, and uses those 16 bytes.
>
> How should a module (or the application) know at which offset to store its private data without overwriting the private data of other modules?
>
> The mbuf dynamic field's registry manages centrally where each module should store its own data, and the data is even accessible by other modules (because they can fetch the offset to the data from the registry)
ok, I see, you need an ability to register/unregister/query layout for
that private buffer (what we have now for dynfields).
Then yes, if we'll add an ability to expand mbuf dynfield[] buffer that
might be useful, and probably will become
more popular then current 'priv_size' apporach.
But I believe it shouldn't be a build time option.
>
>>> Mbuf dynamic fields are shared across all mbuf pools, and serves the
>> need with an existing API. So I am strongly in favor of using the mbuf
>> dynamic fields API for this.
>>> I agree with Konstantin that it would be optimal if the size of the
>> added dynfields area was run-time configurable (as an EAL startup
>> parameter).
>>> However, such a modification to the mbuf library would also require
>> that the performance cost in the dataplane is negligible. We don't want
>> to compromise on mbuf performance for applications not using this new
>> feature.
>>> Randy,
>>> Could you please explore such an approach?
>>>
>>> -Morten
[-- Attachment #2: Type: text/html, Size: 21299 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-29 15:20 ` Morten Brørup
@ 2026-09-29 19:03 ` Randy Tice (rtice)
2026-09-29 19:28 ` Konstantin Ananyev
0 siblings, 1 reply; 24+ messages in thread
From: Randy Tice (rtice) @ 2026-09-29 19:03 UTC (permalink / raw)
To: Morten Brørup, Konstantin Ananyev, dev@dpdk.org
Cc: Bruce Richardson, Harman Kalra, Stephen Hemminger
[-- Attachment #1: Type: text/plain, Size: 8791 bytes --]
Konstantin,
Does this path work for you?
-rt
From: Morten Brørup <mb@smartsharesystems.com>
Date: Tuesday, September 29, 2026 at 11:20 AM
To: Randy Tice (rtice) <rtice@cisco.com>; Konstantin Ananyev <konstantin.v.ananyev@yandex.ru>; dev@dpdk.org <dev@dpdk.org>
Cc: Bruce Richardson <bruce.richardson@intel.com>; Harman Kalra <hkalra@marvell.com>; Stephen Hemminger <stephen@networkplumber.org>
Subject: RE: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
Randy,
This is very close to what I suggested you explore.
But one piece is missing:
Registering fields in this metadata area should be managed through the dynamic mbuf fields API.
Without a central registry, only one module can use the new metadata area; it cannot be used by multiple modules without coordination.
And instead of rolling your own registry of fields in the metadata area, just reuse the dynamic mbuf fields machinery.
I agree with your proposed mbuf layout.
There will be a performance cost for accessing the mbuf private data: rte_mbuf_to_priv() will change from adding a simple constant offset (sizeof(struct rte_mbuf)) to adding the value of a global variable holding the offset, reflecting the startup-time configured metadata area size.
The global variable will be hot in the cache when working on mbuf bursts, so I think this performance cost will be insignificant.
Venlig hilsen / Kind regards,
-Morten Brørup
From: Randy Tice (rtice) [mailto:rtice@cisco.com]
Sent: Tuesday, 29 September 2026 16.45
To: Konstantin Ananyev; Morten Brørup; dev@dpdk.org
Cc: Bruce Richardson; Harman Kalra; Stephen Hemminger
Subject: Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
Hi all,
Thanks for the discussion. We are now where I had hoped we’d get to during
RFC but we are here.
Konstantin, I understand your concern about making sizeof(struct rte_mbuf)
depend on a build-time option. That can create different mbuf layouts between
DPDK builds that otherwise present the same ABI/version, which is not a good
property for a core public structure.
After thinking through this again, I think the current patch may be trying too
hard to make this a dynamic-field allocator feature. The actual requirement is
simpler: a fixed global per-mbuf metadata area that is present in every
pktmbuf object, separate from ordinary application private data, and not
copied by mbuf copy/clone helpers.
The mbuf structure change would look roughly like this:
struct rte_mbuf {
...
uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
+
+ alignas(RTE_CACHE_LINE_SIZE)
+ uint8_t metadata[];
+ /**< Optional cache-line-aligned per-mbuf metadata area. */
};
Since this is a flexible array member, it does not change sizeof(struct
rte_mbuf). The object layout would become:
struct rte_mbuf fixed header
global per-mbuf metadata area
application private data
packet data buffer
With that layout, this could be sized at EAL init time rather than by a build
option, for example:
--mbuf-metadata-size=256
That avoids creating different DPDK builds with different mbuf struct sizes or
different build-time ABI expectations. The configured size would be part of
the process/runtime configuration instead of requiring applications,
libraries, and package providers to agree on a compile-time define.
The official mbuf helpers would account for this area before ordinary
priv_size, so application private data remains available and does not overlap
with the global metadata area.
This would also avoid changing the existing dynamic-field allocator and copy
semantics. The area would not be part of the dynamic-field registry; it would
be explicit per-mbuf metadata storage for applications that deliberately
enable it.
That seems to address the main concerns:
- sizeof(struct rte_mbuf) remains fixed for ABI purposes.
- the metadata area is globally present across pktmbuf pools when enabled.
- ordinary priv_size remains separate and available.
- dynamic-field allocator/copy behavior remains unchanged.
- users that do not enable the EAL option pay no extra per-mbuf storage cost.
- applications do not need to be built against a different mbuf-size define.
If this direction is acceptable, I can take a look at what it means in
practice for EAL configuration, mbuf layout helpers, pool constructors, and
places that currently do direct object-layout math.
Thanks,
-rt
From: Konstantin Ananyev <konstantin.v.ananyev@yandex.ru>
Date: Tuesday, September 29, 2026 at 10:14 AM
To: Morten Brørup <mb@smartsharesystems.com>; Randy Tice (rtice) <rtice@cisco.com>; dev@dpdk.org <dev@dpdk.org>
Cc: Bruce Richardson <bruce.richardson@intel.com>; Harman Kalra <hkalra@marvell.com>; Stephen Hemminger <stephen@networkplumber.org>
Subject: Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
>> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
>> Sent: Tuesday, 29 September 2026 15.13
>>
>> 29.09.2026 13:44, Morten Brørup пишет:
>>>> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru]
>>>> Sent: Tuesday, 29 September 2026 14.00
>>>>
>>>> 28.09.2026 19:16, Randy L Tice пишет:
>>>>> From: Randy L Tice <rtice@cisco.com>
>>>>> Date: Thu, 03 Sep 2026 09:13:28 -0400
>>>>>
>>>>> Add build-time support for optional cache-line-aligned dynamic-
>> field
>>>>> storage at the end of struct rte_mbuf.
>>>>>
>>>>> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE
>> in
>>>>> rte_build_config.h. A non-zero value enables the extra area and
>> grows
>>>>> every mbuf by the configured amount.
>>>> I am strongly opposed to that patch.
>>>> Inside mbuf we already do have priv_size that allows user to store
>>>> his/her specific
>>>> data straight after rte_mbuf in adjacent manner.
>>>> It worked well so far for many use-cases (including VPP) and I don't
>>>> see any
>>>> reason why this is not enough.
>>>> From other side - making size of core rte_mbuf configurable at
>> run-
>>>> time,
>>>> will affect DPDK ABI stability in a negative way.
>>>> Fro my perspective it is much plausible in terms of ABI stability
>> and
>>>> predictability
>>>> to have just one fixed layout for the mbuf.
>>>> Konstantin
>>> The private data area (priv_size) is independent per mbuf pool, and
>> selected at run-time when creating each pool. As Randy explained in the
>> RFC, this is unavailable for mbuf pools created by other components.
>>
>> I think it should be trivial to enforce minimal priv_size across all
>> mbuf pools what will be obeyed by different components
>> (as long as they do use rte_pktmbuf_pool_create() and friends):
>> 1) introduce new EAL parameter 'mbuf-min-priv-size' or so (keep default
>> as zero)
>> 2) make rte_pktmbuf_pool_create_by_ops() and
>> rte_pktmbuf_pool_create_extbuf() to check that input paramter
>> 'priv_size' GE then value specified by EAL parameter, if so then return
>> an error.
> The private data area cannot be used.
> Let's say one module creates an mbuf pool with priv_size of 8, and uses those 8 bytes,
> and some second module creates an mbuf pool with priv_size of 16, and uses those 16 bytes.
>
> How should a module (or the application) know at which offset to store its private data without overwriting the private data of other modules?
>
> The mbuf dynamic field's registry manages centrally where each module should store its own data, and the data is even accessible by other modules (because they can fetch the offset to the data from the registry)
ok, I see, you need an ability to register/unregister/query layout for
that private buffer (what we have now for dynfields).
Then yes, if we'll add an ability to expand mbuf dynfield[] buffer that
might be useful, and probably will become
more popular then current 'priv_size' apporach.
But I believe it shouldn't be a build time option.
>
>>> Mbuf dynamic fields are shared across all mbuf pools, and serves the
>> need with an existing API. So I am strongly in favor of using the mbuf
>> dynamic fields API for this.
>>> I agree with Konstantin that it would be optimal if the size of the
>> added dynfields area was run-time configurable (as an EAL startup
>> parameter).
>>> However, such a modification to the mbuf library would also require
>> that the performance cost in the dataplane is negligible. We don't want
>> to compromise on mbuf performance for applications not using this new
>> feature.
>>> Randy,
>>> Could you please explore such an approach?
>>>
>>> -Morten
[-- Attachment #2: Type: text/html, Size: 30798 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-29 14:44 ` Randy Tice (rtice)
2026-09-29 15:20 ` Morten Brørup
@ 2026-09-29 19:26 ` Konstantin Ananyev
1 sibling, 0 replies; 24+ messages in thread
From: Konstantin Ananyev @ 2026-09-29 19:26 UTC (permalink / raw)
To: Randy Tice (rtice), Morten Brørup, dev@dpdk.org
Cc: Bruce Richardson, Harman Kalra, Stephen Hemminger
Hi Randy.
> Hi all,
>
> Thanks for the discussion. We are now where I had hoped we’d get to
> during
> RFC but we are here.
>
> Konstantin, I understand your concern about making sizeof(struct
> rte_mbuf)
> depend on a build-time option. That can create different mbuf
> layouts between
> DPDK builds that otherwise present the same ABI/version, which is
> not a good
> property for a core public structure.
>
> After thinking through this again, I think the current patch may be
> trying too
> hard to make this a dynamic-field allocator feature. The actual
> requirement is
> simpler: a fixed global per-mbuf metadata area that is present in every
> pktmbuf object, separate from ordinary application private data, and not
> copied by mbuf copy/clone helpers.
>
> The mbuf structure change would look roughly like this:
>
> struct rte_mbuf {
> ...
> uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
> +
> + alignas(RTE_CACHE_LINE_SIZE)
> + uint8_t metadata[];
> + /**< Optional cache-line-aligned per-mbuf metadata area. */
> };
>
> Since this is a flexible array member, it does not change sizeof(struct
> rte_mbuf). The object layout would become:
>
> struct rte_mbuf fixed header
> global per-mbuf metadata area
> application private data
> packet data buffer
>
> With that layout, this could be sized at EAL init time rather than
> by a build
> option, for example:
>
> --mbuf-metadata-size=256
>
Yes, that new proposal looks good to me.
One extra thing: I would like to repeat Morten there:
we probably do need similar mechanism for register/unregister/query that
dynamic metadata layout, as we have for mbuf dynfields now.
Might be we can extend existing mbuf_dynfield API?
or might be we can add a new API specific for this new metadata?
Right now I don't have strong opinion here.
Konstantin
> That avoids creating different DPDK builds with different mbuf struct
> sizes or
> different build-time ABI expectations. The configured size would be
> part of
> the process/runtime configuration instead of requiring applications,
> libraries, and package providers to agree on a compile-time define.
>
> The official mbuf helpers would account for this area before ordinary
> priv_size, so application private data remains available and does
> not overlap
> with the global metadata area.
>
> This would also avoid changing the existing dynamic-field allocator
> and copy
> semantics. The area would not be part of the dynamic-field registry;
> it would
> be explicit per-mbuf metadata storage for applications that deliberately
> enable it.
>
> That seems to address the main concerns:
>
> - sizeof(struct rte_mbuf) remains fixed for ABI purposes.
> - the metadata area is globally present across pktmbuf pools when
> enabled.
> - ordinary priv_size remains separate and available.
> - dynamic-field allocator/copy behavior remains unchanged.
> - users that do not enable the EAL option pay no extra per-mbuf
> storage cost.
> - applications do not need to be built against a different mbuf-size
> define.
>
> If this direction is acceptable, I can take a look at what it means in
> practice for EAL configuration, mbuf layout helpers, pool
> constructors, and
> places that currently do direct object-layout math.
>
> Thanks,
> -rt
>
> *From: *Konstantin Ananyev <konstantin.v.ananyev@yandex.ru>
> *Date: *Tuesday, September 29, 2026 at 10:14 AM
> *To: *Morten Brørup <mb@smartsharesystems.com>; Randy Tice (rtice)
> <rtice@cisco.com>; dev@dpdk.org <dev@dpdk.org>
> *Cc: *Bruce Richardson <bruce.richardson@intel.com>; Harman Kalra
> <hkalra@marvell.com>; Stephen Hemminger <stephen@networkplumber.org>
> *Subject: *Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field
> storage
>
>
>
> >> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru
> <mailto:konstantin.v.ananyev@yandex.ru>]
> >> Sent: Tuesday, 29 September 2026 15.13
> >>
> >> 29.09.2026 13:44, Morten Brørup пишет:
> >>>> From: Konstantin Ananyev [mailto:konstantin.v.ananyev@yandex.ru
> <mailto:konstantin.v.ananyev@yandex.ru>]
> >>>> Sent: Tuesday, 29 September 2026 14.00
> >>>>
> >>>> 28.09.2026 19:16, Randy L Tice пишет:
> >>>>> From: Randy L Tice <rtice@cisco.com>
> >>>>> Date: Thu, 03 Sep 2026 09:13:28 -0400
> >>>>>
> >>>>> Add build-time support for optional cache-line-aligned dynamic-
> >> field
> >>>>> storage at the end of struct rte_mbuf.
> >>>>>
> >>>>> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE
> >> in
> >>>>> rte_build_config.h. A non-zero value enables the extra area and
> >> grows
> >>>>> every mbuf by the configured amount.
> >>>> I am strongly opposed to that patch.
> >>>> Inside mbuf we already do have priv_size that allows user to store
> >>>> his/her specific
> >>>> data straight after rte_mbuf in adjacent manner.
> >>>> It worked well so far for many use-cases (including VPP) and I don't
> >>>> see any
> >>>> reason why this is not enough.
> >>>> From other side - making size of core rte_mbuf configurable at
> >> run-
> >>>> time,
> >>>> will affect DPDK ABI stability in a negative way.
> >>>> Fro my perspective it is much plausible in terms of ABI stability
> >> and
> >>>> predictability
> >>>> to have just one fixed layout for the mbuf.
> >>>> Konstantin
> >>> The private data area (priv_size) is independent per mbuf pool, and
> >> selected at run-time when creating each pool. As Randy explained in the
> >> RFC, this is unavailable for mbuf pools created by other components.
> >>
> >> I think it should be trivial to enforce minimal priv_size across all
> >> mbuf pools what will be obeyed by different components
> >> (as long as they do use rte_pktmbuf_pool_create() and friends):
> >> 1) introduce new EAL parameter 'mbuf-min-priv-size' or so (keep default
> >> as zero)
> >> 2) make rte_pktmbuf_pool_create_by_ops() and
> >> rte_pktmbuf_pool_create_extbuf() to check that input paramter
> >> 'priv_size' GE then value specified by EAL parameter, if so then return
> >> an error.
> > The private data area cannot be used.
> > Let's say one module creates an mbuf pool with priv_size of 8, and
> uses those 8 bytes,
> > and some second module creates an mbuf pool with priv_size of 16,
> and uses those 16 bytes.
> >
> > How should a module (or the application) know at which offset to
> store its private data without overwriting the private data of other
> modules?
> >
> > The mbuf dynamic field's registry manages centrally where each
> module should store its own data, and the data is even accessible by
> other modules (because they can fetch the offset to the data from the
> registry)
> ok, I see, you need an ability to register/unregister/query layout for
> that private buffer (what we have now for dynfields).
> Then yes, if we'll add an ability to expand mbuf dynfield[] buffer that
> might be useful, and probably will become
> more popular then current 'priv_size' apporach.
> But I believe it shouldn't be a build time option.
> >
> >>> Mbuf dynamic fields are shared across all mbuf pools, and serves the
> >> need with an existing API. So I am strongly in favor of using the mbuf
> >> dynamic fields API for this.
> >>> I agree with Konstantin that it would be optimal if the size of the
> >> added dynfields area was run-time configurable (as an EAL startup
> >> parameter).
> >>> However, such a modification to the mbuf library would also require
> >> that the performance cost in the dataplane is negligible. We don't want
> >> to compromise on mbuf performance for applications not using this new
> >> feature.
> >>> Randy,
> >>> Could you please explore such an approach?
> >>>
> >>> -Morten
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field storage
2026-09-29 19:03 ` Randy Tice (rtice)
@ 2026-09-29 19:28 ` Konstantin Ananyev
0 siblings, 0 replies; 24+ messages in thread
From: Konstantin Ananyev @ 2026-09-29 19:28 UTC (permalink / raw)
To: Randy Tice (rtice), Morten Brørup, dev@dpdk.org
Cc: Bruce Richardson, Harman Kalra, Stephen Hemminger
> Konstantin,
> Does this path work for you?
Yes, it does.
I just replied to your previous mail, probably our mails collided.
Konstantin
> -rt
>
> *From: *Morten Brørup <mb@smartsharesystems.com>
> *Date: *Tuesday, September 29, 2026 at 11:20 AM
> *To: *Randy Tice (rtice) <rtice@cisco.com>; Konstantin Ananyev
> <konstantin.v.ananyev@yandex.ru>; dev@dpdk.org <dev@dpdk.org>
> *Cc: *Bruce Richardson <bruce.richardson@intel.com>; Harman Kalra
> <hkalra@marvell.com>; Stephen Hemminger <stephen@networkplumber.org>
> *Subject: *RE: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field
> storage
>
> Randy,
>
> This is very close to what I suggested you explore.
>
> But one piece is missing:
>
> Registering fields in this metadata area should be managed through the
> dynamic mbuf fields API.
>
> Without a central registry, only one module can use the new metadata
> area; it cannot be used by multiple modules without coordination.
>
> And instead of rolling your own registry of fields in the metadata
> area, just reuse the dynamic mbuf fields machinery.
>
> I agree with your proposed mbuf layout.
>
> There will be a performance cost for accessing the mbuf private data:
> rte_mbuf_to_priv() will change from adding a simple constant offset
> (sizeof(struct rte_mbuf)) to adding the value of a global variable
> holding the offset, reflecting the startup-time configured metadata
> area size.
>
> The global variable will be hot in the cache when working on mbuf
> bursts, so I think this performance cost will be insignificant.
>
> Venlig hilsen / Kind regards,
>
> -Morten Brørup
>
> *From:* Randy Tice (rtice) [mailto:rtice@cisco.com]
> *Sent:* Tuesday, 29 September 2026 16.45
> *To:* Konstantin Ananyev; Morten Brørup; dev@dpdk.org
> *Cc:* Bruce Richardson; Harman Kalra; Stephen Hemminger
> *Subject:* Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field
> storage
>
> Hi all,
>
> Thanks for the discussion. We are now where I had hoped we’d get to
> during
>
> RFC but we are here.
>
> Konstantin, I understand your concern about making sizeof(struct rte_mbuf)
>
> depend on a build-time option. That can create different mbuf
> layouts between
>
> DPDK builds that otherwise present the same ABI/version, which is
> not a good
>
> property for a core public structure.
>
> After thinking through this again, I think the current patch may be
> trying too
>
> hard to make this a dynamic-field allocator feature. The actual
> requirement is
>
> simpler: a fixed global per-mbuf metadata area that is present in every
>
> pktmbuf object, separate from ordinary application private data, and not
>
> copied by mbuf copy/clone helpers.
>
> The mbuf structure change would look roughly like this:
>
> struct rte_mbuf {
>
> ...
>
> uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
>
> +
>
> + alignas(RTE_CACHE_LINE_SIZE)
>
> + uint8_t metadata[];
>
> + /**< Optional cache-line-aligned per-mbuf metadata area. */
>
> };
>
> Since this is a flexible array member, it does not change sizeof(struct
>
> rte_mbuf). The object layout would become:
>
> struct rte_mbuf fixed header
>
> global per-mbuf metadata area
>
> application private data
>
> packet data buffer
>
> With that layout, this could be sized at EAL init time rather than
> by a build
>
> option, for example:
>
> --mbuf-metadata-size=256
>
> That avoids creating different DPDK builds with different mbuf
> struct sizes or
>
> different build-time ABI expectations. The configured size would be
> part of
>
> the process/runtime configuration instead of requiring applications,
>
> libraries, and package providers to agree on a compile-time define.
>
> The official mbuf helpers would account for this area before ordinary
>
> priv_size, so application private data remains available and does not
> overlap
>
> with the global metadata area.
>
> This would also avoid changing the existing dynamic-field allocator
> and copy
>
> semantics. The area would not be part of the dynamic-field registry;
> it would
>
> be explicit per-mbuf metadata storage for applications that deliberately
>
> enable it.
>
> That seems to address the main concerns:
>
> - sizeof(struct rte_mbuf) remains fixed for ABI purposes.
>
> - the metadata area is globally present across pktmbuf pools when
> enabled.
>
> - ordinary priv_size remains separate and available.
>
> - dynamic-field allocator/copy behavior remains unchanged.
>
> - users that do not enable the EAL option pay no extra per-mbuf
> storage cost.
>
> - applications do not need to be built against a different mbuf-size
> define.
>
> If this direction is acceptable, I can take a look at what it means in
>
> practice for EAL configuration, mbuf layout helpers, pool
> constructors, and
>
> places that currently do direct object-layout math.
>
> Thanks,
>
> -rt
>
> *From: *Konstantin Ananyev <konstantin.v.ananyev@yandex.ru>
> *Date: *Tuesday, September 29, 2026 at 10:14 AM
> *To: *Morten Brørup <mb@smartsharesystems.com>; Randy Tice (rtice)
> <rtice@cisco.com>; dev@dpdk.org <dev@dpdk.org>
> *Cc: *Bruce Richardson <bruce.richardson@intel.com>; Harman Kalra
> <hkalra@marvell.com>; Stephen Hemminger <stephen@networkplumber.org>
> *Subject: *Re: [PATCH v3 1/1] mbuf: add optional no-copy dynamic field
> storage
>
>
>
> >> From: Konstantin Ananyev [_mailto:konstantin.v.ananyev@yandex.ru_
> <mailto:konstantin.v.ananyev@yandex.ru>]
> >> Sent: Tuesday, 29 September 2026 15.13
> >>
> >> 29.09.2026 13:44, Morten Brørup пишет:
> >>>> From: Konstantin Ananyev [_mailto:konstantin.v.ananyev@yandex.ru_
> <mailto:konstantin.v.ananyev@yandex.ru>]
> >>>> Sent: Tuesday, 29 September 2026 14.00
> >>>>
> >>>> 28.09.2026 19:16, Randy L Tice пишет:
> >>>>> From: Randy L Tice <rtice@cisco.com>
> >>>>> Date: Thu, 03 Sep 2026 09:13:28 -0400
> >>>>>
> >>>>> Add build-time support for optional cache-line-aligned dynamic-
> >> field
> >>>>> storage at the end of struct rte_mbuf.
> >>>>>
> >>>>> The mbuf_dynfield3_size Meson option sets RTE_MBUF_DYNFIELD3_SIZE
> >> in
> >>>>> rte_build_config.h. A non-zero value enables the extra area and
> >> grows
> >>>>> every mbuf by the configured amount.
> >>>> I am strongly opposed to that patch.
> >>>> Inside mbuf we already do have priv_size that allows user to store
> >>>> his/her specific
> >>>> data straight after rte_mbuf in adjacent manner.
> >>>> It worked well so far for many use-cases (including VPP) and I don't
> >>>> see any
> >>>> reason why this is not enough.
> >>>> From other side - making size of core rte_mbuf configurable at
> >> run-
> >>>> time,
> >>>> will affect DPDK ABI stability in a negative way.
> >>>> Fro my perspective it is much plausible in terms of ABI stability
> >> and
> >>>> predictability
> >>>> to have just one fixed layout for the mbuf.
> >>>> Konstantin
> >>> The private data area (priv_size) is independent per mbuf pool, and
> >> selected at run-time when creating each pool. As Randy explained in the
> >> RFC, this is unavailable for mbuf pools created by other components.
> >>
> >> I think it should be trivial to enforce minimal priv_size across all
> >> mbuf pools what will be obeyed by different components
> >> (as long as they do use rte_pktmbuf_pool_create() and friends):
> >> 1) introduce new EAL parameter 'mbuf-min-priv-size' or so (keep default
> >> as zero)
> >> 2) make rte_pktmbuf_pool_create_by_ops() and
> >> rte_pktmbuf_pool_create_extbuf() to check that input paramter
> >> 'priv_size' GE then value specified by EAL parameter, if so then return
> >> an error.
> > The private data area cannot be used.
> > Let's say one module creates an mbuf pool with priv_size of 8, and
> uses those 8 bytes,
> > and some second module creates an mbuf pool with priv_size of 16,
> and uses those 16 bytes.
> >
> > How should a module (or the application) know at which offset to
> store its private data without overwriting the private data of other
> modules?
> >
> > The mbuf dynamic field's registry manages centrally where each
> module should store its own data, and the data is even accessible by
> other modules (because they can fetch the offset to the data from the
> registry)
> ok, I see, you need an ability to register/unregister/query layout for
> that private buffer (what we have now for dynfields).
> Then yes, if we'll add an ability to expand mbuf dynfield[] buffer that
> might be useful, and probably will become
> more popular then current 'priv_size' apporach.
> But I believe it shouldn't be a build time option.
> >
> >>> Mbuf dynamic fields are shared across all mbuf pools, and serves the
> >> need with an existing API. So I am strongly in favor of using the mbuf
> >> dynamic fields API for this.
> >>> I agree with Konstantin that it would be optimal if the size of the
> >> added dynfields area was run-time configurable (as an EAL startup
> >> parameter).
> >>> However, such a modification to the mbuf library would also require
> >> that the performance cost in the dataplane is negligible. We don't want
> >> to compromise on mbuf performance for applications not using this new
> >> feature.
> >>> Randy,
> >>> Could you please explore such an approach?
> >>>
> >>> -Morten
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v4 0/1] mbuf: add runtime metadata dynamic-field storage
2026-09-28 18:16 ` [PATCH v3 0/1] mbuf: add optional no-copy dynamic field storage Randy L Tice
2026-09-28 18:16 ` [PATCH v3 1/1] " Randy L Tice
@ 2026-10-06 15:54 ` Randy L Tice
2026-10-06 15:54 ` [PATCH v4 1/1] " Randy L Tice
2026-10-06 16:51 ` [PATCH v4 0/1] " Stephen Hemminger
1 sibling, 2 replies; 24+ messages in thread
From: Randy L Tice @ 2026-10-06 15:54 UTC (permalink / raw)
To: dev; +Cc: Morten Brørup, Stephen Hemminger, Nithin Dabilpuram,
Harman Kalra
This revision changes direction from the v3 build-time dynfield3
layout. It adds a runtime EAL-configured per-mbuf metadata area that is
placed after the fixed struct rte_mbuf header and before per-pool private
data. This keeps sizeof(struct rte_mbuf) fixed while allowing deployments
that need globally consistent per-mbuf metadata to reserve that storage.
The metadata area is still managed by the mbuf dynamic-field registry.
Fields registered with RTE_MBUF_DYNFIELD_F_METADATA are allocated from the
metadata area and are not copied by generic mbuf copy, clone, or attach
operations. Fields registered without the flag continue to use the existing
copied mbuf dynamic-field storage and cannot overlap the metadata area.
The existing per-pool private data area does not provide a central layout
registry and is configured independently for each mbuf pool. That makes it
hard for multiple libraries, drivers, or application modules to safely share
metadata without out-of-band coordination, especially when pools are created
by different components.
Mbuf object layout calculations that need to include the optional metadata
area now use rte_mbuf_size(). The primary process validates and publishes
the metadata size through the shared mem config; secondary processes may
omit the option, but if provided it must match the primary value.
The octeontx mempool driver rejects allocation when mbuf metadata is
configured because the hardware mbuf header offset must remain 128 bytes.
Validation:
- git diff --check HEAD~1..HEAD
- devtools/check-git-log.sh -n1
- devtools/checkpatches.sh -n1
- ninja -C /tmp/dpdk-upstream-metadata-test-build -j8 app/dpdk-test
- app/dpdk-test --mbuf-metadata-size=384 mbuf_autotest
- app/dpdk-test --mbuf-metadata-size=512 mbuf_autotest
- focused scans for raw post-mbuf pointer arithmetic and direct
sizeof(struct rte_mbuf) usage in changed files
- AGENTS.md plus dpdk-codereview review gate
Randy Tice (1):
mbuf: add runtime metadata dynamic-field storage
app/test-crypto-perf/cperf_test_common.c | 8 +-
app/test-pmd/testpmd.c | 2 +-
app/test/suites/meson.build | 14 +++
app/test/test_cryptodev.c | 4 +-
app/test/test_cryptodev.h | 3 +-
app/test/test_event_crypto_adapter.c | 2 +-
app/test/test_mbuf.c | 82 ++++++++++++++---
app/test/test_pdcp.c | 2 +-
doc/guides/linux_gsg/eal_args.include.rst | 8 ++
doc/guides/prog_guide/mbuf_lib.rst | 16 ++++
doc/guides/rel_notes/release_26_11.rst | 21 ++++-
drivers/crypto/cnxk/cn10k_cryptodev_ops.c | 8 +-
drivers/crypto/cnxk/cn20k_cryptodev_ops.c | 8 +-
drivers/event/cnxk/cn10k_worker.h | 6 +-
drivers/event/cnxk/cn20k_eventdev.c | 2 +-
drivers/event/cnxk/cn20k_worker.h | 10 +--
drivers/event/cnxk/cn9k_worker.h | 23 ++---
drivers/event/cnxk/cnxk_eventdev_adptr.c | 2 +-
drivers/mempool/dpaa/dpaa_mempool.c | 2 +-
drivers/mempool/dpaa2/dpaa2_hw_mempool.c | 6 +-
drivers/mempool/octeontx/meson.build | 1 -
.../mempool/octeontx/rte_mempool_octeontx.c | 6 ++
drivers/net/af_xdp/rte_eth_af_xdp.c | 6 +-
drivers/net/bnxt/bnxt_rxr.c | 2 +-
drivers/net/bnxt/bnxt_txr.c | 2 +-
drivers/net/cnxk/cn10k_ethdev_sec.c | 2 +-
drivers/net/cnxk/cn10k_rx.h | 58 +++++++------
drivers/net/cnxk/cn20k_ethdev_sec.c | 2 +-
drivers/net/cnxk/cn20k_rx.h | 19 ++--
drivers/net/cnxk/cnxk_eswitch.c | 4 +-
drivers/net/cnxk/cnxk_ethdev.c | 6 +-
drivers/net/cnxk/cnxk_ethdev_dp.h | 2 +-
drivers/net/cnxk/cnxk_ethdev_sec.c | 6 +-
drivers/net/intel/fm10k/fm10k_ethdev.c | 2 +-
drivers/net/mlx5/mlx5_trigger.c | 6 +-
drivers/net/nfp/flower/nfp_flower.c | 2 +-
drivers/net/nfp/nfp_rxtx.c | 2 +-
drivers/net/pfe/pfe_hif.c | 2 +-
drivers/net/pfe/pfe_hif_lib.c | 4 +-
drivers/net/sfc/sfc_rx.c | 2 +-
drivers/net/softnic/rte_eth_softnic_mempool.c | 4 +-
examples/fips_validation/fips_validation.h | 2 +-
examples/fips_validation/main.c | 2 +-
examples/ntb/ntb_fwd.c | 2 +-
lib/cryptodev/rte_crypto.h | 4 +-
lib/eal/common/eal_common_config.c | 3 +
lib/eal/common/eal_common_mcfg.c | 16 +++-
lib/eal/common/eal_common_options.c | 20 +++++
lib/eal/common/eal_internal_cfg.h | 3 +
lib/eal/common/eal_memcfg.h | 4 +-
lib/eal/common/eal_option_list.h | 1 +
lib/eal/common/eal_private.h | 1 +
lib/eal/freebsd/eal.c | 3 +-
lib/eal/linux/eal.c | 3 +-
lib/mbuf/mbuf_history.c | 4 +-
lib/mbuf/rte_mbuf.c | 17 ++--
lib/mbuf/rte_mbuf.h | 9 +-
lib/mbuf/rte_mbuf_core.h | 37 ++++++++
lib/mbuf/rte_mbuf_dyn.c | 87 +++++++++++++++----
lib/mbuf/rte_mbuf_dyn.h | 11 ++-
lib/pcapng/rte_pcapng.c | 2 +-
lib/vhost/vhost.h | 2 +-
62 files changed, 438 insertions(+), 164 deletions(-)
--
2.35.6
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v4 1/1] mbuf: add runtime metadata dynamic-field storage
2026-10-06 15:54 ` [PATCH v4 0/1] mbuf: add runtime metadata dynamic-field storage Randy L Tice
@ 2026-10-06 15:54 ` Randy L Tice
2026-10-06 16:51 ` [PATCH v4 0/1] " Stephen Hemminger
1 sibling, 0 replies; 24+ messages in thread
From: Randy L Tice @ 2026-10-06 15:54 UTC (permalink / raw)
To: dev; +Cc: Morten Brørup, Stephen Hemminger, Nithin Dabilpuram,
Harman Kalra
From: Randy L Tice <rtice@cisco.com>
Date: Thu, 03 Sep 2026 09:13:28 -0400
Add an EAL option to reserve optional cache-line-aligned metadata
storage in every pktmbuf object without changing sizeof(struct rte_mbuf).
The --mbuf-metadata-size option configures the metadata size at primary
process initialization time and shares the value with secondary
processes.
The storage is placed after the fixed struct rte_mbuf header and before
application private data. Mbuf pool constructors, private-data accessors,
and drivers that program hardware offsets from the start of an mbuf now
use rte_mbuf_size() so the configured metadata area is included in mbuf
object layout calculations.
The metadata area is managed by the existing mbuf dynamic-field registry.
Fields registered with RTE_MBUF_DYNFIELD_F_METADATA are allocated from
this metadata area and are not copied by generic mbuf copy and clone
operations. Dynamic fields registered without this flag continue to use
the existing copied dynamic-field storage and are prevented from
overlapping the metadata area.
The existing per-pool private-data area is not sufficient for this use
case because it has no global registry and is configured independently
for each mbuf pool. Multiple modules using private data would need
out-of-band coordination to avoid overlapping layouts, especially when
mbuf pools are created by different components.
Reject octeontx mempool allocation when mbuf metadata is configured,
because that mempool requires the hardware mbuf header offset to remain
128 bytes.
Signed-off-by: Randy L Tice <rtice@cisco.com>
---
app/test-crypto-perf/cperf_test_common.c | 8 +-
app/test-pmd/testpmd.c | 2 +-
app/test/suites/meson.build | 14 +++
app/test/test_cryptodev.c | 4 +-
app/test/test_cryptodev.h | 3 +-
app/test/test_event_crypto_adapter.c | 2 +-
app/test/test_mbuf.c | 82 ++++++++++++++---
app/test/test_pdcp.c | 2 +-
doc/guides/linux_gsg/eal_args.include.rst | 8 ++
doc/guides/prog_guide/mbuf_lib.rst | 16 ++++
doc/guides/rel_notes/release_26_11.rst | 21 ++++-
drivers/crypto/cnxk/cn10k_cryptodev_ops.c | 8 +-
drivers/crypto/cnxk/cn20k_cryptodev_ops.c | 8 +-
drivers/event/cnxk/cn10k_worker.h | 6 +-
drivers/event/cnxk/cn20k_eventdev.c | 2 +-
drivers/event/cnxk/cn20k_worker.h | 10 +--
drivers/event/cnxk/cn9k_worker.h | 23 ++---
drivers/event/cnxk/cnxk_eventdev_adptr.c | 2 +-
drivers/mempool/dpaa/dpaa_mempool.c | 2 +-
drivers/mempool/dpaa2/dpaa2_hw_mempool.c | 6 +-
drivers/mempool/octeontx/meson.build | 1 -
.../mempool/octeontx/rte_mempool_octeontx.c | 6 ++
drivers/net/af_xdp/rte_eth_af_xdp.c | 6 +-
drivers/net/bnxt/bnxt_rxr.c | 2 +-
drivers/net/bnxt/bnxt_txr.c | 2 +-
drivers/net/cnxk/cn10k_ethdev_sec.c | 2 +-
drivers/net/cnxk/cn10k_rx.h | 58 +++++++------
drivers/net/cnxk/cn20k_ethdev_sec.c | 2 +-
drivers/net/cnxk/cn20k_rx.h | 19 ++--
drivers/net/cnxk/cnxk_eswitch.c | 4 +-
drivers/net/cnxk/cnxk_ethdev.c | 6 +-
drivers/net/cnxk/cnxk_ethdev_dp.h | 2 +-
drivers/net/cnxk/cnxk_ethdev_sec.c | 6 +-
drivers/net/intel/fm10k/fm10k_ethdev.c | 2 +-
drivers/net/mlx5/mlx5_trigger.c | 6 +-
drivers/net/nfp/flower/nfp_flower.c | 2 +-
drivers/net/nfp/nfp_rxtx.c | 2 +-
drivers/net/pfe/pfe_hif.c | 2 +-
drivers/net/pfe/pfe_hif_lib.c | 4 +-
drivers/net/sfc/sfc_rx.c | 2 +-
drivers/net/softnic/rte_eth_softnic_mempool.c | 4 +-
examples/fips_validation/fips_validation.h | 2 +-
examples/fips_validation/main.c | 2 +-
examples/ntb/ntb_fwd.c | 2 +-
lib/cryptodev/rte_crypto.h | 4 +-
lib/eal/common/eal_common_config.c | 3 +
lib/eal/common/eal_common_mcfg.c | 16 +++-
lib/eal/common/eal_common_options.c | 20 +++++
lib/eal/common/eal_internal_cfg.h | 3 +
lib/eal/common/eal_memcfg.h | 4 +-
lib/eal/common/eal_option_list.h | 1 +
lib/eal/common/eal_private.h | 1 +
lib/eal/freebsd/eal.c | 3 +-
lib/eal/linux/eal.c | 3 +-
lib/mbuf/mbuf_history.c | 4 +-
lib/mbuf/rte_mbuf.c | 17 ++--
lib/mbuf/rte_mbuf.h | 9 +-
lib/mbuf/rte_mbuf_core.h | 37 ++++++++
lib/mbuf/rte_mbuf_dyn.c | 87 +++++++++++++++----
lib/mbuf/rte_mbuf_dyn.h | 11 ++-
lib/pcapng/rte_pcapng.c | 2 +-
lib/vhost/vhost.h | 2 +-
62 files changed, 438 insertions(+), 164 deletions(-)
diff --git a/app/test-crypto-perf/cperf_test_common.c b/app/test-crypto-perf/cperf_test_common.c
index 0bcaa6dfd8..9cb0e61ce7 100644
--- a/app/test-crypto-perf/cperf_test_common.c
+++ b/app/test-crypto-perf/cperf_test_common.c
@@ -21,7 +21,7 @@ fill_single_seg_mbuf(struct rte_mbuf *m, struct rte_mempool *mp,
void *obj, uint32_t mbuf_offset, uint16_t segment_sz,
uint16_t headroom, uint16_t data_len)
{
- uint32_t mbuf_hdr_size = sizeof(struct rte_mbuf);
+ uint32_t mbuf_hdr_size = rte_mbuf_size();
/* start of buffer is after mbuf structure and priv data */
m->priv_size = 0;
@@ -47,7 +47,7 @@ fill_multi_seg_mbuf(struct rte_mbuf *m, struct rte_mempool *mp,
void *obj, uint32_t mbuf_offset, uint16_t segment_sz,
uint16_t headroom, uint16_t data_len, uint16_t segments_nb)
{
- uint16_t mbuf_hdr_size = sizeof(struct rte_mbuf);
+ uint32_t mbuf_hdr_size = rte_mbuf_size();
uint16_t remaining_segments = segments_nb;
rte_iova_t next_seg_phys_addr = rte_mempool_virt2iova(obj) +
mbuf_offset + mbuf_hdr_size;
@@ -196,7 +196,7 @@ cperf_alloc_common_memory(const struct cperf_options *options,
crypto_op_private_size;
uint16_t crypto_op_total_size_padded =
RTE_CACHE_LINE_ROUNDUP(crypto_op_total_size);
- uint32_t mbuf_size = sizeof(struct rte_mbuf) + options->segment_sz;
+ uint32_t mbuf_size = rte_mbuf_size() + options->segment_sz;
uint32_t max_size = options->max_buffer_size + options->digest_sz;
uint32_t segment_data_len = options->segment_sz - options->headroom_sz -
options->tailroom_sz;
@@ -228,7 +228,7 @@ cperf_alloc_common_memory(const struct cperf_options *options,
(mbuf_size * segments_nb);
params.dst_buf_offset = *dst_buf_offset;
/* Destination buffer will be one segment only */
- obj_size += max_size + sizeof(struct rte_mbuf) +
+ obj_size += max_size + rte_mbuf_size() +
options->headroom_sz + options->tailroom_sz;
}
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index cab2fa1556..6b42f27492 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -1268,7 +1268,7 @@ mbuf_pool_create(uint16_t mbuf_seg_size, unsigned nb_mbuf,
#ifndef RTE_EXEC_ENV_WINDOWS
uint32_t mb_size;
- mb_size = sizeof(struct rte_mbuf) + mbuf_seg_size;
+ mb_size = rte_mbuf_size() + mbuf_seg_size;
#endif
mbuf_poolname_build(socket_id, pool_name, sizeof(pool_name), size_idx);
if (!is_proc_primary()) {
diff --git a/app/test/suites/meson.build b/app/test/suites/meson.build
index 786c459c24..2c051d3796 100644
--- a/app/test/suites/meson.build
+++ b/app/test/suites/meson.build
@@ -122,6 +122,20 @@ foreach suite:test_suites
endif
endforeach
+mbuf_metadata_args = test_no_huge_args + ['--no-shconf', '--mbuf-metadata-size=256']
+if get_option('default_library') == 'shared'
+ mbuf_metadata_args += ['-d', dpdk_drivers_build_dir]
+endif
+if is_linux
+ mbuf_metadata_args += ['--file-prefix=mbuf_autotest_with_metadata']
+endif
+test('mbuf_autotest_with_metadata', dpdk_test,
+ args : mbuf_metadata_args,
+ env: ['DPDK_TEST=mbuf_autotest'],
+ timeout : timeout_seconds_fast,
+ is_parallel : false,
+ suite : 'fast-tests')
+
# standalone test for telemetry
if not is_windows and dpdk_conf.has('RTE_LIB_TELEMETRY')
test_args = [dpdk_test]
diff --git a/app/test/test_cryptodev.c b/app/test/test_cryptodev.c
index fd02107baf..fff0ff487a 100644
--- a/app/test/test_cryptodev.c
+++ b/app/test/test_cryptodev.c
@@ -263,11 +263,11 @@ create_mbuf_from_heap(int pkt_len, uint8_t pattern)
/* Set the default values to the mbuf */
m->nb_segs = 1;
m->port = RTE_MBUF_PORT_INVALID;
- m->buf_len = MBUF_SIZE - sizeof(struct rte_mbuf) - RTE_PKTMBUF_HEADROOM;
+ m->buf_len = MBUF_SIZE - rte_mbuf_size() - RTE_PKTMBUF_HEADROOM;
rte_pktmbuf_reset_headroom(m);
__rte_mbuf_sanity_check(m, 1);
- m->buf_addr = (char *)m + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM;
+ m->buf_addr = (char *)m + rte_mbuf_size() + RTE_PKTMBUF_HEADROOM;
memset(m->buf_addr, pattern, m->buf_len);
dst = (uint8_t *)rte_pktmbuf_append(m, pkt_len);
diff --git a/app/test/test_cryptodev.h b/app/test/test_cryptodev.h
index d125dea958..d7e403df33 100644
--- a/app/test/test_cryptodev.h
+++ b/app/test/test_cryptodev.h
@@ -5,6 +5,7 @@
#define TEST_CRYPTODEV_H_
#include <rte_cryptodev.h>
+#include <rte_mbuf.h>
#include <rte_security.h>
#define MAX_NUM_OPS_INFLIGHT (4096)
@@ -16,7 +17,7 @@
#define NUM_MBUFS (8191)
#define MBUF_CACHE_SIZE (256)
#define MBUF_DATAPAYLOAD_SIZE (4096 + DIGEST_BYTE_LENGTH_SHA512)
-#define MBUF_SIZE (sizeof(struct rte_mbuf) + \
+#define MBUF_SIZE (rte_mbuf_size() + \
RTE_PKTMBUF_HEADROOM + MBUF_DATAPAYLOAD_SIZE)
#define LARGE_MBUF_DATAPAYLOAD_SIZE (UINT16_MAX - RTE_PKTMBUF_HEADROOM)
#define LARGE_MBUF_SIZE (RTE_PKTMBUF_HEADROOM + LARGE_MBUF_DATAPAYLOAD_SIZE)
diff --git a/app/test/test_event_crypto_adapter.c b/app/test/test_event_crypto_adapter.c
index cac1584f40..8ff1c83c16 100644
--- a/app/test/test_event_crypto_adapter.c
+++ b/app/test/test_event_crypto_adapter.c
@@ -48,7 +48,7 @@ test_event_crypto_adapter(void)
#define NUM_CORES 1
#define CRYPTODEV_NAME_NULL_PMD crypto_null
-#define MBUF_SIZE (sizeof(struct rte_mbuf) + \
+#define MBUF_SIZE (rte_mbuf_size() + \
RTE_PKTMBUF_HEADROOM + PACKET_LENGTH)
#define IV_OFFSET (sizeof(struct rte_crypto_op) + \
sizeof(struct rte_crypto_sym_op) + \
diff --git a/app/test/test_mbuf.c b/app/test/test_mbuf.c
index db23259745..b7caf7fffd 100644
--- a/app/test/test_mbuf.c
+++ b/app/test/test_mbuf.c
@@ -625,11 +625,11 @@ test_attach_from_different_pool(struct rte_mempool *pktmbuf_pool,
GOTO_FAIL("data room size should be 0\n");
if (rte_pktmbuf_priv_size(clone->pool) != MBUF2_PRIV_SIZE)
GOTO_FAIL("data room size should be %d\n", MBUF2_PRIV_SIZE);
- memset(clone + 1, 0, MBUF2_PRIV_SIZE);
+ memset(rte_mbuf_to_priv(clone), 0, MBUF2_PRIV_SIZE);
/* save data pointer to compare it after detach() */
c_data = rte_pktmbuf_mtod(clone, char *);
- if (c_data != (char *)clone + sizeof(*clone) + MBUF2_PRIV_SIZE)
+ if (c_data != (char *)clone + rte_mbuf_size() + MBUF2_PRIV_SIZE)
GOTO_FAIL("bad data pointer in clone");
if (rte_pktmbuf_headroom(clone) != 0)
GOTO_FAIL("bad headroom in clone");
@@ -654,11 +654,11 @@ test_attach_from_different_pool(struct rte_mempool *pktmbuf_pool,
GOTO_FAIL("data room size should be 0\n");
if (rte_pktmbuf_priv_size(clone2->pool) != MBUF2_PRIV_SIZE)
GOTO_FAIL("data room size should be %d\n", MBUF2_PRIV_SIZE);
- memset(clone2 + 1, 0, MBUF2_PRIV_SIZE);
+ memset(rte_mbuf_to_priv(clone2), 0, MBUF2_PRIV_SIZE);
/* save data pointer to compare it after detach() */
c_data2 = rte_pktmbuf_mtod(clone2, char *);
- if (c_data2 != (char *)clone2 + sizeof(*clone2) + MBUF2_PRIV_SIZE)
+ if (c_data2 != (char *)clone2 + rte_mbuf_size() + MBUF2_PRIV_SIZE)
GOTO_FAIL("bad data pointer in clone2");
if (rte_pktmbuf_headroom(clone2) != 0)
GOTO_FAIL("bad headroom in clone2");
@@ -2561,15 +2561,15 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
.align = alignof(uint16_t),
.flags = 0,
};
- const struct rte_mbuf_dynfield dynfield3 = {
- .name = "test-dynfield3",
+ const struct rte_mbuf_dynfield dynfield_fixed = {
+ .name = "test-dynfield-fixed",
.size = sizeof(uint8_t),
.align = alignof(uint8_t),
.flags = 0,
};
const struct rte_mbuf_dynfield dynfield_fail_big = {
.name = "test-dynfield-fail-big",
- .size = 256,
+ .size = sizeof(struct rte_mbuf),
.align = 1,
.flags = 0,
};
@@ -2583,7 +2583,25 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
.name = "test-dynfield",
.size = sizeof(uint8_t),
.align = alignof(uint8_t),
- .flags = 1,
+ .flags = RTE_MBUF_DYNFIELD_F_METADATA << 1,
+ };
+ const struct rte_mbuf_dynfield metadata_field = {
+ .name = "test-metadata-field",
+ .size = sizeof(uint64_t),
+ .align = alignof(uint64_t),
+ .flags = RTE_MBUF_DYNFIELD_F_METADATA,
+ };
+ const struct rte_mbuf_dynfield dynfield_metadata_bad_offset = {
+ .name = "test-dynfield-metadata-bad-offset",
+ .size = sizeof(uint64_t),
+ .align = alignof(uint64_t),
+ .flags = RTE_MBUF_DYNFIELD_F_METADATA,
+ };
+ const struct rte_mbuf_dynfield dynfield_copy_bad_offset = {
+ .name = "test-dynfield-copy-bad-offset",
+ .size = 2 * sizeof(uint64_t),
+ .align = alignof(uint64_t),
+ .flags = 0,
};
const struct rte_mbuf_dynflag dynflag_fail_flag = {
.name = "test-dynflag",
@@ -2602,7 +2620,9 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
.flags = 0,
};
struct rte_mbuf *m = NULL;
+ struct rte_mbuf *mc = NULL;
int offset, offset2, offset3;
+ int metadata_field_offset;
int flag, flag2, flag3;
int ret;
@@ -2624,7 +2644,7 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
GOTO_FAIL("failed to register dynamic field 2, offset2=%d: %s",
offset2, strerror(errno));
- offset3 = rte_mbuf_dynfield_register_offset(&dynfield3,
+ offset3 = rte_mbuf_dynfield_register_offset(&dynfield_fixed,
offsetof(struct rte_mbuf, dynfield1[1]));
if (offset3 != offsetof(struct rte_mbuf, dynfield1[1])) {
if (rte_errno == EBUSY)
@@ -2654,6 +2674,33 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
if (ret != -1)
GOTO_FAIL("dynamic field creation should fail (invalid flag)");
+ if (rte_mbuf_metadata_size_get() > 0) {
+ metadata_field_offset = rte_mbuf_dynfield_register_offset(&metadata_field,
+ offsetof(struct rte_mbuf, metadata));
+ if (metadata_field_offset != offsetof(struct rte_mbuf, metadata))
+ GOTO_FAIL("failed to register metadata field, offset=%d: %s",
+ metadata_field_offset, strerror(errno));
+
+ ret = rte_mbuf_dynfield_register_offset(&dynfield_metadata_bad_offset,
+ offsetof(struct rte_mbuf, dynfield1[0]));
+ if (ret != -1)
+ GOTO_FAIL("metadata dynamic field creation should fail outside metadata");
+
+ ret = rte_mbuf_dynfield_register_offset(&dynfield_copy_bad_offset,
+ offsetof(struct rte_mbuf, metadata));
+ if (ret != -1)
+ GOTO_FAIL("copied dynamic field creation should fail in metadata");
+
+ ret = rte_mbuf_dynfield_register_offset(&dynfield_copy_bad_offset,
+ offsetof(struct rte_mbuf, metadata) - sizeof(uint64_t));
+ if (ret != -1)
+ GOTO_FAIL("copied dynamic field creation should fail when straddling metadata");
+ } else {
+ ret = rte_mbuf_dynfield_register(&metadata_field);
+ if (ret != -1)
+ GOTO_FAIL("metadata dynamic field creation should fail without metadata area");
+ }
+
ret = rte_mbuf_dynflag_register(&dynflag_fail_flag);
if (ret != -1)
GOTO_FAIL("dynamic flag creation should fail (invalid flag)");
@@ -2693,13 +2740,29 @@ test_mbuf_dyn(struct rte_mempool *pktmbuf_pool)
if (*RTE_MBUF_DYNFIELD(m, offset2, uint16_t *) != 1000)
GOTO_FAIL("failed to read dynamic field");
+ if (rte_mbuf_metadata_size_get() > 0) {
+ *RTE_MBUF_DYNFIELD(m, metadata_field_offset, uint64_t *) =
+ UINT64_C(0x8877665544332211);
+ mc = rte_pktmbuf_alloc(pktmbuf_pool);
+ if (mc == NULL)
+ GOTO_FAIL("Cannot allocate mbuf for dynamic field copy test");
+ *RTE_MBUF_DYNFIELD(mc, metadata_field_offset, uint64_t *) =
+ UINT64_C(0xa5a5a5a5a5a5a5a5);
+ rte_mbuf_dynfield_copy(mc, m);
+ if (*RTE_MBUF_DYNFIELD(mc, metadata_field_offset, uint64_t *) !=
+ UINT64_C(0xa5a5a5a5a5a5a5a5))
+ GOTO_FAIL("copied metadata dynamic field");
+ }
+
/* set a dynamic flag */
m->ol_flags |= (1ULL << flag);
rte_mbuf_dyn_dump(stdout);
+ rte_pktmbuf_free(mc);
rte_pktmbuf_free(m);
return 0;
fail:
+ rte_pktmbuf_free(mc);
rte_pktmbuf_free(m);
return -1;
}
@@ -2776,7 +2839,6 @@ test_mbuf(void)
struct rte_mempool *pktmbuf_pool = NULL;
struct rte_mempool *pktmbuf_pool2 = NULL;
-
RTE_BUILD_BUG_ON(sizeof(struct rte_mbuf) != RTE_CACHE_LINE_MIN_SIZE * 2);
/* create pktmbuf pool if it does not exist */
diff --git a/app/test/test_pdcp.c b/app/test/test_pdcp.c
index 784e2dddd9..c91bb68dae 100644
--- a/app/test/test_pdcp.c
+++ b/app/test/test_pdcp.c
@@ -27,7 +27,7 @@
#define NB_BASIC_TESTS RTE_DIM(pdcp_test_params)
#define NB_SDAP_TESTS RTE_DIM(list_pdcp_sdap_tests)
#define PDCP_IV_LEN 16
-#define PDCP_MBUF_SIZE (sizeof(struct rte_mbuf) + \
+#define PDCP_MBUF_SIZE (rte_mbuf_size() + \
RTE_PKTMBUF_HEADROOM + RTE_PDCP_CTRL_PDU_SIZE_MAX)
/* Assert that condition is true, or goto the mark */
diff --git a/doc/guides/linux_gsg/eal_args.include.rst b/doc/guides/linux_gsg/eal_args.include.rst
index 32c24c8e41..eb7048a4cf 100644
--- a/doc/guides/linux_gsg/eal_args.include.rst
+++ b/doc/guides/linux_gsg/eal_args.include.rst
@@ -285,6 +285,14 @@ Other options
Pool ops name for mbuf to use.
+* ``--mbuf-metadata-size``:
+
+ Size of the global per-mbuf metadata area.
+ The value must be a multiple of the cache line size and is shared by the
+ primary process with secondary processes.
+ Secondary processes may omit this option, but if specified it must match
+ the value used by the primary process.
+
* ``--telemetry``:
Enable telemetry (enabled by default).
diff --git a/doc/guides/prog_guide/mbuf_lib.rst b/doc/guides/prog_guide/mbuf_lib.rst
index cf64add109..96dd02cbd7 100644
--- a/doc/guides/prog_guide/mbuf_lib.rst
+++ b/doc/guides/prog_guide/mbuf_lib.rst
@@ -234,6 +234,22 @@ The dynamic fields and flags are managed with the functions ``rte_mbuf_dyn*``.
It is not possible to unregister fields or flags.
+The EAL option ``--mbuf-metadata-size`` can add extra cache-line-aligned
+metadata storage to every pktmbuf object. This storage is placed after the
+fixed ``struct rte_mbuf`` header and before the application private data area,
+so ``sizeof(struct rte_mbuf)`` does not change. The option defaults to ``0``.
+The primary process configures this value and shares it with secondary
+processes. Secondary processes may omit this option, but if specified it must
+match the primary process value.
+The extra storage is reserved for dynamic fields registered with
+``RTE_MBUF_DYNFIELD_F_METADATA``. These fields are not copied by mbuf copy,
+clone, or attach operations. Dynamic fields registered without this flag
+continue to use the existing copied dynamic-field storage and cannot overlap the
+metadata area. Code that needs the runtime mbuf object size, such as
+application private-data accessors or drivers programming offsets from the
+start of an mbuf, should use ``rte_mbuf_size()`` instead of
+``sizeof(struct rte_mbuf)``.
+
.. _direct_indirect_buffer:
Direct and Indirect Buffers
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index dec96ccbc7..4e27d466eb 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -60,6 +60,26 @@ New Features
Added the experimental ``rte_cpu_socket_id()`` function
to map an OS logical CPU ID to the NUMA socket containing that CPU.
+* **Added optional mbuf metadata storage.**
+
+ Added ``--mbuf-metadata-size`` EAL option to enable a
+ cache-line-aligned metadata area in every pktmbuf object.
+ The storage is placed after the fixed ``struct rte_mbuf`` header and
+ before application private data, leaving ``sizeof(struct rte_mbuf)``
+ unchanged.
+ The primary process configures this value and shares it with secondary
+ processes.
+ Secondary processes may omit the option, but if specified it must match
+ the primary process value.
+ The metadata area is reserved for dynamic fields registered with
+ ``RTE_MBUF_DYNFIELD_F_METADATA``.
+ These fields are not copied by generic mbuf copy, clone, or attach
+ operations.
+ Dynamic fields registered without this flag continue to use the existing
+ copied dynamic-field storage and cannot overlap the metadata area.
+ Code that needs the runtime mbuf object size should use
+ ``rte_mbuf_size()`` instead of ``sizeof(struct rte_mbuf)``.
+
* **Added TPID support to VLAN tag insertion.**
Added ``rte_vlan_insert_tpid()`` to the net library.
@@ -338,7 +358,6 @@ Known Issues
Also, make sure to start the actual text at the margin.
=======================================================
-
Tested Platforms
----------------
diff --git a/drivers/crypto/cnxk/cn10k_cryptodev_ops.c b/drivers/crypto/cnxk/cn10k_cryptodev_ops.c
index 870e65c049..670930e1f7 100644
--- a/drivers/crypto/cnxk/cn10k_cryptodev_ops.c
+++ b/drivers/crypto/cnxk/cn10k_cryptodev_ops.c
@@ -1406,7 +1406,7 @@ cn10k_cryptodev_sec_inb_rx_inject(void *dev, struct rte_mbuf **pkts,
wqe_hdr = (wqe_hdr - 1) & ~(BIT_ULL(7) - 1);
/* Pointer to WQE header */
- *(uint64_t *)(m + 1) = wqe_hdr;
+ *(uint64_t *)RTE_PTR_ADD(m, rte_mbuf_size()) = wqe_hdr;
/* Reserve SG list after end of last mbuf data location. */
rxphdr = wqe_hdr + 8;
@@ -1422,8 +1422,8 @@ cn10k_cryptodev_sec_inb_rx_inject(void *dev, struct rte_mbuf **pkts,
/* Reserve space for WQE, NIX_RX_PARSE_S and SG_S.
* Populate SG_S with num segs and seg length
*/
- wqe_hdr = (uintptr_t)(m + 1);
- *(uint64_t *)(m + 1) = wqe_hdr;
+ wqe_hdr = (uintptr_t)RTE_PTR_ADD(m, rte_mbuf_size());
+ *(uint64_t *)wqe_hdr = wqe_hdr;
sg2 = (struct roc_sg2list_comp *)(wqe_hdr + 8 * 8);
sg2->u.s.len[0] = rte_pktmbuf_pkt_len(m);
@@ -1443,7 +1443,7 @@ cn10k_cryptodev_sec_inb_rx_inject(void *dev, struct rte_mbuf **pkts,
/* Word 2 and 3 */
inst_23 = vdupq_n_u64(0);
- u64_1 = (((uint64_t)m + sizeof(struct rte_mbuf)) >> 3) << 3 | 1;
+ u64_1 = (((uint64_t)m + rte_mbuf_size()) >> 3) << 3 | 1;
inst_23 = vsetq_lane_u64(u64_1, inst_23, 1);
vst1q_u64(&inst->w2.u64, inst_23);
diff --git a/drivers/crypto/cnxk/cn20k_cryptodev_ops.c b/drivers/crypto/cnxk/cn20k_cryptodev_ops.c
index 18100ff1f8..670473fded 100644
--- a/drivers/crypto/cnxk/cn20k_cryptodev_ops.c
+++ b/drivers/crypto/cnxk/cn20k_cryptodev_ops.c
@@ -1557,7 +1557,7 @@ cn20k_cryptodev_sec_inb_rx_inject(void *dev, struct rte_mbuf **pkts,
wqe_hdr = (wqe_hdr - 1) & ~(BIT_ULL(7) - 1);
/* Pointer to WQE header */
- *(uint64_t *)(m + 1) = wqe_hdr;
+ *(uint64_t *)RTE_PTR_ADD(m, rte_mbuf_size()) = wqe_hdr;
/* Reserve SG list after end of last mbuf data location. */
rxphdr = wqe_hdr + 8;
@@ -1573,8 +1573,8 @@ cn20k_cryptodev_sec_inb_rx_inject(void *dev, struct rte_mbuf **pkts,
/* Reserve space for WQE, NIX_RX_PARSE_S and SG_S.
* Populate SG_S with num segs and seg length
*/
- wqe_hdr = (uintptr_t)(m + 1);
- *(uint64_t *)(m + 1) = wqe_hdr;
+ wqe_hdr = (uintptr_t)RTE_PTR_ADD(m, rte_mbuf_size());
+ *(uint64_t *)wqe_hdr = wqe_hdr;
sg2 = (struct roc_sg2list_comp *)(wqe_hdr + 8 * 8);
sg2->u.s.len[0] = rte_pktmbuf_pkt_len(m);
@@ -1594,7 +1594,7 @@ cn20k_cryptodev_sec_inb_rx_inject(void *dev, struct rte_mbuf **pkts,
/* Word 2 and 3 */
inst_23 = vdupq_n_u64(0);
- u64_1 = (((uint64_t)m + sizeof(struct rte_mbuf)) >> 3) << 3 | 1;
+ u64_1 = (((uint64_t)m + rte_mbuf_size()) >> 3) << 3 | 1;
inst_23 = vsetq_lane_u64(u64_1, inst_23, 1);
vst1q_u64(&inst->w2.u64, inst_23);
diff --git a/drivers/event/cnxk/cn10k_worker.h b/drivers/event/cnxk/cn10k_worker.h
index 9b6abdf18d..3c642f63a2 100644
--- a/drivers/event/cnxk/cn10k_worker.h
+++ b/drivers/event/cnxk/cn10k_worker.h
@@ -95,7 +95,7 @@ cn10k_process_vwqe(uintptr_t vwqe, uint16_t port_id, const uint32_t flags, struc
uint64_t sg_w1;
mbuf = (struct rte_mbuf *)((uintptr_t)wqe[0] -
- sizeof(struct rte_mbuf));
+ rte_mbuf_size());
/* Pick first mbuf's aura handle assuming all
* mbufs are from a vec and are from same RQ.
*/
@@ -114,7 +114,7 @@ cn10k_process_vwqe(uintptr_t vwqe, uint16_t port_id, const uint32_t flags, struc
struct nix_cqe_hdr_s *cqe = (struct nix_cqe_hdr_s *)wqe[0];
mbuf = (struct rte_mbuf *)((char *)cqe -
- sizeof(struct rte_mbuf));
+ rte_mbuf_size());
/* Mark mempool obj as "get" as it is alloc'ed by NIX */
RTE_MEMPOOL_CHECK_COOKIES(mbuf->pool, (void **)&mbuf, 1, 1);
@@ -171,7 +171,7 @@ cn10k_sso_hws_post_process(struct cn10k_sso_hws *ws, uint64_t *u64,
uintptr_t cpth = 0;
uint64_t mbuf;
- mbuf = u64[1] - sizeof(struct rte_mbuf);
+ mbuf = u64[1] - rte_mbuf_size();
rte_prefetch0((void *)mbuf);
/* Mark mempool obj as "get" as it is alloc'ed by NIX */
diff --git a/drivers/event/cnxk/cn20k_eventdev.c b/drivers/event/cnxk/cn20k_eventdev.c
index 29495eb4f9..d763adf0c4 100644
--- a/drivers/event/cnxk/cn20k_eventdev.c
+++ b/drivers/event/cnxk/cn20k_eventdev.c
@@ -760,7 +760,7 @@ cn20k_sso_rxq_enable(struct cnxk_eth_dev *cnxk_eth_dev, uint16_t rq_id, uint16_t
rq->sso_ena = 1;
rq->tt = tt;
rq->hwgrp = queue_conf->ev.queue_id;
- wqe_skip = RTE_ALIGN_CEIL(sizeof(struct rte_mbuf), ROC_CACHE_LINE_SZ);
+ wqe_skip = RTE_ALIGN_CEIL(rte_mbuf_size(), ROC_CACHE_LINE_SZ);
wqe_skip = wqe_skip / ROC_CACHE_LINE_SZ;
rq->wqe_skip = wqe_skip;
diff --git a/drivers/event/cnxk/cn20k_worker.h b/drivers/event/cnxk/cn20k_worker.h
index 6442113e09..e6f969b0a2 100644
--- a/drivers/event/cnxk/cn20k_worker.h
+++ b/drivers/event/cnxk/cn20k_worker.h
@@ -48,7 +48,7 @@ cn20k_process_vwqe(uintptr_t vwqe, uint16_t port_id, const uint32_t flags, struc
{
uint64_t mbuf_init = 0x100010000ULL | RTE_PKTMBUF_HEADROOM;
struct cnxk_timesync_info *tstamp = ws->tstamp[port_id];
- uint8_t m_sz = sizeof(struct rte_mbuf);
+ uint32_t m_sz = rte_mbuf_size();
void *lookup_mem = ws->lookup_mem;
uint64_t meta_aura = 0, laddr = 0;
uintptr_t lbase = ws->lmt_base;
@@ -93,7 +93,7 @@ cn20k_process_vwqe(uintptr_t vwqe, uint16_t port_id, const uint32_t flags, struc
if (flags & NIX_RX_OFFLOAD_SECURITY_F && non_vec) {
uint64_t sg_w1;
- mbuf = (struct rte_mbuf *)((uintptr_t)wqe[0] - sizeof(struct rte_mbuf));
+ mbuf = (struct rte_mbuf *)((uintptr_t)wqe[0] - m_sz);
/* Pick first mbuf's aura handle assuming all
* mbufs are from a vec and are from same RQ.
*/
@@ -114,7 +114,7 @@ cn20k_process_vwqe(uintptr_t vwqe, uint16_t port_id, const uint32_t flags, struc
while (non_vec) {
struct nix_cqe_hdr_s *cqe = (struct nix_cqe_hdr_s *)wqe[0];
- mbuf = (struct rte_mbuf *)((char *)cqe - sizeof(struct rte_mbuf));
+ mbuf = (struct rte_mbuf *)((char *)cqe - m_sz);
/* Mark mempool obj as "get" as it is alloc'ed by NIX */
RTE_MEMPOOL_CHECK_COOKIES(mbuf->pool, (void **)&mbuf, 1, 1);
@@ -165,7 +165,7 @@ cn20k_process_vwqe(uintptr_t vwqe, uint16_t port_id, const uint32_t flags, struc
static __rte_always_inline void
cn20k_sso_hws_post_process(struct cn20k_sso_hws *ws, uint64_t *u64, const uint32_t flags)
{
- uint8_t m_sz = sizeof(struct rte_mbuf);
+ uint32_t m_sz = rte_mbuf_size();
uintptr_t sa_base = 0;
u64[0] = (u64[0] & (0x3ull << 32)) << 6 | (u64[0] & (0x3FFull << 36)) << 4 |
@@ -183,7 +183,7 @@ cn20k_sso_hws_post_process(struct cn20k_sso_hws *ws, uint64_t *u64, const uint32
uintptr_t cpth = 0;
uint64_t mbuf;
- mbuf = u64[1] - sizeof(struct rte_mbuf);
+ mbuf = u64[1] - m_sz;
rte_prefetch0((void *)mbuf);
/* Mark mempool obj as "get" as it is alloc'ed by NIX */
diff --git a/drivers/event/cnxk/cn9k_worker.h b/drivers/event/cnxk/cn9k_worker.h
index 513d397991..7fa467a210 100644
--- a/drivers/event/cnxk/cn9k_worker.h
+++ b/drivers/event/cnxk/cn9k_worker.h
@@ -232,21 +232,22 @@ cn9k_sso_hws_dual_get_work(uint64_t base, uint64_t pair_base,
" tbnz %[tag], 63, .Lrty%= \n"
".Ldone%=: str %[gw], [%[pong]] \n"
" dmb ld \n"
- " sub %[mbuf], %[wqp], #0x80 \n"
+ " sub %[mbuf], %[wqp], %[mbuf_hdr_sz]\n"
" prfm pldl1keep, [%[mbuf]] \n"
: [tag] "=&r"(gw.u64[0]), [wqp] "=&r"(gw.u64[1]),
[mbuf] "=&r"(mbuf)
: [tag_loc] "r"(base + SSOW_LF_GWS_TAG),
[wqp_loc] "r"(base + SSOW_LF_GWS_WQP),
[gw] "r"(dws->gw_wdata),
- [pong] "r"(pair_base + SSOW_LF_GWS_OP_GET_WORK0));
+ [pong] "r"(pair_base + SSOW_LF_GWS_OP_GET_WORK0),
+ [mbuf_hdr_sz] "r" ((uint64_t)rte_mbuf_size()));
#else
gw.u64[0] = plt_read64(base + SSOW_LF_GWS_TAG);
while ((BIT_ULL(63)) & gw.u64[0])
gw.u64[0] = plt_read64(base + SSOW_LF_GWS_TAG);
gw.u64[1] = plt_read64(base + SSOW_LF_GWS_WQP);
plt_write64(dws->gw_wdata, pair_base + SSOW_LF_GWS_OP_GET_WORK0);
- mbuf = (uint64_t)((char *)gw.u64[1] - sizeof(struct rte_mbuf));
+ mbuf = (uint64_t)((char *)gw.u64[1] - rte_mbuf_size());
#endif
if (gw.u64[1])
@@ -284,19 +285,20 @@ cn9k_sso_hws_get_work(struct cn9k_sso_hws *ws, struct rte_event *ev,
" ldr %[wqp], [%[wqp_loc]] \n"
" tbnz %[tag], 63, .Lrty%= \n"
".Ldone%=: dmb ld \n"
- " sub %[mbuf], %[wqp], #0x80 \n"
+ " sub %[mbuf], %[wqp], %[mbuf_hdr_sz]\n"
" prfm pldl1keep, [%[mbuf]] \n"
: [tag] "=&r"(gw.u64[0]), [wqp] "=&r"(gw.u64[1]),
[mbuf] "=&r"(mbuf)
: [tag_loc] "r"(ws->base + SSOW_LF_GWS_TAG),
- [wqp_loc] "r"(ws->base + SSOW_LF_GWS_WQP));
+ [wqp_loc] "r"(ws->base + SSOW_LF_GWS_WQP),
+ [mbuf_hdr_sz] "r" ((uint64_t)rte_mbuf_size()));
#else
gw.u64[0] = plt_read64(ws->base + SSOW_LF_GWS_TAG);
while ((BIT_ULL(63)) & gw.u64[0])
gw.u64[0] = plt_read64(ws->base + SSOW_LF_GWS_TAG);
gw.u64[1] = plt_read64(ws->base + SSOW_LF_GWS_WQP);
- mbuf = (uint64_t)((char *)gw.u64[1] - sizeof(struct rte_mbuf));
+ mbuf = (uint64_t)((char *)gw.u64[1] - rte_mbuf_size());
#endif
if (gw.u64[1])
@@ -332,18 +334,19 @@ cn9k_sso_hws_get_work_empty(uint64_t base, struct rte_event *ev,
" ldr %[wqp], [%[wqp_loc]] \n"
" tbnz %[tag], 63, .Lrty%= \n"
".Ldone%=: dmb ld \n"
- " sub %[mbuf], %[wqp], #0x80 \n"
+ " sub %[mbuf], %[wqp], %[mbuf_hdr_sz]\n"
: [tag] "=&r"(gw.u64[0]), [wqp] "=&r"(gw.u64[1]),
[mbuf] "=&r"(mbuf)
: [tag_loc] "r"(base + SSOW_LF_GWS_TAG),
- [wqp_loc] "r"(base + SSOW_LF_GWS_WQP));
+ [wqp_loc] "r"(base + SSOW_LF_GWS_WQP),
+ [mbuf_hdr_sz] "r" ((uint64_t)rte_mbuf_size()));
#else
gw.u64[0] = plt_read64(base + SSOW_LF_GWS_TAG);
while ((BIT_ULL(63)) & gw.u64[0])
gw.u64[0] = plt_read64(base + SSOW_LF_GWS_TAG);
gw.u64[1] = plt_read64(base + SSOW_LF_GWS_WQP);
- mbuf = (uint64_t)((char *)gw.u64[1] - sizeof(struct rte_mbuf));
+ mbuf = (uint64_t)((char *)gw.u64[1] - rte_mbuf_size());
#endif
if (gw.u64[1])
@@ -671,7 +674,7 @@ cn9k_sso_hws_xmit_sec_one(const struct cn9k_eth_txq *txq, uint64_t base,
cmd23 = vsetq_lane_u64((((uint64_t)RTE_EVENT_TYPE_CPU << 28) |
CNXK_ETHDEV_SEC_OUTB_EV_SUB << 20),
cmd23, 0);
- cmd23 = vsetq_lane_u64(((uintptr_t)m + sizeof(struct rte_mbuf)) | 1,
+ cmd23 = vsetq_lane_u64(((uintptr_t)m + rte_mbuf_size()) | 1,
cmd23, 1);
dptr += l2_len - ROC_ONF_IPSEC_OUTB_MAX_L2_INFO_SZ -
diff --git a/drivers/event/cnxk/cnxk_eventdev_adptr.c b/drivers/event/cnxk/cnxk_eventdev_adptr.c
index 5678e5d264..843badbc2e 100644
--- a/drivers/event/cnxk/cnxk_eventdev_adptr.c
+++ b/drivers/event/cnxk/cnxk_eventdev_adptr.c
@@ -135,7 +135,7 @@ cnxk_sso_rxq_enable(struct cnxk_eth_dev *cnxk_eth_dev, uint16_t rq_id,
rq->tt = ev->sched_type;
rq->hwgrp = ev->queue_id;
rq->flow_tag_width = 20;
- wqe_skip = RTE_ALIGN_CEIL(sizeof(struct rte_mbuf), ROC_CACHE_LINE_SZ);
+ wqe_skip = RTE_ALIGN_CEIL(rte_mbuf_size(), ROC_CACHE_LINE_SZ);
wqe_skip = wqe_skip / ROC_CACHE_LINE_SZ;
rq->wqe_skip = wqe_skip;
rq->tag_mask = (port_id & 0xF) << 20;
diff --git a/drivers/mempool/dpaa/dpaa_mempool.c b/drivers/mempool/dpaa/dpaa_mempool.c
index 2f8555a026..f5a107ed23 100644
--- a/drivers/mempool/dpaa/dpaa_mempool.c
+++ b/drivers/mempool/dpaa/dpaa_mempool.c
@@ -110,7 +110,7 @@ dpaa_mbuf_create_pool(struct rte_mempool *mp)
rte_dpaa_bpid_info[bpid].size = elem_max_size;
rte_dpaa_bpid_info[bpid].bp = bp;
rte_dpaa_bpid_info[bpid].meta_data_size =
- sizeof(struct rte_mbuf) + rte_pktmbuf_priv_size(mp);
+ rte_mbuf_size() + rte_pktmbuf_priv_size(mp);
rte_dpaa_bpid_info[bpid].dpaa_ops_index = mp->ops_index;
rte_dpaa_bpid_info[bpid].ptov_off = 0;
rte_dpaa_bpid_info[bpid].flags = 0;
diff --git a/drivers/mempool/dpaa2/dpaa2_hw_mempool.c b/drivers/mempool/dpaa2/dpaa2_hw_mempool.c
index ee001d8ce0..003e8243a0 100644
--- a/drivers/mempool/dpaa2/dpaa2_hw_mempool.c
+++ b/drivers/mempool/dpaa2/dpaa2_hw_mempool.c
@@ -119,7 +119,7 @@ rte_hw_mbuf_create_pool(struct rte_mempool *mp)
/* Set parameters of buffer pool list */
bp_list->buf_pool.num_bufs = mp->size;
bp_list->buf_pool.size = mp->elt_size
- - sizeof(struct rte_mbuf) - rte_pktmbuf_priv_size(mp);
+ - rte_mbuf_size() - rte_pktmbuf_priv_size(mp);
bp_list->buf_pool.bpid = dpbp_attr.bpid;
bp_list->buf_pool.h_bpool_mem = NULL;
bp_list->buf_pool.dpbp_node = avail_dpbp;
@@ -138,7 +138,7 @@ rte_hw_mbuf_create_pool(struct rte_mempool *mp)
bpid = dpbp_attr.bpid;
- rte_dpaa2_bpid_info[bpid].meta_data_size = sizeof(struct rte_mbuf)
+ rte_dpaa2_bpid_info[bpid].meta_data_size = rte_mbuf_size()
+ rte_pktmbuf_priv_size(mp);
rte_dpaa2_bpid_info[bpid].bp_list = bp_list;
rte_dpaa2_bpid_info[bpid].bpid = bpid;
@@ -332,7 +332,7 @@ int rte_dpaa2_bpid_info_init(struct rte_mempool *mp)
sizeof(struct dpaa2_bp_info) * MAX_BPID);
}
- rte_dpaa2_bpid_info[bpid].meta_data_size = sizeof(struct rte_mbuf)
+ rte_dpaa2_bpid_info[bpid].meta_data_size = rte_mbuf_size()
+ rte_pktmbuf_priv_size(mp);
rte_dpaa2_bpid_info[bpid].bp_list = bp_info->bp_list;
rte_dpaa2_bpid_info[bpid].bpid = bpid;
diff --git a/drivers/mempool/octeontx/meson.build b/drivers/mempool/octeontx/meson.build
index 3ccecac75d..3344defac8 100644
--- a/drivers/mempool/octeontx/meson.build
+++ b/drivers/mempool/octeontx/meson.build
@@ -6,7 +6,6 @@ if not is_linux or not dpdk_conf.get('RTE_ARCH_64')
reason = 'only supported on 64-bit Linux'
subdir_done()
endif
-
sources = files(
'octeontx_fpavf.c',
'rte_mempool_octeontx.c',
diff --git a/drivers/mempool/octeontx/rte_mempool_octeontx.c b/drivers/mempool/octeontx/rte_mempool_octeontx.c
index 631e521b58..20c96774a1 100644
--- a/drivers/mempool/octeontx/rte_mempool_octeontx.c
+++ b/drivers/mempool/octeontx/rte_mempool_octeontx.c
@@ -2,6 +2,7 @@
* Copyright(c) 2017 Cavium, Inc
*/
+#include <errno.h>
#include <stdio.h>
#include <rte_mempool.h>
#include <rte_malloc.h>
@@ -17,6 +18,11 @@ octeontx_fpavf_alloc(struct rte_mempool *mp)
uint32_t object_size;
int rc = 0;
+ if (rte_mbuf_metadata_size_get() != 0) {
+ fpavf_log_err("mbuf metadata area is not supported");
+ return -ENOTSUP;
+ }
+
object_size = mp->elt_size + mp->header_size + mp->trailer_size;
pool = octeontx_fpa_bufpool_create(object_size, memseg_count,
diff --git a/drivers/net/af_xdp/rte_eth_af_xdp.c b/drivers/net/af_xdp/rte_eth_af_xdp.c
index 9c99dcec20..1bff1335bd 100644
--- a/drivers/net/af_xdp/rte_eth_af_xdp.c
+++ b/drivers/net/af_xdp/rte_eth_af_xdp.c
@@ -431,7 +431,7 @@ af_xdp_rx_zc(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
bufs[i] = (struct rte_mbuf *)
xsk_umem__get_data(umem->buffer, addr +
umem->mb_pool->header_size);
- bufs[i]->data_off = offset - sizeof(struct rte_mbuf) -
+ bufs[i]->data_off = offset - rte_mbuf_size() -
rte_pktmbuf_priv_size(umem->mb_pool) -
umem->mb_pool->header_size;
bufs[i]->port = rxq->port;
@@ -1061,7 +1061,7 @@ eth_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
#if defined(XDP_UMEM_UNALIGNED_CHUNK_FLAG)
dev_info->max_rx_pktlen = getpagesize() -
sizeof(struct rte_mempool_objhdr) -
- sizeof(struct rte_mbuf) -
+ rte_mbuf_size() -
RTE_PKTMBUF_HEADROOM - XDP_PACKET_HEADROOM;
#else
dev_info->max_rx_pktlen = ETH_AF_XDP_FRAME_SIZE - XDP_PACKET_HEADROOM;
@@ -1419,7 +1419,7 @@ xsk_umem_info *xdp_umem_configure(struct pmd_internals *internals,
rte_mempool_calc_obj_size(mb_pool->elt_size,
mb_pool->flags, NULL);
usr_config.frame_headroom = mb_pool->header_size +
- sizeof(struct rte_mbuf) +
+ rte_mbuf_size() +
rte_pktmbuf_priv_size(mb_pool) +
RTE_PKTMBUF_HEADROOM;
diff --git a/drivers/net/bnxt/bnxt_rxr.c b/drivers/net/bnxt/bnxt_rxr.c
index 98bdbc136a..d18925ef4c 100644
--- a/drivers/net/bnxt/bnxt_rxr.c
+++ b/drivers/net/bnxt/bnxt_rxr.c
@@ -1527,7 +1527,7 @@ int bnxt_init_rx_ring_struct(struct bnxt_rx_queue *rxq, unsigned int socket_id)
struct bnxt_rx_ring_info *rxr;
struct bnxt_ring *ring;
- rxq->rx_buf_size = BNXT_MAX_PKT_LEN + sizeof(struct rte_mbuf);
+ rxq->rx_buf_size = BNXT_MAX_PKT_LEN + rte_mbuf_size();
if (rxq->rx_ring != NULL) {
rxr = rxq->rx_ring;
diff --git a/drivers/net/bnxt/bnxt_txr.c b/drivers/net/bnxt/bnxt_txr.c
index 36188346f1..285cb0bb43 100644
--- a/drivers/net/bnxt/bnxt_txr.c
+++ b/drivers/net/bnxt/bnxt_txr.c
@@ -213,7 +213,7 @@ bnxt_invalid_nb_segs(struct rte_mbuf *tx_pkt)
static int bnxt_invalid_mbuf(struct rte_mbuf *mbuf)
{
- uint32_t mbuf_size = sizeof(struct rte_mbuf) + mbuf->priv_size;
+ uint32_t mbuf_size = rte_mbuf_size() + mbuf->priv_size;
const char *reason;
if (unlikely(rte_eal_iova_mode() != RTE_IOVA_VA &&
diff --git a/drivers/net/cnxk/cn10k_ethdev_sec.c b/drivers/net/cnxk/cn10k_ethdev_sec.c
index 0682294099..8b338b755b 100644
--- a/drivers/net/cnxk/cn10k_ethdev_sec.c
+++ b/drivers/net/cnxk/cn10k_ethdev_sec.c
@@ -551,7 +551,7 @@ cn10k_eth_sec_sso_work_cb(uint64_t *gw, void *args, enum nix_inl_event_type type
switch ((gw[0] >> 28) & 0xF) {
case RTE_EVENT_TYPE_ETHDEV:
/* Event from inbound inline dev due to IPSEC packet bad L4 */
- mbuf = (struct rte_mbuf *)(gw[1] - sizeof(struct rte_mbuf));
+ mbuf = (struct rte_mbuf *)(gw[1] - rte_mbuf_size());
plt_nix_dbg("Received mbuf %p from inline dev inbound", mbuf);
cnxk_pktmbuf_free_no_cache(mbuf);
return;
diff --git a/drivers/net/cnxk/cn10k_rx.h b/drivers/net/cnxk/cn10k_rx.h
index e55910b575..cf06bf09cd 100644
--- a/drivers/net/cnxk/cn10k_rx.h
+++ b/drivers/net/cnxk/cn10k_rx.h
@@ -174,7 +174,8 @@ static __rte_always_inline void
nix_sec_reass_first_frag_update(struct rte_mbuf *head, const uint8_t *m_ipptr,
uint64_t fsz, uint64_t cq_w1, uint16_t *ihl)
{
- union nix_rx_parse_u *rx = (union nix_rx_parse_u *)((uintptr_t)(head + 1) + 8);
+ union nix_rx_parse_u *rx =
+ (union nix_rx_parse_u *)RTE_PTR_ADD(head, rte_mbuf_size() + 8);
uint16_t fragx_sum = vaddv_u16(vreinterpret_u16_u64(vdup_n_u64(fsz)));
uint8_t lcptr = rx->lcptr;
uint16_t tot_len;
@@ -287,7 +288,7 @@ nix_sec_attach_frags(const struct cpt_cn10k_parse_hdr_s *hdr,
nix_sec_reass_frags_get(hdr, next_mbufs);
/* Frag-0: */
- wqe = (uint64_t *)(head + 1);
+ wqe = (uint64_t *)RTE_PTR_ADD(head, rte_mbuf_size());
rlen = ((*(wqe + 10)) >> 16) & 0xFFFF;
frag_rx = (union nix_rx_parse_u *)(wqe + 1);
@@ -304,7 +305,7 @@ nix_sec_attach_frags(const struct cpt_cn10k_parse_hdr_s *hdr,
cnxk_ip_reassembly_dynfield(mbuf, off)->next_frag = next_mbufs[frag_i];
cnxk_ip_reassembly_dynfield(mbuf, off)->nb_frags = num_frags;
mbuf = next_mbufs[frag_i];
- wqe = (uint64_t *)(mbuf + 1);
+ wqe = (uint64_t *)RTE_PTR_ADD(mbuf, rte_mbuf_size());
rlen = ((*(wqe + 10)) >> 16) & 0xFFFF;
frag_rx = (union nix_rx_parse_u *)(wqe + 1);
@@ -353,7 +354,7 @@ nix_sec_reassemble_frags(const struct cpt_cn10k_parse_hdr_s *hdr, struct rte_mbu
fsz_w1 = nix_sec_reass_frags_get(hdr, next_mbufs);
/* Frag-0: */
- wqe = (uint64_t *)(head + 1);
+ wqe = (uint64_t *)RTE_PTR_ADD(head, rte_mbuf_size());
/* First fragment data len is already update by caller */
m_ipptr = ((const uint8_t *)hdr + ((cq_w5 >> 16) & 0xFF));
@@ -363,7 +364,7 @@ nix_sec_reassemble_frags(const struct cpt_cn10k_parse_hdr_s *hdr, struct rte_mbu
/* Frag-1: */
head->next = next_mbufs[0];
mbuf = next_mbufs[0];
- wqe = (uint64_t *)(mbuf + 1);
+ wqe = (uint64_t *)RTE_PTR_ADD(mbuf, rte_mbuf_size());
frag_rx = (union nix_rx_parse_u *)(wqe + 1);
frag_size = fsz_w1 & 0xFFFF;
fsz_w1 >>= 16;
@@ -379,7 +380,7 @@ nix_sec_reassemble_frags(const struct cpt_cn10k_parse_hdr_s *hdr, struct rte_mbu
if (num_frags > 2) {
mbuf->next = next_mbufs[1];
mbuf = next_mbufs[1];
- wqe = (uint64_t *)(mbuf + 1);
+ wqe = (uint64_t *)RTE_PTR_ADD(mbuf, rte_mbuf_size());
frag_rx = (union nix_rx_parse_u *)(wqe + 1);
frag_size = fsz_w1 & 0xFFFF;
fsz_w1 >>= 16;
@@ -396,7 +397,7 @@ nix_sec_reassemble_frags(const struct cpt_cn10k_parse_hdr_s *hdr, struct rte_mbu
if (num_frags > 3) {
mbuf->next = next_mbufs[2];
mbuf = next_mbufs[2];
- wqe = (uint64_t *)(mbuf + 1);
+ wqe = (uint64_t *)RTE_PTR_ADD(mbuf, rte_mbuf_size());
frag_rx = (union nix_rx_parse_u *)(wqe + 1);
frag_size = fsz_w1 & 0xFFFF;
fsz_w1 >>= 16;
@@ -473,7 +474,7 @@ nix_sec_meta_to_mbuf_sc(uint64_t cq_w1, uint64_t cq_w5, const uint64_t sa_base,
inner = nix_sec_oop_process(hdr, mbuf, &mbuf_init);
} else {
inner = (struct rte_mbuf *)(rte_be_to_cpu_64(hdr->wqe_ptr) -
- sizeof(struct rte_mbuf));
+ rte_mbuf_size());
/* Store meta in lmtline to free
* Assume all meta's from same aura.
@@ -728,15 +729,16 @@ nix_cqe_xtract_mseg(const union nix_rx_parse_u *rx, struct rte_mbuf *mbuf,
/* Use inner rx parse for meta pkts sg list */
if (cq_w1 & BIT(11) && flags & NIX_RX_OFFLOAD_SECURITY_F) {
const uint64_t *wqe;
- /* Rx Inject packet must have Match ID 0xFFFF and for this
- * wqe will get from address stored at mbuf+1 location
- */
- rx_inj = ((flags & NIX_RX_REAS_F) && ((hdr->w0.match_id == 0xFFFFU) ||
- (hdr->w0.cookie == 0xFFFFFFFFU)));
- if (rx_inj)
- wqe = (const uint64_t *)*((uint64_t *)(mbuf + 1));
- else
- wqe = (const uint64_t *)(mbuf + 1);
+ /* Rx Inject packet must have Match ID 0xFFFF. For this,
+ * WQE is fetched from the post-mbuf header area.
+ */
+ rx_inj = ((flags & NIX_RX_REAS_F) && ((hdr->w0.match_id == 0xFFFFU) ||
+ (hdr->w0.cookie == 0xFFFFFFFFU)));
+ if (rx_inj)
+ wqe = (const uint64_t *)*((uint64_t *)
+ RTE_PTR_ADD(mbuf, rte_mbuf_size()));
+ else
+ wqe = (const uint64_t *)RTE_PTR_ADD(mbuf, rte_mbuf_size());
if (!(flags & NIX_RX_REAS_F) || hdr->w0.pkt_fmt != ROC_IE_OT_SA_PKT_FMT_FULL)
rx = (const union nix_rx_parse_u *)(wqe + 1);
@@ -826,7 +828,8 @@ nix_cqe_xtract_mseg(const union nix_rx_parse_u *rx, struct rte_mbuf *mbuf,
struct rte_mbuf *next_frag = next_mbufs[frag_i];
uint16_t lcptr, ldptr = 0;
- rx = (const union nix_rx_parse_u *)((uintptr_t)(next_frag + 1) + 8);
+ rx = (const union nix_rx_parse_u *)RTE_PTR_ADD(next_frag,
+ rte_mbuf_size() + 8);
lcptr = (*((const uint64_t *)rx + 4) >> 16) & 0xFF;
eol = ((const rte_iova_t *)(rx + 1) + ((rx->desc_sizem1 + 1) << 1));
sg = *(const uint64_t *)(rx + 1);
@@ -1325,11 +1328,12 @@ cn10k_nix_inj_pkts(struct rte_security_session **sess, struct cnxk_ethdev_inj_cf
if (m->nb_segs > 1) {
/* Will reserve NIX Rx descriptor with SG list after end of
- * last mbuf data location. and pointer to this will be
- * stored at 1st mbuf space for Rx path multi-seg processing.
+ * last mbuf data location. Pointer to this will be stored
+ * in the post-mbuf header area for Rx path multi-seg
+ * processing.
*/
/* Pointer to WQE header */
- *(uint64_t *)(m + 1) = cptres;
+ *(uint64_t *)RTE_PTR_ADD(m, rte_mbuf_size()) = cptres;
/* Reserve 8 Dwords of WQE Hdr + Rx Parse Hdr */
rxphdr = cptres + 8;
dptr = rxphdr + 7 * 8;
@@ -1355,7 +1359,7 @@ cn10k_nix_inj_pkts(struct rte_security_session **sess, struct cnxk_ethdev_inj_cf
/* Set PF func */
w0 &= 0xFFFF000000000000UL;
cmd23 = vsetq_lane_u64(w0, cmd23, 0);
- cmd23 = vsetq_lane_u64(((uint64_t)m + sizeof(struct rte_mbuf)) | 1, cmd23, 1);
+ cmd23 = vsetq_lane_u64(((uint64_t)m + rte_mbuf_size()) | 1, cmd23, 1);
sa_base &= ~0xFFFFUL;
sa = (uintptr_t)roc_nix_inl_ot_ipsec_inb_sa(sa_base, sess_priv.sa_idx);
@@ -1499,7 +1503,7 @@ cn10k_nix_recv_pkts_vector(void *args, struct rte_mbuf **mbufs, uint16_t pkts,
uint16_t port;
mbuf0 = (struct rte_mbuf *)((uintptr_t)mbufs[0] -
- sizeof(struct rte_mbuf));
+ rte_mbuf_size());
/* Pick first mbuf's aura handle assuming all
* mbufs are from a vec and are from same RQ.
*/
@@ -1651,10 +1655,10 @@ cn10k_nix_recv_pkts_vector(void *args, struct rte_mbuf **mbufs, uint16_t pkts,
} else {
mbuf01 =
vsubq_u64(vld1q_u64((uint64_t *)cq0),
- vdupq_n_u64(sizeof(struct rte_mbuf)));
+ vdupq_n_u64(rte_mbuf_size()));
mbuf23 =
vsubq_u64(vld1q_u64((uint64_t *)(cq0 + 16)),
- vdupq_n_u64(sizeof(struct rte_mbuf)));
+ vdupq_n_u64(rte_mbuf_size()));
}
/* Move mbufs to scalar registers for future use */
@@ -1793,9 +1797,9 @@ cn10k_nix_recv_pkts_vector(void *args, struct rte_mbuf **mbufs, uint16_t pkts,
wqe23 = vrev64q_u8(wqe23);
/* Adjust wqe pointers to point to mbuf */
wqe01 = vsubq_u64(wqe01,
- vdupq_n_u64(sizeof(struct rte_mbuf)));
+ vdupq_n_u64(rte_mbuf_size()));
wqe23 = vsubq_u64(wqe23,
- vdupq_n_u64(sizeof(struct rte_mbuf)));
+ vdupq_n_u64(rte_mbuf_size()));
/* Extract sa idx from cookie area and add to sa_base */
sa01 = vzip1q_u64(inner0, inner1);
diff --git a/drivers/net/cnxk/cn20k_ethdev_sec.c b/drivers/net/cnxk/cn20k_ethdev_sec.c
index 65f0235a46..945b253ff4 100644
--- a/drivers/net/cnxk/cn20k_ethdev_sec.c
+++ b/drivers/net/cnxk/cn20k_ethdev_sec.c
@@ -546,7 +546,7 @@ cn20k_eth_sec_sso_work_cb(uint64_t *gw, void *args, enum nix_inl_event_type type
switch ((gw[0] >> 28) & 0xF) {
case RTE_EVENT_TYPE_ETHDEV:
/* Event from inbound inline dev due to IPSEC packet bad L4 */
- mbuf = (struct rte_mbuf *)(gw[1] - sizeof(struct rte_mbuf));
+ mbuf = (struct rte_mbuf *)(gw[1] - rte_mbuf_size());
plt_nix_dbg("Received mbuf %p from inline dev inbound", mbuf);
cnxk_pktmbuf_free_no_cache(mbuf);
return;
diff --git a/drivers/net/cnxk/cn20k_rx.h b/drivers/net/cnxk/cn20k_rx.h
index f8fa6de2b9..25cdab5704 100644
--- a/drivers/net/cnxk/cn20k_rx.h
+++ b/drivers/net/cnxk/cn20k_rx.h
@@ -215,7 +215,7 @@ nix_sec_oop_process(uintptr_t cpth, uint64_t buf_sz)
offset = addr % (buf_sz & 0xFFFFFFFF);
mbuf = (struct rte_mbuf *)(addr - offset + (buf_sz >> 32));
- rx = (union nix_rx_parse_u *)(((uintptr_t)(mbuf + 1)) + 8);
+ rx = (union nix_rx_parse_u *)RTE_PTR_ADD(mbuf, rte_mbuf_size() + 8);
mbuf->pkt_len = rx->pkt_lenm1 + 1;
mbuf->data_len = rx->pkt_lenm1 + 1;
mbuf->data_off = addr - (uint64_t)mbuf->buf_addr;
@@ -702,7 +702,7 @@ cn20k_nix_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t pkts, co
uint64_t mbuf_init = rxq->mbuf_initializer;
const void *lookup_mem = rxq->lookup_mem;
const uint64_t data_off = rxq->data_off;
- uint8_t m_sz = sizeof(struct rte_mbuf);
+ uint32_t m_sz = rte_mbuf_size();
const uint64_t wdata = rxq->wdata;
const uint32_t qmask = rxq->qmask;
const uintptr_t desc = rxq->desc;
@@ -815,7 +815,7 @@ cn20k_nix_flush_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t pk
uint64_t mbuf_init = rxq->mbuf_initializer;
const void *lookup_mem = rxq->lookup_mem;
const uint64_t data_off = rxq->data_off;
- uint8_t m_sz = sizeof(struct rte_mbuf);
+ uint32_t m_sz = rte_mbuf_size();
const uint64_t wdata = rxq->wdata;
const uint32_t qmask = rxq->qmask;
const uintptr_t desc = rxq->desc;
@@ -1016,7 +1016,7 @@ cn20k_nix_inj_pkts(struct rte_security_session **sess, struct cnxk_ethdev_inj_cf
/* Set PF func */
w0 &= 0xFFFF000000000000UL;
cmd23 = vsetq_lane_u64(w0, cmd23, 0);
- cmd23 = vsetq_lane_u64(((uint64_t)m + sizeof(struct rte_mbuf)) | 1, cmd23, 1);
+ cmd23 = vsetq_lane_u64(((uint64_t)m + rte_mbuf_size()) | 1, cmd23, 1);
sa = (uintptr_t)roc_nix_inl_ow_ipsec_inb_sa(sa_base, sess_priv.sa_idx);
ucode_cmd[0] = (ROC_IE_OW_MAJOR_OP_PROCESS_INBOUND_IPSEC << 48 | 1UL << 54 |
@@ -1179,7 +1179,8 @@ cn20k_nix_recv_pkts_vector(void *args, struct rte_mbuf **mbufs, uint16_t pkts, c
uint64_t sg_w1;
uint16_t port;
- mbuf0 = (struct rte_mbuf *)((uintptr_t)mbufs[0] - sizeof(struct rte_mbuf));
+ mbuf0 = (struct rte_mbuf *)((uintptr_t)mbufs[0] -
+ rte_mbuf_size());
/* Pick first mbuf's aura handle assuming all
* mbufs are from a vec and are from same RQ.
*/
@@ -1293,9 +1294,9 @@ cn20k_nix_recv_pkts_vector(void *args, struct rte_mbuf **mbufs, uint16_t pkts, c
mbuf23 = vqsubq_u64(mbuf23, data_off);
} else {
mbuf01 = vsubq_u64(vld1q_u64((uint64_t *)cq0),
- vdupq_n_u64(sizeof(struct rte_mbuf)));
+ vdupq_n_u64(rte_mbuf_size()));
mbuf23 = vsubq_u64(vld1q_u64((uint64_t *)(cq0 + 16)),
- vdupq_n_u64(sizeof(struct rte_mbuf)));
+ vdupq_n_u64(rte_mbuf_size()));
}
/* Move mbufs to scalar registers for future use */
@@ -1428,8 +1429,8 @@ cn20k_nix_recv_pkts_vector(void *args, struct rte_mbuf **mbufs, uint16_t pkts, c
wqe23 = vzip2q_u64(inner2, inner3);
/* Adjust wqe pointers to point to mbuf */
- wqe01 = vsubq_u64(wqe01, vdupq_n_u64(sizeof(struct rte_mbuf)));
- wqe23 = vsubq_u64(wqe23, vdupq_n_u64(sizeof(struct rte_mbuf)));
+ wqe01 = vsubq_u64(wqe01, vdupq_n_u64(rte_mbuf_size()));
+ wqe23 = vsubq_u64(wqe23, vdupq_n_u64(rte_mbuf_size()));
/* Extract sa idx from cookie area and add to sa_base */
sa01 = vzip1q_u64(inner0, inner1);
diff --git a/drivers/net/cnxk/cnxk_eswitch.c b/drivers/net/cnxk/cnxk_eswitch.c
index 57e2c490df..8781156611 100644
--- a/drivers/net/cnxk/cnxk_eswitch.c
+++ b/drivers/net/cnxk/cnxk_eswitch.c
@@ -393,11 +393,11 @@ cnxk_eswitch_rxq_setup(struct cnxk_eswitch_dev *eswitch_dev, uint16_t qid, uint1
rq->wqe_caching = ROC_NIX_RQ_DEFAULT_WQE_CACHING;
/* Calculate first mbuf skip */
- first_skip = (sizeof(struct rte_mbuf));
+ first_skip = rte_mbuf_size();
first_skip += RTE_PKTMBUF_HEADROOM;
first_skip += rte_pktmbuf_priv_size(lpb_pool);
rq->first_skip = first_skip;
- rq->later_skip = sizeof(struct rte_mbuf) + rte_pktmbuf_priv_size(lpb_pool);
+ rq->later_skip = rte_mbuf_size() + rte_pktmbuf_priv_size(lpb_pool);
rq->lpb_size = lpb_pool->elt_size;
if (roc_errata_nix_no_meta_aura())
rq->lpb_drop_ena = true;
diff --git a/drivers/net/cnxk/cnxk_ethdev.c b/drivers/net/cnxk/cnxk_ethdev.c
index 9a4da870cd..24fe1f455b 100644
--- a/drivers/net/cnxk/cnxk_ethdev.c
+++ b/drivers/net/cnxk/cnxk_ethdev.c
@@ -963,11 +963,11 @@ cnxk_nix_rx_queue_setup(struct rte_eth_dev *eth_dev, uint16_t qid,
rq->wqe_caching = ROC_NIX_RQ_DEFAULT_WQE_CACHING;
/* Calculate first mbuf skip */
- first_skip = (sizeof(struct rte_mbuf));
+ first_skip = rte_mbuf_size();
first_skip += RTE_PKTMBUF_HEADROOM;
first_skip += rte_pktmbuf_priv_size(lpb_pool);
rq->first_skip = first_skip;
- rq->later_skip = sizeof(struct rte_mbuf) + rte_pktmbuf_priv_size(lpb_pool);
+ rq->later_skip = rte_mbuf_size() + rte_pktmbuf_priv_size(lpb_pool);
rq->lpb_size = lpb_pool->elt_size;
if (roc_errata_nix_no_meta_aura())
rq->lpb_drop_ena = !(dev->rx_offloads & RTE_ETH_RX_OFFLOAD_SECURITY);
@@ -978,7 +978,7 @@ cnxk_nix_rx_queue_setup(struct rte_eth_dev *eth_dev, uint16_t qid,
/* WQE skip is needed when poll mode is enabled in CN10KA_B0 and above
* for Inline IPsec traffic to CQ without inline device.
*/
- wqe_skip = RTE_ALIGN_CEIL(sizeof(struct rte_mbuf), ROC_CACHE_LINE_SZ);
+ wqe_skip = RTE_ALIGN_CEIL(rte_mbuf_size(), ROC_CACHE_LINE_SZ);
wqe_skip = wqe_skip / ROC_CACHE_LINE_SZ;
rq->wqe_skip = wqe_skip;
}
diff --git a/drivers/net/cnxk/cnxk_ethdev_dp.h b/drivers/net/cnxk/cnxk_ethdev_dp.h
index cd31a36936..cbf9564a4a 100644
--- a/drivers/net/cnxk/cnxk_ethdev_dp.h
+++ b/drivers/net/cnxk/cnxk_ethdev_dp.h
@@ -113,7 +113,7 @@ cnxk_pktmbuf_detach(struct rte_mbuf *m, uint64_t *aura)
refcount = rte_mbuf_refcnt_update(md, -1);
priv_size = rte_pktmbuf_priv_size(mp);
- mbuf_size = (uint32_t)(sizeof(struct rte_mbuf) + priv_size);
+ mbuf_size = rte_mbuf_size() + priv_size;
buf_len = rte_pktmbuf_data_room_size(mp);
m->priv_size = priv_size;
diff --git a/drivers/net/cnxk/cnxk_ethdev_sec.c b/drivers/net/cnxk/cnxk_ethdev_sec.c
index 61eb55ba43..c1f9bf8669 100644
--- a/drivers/net/cnxk/cnxk_ethdev_sec.c
+++ b/drivers/net/cnxk/cnxk_ethdev_sec.c
@@ -102,7 +102,7 @@ cnxk_nix_inl_meta_pool_cb(uint64_t *aura_handle, uintptr_t *mpool, uint32_t buf_
}
/* Init mempool private area */
- first_skip = sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM;
+ first_skip = rte_mbuf_size() + RTE_PKTMBUF_HEADROOM;
memset(&mbp_priv, 0, sizeof(mbp_priv));
mbp_priv.mbuf_data_room_size = (buf_sz - first_skip +
RTE_PKTMBUF_HEADROOM);
@@ -648,7 +648,7 @@ rte_pmd_cnxk_inl_ipsec_res(struct rte_mbuf *mbuf)
if (!mbuf || !(mbuf->ol_flags & RTE_MBUF_F_RX_SEC_OFFLOAD))
return NULL;
- wqe = (uintptr_t)(mbuf + 1);
+ wqe = (uintptr_t)RTE_PTR_ADD(mbuf, rte_mbuf_size());
rx = (const union nix_rx_parse_u *)(wqe + 8);
desc_size = (rx->desc_sizem1 + 1) * 16;
@@ -874,7 +874,7 @@ cnxk_nix_inl_dev_probe(struct rte_pci_driver *pci_drv,
}
/* WQE skip is one for DPDK */
- wqe_skip = RTE_ALIGN_CEIL(sizeof(struct rte_mbuf), ROC_CACHE_LINE_SZ);
+ wqe_skip = RTE_ALIGN_CEIL(rte_mbuf_size(), ROC_CACHE_LINE_SZ);
wqe_skip = wqe_skip / ROC_CACHE_LINE_SZ;
inl_dev->wqe_skip = wqe_skip;
rc = roc_nix_inl_dev_init(inl_dev);
diff --git a/drivers/net/intel/fm10k/fm10k_ethdev.c b/drivers/net/intel/fm10k/fm10k_ethdev.c
index 3b2daba79e..ac5a0152e5 100644
--- a/drivers/net/intel/fm10k/fm10k_ethdev.c
+++ b/drivers/net/intel/fm10k/fm10k_ethdev.c
@@ -1758,7 +1758,7 @@ mempool_element_size_valid(struct rte_mempool *mp)
uint32_t min_size;
/* elt_size includes mbuf header and headroom */
- min_size = mp->elt_size - sizeof(struct rte_mbuf) -
+ min_size = mp->elt_size - rte_mbuf_size() -
RTE_PKTMBUF_HEADROOM;
/* account for up to 512B of alignment */
diff --git a/drivers/net/mlx5/mlx5_trigger.c b/drivers/net/mlx5/mlx5_trigger.c
index 2a91e02b45..fe14a92e56 100644
--- a/drivers/net/mlx5/mlx5_trigger.c
+++ b/drivers/net/mlx5/mlx5_trigger.c
@@ -122,15 +122,15 @@ mlx5_txq_start(struct rte_eth_dev *dev)
static struct rte_mbuf *
mlx5_alloc_null_mbuf(uint32_t data_len)
{
- size_t alloc_size = sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM +
+ size_t alloc_size = rte_mbuf_size() + RTE_PKTMBUF_HEADROOM +
rte_align32pow2(data_len);
struct rte_mbuf *m;
m = mlx5_malloc(MLX5_MEM_ZERO, alloc_size, 0, SOCKET_ID_ANY);
if (m == NULL)
return NULL;
- m->buf_addr = RTE_PTR_ADD(m, sizeof(*m));
- m->buf_len = alloc_size - sizeof(*m);
+ m->buf_addr = RTE_PTR_ADD(m, rte_mbuf_size());
+ m->buf_len = alloc_size - rte_mbuf_size();
rte_mbuf_iova_set(m, rte_mem_virt2iova(m->buf_addr));
m->data_off = RTE_PKTMBUF_HEADROOM;
m->refcnt = 1;
diff --git a/drivers/net/nfp/flower/nfp_flower.c b/drivers/net/nfp/flower/nfp_flower.c
index 14a8992376..341067a91d 100644
--- a/drivers/net/nfp/flower/nfp_flower.c
+++ b/drivers/net/nfp/flower/nfp_flower.c
@@ -446,7 +446,7 @@ nfp_flower_init_ctrl_vnic(struct nfp_app_fw_flower *app_fw_flower,
*/
rxq->mem_pool = mp;
rxq->mbuf_size = rxq->mem_pool->elt_size;
- rxq->mbuf_size -= (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM);
+ rxq->mbuf_size -= (rte_mbuf_size() + RTE_PKTMBUF_HEADROOM);
hw->flbufsz = rxq->mbuf_size;
rxq->rx_count = CTRL_VNIC_NB_DESC;
diff --git a/drivers/net/nfp/nfp_rxtx.c b/drivers/net/nfp/nfp_rxtx.c
index 19c8324549..154d1299de 100644
--- a/drivers/net/nfp/nfp_rxtx.c
+++ b/drivers/net/nfp/nfp_rxtx.c
@@ -673,7 +673,7 @@ nfp_net_rx_queue_setup(struct rte_eth_dev *dev,
*/
rxq->mem_pool = mp;
rxq->mbuf_size = rxq->mem_pool->elt_size;
- rxq->mbuf_size -= (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM);
+ rxq->mbuf_size -= (rte_mbuf_size() + RTE_PKTMBUF_HEADROOM);
nfp_rx_queue_setup_flbufsz(hw, rxq);
rxq->rx_count = nb_desc;
diff --git a/drivers/net/pfe/pfe_hif.c b/drivers/net/pfe/pfe_hif.c
index abb9cde996..fb99ae64c8 100644
--- a/drivers/net/pfe/pfe_hif.c
+++ b/drivers/net/pfe/pfe_hif.c
@@ -63,7 +63,7 @@ pfe_hif_release_buffers(struct pfe_hif *hif)
if (i < hif->shm->rx_buf_pool_cnt &&
!hif->shm->rx_buf_pool[i]) {
mbuf = hif->rx_buf_vaddr[i] + PFE_PKT_HEADER_SZ
- - sizeof(struct rte_mbuf)
+ - rte_mbuf_size()
- RTE_PKTMBUF_HEADROOM
- mb_priv->mbuf_priv_size;
hif->shm->rx_buf_pool[i] = mbuf;
diff --git a/drivers/net/pfe/pfe_hif_lib.c b/drivers/net/pfe/pfe_hif_lib.c
index 541ba365c6..eda87040fa 100644
--- a/drivers/net/pfe/pfe_hif_lib.c
+++ b/drivers/net/pfe/pfe_hif_lib.c
@@ -115,7 +115,7 @@ hif_lib_client_release_rx_buffers(struct hif_client_s *client)
* "mbuf->data_offset - PFE_PKT_HEADER_SZ"
*/
buf = buf + PFE_PKT_HEADER_SZ
- - sizeof(struct rte_mbuf)
+ - rte_mbuf_size()
- RTE_PKTMBUF_HEADROOM
- mb_priv->mbuf_priv_size;
rte_pktmbuf_free((struct rte_mbuf *)buf);
@@ -413,7 +413,7 @@ hif_lib_receive_pkt(struct hif_client_rx_queue *queue,
mb_priv = rte_mempool_get_priv(pool);
mbuf = desc->data + PFE_PKT_HEADER_SZ
- - sizeof(struct rte_mbuf)
+ - rte_mbuf_size()
- RTE_PKTMBUF_HEADROOM
- mb_priv->mbuf_priv_size;
mbuf->next = NULL;
diff --git a/drivers/net/sfc/sfc_rx.c b/drivers/net/sfc/sfc_rx.c
index 1d4101b9e9..204c40be9a 100644
--- a/drivers/net/sfc/sfc_rx.c
+++ b/drivers/net/sfc/sfc_rx.c
@@ -1002,7 +1002,7 @@ sfc_rx_mbuf_data_alignment(struct rte_mempool *mb_pool)
order = rte_bsf32(RTE_CACHE_LINE_SIZE);
/* Data offset from mbuf object start */
- data_off = sizeof(struct rte_mbuf) + rte_pktmbuf_priv_size(mb_pool) +
+ data_off = rte_mbuf_size() + rte_pktmbuf_priv_size(mb_pool) +
RTE_PKTMBUF_HEADROOM;
order = MIN(order, rte_bsf32(data_off));
diff --git a/drivers/net/softnic/rte_eth_softnic_mempool.c b/drivers/net/softnic/rte_eth_softnic_mempool.c
index d5c569f94e..0721bcec39 100644
--- a/drivers/net/softnic/rte_eth_softnic_mempool.c
+++ b/drivers/net/softnic/rte_eth_softnic_mempool.c
@@ -10,7 +10,7 @@
#include "rte_eth_softnic_internals.h"
-#define BUFFER_SIZE_MIN (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
+#define BUFFER_SIZE_MIN (rte_mbuf_size() + RTE_PKTMBUF_HEADROOM)
int
softnic_mempool_init(struct pmd_internals *p)
@@ -78,7 +78,7 @@ softnic_mempool_create(struct pmd_internals *p,
params->pool_size,
params->cache_size,
0,
- params->buffer_size - sizeof(struct rte_mbuf),
+ params->buffer_size - rte_mbuf_size(),
p->params.cpu_id);
if (m == NULL)
diff --git a/examples/fips_validation/fips_validation.h b/examples/fips_validation/fips_validation.h
index 881c759033..d4ef32fc9b 100644
--- a/examples/fips_validation/fips_validation.h
+++ b/examples/fips_validation/fips_validation.h
@@ -16,7 +16,7 @@
#define MAX_CASE_LINE 15
#define MAX_LINE_CHAR 204800 /* max number of characters per line */
#define MAX_NB_TESTS 10240
-#define DEF_MBUF_SEG_SIZE (UINT16_MAX - sizeof(struct rte_mbuf) - \
+#define DEF_MBUF_SEG_SIZE (UINT16_MAX - rte_mbuf_size() - \
RTE_PKTMBUF_HEADROOM)
#define MAX_STRING_SIZE 64
#define MAX_FILE_NAME_SIZE 256
diff --git a/examples/fips_validation/main.c b/examples/fips_validation/main.c
index 2b6d55aa51..9cfb480441 100644
--- a/examples/fips_validation/main.c
+++ b/examples/fips_validation/main.c
@@ -200,7 +200,7 @@ cryptodev_fips_validate_app_init(void)
ret = -ENOMEM;
env.mpool = rte_pktmbuf_pool_create("FIPS_MEMPOOL", nb_mbufs,
- 0, 0, sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM +
+ 0, 0, rte_mbuf_size() + RTE_PKTMBUF_HEADROOM +
env.mbuf_data_room, rte_socket_id());
if (!env.mpool)
return ret;
diff --git a/examples/ntb/ntb_fwd.c b/examples/ntb/ntb_fwd.c
index 33f3c1ef17..00e9256f0b 100644
--- a/examples/ntb/ntb_fwd.c
+++ b/examples/ntb/ntb_fwd.c
@@ -1108,7 +1108,7 @@ ntb_mbuf_pool_create(uint16_t mbuf_seg_size, uint32_t nb_mbuf,
snprintf(pool_name, sizeof(pool_name), "ntb_mbuf_pool_%u", socket_id);
mp = rte_mempool_create_empty(pool_name, nb_mbuf,
- (mbuf_seg_size + sizeof(struct rte_mbuf)),
+ mbuf_seg_size + rte_mbuf_size(),
MEMPOOL_CACHE_SIZE,
sizeof(struct rte_pktmbuf_pool_private),
socket_id, 0);
diff --git a/lib/cryptodev/rte_crypto.h b/lib/cryptodev/rte_crypto.h
index dcf4a36fb2..646a246578 100644
--- a/lib/cryptodev/rte_crypto.h
+++ b/lib/cryptodev/rte_crypto.h
@@ -426,8 +426,8 @@ rte_crypto_sym_op_alloc_from_mbuf_priv_data(struct rte_mbuf *m)
sizeof(struct rte_crypto_sym_op))))
return NULL;
- /* private data starts immediately after the mbuf header in the mbuf. */
- struct rte_crypto_op *op = (struct rte_crypto_op *)(m + 1);
+ /* private data starts after the mbuf object header. */
+ struct rte_crypto_op *op = rte_mbuf_to_priv(m);
__rte_crypto_op_reset(op, RTE_CRYPTO_OP_TYPE_SYMMETRIC);
diff --git a/lib/eal/common/eal_common_config.c b/lib/eal/common/eal_common_config.c
index e2e69a75fb..f93060c1f6 100644
--- a/lib/eal/common/eal_common_config.c
+++ b/lib/eal/common/eal_common_config.c
@@ -9,6 +9,9 @@
#include "eal_filesystem.h"
#include "eal_memcfg.h"
+RTE_EXPORT_SYMBOL(rte_mbuf_metadata_size)
+uint16_t rte_mbuf_metadata_size;
+
/* early configuration structure, when memory config is not mmapped */
static struct rte_mem_config early_mem_config = {
.mlock = RTE_RWLOCK_INITIALIZER,
diff --git a/lib/eal/common/eal_common_mcfg.c b/lib/eal/common/eal_common_mcfg.c
index 84ee3f3959..c034f09a0a 100644
--- a/lib/eal/common/eal_common_mcfg.c
+++ b/lib/eal/common/eal_common_mcfg.c
@@ -46,15 +46,27 @@ eal_mcfg_check_version(void)
return 0;
}
-void
+int
eal_mcfg_update_internal(void)
{
struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config;
struct internal_config *internal_conf =
eal_get_internal_configuration();
+ if (internal_conf->mbuf_metadata_size_set &&
+ internal_conf->mbuf_metadata_size != mcfg->mbuf_metadata_size) {
+ EAL_LOG(ERR,
+ "Secondary process mbuf metadata size %zu does not match primary value %u",
+ internal_conf->mbuf_metadata_size, mcfg->mbuf_metadata_size);
+ return -1;
+ }
+
internal_conf->legacy_mem = mcfg->legacy_mem;
internal_conf->single_file_segments = mcfg->single_file_segments;
+ internal_conf->mbuf_metadata_size = mcfg->mbuf_metadata_size;
+ rte_mbuf_metadata_size = mcfg->mbuf_metadata_size;
+
+ return 0;
}
void
@@ -66,6 +78,8 @@ eal_mcfg_update_from_internal(void)
mcfg->legacy_mem = internal_conf->legacy_mem;
mcfg->single_file_segments = internal_conf->single_file_segments;
+ mcfg->mbuf_metadata_size = internal_conf->mbuf_metadata_size;
+ rte_mbuf_metadata_size = internal_conf->mbuf_metadata_size;
/* record current DPDK version */
mcfg->version = RTE_VERSION;
}
diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c
index 42cdef632f..a1c09ab9fd 100644
--- a/lib/eal/common/eal_common_options.c
+++ b/lib/eal/common/eal_common_options.c
@@ -554,6 +554,9 @@ eal_reset_internal_config(struct internal_config *internal_cfg)
internal_cfg->create_uio_dev = 0;
internal_cfg->iova_mode = RTE_IOVA_DC;
internal_cfg->user_mbuf_pool_ops_name = NULL;
+ internal_cfg->mbuf_metadata_size = 0;
+ internal_cfg->mbuf_metadata_size_set = false;
+ rte_mbuf_metadata_size = 0;
CPU_ZERO(&internal_cfg->ctrl_cpuset);
internal_cfg->init_complete = 0;
internal_cfg->max_simd_bitwidth.bitwidth = RTE_VECT_DEFAULT_SIMD_BITWIDTH;
@@ -2409,6 +2412,23 @@ eal_parse_args(void)
return -1;
}
}
+ if (args.mbuf_metadata_size != NULL) {
+ char *end = NULL;
+ unsigned long size;
+
+ errno = 0;
+ size = strtoul(args.mbuf_metadata_size, &end, 0);
+ if (errno != 0 || args.mbuf_metadata_size[0] == '\0' ||
+ end == NULL || *end != '\0' ||
+ size > UINT16_MAX ||
+ size % RTE_CACHE_LINE_SIZE != 0) {
+ EAL_LOG(ERR, "invalid mbuf metadata size parameter");
+ return -1;
+ }
+ int_cfg->mbuf_metadata_size = size;
+ int_cfg->mbuf_metadata_size_set = true;
+ rte_mbuf_metadata_size = size;
+ }
#ifndef RTE_EXEC_ENV_WINDOWS
/* create runtime data directory. In no_shconf mode, skip any errors */
diff --git a/lib/eal/common/eal_internal_cfg.h b/lib/eal/common/eal_internal_cfg.h
index 07cd35167d..88684c5348 100644
--- a/lib/eal/common/eal_internal_cfg.h
+++ b/lib/eal/common/eal_internal_cfg.h
@@ -94,6 +94,9 @@ struct internal_config {
char *hugepage_dir; /**< specific hugetlbfs directory to use */
char *user_mbuf_pool_ops_name;
/**< user defined mbuf pool ops name */
+ size_t mbuf_metadata_size; /**< Global per-mbuf metadata size. */
+ bool mbuf_metadata_size_set;
+ /**< True if mbuf metadata size was explicitly set. */
unsigned num_hugepage_sizes; /**< how many sizes on this system */
struct hugepage_info hugepage_info[MAX_HUGEPAGE_SIZES];
uint64_t hugepage_mem_sz_limits[MAX_HUGEPAGE_SIZES];
diff --git a/lib/eal/common/eal_memcfg.h b/lib/eal/common/eal_memcfg.h
index 2b3b3b62ba..99a2af1e2b 100644
--- a/lib/eal/common/eal_memcfg.h
+++ b/lib/eal/common/eal_memcfg.h
@@ -77,6 +77,8 @@ struct rte_mem_config {
uint32_t legacy_mem; /**< stored legacy mem parameter. */
uint32_t single_file_segments;
/**< stored single file segments parameter. */
+ uint32_t mbuf_metadata_size;
+ /**< stored per-mbuf metadata area size. */
uint64_t tsc_hz;
/**< TSC rate */
@@ -87,7 +89,7 @@ struct rte_mem_config {
};
/* update internal config from shared mem config */
-void
+int
eal_mcfg_update_internal(void);
/* update shared mem config from internal config */
diff --git a/lib/eal/common/eal_option_list.h b/lib/eal/common/eal_option_list.h
index b72f243cc6..e14cf68770 100644
--- a/lib/eal/common/eal_option_list.h
+++ b/lib/eal/common/eal_option_list.h
@@ -48,6 +48,7 @@ OPT_STR_ARG("--log-color", NULL, "Enable/disable color in log output", log_color
LIST_ARG("--log-level", NULL, "Log level for loggers; use log-level=help for list of log types and levels", log_level)
OPT_STR_ARG("--log-timestamp", NULL, "Enable/disable timestamp in log output", log_timestamp)
STR_ARG("--main-lcore", NULL, "Select which core to use for the main thread", main_lcore)
+STR_ARG("--mbuf-metadata-size", NULL, "Global per-mbuf metadata area size", mbuf_metadata_size)
STR_ARG("--mbuf-pool-ops-name", NULL, "User defined mbuf default pool ops name", mbuf_pool_ops_name)
STR_ARG("--memory-channels", "-n", "Number of memory channels per socket", memory_channels)
STR_ARG("--memory-ranks", "-r", "Force number of memory ranks (don't detect)", memory_ranks)
diff --git a/lib/eal/common/eal_private.h b/lib/eal/common/eal_private.h
index 6340bab8be..e279150ddf 100644
--- a/lib/eal/common/eal_private.h
+++ b/lib/eal/common/eal_private.h
@@ -40,6 +40,7 @@ struct lcore_config {
};
extern struct lcore_config lcore_config[RTE_MAX_LCORE];
+extern uint16_t rte_mbuf_metadata_size;
/**
* The global RTE configuration structure.
diff --git a/lib/eal/freebsd/eal.c b/lib/eal/freebsd/eal.c
index 8b1ba5b99b..e5b4f6bdab 100644
--- a/lib/eal/freebsd/eal.c
+++ b/lib/eal/freebsd/eal.c
@@ -317,7 +317,8 @@ rte_config_init(void)
EAL_LOG(ERR, "Primary process refused secondary attachment");
return -1;
}
- eal_mcfg_update_internal();
+ if (eal_mcfg_update_internal() < 0)
+ return -1;
break;
case RTE_PROC_AUTO:
case RTE_PROC_INVALID:
diff --git a/lib/eal/linux/eal.c b/lib/eal/linux/eal.c
index fc2e9b8c0e..0577cce7d5 100644
--- a/lib/eal/linux/eal.c
+++ b/lib/eal/linux/eal.c
@@ -401,7 +401,8 @@ rte_config_init(void)
EAL_LOG(ERR, "Primary process refused secondary attachment");
return -1;
}
- eal_mcfg_update_internal();
+ if (eal_mcfg_update_internal() < 0)
+ return -1;
break;
case RTE_PROC_AUTO:
case RTE_PROC_INVALID:
diff --git a/lib/mbuf/mbuf_history.c b/lib/mbuf/mbuf_history.c
index b025d12fc8..fb06d7fcfa 100644
--- a/lib/mbuf/mbuf_history.c
+++ b/lib/mbuf/mbuf_history.c
@@ -85,7 +85,7 @@ mbuf_history_get_stats(struct rte_mempool *mp, FILE *f)
.f = f
};
- if (mp->elt_size < sizeof(struct rte_mbuf)) {
+ if (mp->elt_size < rte_mbuf_size()) {
MBUF_LOG(ERR, "Invalid mempool element size (less than mbuf)");
return;
}
@@ -129,7 +129,7 @@ mbuf_history_get_stats(struct rte_mempool *mp, FILE *f)
static void
mbuf_history_get_stats_walking(struct rte_mempool *mp, void *arg)
{
- if (mp->elt_size < sizeof(struct rte_mbuf))
+ if (mp->elt_size < rte_mbuf_size())
return; /* silently ignore while walking in all mempools */
mbuf_history_get_stats(mp, arg);
diff --git a/lib/mbuf/rte_mbuf.c b/lib/mbuf/rte_mbuf.c
index 005bfaa573..97f6680992 100644
--- a/lib/mbuf/rte_mbuf.c
+++ b/lib/mbuf/rte_mbuf.c
@@ -39,7 +39,7 @@ rte_pktmbuf_pool_init(struct rte_mempool *mp, void *opaque_arg)
RTE_ASSERT(mp->private_data_size >=
sizeof(struct rte_pktmbuf_pool_private));
- RTE_ASSERT(mp->elt_size >= sizeof(struct rte_mbuf));
+ RTE_ASSERT(mp->elt_size >= rte_mbuf_size());
rte_mbuf_history_init();
@@ -47,15 +47,15 @@ rte_pktmbuf_pool_init(struct rte_mempool *mp, void *opaque_arg)
user_mbp_priv = opaque_arg;
if (user_mbp_priv == NULL) {
memset(&default_mbp_priv, 0, sizeof(default_mbp_priv));
- if (mp->elt_size > sizeof(struct rte_mbuf))
- roomsz = mp->elt_size - sizeof(struct rte_mbuf);
+ if (mp->elt_size > rte_mbuf_size())
+ roomsz = mp->elt_size - rte_mbuf_size();
else
roomsz = 0;
default_mbp_priv.mbuf_data_room_size = roomsz;
user_mbp_priv = &default_mbp_priv;
}
- RTE_ASSERT(mp->elt_size >= sizeof(struct rte_mbuf) +
+ RTE_ASSERT(mp->elt_size >= rte_mbuf_size() +
((user_mbp_priv->flags & RTE_PKTMBUF_POOL_F_PINNED_EXT_BUF) ?
sizeof(struct rte_mbuf_ext_shared_info) :
user_mbp_priv->mbuf_data_room_size) +
@@ -86,7 +86,7 @@ rte_pktmbuf_init(struct rte_mempool *mp,
sizeof(struct rte_pktmbuf_pool_private));
priv_size = rte_pktmbuf_priv_size(mp);
- mbuf_size = sizeof(struct rte_mbuf) + priv_size;
+ mbuf_size = rte_mbuf_size() + priv_size;
buf_len = rte_pktmbuf_data_room_size(mp);
RTE_ASSERT(RTE_ALIGN(priv_size, RTE_MBUF_PRIV_ALIGN) == priv_size);
@@ -177,7 +177,7 @@ __rte_pktmbuf_init_extmem(struct rte_mempool *mp,
struct rte_mbuf_ext_shared_info *shinfo;
priv_size = rte_pktmbuf_priv_size(mp);
- mbuf_size = sizeof(struct rte_mbuf) + priv_size;
+ mbuf_size = rte_mbuf_size() + priv_size;
buf_len = rte_pktmbuf_data_room_size(mp);
RTE_ASSERT(RTE_ALIGN(priv_size, RTE_MBUF_PRIV_ALIGN) == priv_size);
@@ -241,7 +241,7 @@ rte_pktmbuf_pool_create_by_ops(const char *name, unsigned int n,
rte_errno = EINVAL;
return NULL;
}
- elt_size = sizeof(struct rte_mbuf) + (unsigned)priv_size +
+ elt_size = rte_mbuf_size() + (unsigned int)priv_size +
(unsigned)data_room_size;
memset(&mbp_priv, 0, sizeof(mbp_priv));
mbp_priv.mbuf_data_room_size = data_room_size;
@@ -332,8 +332,7 @@ rte_pktmbuf_pool_create_extbuf(const char *name, unsigned int n,
rte_errno = ENOMEM;
return NULL;
}
- elt_size = sizeof(struct rte_mbuf) +
- (unsigned int)priv_size +
+ elt_size = rte_mbuf_size() + (unsigned int)priv_size +
sizeof(struct rte_mbuf_ext_shared_info);
memset(&mbp_priv, 0, sizeof(mbp_priv));
diff --git a/lib/mbuf/rte_mbuf.h b/lib/mbuf/rte_mbuf.h
index 60ec8158cd..559b58a968 100644
--- a/lib/mbuf/rte_mbuf.h
+++ b/lib/mbuf/rte_mbuf.h
@@ -217,7 +217,8 @@ rte_mbuf_data_iova_default(const struct rte_mbuf *mb)
static inline struct rte_mbuf *
rte_mbuf_from_indirect(struct rte_mbuf *mi)
{
- return (struct rte_mbuf *)RTE_PTR_SUB(mi->buf_addr, sizeof(*mi) + mi->priv_size);
+ return (struct rte_mbuf *)RTE_PTR_SUB(mi->buf_addr,
+ rte_mbuf_size() + mi->priv_size);
}
/**
@@ -238,7 +239,7 @@ rte_mbuf_from_indirect(struct rte_mbuf *mi)
static inline char *
rte_mbuf_buf_addr(struct rte_mbuf *mb, struct rte_mempool *mp)
{
- return (char *)mb + sizeof(*mb) + rte_pktmbuf_priv_size(mp);
+ return (char *)mb + rte_mbuf_size() + rte_pktmbuf_priv_size(mp);
}
/**
@@ -289,7 +290,7 @@ rte_mbuf_to_baddr(struct rte_mbuf *md)
static inline void *
rte_mbuf_to_priv(struct rte_mbuf *m)
{
- return RTE_PTR_ADD(m, sizeof(struct rte_mbuf));
+ return RTE_PTR_ADD(m, rte_mbuf_size());
}
/**
@@ -1390,7 +1391,7 @@ static inline void rte_pktmbuf_detach(struct rte_mbuf *m)
__rte_pktmbuf_free_direct(m);
}
priv_size = rte_pktmbuf_priv_size(mp);
- mbuf_size = (uint32_t)(sizeof(struct rte_mbuf) + priv_size);
+ mbuf_size = rte_mbuf_size() + priv_size;
buf_len = rte_pktmbuf_data_room_size(mp);
m->priv_size = priv_size;
diff --git a/lib/mbuf/rte_mbuf_core.h b/lib/mbuf/rte_mbuf_core.h
index 98b0bd9ca7..68660e2020 100644
--- a/lib/mbuf/rte_mbuf_core.h
+++ b/lib/mbuf/rte_mbuf_core.h
@@ -20,12 +20,29 @@
#include <stdint.h>
#include <rte_byteorder.h>
+#include <rte_common.h>
#include <rte_stdatomic.h>
#ifdef __cplusplus
extern "C" {
#endif
+extern uint16_t rte_mbuf_metadata_size;
+
+/**
+ * Return the configured per-mbuf metadata size.
+ *
+ * The value is configured with the ``--mbuf-metadata-size`` EAL option.
+ *
+ * @return
+ * The per-mbuf metadata size in bytes.
+ */
+static inline uint16_t
+rte_mbuf_metadata_size_get(void)
+{
+ return rte_mbuf_metadata_size;
+}
+
/*
* Packet Offload Features Flags. It also carry packet type information.
* Critical resources. Both rx/tx shared these bits. Be cautious on any change
@@ -686,8 +703,28 @@ struct __rte_cache_aligned rte_mbuf {
uint16_t timesync;
uint32_t dynfield1[9]; /**< Reserved for dynamic fields. */
+
+ alignas(RTE_CACHE_LINE_SIZE)
+ RTE_MARKER8 metadata;
+ /**< Optional cache-line-aligned per-mbuf metadata area. */
};
+/**
+ * Return the mbuf object size.
+ *
+ * This is the fixed ``struct rte_mbuf`` size plus the runtime-configured
+ * per-mbuf metadata size. Use this helper for object-layout calculations that
+ * need to include the metadata area, such as locating application private data.
+ *
+ * @return
+ * The mbuf object size in bytes.
+ */
+static inline uint32_t
+rte_mbuf_size(void)
+{
+ return sizeof(struct rte_mbuf) + rte_mbuf_metadata_size_get();
+}
+
/**
* Function typedef of callback to free externally attached buffer.
*/
diff --git a/lib/mbuf/rte_mbuf_dyn.c b/lib/mbuf/rte_mbuf_dyn.c
index 5987c9dee8..9a24900918 100644
--- a/lib/mbuf/rte_mbuf_dyn.c
+++ b/lib/mbuf/rte_mbuf_dyn.c
@@ -3,6 +3,7 @@
*/
#include <stdalign.h>
+#include <stddef.h>
#include <sys/queue.h>
#include <stdint.h>
#include <limits.h>
@@ -46,14 +47,15 @@ static struct rte_tailq_elem mbuf_dynflag_tailq = {
EAL_REGISTER_TAILQ(mbuf_dynflag_tailq);
struct mbuf_dyn_shm {
+ size_t free_space_size;
+ /** Bitfield of available flags. */
+ uint64_t free_flags;
/**
* For each mbuf byte, free_space[i] != 0 if space is free.
* The value is the size of the biggest aligned element that
* can fit in the zone.
*/
- uint8_t free_space[sizeof(struct rte_mbuf)];
- /** Bitfield of available flags. */
- uint64_t free_flags;
+ uint16_t free_space[];
};
static struct mbuf_dyn_shm *shm;
@@ -67,15 +69,15 @@ process_score(void)
size_t off, align, size, i;
/* first, erase previous info */
- for (i = 0; i < sizeof(struct rte_mbuf); i++) {
+ for (i = 0; i < shm->free_space_size; i++) {
if (shm->free_space[i])
shm->free_space[i] = 1;
}
off = 0;
- while (off < sizeof(struct rte_mbuf)) {
+ while (off < shm->free_space_size) {
/* get the size of the free zone */
- for (size = 0; (off + size) < sizeof(struct rte_mbuf) &&
+ for (size = 0; (off + size) < shm->free_space_size &&
shm->free_space[off + size]; size++)
;
if (size == 0) {
@@ -99,21 +101,38 @@ process_score(void)
}
}
+static size_t
+mbuf_dynfield_size(void)
+{
+ return rte_mbuf_size();
+}
+
+static void
+mark_free_space(size_t offset, size_t size)
+{
+ size_t i;
+
+ for (i = offset; i < offset + size; i++)
+ shm->free_space[i] = 1;
+}
+
/* Mark the area occupied by a mbuf field as available in the shm. */
#define mark_free(field) \
- memset(&shm->free_space[offsetof(struct rte_mbuf, field)], \
- 1, sizeof(((struct rte_mbuf *)0)->field))
+ mark_free_space(offsetof(struct rte_mbuf, field), \
+ sizeof(((struct rte_mbuf *)0)->field))
/* Allocate and initialize the shared memory. Assume tailq is locked */
static int
init_shared_mem(void)
{
const struct rte_memzone *mz;
+ size_t shm_size;
uint64_t mask;
+ shm_size = sizeof(*shm) + mbuf_dynfield_size() * sizeof(shm->free_space[0]);
if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
mz = rte_memzone_reserve_aligned(RTE_MBUF_DYN_MZNAME,
- sizeof(struct mbuf_dyn_shm),
+ shm_size,
SOCKET_ID_ANY, 0,
RTE_CACHE_LINE_SIZE);
} else {
@@ -130,11 +149,14 @@ init_shared_mem(void)
/* init free_space, keep it sync'd with
* rte_mbuf_dynfield_copy().
*/
- memset(shm, 0, sizeof(*shm));
+ memset(shm, 0, shm_size);
+ shm->free_space_size = mbuf_dynfield_size();
mark_free(dynfield1);
#if !RTE_IOVA_IN_MBUF
mark_free(dynfield2);
#endif
+ mark_free_space(offsetof(struct rte_mbuf, metadata),
+ rte_mbuf_metadata_size_get());
/* init free_flags */
for (mask = RTE_MBUF_F_FIRST_FREE; mask <= RTE_MBUF_F_LAST_FREE; mask <<= 1)
@@ -147,14 +169,40 @@ init_shared_mem(void)
}
/* check if this offset can be used */
+static bool
+dynfield_in_metadata(size_t offset, size_t size)
+{
+ size_t metadata_offset = offsetof(struct rte_mbuf, metadata);
+ size_t metadata_size = rte_mbuf_metadata_size_get();
+
+ return offset >= metadata_offset &&
+ size <= metadata_size &&
+ offset - metadata_offset <= metadata_size - size;
+}
+
+static bool
+dynfield_overlaps_metadata(size_t offset, size_t size)
+{
+ size_t metadata_offset = offsetof(struct rte_mbuf, metadata);
+ size_t metadata_end = metadata_offset + rte_mbuf_metadata_size_get();
+
+ return offset < metadata_end && offset + size > metadata_offset;
+}
+
static int
-check_offset(size_t offset, size_t size, size_t align)
+check_offset(size_t offset, size_t size, size_t align, unsigned int flags)
{
size_t i;
+ if ((flags & RTE_MBUF_DYNFIELD_F_METADATA) != 0 &&
+ !dynfield_in_metadata(offset, size))
+ return -1;
+ if ((flags & RTE_MBUF_DYNFIELD_F_METADATA) == 0 &&
+ dynfield_overlaps_metadata(offset, size))
+ return -1;
if ((offset & (align - 1)) != 0)
return -1;
- if (offset + size > sizeof(struct rte_mbuf))
+ if (offset + size > shm->free_space_size)
return -1;
for (i = 0; i < size; i++) {
@@ -265,10 +313,10 @@ __rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
* containing room for larger fields are kept for later.
*/
for (offset = 0;
- offset < sizeof(struct rte_mbuf);
+ offset < shm->free_space_size;
offset++) {
if (check_offset(offset, params->size,
- params->align) == 0 &&
+ params->align, params->flags) == 0 &&
shm->free_space[offset] < best_zone) {
best_zone = shm->free_space[offset];
req = offset;
@@ -279,7 +327,8 @@ __rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
return -1;
}
} else {
- if (check_offset(req, params->size, params->align) < 0) {
+ if (check_offset(req, params->size, params->align,
+ params->flags) < 0) {
rte_errno = EBUSY;
return -1;
}
@@ -334,7 +383,7 @@ rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
{
int ret;
- if (params->size >= sizeof(struct rte_mbuf)) {
+ if (params->size >= mbuf_dynfield_size()) {
rte_errno = EINVAL;
return -1;
}
@@ -342,7 +391,7 @@ rte_mbuf_dynfield_register_offset(const struct rte_mbuf_dynfield *params,
rte_errno = EINVAL;
return -1;
}
- if (params->flags != 0) {
+ if ((params->flags & ~RTE_MBUF_DYNFIELD_F_METADATA) != 0) {
rte_errno = EINVAL;
return -1;
}
@@ -570,10 +619,10 @@ void rte_mbuf_dyn_dump(FILE *out)
dynflag->params.flags);
}
fprintf(out, "Free space in mbuf (0 = occupied, value = free zone alignment):\n");
- for (i = 0; i < sizeof(struct rte_mbuf); i++) {
+ for (i = 0; i < shm->free_space_size; i++) {
if ((i % 8) == 0)
fprintf(out, " %4.4zx: ", i);
- fprintf(out, "%2.2x%s", shm->free_space[i],
+ fprintf(out, "%4.4x%s", shm->free_space[i],
(i % 8 != 7) ? " " : "\n");
}
fprintf(out, "Free bit in mbuf->ol_flags (0 = occupied, 1 = free):\n");
diff --git a/lib/mbuf/rte_mbuf_dyn.h b/lib/mbuf/rte_mbuf_dyn.h
index 20ce505bb4..e1d52fcc0e 100644
--- a/lib/mbuf/rte_mbuf_dyn.h
+++ b/lib/mbuf/rte_mbuf_dyn.h
@@ -69,6 +69,7 @@
#include <stdio.h>
#include <stdint.h>
+#include <rte_bitops.h>
#include <rte_stdatomic.h>
#ifdef __cplusplus
@@ -80,6 +81,14 @@ extern "C" {
*/
#define RTE_MBUF_DYN_NAMESIZE 64
+/**
+ * Allocate this dynamic field from the mbuf metadata area.
+ *
+ * Metadata dynamic fields are not copied during mbuf clone, copy, or attach.
+ * The metadata area is configured by the mbuf-metadata-size EAL option.
+ */
+#define RTE_MBUF_DYNFIELD_F_METADATA RTE_BIT32(0)
+
/**
* Structure describing the parameters of a mbuf dynamic field.
*/
@@ -87,7 +96,7 @@ struct rte_mbuf_dynfield {
char name[RTE_MBUF_DYN_NAMESIZE]; /**< Name of the field. */
size_t size; /**< The number of bytes to reserve. */
size_t align; /**< The alignment constraint (power of 2). */
- unsigned int flags; /**< Reserved for future use, must be 0. */
+ unsigned int flags; /**< Dynamic field flags. */
};
/**
diff --git a/lib/pcapng/rte_pcapng.c b/lib/pcapng/rte_pcapng.c
index b5d1026891..077e6d936f 100644
--- a/lib/pcapng/rte_pcapng.c
+++ b/lib/pcapng/rte_pcapng.c
@@ -473,7 +473,7 @@ rte_pcapng_mbuf_size(uint32_t length)
sizeof(struct rte_vlan_hdr) <= RTE_PKTMBUF_HEADROOM);
/* The flags and queue information are added at the end. */
- return sizeof(struct rte_mbuf)
+ return rte_mbuf_size()
+ RTE_ALIGN(length, sizeof(uint32_t))
+ pcapng_optlen(sizeof(uint32_t)) /* flag option */
+ pcapng_optlen(sizeof(uint32_t)) /* queue option */
diff --git a/lib/vhost/vhost.h b/lib/vhost/vhost.h
index bb4708aed5..e8887928b1 100644
--- a/lib/vhost/vhost.h
+++ b/lib/vhost/vhost.h
@@ -1070,7 +1070,7 @@ restore_mbuf(struct rte_mbuf *m)
while (m) {
priv_size = rte_pktmbuf_priv_size(m->pool);
- mbuf_size = sizeof(struct rte_mbuf) + priv_size;
+ mbuf_size = rte_mbuf_size() + priv_size;
/* start of buffer is after mbuf structure and priv data */
m->buf_addr = (char *)m + mbuf_size;
--
2.35.6
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v4 0/1] mbuf: add runtime metadata dynamic-field storage
2026-10-06 15:54 ` [PATCH v4 0/1] mbuf: add runtime metadata dynamic-field storage Randy L Tice
2026-10-06 15:54 ` [PATCH v4 1/1] " Randy L Tice
@ 2026-10-06 16:51 ` Stephen Hemminger
2026-10-08 16:15 ` Randy Tice (rtice)
1 sibling, 1 reply; 24+ messages in thread
From: Stephen Hemminger @ 2026-10-06 16:51 UTC (permalink / raw)
To: Randy L Tice; +Cc: dev, Morten Brørup, Nithin Dabilpuram, Harman Kalra
On Tue, 06 Oct 2026 11:54:37 -0400
Randy L Tice <rtice@cisco.com> wrote:
> This revision changes direction from the v3 build-time dynfield3
> layout. It adds a runtime EAL-configured per-mbuf metadata area that is
> placed after the fixed struct rte_mbuf header and before per-pool private
> data. This keeps sizeof(struct rte_mbuf) fixed while allowing deployments
> that need globally consistent per-mbuf metadata to reserve that storage.
>
> The metadata area is still managed by the mbuf dynamic-field registry.
> Fields registered with RTE_MBUF_DYNFIELD_F_METADATA are allocated from the
> metadata area and are not copied by generic mbuf copy, clone, or attach
> operations. Fields registered without the flag continue to use the existing
> copied mbuf dynamic-field storage and cannot overlap the metadata area.
>
> The existing per-pool private data area does not provide a central layout
> registry and is configured independently for each mbuf pool. That makes it
> hard for multiple libraries, drivers, or application modules to safely share
> metadata without out-of-band coordination, especially when pools are created
> by different components.
>
> Mbuf object layout calculations that need to include the optional metadata
> area now use rte_mbuf_size(). The primary process validates and publishes
> the metadata size through the shared mem config; secondary processes may
> omit the option, but if provided it must match the primary value.
>
> The octeontx mempool driver rejects allocation when mbuf metadata is
> configured because the hardware mbuf header offset must remain 128 bytes.
I did a brief look at this and still not convinced about the exact use case.
What is the problem this is trying to solve and why can't it be done
by using existing API's.
On a reviewer level, you need to break this up into:
- core mbuf changes
- per-driver patch to use those core mbuf changes
- example of usage
- documentation
- any review safe guards that future changes don't break this.
The more detailed AI review focused on problems as well...
[PATCH v4 1/1] mbuf: add runtime metadata dynamic-field storage
Does not apply to current main: rte_mbuf_from_indirect() and
rte_mbuf_to_priv() now have RTE_ASSERT lines. Needs rebase.
Fixed up by hand for testing; builds with -Dwerror=true for the cnxk,
bnxt, nfp, softnic and mempool drivers. mbuf_autotest passes with
metadata size 0, 64, 256 and 65472.
Error
lib/mbuf/rte_mbuf_core.h: RTE_MARKER8 is not defined when building
with MSVC (rte_common.h wraps the marker typedefs in
#ifndef RTE_TOOLCHAIN_MSVC). The markers were removed from struct
rte_mbuf for exactly this reason; this re-adds one and breaks the
Windows MSVC build. The marker is also unnecessary: the metadata
area starts at sizeof(struct rte_mbuf), so use that instead of
offsetof(struct rte_mbuf, metadata) and drop the member.
lib/eal/common/eal_common_config.c: rte_mbuf_metadata_size is
exported with RTE_EXPORT_SYMBOL and lands in DPDK_27 (stable).
New API must be experimental. Same for rte_mbuf_metadata_size_get(),
rte_mbuf_size() and RTE_MBUF_DYNFIELD_F_METADATA. Exporting a bare
variable as stable ABI is worse than a function; it freezes the type
and the symbol location.
drivers/net/cnxk, drivers/event/cnxk: the hardware skip fields are
narrow and roc_nix_rq_init() only checks 8-byte alignment, not range:
wqe_skip 2 bits, in 128B lines -> rte_mbuf_size() <= 384
later_skip 6 bits, in 8B units -> mbuf + meta + priv <= 504
first_skip 7 bits, in 8B units -> mbuf + meta + priv
+ headroom <= 1016
With --mbuf-metadata-size=384, wqe_skip becomes 4 and is truncated to
0. With metadata 256 and priv_size 128, later_skip is 512 and is
truncated to 0. Hardware then writes the WQE/packet over the mbuf.
The driver must reject configurations that do not fit, as was done
for octeontx.
Warning
EAL defines and exports a symbol named rte_mbuf_*, and it is declared
extern in both eal_private.h and rte_mbuf_core.h. Follow the existing
--mbuf-pool-ops-name pattern: EAL stores the value and provides a
getter (rte_eal_mbuf_user_pool_ops() equivalent); mbuf owns any
mbuf-named symbols.
Fast path cost. rte_mbuf_size() turns a compile-time constant into a
load of a global (through the GOT in shared builds) in
rte_mbuf_to_priv(), rte_mbuf_from_indirect(), rte_mbuf_buf_addr() and
rte_pktmbuf_detach(), all inline and used per packet. cnxk Rx vector
paths now do vdupq_n_u64(rte_mbuf_size()) inside the burst loop, and
the cn9k get_work asm needs an extra register load per event. The
cnxk paths should cache the value in rxq/ws at setup like data_off.
Need l3fwd or testpmd numbers with the option unset, on cnxk at
least, before this goes in.
lib/eal/common/eal_common_options.c: upper bound of UINT16_MAX is not
justified. Beyond the cnxk limits above, examples/fips_validation
DEF_MBUF_SEG_SIZE is UINT16_MAX - rte_mbuf_size() - headroom and
wraps for large values. Cap the option at a small number of cache
lines and document the limit.
drivers/net/cnxk/cn10k_rx.h nix_cqe_xtract_mseg(): the rx_inj block
was reindented one tab too deep; it is still inside the same if.
Only the two wqe assignment lines need to change.
Too much in one patch. Put the mbuf helpers first, then the
conversions, then the feature:
1. mbuf: add rte_mbuf_size() returning sizeof(struct rte_mbuf),
no behaviour change
2. convert libs, apps and examples
3. convert drivers (one per driver family, so maintainers can ack)
4. add the EAL option, metadata dynfield flag, driver rejections
and docs
Patches 1-3 are a no-op and can be reviewed and merged on their own.
Patch 4 is then small enough to review for what it actually changes.
It also makes it bisectable when a driver conversion is wrong.
Many of the conversions open-code rte_mbuf_size() where an existing
helper fits: RTE_PTR_ADD(m, rte_mbuf_size()) is rte_mbuf_to_priv(m),
and mbuf + rte_mbuf_size() + priv_size is rte_mbuf_buf_addr(). Use
the helpers so drivers stop depending on the layout directly.
Info
Unrelated whitespace churn: blank line removed in
drivers/mempool/octeontx/meson.build, in the release notes Known
Issues section, and in test_mbuf(). Drop these.
lib/mbuf/rte_mbuf_dyn.c: the "check if this offset can be used"
comment now sits above dynfield_in_metadata() instead of
check_offset().
drivers/mempool/octeontx: the check rejects every fpavf pool, not
just pktmbuf pools. mp->private_data_size is known at alloc time, so
the check could be limited to pools carrying
rte_pktmbuf_pool_private. Not required since net/octeontx is the only
user that cares.
app/test/test_mbuf.c: with metadata configured, dynfield_fail_big
(size 128) passes the size check and fails only because no free
space exists, so the size limit path is no longer tested in the
metadata run.
Any out-of-tree code using (m + 1) or sizeof(struct rte_mbuf) to find
private data silently breaks when a user adds this EAL option. That
belongs in the API changes section of the release notes, not only
under New Features.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v4 0/1] mbuf: add runtime metadata dynamic-field storage
2026-10-06 16:51 ` [PATCH v4 0/1] " Stephen Hemminger
@ 2026-10-08 16:15 ` Randy Tice (rtice)
0 siblings, 0 replies; 24+ messages in thread
From: Randy Tice (rtice) @ 2026-10-08 16:15 UTC (permalink / raw)
To: Stephen Hemminger
Cc: dev@dpdk.org, Morten Brørup, Nithin Dabilpuram, Harman Kalra
[-- Attachment #1: Type: text/plain, Size: 15811 bytes --]
Stephen,
Please see inline, look for #RT:
Thanks,
-rt
From: Stephen Hemminger <stephen@networkplumber.org>
Date: Tuesday, October 6, 2026 at 12:51 PM
To: Randy Tice (rtice) <rtice@cisco.com>
Cc: dev@dpdk.org <dev@dpdk.org>; Morten Brørup <mb@smartsharesystems.com>; Nithin Dabilpuram <ndabilpuram@marvell.com>; Harman Kalra <hkalra@marvell.com>
Subject: Re: [PATCH v4 0/1] mbuf: add runtime metadata dynamic-field storage
On Tue, 06 Oct 2026 11:54:37 -0400
Randy L Tice <rtice@cisco.com> wrote:
> This revision changes direction from the v3 build-time dynfield3
> layout. It adds a runtime EAL-configured per-mbuf metadata area that is
> placed after the fixed struct rte_mbuf header and before per-pool private
> data. This keeps sizeof(struct rte_mbuf) fixed while allowing deployments
> that need globally consistent per-mbuf metadata to reserve that storage.
>
> The metadata area is still managed by the mbuf dynamic-field registry.
> Fields registered with RTE_MBUF_DYNFIELD_F_METADATA are allocated from the
> metadata area and are not copied by generic mbuf copy, clone, or attach
> operations. Fields registered without the flag continue to use the existing
> copied mbuf dynamic-field storage and cannot overlap the metadata area.
>
> The existing per-pool private data area does not provide a central layout
> registry and is configured independently for each mbuf pool. That makes it
> hard for multiple libraries, drivers, or application modules to safely share
> metadata without out-of-band coordination, especially when pools are created
> by different components.
>
> Mbuf object layout calculations that need to include the optional metadata
> area now use rte_mbuf_size(). The primary process validates and publishes
> the metadata size through the shared mem config; secondary processes may
> omit the option, but if provided it must match the primary value.
>
> The octeontx mempool driver rejects allocation when mbuf metadata is
> configured because the hardware mbuf header offset must remain 128 bytes.
I did a brief look at this and still not convinced about the exact use case.
What is the problem this is trying to solve and why can't it be done
by using existing API’s.
#RT:
Our implementation currently requires an additional 256 bytes of metadata per
mbuf. That does not fit in the existing dynamic-field storage, so today we
carry a private patch to extend struct rte_mbuf. The goal of this work is to
replace that private struct change with a supported upstream mechanism.
We did look at using mbuf private data first. It can work when the application
owns all mbuf pool creation, but it is not sufficient for our case without
another global/base reservation mechanism. Some mbuf pools are created by
drivers or libraries rather than directly by the application; the CNXK inline
IPsec/OOP meta pool is one example (NIX_INL_META_POOL, created through
cnxk_nix_inl_meta_pool_cb()). To make private data work there, we had to add
an EAL argument that reserved a base private size for all pktmbufs, including
PMD-created pools.
Private data also lacks a central layout registry. If multiple modules use
private data, they must coordinate offsets out of band to avoid overlaying
each other. The dynamic-field registry solves that coordination problem, but
the existing copied dynamic-field area is too small and has copy/clone
semantics that are wrong for this metadata.
That is why this version uses a globally configured per-mbuf metadata area
managed by the dynamic-field registry, with explicit metadata fields that are
not copied by generic copy/clone/attach paths. This direction came out of the
prior discussion with you, Morten, and me: avoid a Cisco-private mbuf struct
patch, avoid per-pool private-data layout coordination, and keep sizeof(struct
rte_mbuf) fixed.
On a reviewer level, you need to break this up into:
- core mbuf changes
- per-driver patch to use those core mbuf changes
- example of usage
- documentation
- any review safe guards that future changes don't break this.
#RT:
I will split the next revision into a series instead of carrying
this as one patch. The exact split may depend on the helper model we settle
on, but the intent will be the same: introduce any mbuf layout helper first
with no behavior change, then convert core/libs/apps/examples, then convert
drivers by family so maintainers can review/ack their areas, and finally add
the EAL metadata option, registry flag, validation/rejection logic, tests,
and documentation. CNXK will be its own driver patch because it needs range
checks and fast-path caching rather than a purely mechanical conversion.
The more detailed AI review focused on problems as well...
[PATCH v4 1/1] mbuf: add runtime metadata dynamic-field storage
Does not apply to current main: rte_mbuf_from_indirect() and
rte_mbuf_to_priv() now have RTE_ASSERT lines. Needs rebase.
Fixed up by hand for testing; builds with -Dwerror=true for the cnxk,
bnxt, nfp, softnic and mempool drivers. mbuf_autotest passes with
metadata size 0, 64, 256 and 65472.
#RT:
Agreed. I will rebase v5 on current main and preserve the new RTE_ASSERT
checks in rte_mbuf_from_indirect() and rte_mbuf_to_priv(). Thanks for fixing
it up locally and for the additional build/test coverage. I will also
include comparable validation results in the next revision, including normal
metadata sizes and rejected/limit cases rather than relying on very large
values such as 65472 if we cap the option more tightly.
Error
lib/mbuf/rte_mbuf_core.h: RTE_MARKER8 is not defined when building
with MSVC (rte_common.h wraps the marker typedefs in
#ifndef RTE_TOOLCHAIN_MSVC). The markers were removed from struct
rte_mbuf for exactly this reason; this re-adds one and breaks the
Windows MSVC build. The marker is also unnecessary: the metadata
area starts at sizeof(struct rte_mbuf), so use that instead of
offsetof(struct rte_mbuf, metadata) and drop the member.
#RT:
I will fix this!
lib/eal/common/eal_common_config.c: rte_mbuf_metadata_size is
exported with RTE_EXPORT_SYMBOL and lands in DPDK_27 (stable).
New API must be experimental. Same for rte_mbuf_metadata_size_get(),
rte_mbuf_size() and RTE_MBUF_DYNFIELD_F_METADATA. Exporting a bare
variable as stable ABI is worse than a function; it freezes the type
and the symbol location.
#RT:
I agree with the concern about exporting
rte_mbuf_metadata_size as a writable global. That is the wrong ABI shape and I
will remove it.
I want to pause before spinning v5, though, because this exposes a design
choice around the existing mbuf helpers. The current patch makes helpers such
as rte_mbuf_to_priv(), rte_mbuf_buf_addr(), rte_mbuf_from_indirect(), and
rte_pktmbuf_detach() metadata-aware. That gives the least ambiguous semantics
— private data and buffer data remain after the full mbuf object — but those
helpers are public inline functions, so they need some runtime-visible mbuf
object size. If that value is hidden entirely inside EAL/mbuf C files, the
inline helpers cannot access it without becoming out-of-line calls.
The alternative is an explicit opt-in model: leave existing stable helpers
with their current fixed sizeof(struct rte_mbuf) behavior, use internal cached
layout values in PMDs/libraries that support metadata, and expose separate
metadata-aware helpers for applications that do raw mbuf layout math. For
example, rte_mbuf_to_priv() would continue to return (char *)m + sizeof(struct
rte_mbuf), while a new metadata-aware helper would return (char *)m +
sizeof(struct rte_mbuf) + metadata_size. PMDs that support metadata would use
cached internal equivalents of the latter during setup/fast path preparation,
without requiring the application to compile with experimental APIs. That
avoids silently changing existing helper performance/behavior, and the new
application-facing helpers could be experimental. The downside is parallel
helper semantics and a requirement that metadata-enabled code use the new
helpers correctly.
For PMDs, I think the right implementation either way is to avoid repeated
runtime helper loads in fast paths. The metadata size is system-wide after EAL
init, so DPDK can compute an internal mbuf object size once and PMDs can cache
any derived queue/device values during setup. CNXK also needs explicit guards:
the hardware skip fields cap the supported metadata size and related private-
data/headroom combinations. In particular, with the current 128-byte mbuf
header, 256 bytes of metadata already reaches the wqe_skip limit, so CNXK
support must reject configurations that exceed the hardware-encodable offsets.
Before I rework the series, I’d like your guidance on which ABI/API model you
think is acceptable upstream:
1. make the existing helpers metadata-aware, accepting a small stable read
path for the configured mbuf object size; or
2. keep existing helpers fixed and add explicit metadata-aware APIs for opt-in
users, while PMDs/libraries use internal cached layout values.
My preference is to remove the writable global, keep PMD fast paths cached,
add the necessary driver guards, and avoid requiring applications to enable
experimental API merely because a PMD supports metadata. I’m open to either
helper model, but I’d rather align on that before producing another revision.
drivers/net/cnxk, drivers/event/cnxk: the hardware skip fields are
narrow and roc_nix_rq_init() only checks 8-byte alignment, not range:
wqe_skip 2 bits, in 128B lines -> rte_mbuf_size() <= 384
later_skip 6 bits, in 8B units -> mbuf + meta + priv <= 504
first_skip 7 bits, in 8B units -> mbuf + meta + priv
+ headroom <= 1016
With --mbuf-metadata-size=384, wqe_skip becomes 4 and is truncated to
0. With metadata 256 and priv_size 128, later_skip is 512 and is
truncated to 0. Hardware then writes the WQE/packet over the mbuf.
The driver must reject configurations that do not fit, as was done
for octeontx.
#RT:
Agreed. This is a real correctness issue, not just a local type-width issue.
The local roc_nix_rq fields are wider, but the programmed CNXK hardware
context fields are limited to wqe_skip:2, later_skip:6, and first_skip:7, so
values beyond those limits can truncate. I will add explicit CNXK validation
before programming the RQ context and reject unsupported combinations. The
checks need to cover both the global metadata size and the per-pool layout:
wqe_skip limits sizeof(struct rte_mbuf) + metadata_size to 384 bytes,
later_skip limits sizeof(struct rte_mbuf) + metadata_size + priv_size to 504
bytes, and first_skip limits sizeof(struct rte_mbuf) + metadata_size +
priv_size + headroom to 1016 bytes. With the current 128-byte mbuf header,
--mbuf-metadata-size=256 is already the CNXK metadata ceiling from wqe_skip,
while remaining private-data headroom is checked per pool.
Warning
EAL defines and exports a symbol named rte_mbuf_*, and it is declared
extern in both eal_private.h and rte_mbuf_core.h. Follow the existing
--mbuf-pool-ops-name pattern: EAL stores the value and provides a
getter (rte_eal_mbuf_user_pool_ops() equivalent); mbuf owns any
mbuf-named symbols.
#RT:
Agreed. I will remove the rte_mbuf_* storage/export from EAL. The configured
value should be stored and shared by EAL, following the existing --mbuf-
pool-ops-name style, while mbuf-owned APIs/helpers remain in the mbuf
library. The next revision will separate EAL configuration/storage from mbuf
naming/ownership instead of declaring the same external value through both
EAL-private and mbuf public headers.
Fast path cost. rte_mbuf_size() turns a compile-time constant into a
load of a global (through the GOT in shared builds) in
rte_mbuf_to_priv(), rte_mbuf_from_indirect(), rte_mbuf_buf_addr() and
rte_pktmbuf_detach(), all inline and used per packet. cnxk Rx vector
paths now do vdupq_n_u64(rte_mbuf_size()) inside the burst loop, and
the cn9k get_work asm needs an extra register load per event. The
cnxk paths should cache the value in rxq/ws at setup like data_off.
Need l3fwd or testpmd numbers with the option unset, on cnxk at
least, before this goes in.
lib/eal/common/eal_common_options.c: upper bound of UINT16_MAX is not
justified. Beyond the cnxk limits above, examples/fips_validation
DEF_MBUF_SEG_SIZE is UINT16_MAX - rte_mbuf_size() - headroom and
wraps for large values. Cap the option at a small number of cache
lines and document the limit.
#RT:
Agreed. UINT16_MAX is not a justified upper bound. I will cap the option to
A small documented number of cache lines and add boundary tests. CNXK also
needs separate queue/pool validation because its later_skip and first_skip limits
include priv_size and headroom; with the current 128-byte mbuf header, its
global metadata ceiling is 256 bytes due to wqe_skip.
drivers/net/cnxk/cn10k_rx.h nix_cqe_xtract_mseg(): the rx_inj block
was reindented one tab too deep; it is still inside the same if.
Only the two wqe assignment lines need to change.
Too much in one patch. Put the mbuf helpers first, then the
conversions, then the feature:
1. mbuf: add rte_mbuf_size() returning sizeof(struct rte_mbuf),
no behaviour change
2. convert libs, apps and examples
3. convert drivers (one per driver family, so maintainers can ack)
4. add the EAL option, metadata dynfield flag, driver rejections
and docs
Patches 1-3 are a no-op and can be reviewed and merged on their own.
Patch 4 is then small enough to review for what it actually changes.
It also makes it bisectable when a driver conversion is wrong.
Many of the conversions open-code rte_mbuf_size() where an existing
helper fits: RTE_PTR_ADD(m, rte_mbuf_size()) is rte_mbuf_to_priv(m),
and mbuf + rte_mbuf_size() + priv_size is rte_mbuf_buf_addr(). Use
the helpers so drivers stop depending on the layout directly.
Info
Unrelated whitespace churn: blank line removed in
drivers/mempool/octeontx/meson.build, in the release notes Known
Issues section, and in test_mbuf(). Drop these.
lib/mbuf/rte_mbuf_dyn.c: the "check if this offset can be used"
comment now sits above dynfield_in_metadata() instead of
check_offset().
drivers/mempool/octeontx: the check rejects every fpavf pool, not
just pktmbuf pools. mp->private_data_size is known at alloc time, so
the check could be limited to pools carrying
rte_pktmbuf_pool_private. Not required since net/octeontx is the only
user that cares.
app/test/test_mbuf.c: with metadata configured, dynfield_fail_big
(size 128) passes the size check and fails only because no free
space exists, so the size limit path is no longer tested in the
metadata run.
Any out-of-tree code using (m + 1) or sizeof(struct rte_mbuf) to find
private data silently breaks when a user adds this EAL option. That
belongs in the API changes section of the release notes, not only
under New Features.
#RT:
I will also clean up the lower-level review items in the next revision:
remove unrelated whitespace churn, restore the misplaced check_offset()
comment, tighten or document the octeontx rejection as appropriate, keep the
mbuf tests covering the intended size-limit path, and move the out-of-tree
layout impact note into the API changes release-note section.
[-- Attachment #2: Type: text/html, Size: 27476 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2026-10-08 16:15 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-24 19:37 [PATCH 0/1] mbuf: add optional dynfield3 storage Randy L Tice
2026-09-24 19:37 ` [PATCH 1/1] " Randy L Tice
2026-09-25 9:48 ` Morten Brørup
2026-09-25 19:31 ` [PATCH v2 0/1] " Randy L Tice
2026-09-25 19:31 ` [PATCH v2 1/1] " Randy L Tice
2026-09-26 16:51 ` Stephen Hemminger
2026-09-28 14:45 ` Randy Tice (rtice)
2026-09-28 18:16 ` [PATCH v3 0/1] mbuf: add optional no-copy dynamic field storage Randy L Tice
2026-09-28 18:16 ` [PATCH v3 1/1] " Randy L Tice
2026-09-29 7:15 ` Morten Brørup
2026-09-29 11:59 ` Konstantin Ananyev
2026-09-29 12:44 ` Morten Brørup
2026-09-29 13:12 ` Konstantin Ananyev
2026-09-29 13:34 ` Morten Brørup
2026-09-29 14:14 ` Konstantin Ananyev
2026-09-29 14:44 ` Randy Tice (rtice)
2026-09-29 15:20 ` Morten Brørup
2026-09-29 19:03 ` Randy Tice (rtice)
2026-09-29 19:28 ` Konstantin Ananyev
2026-09-29 19:26 ` Konstantin Ananyev
2026-10-06 15:54 ` [PATCH v4 0/1] mbuf: add runtime metadata dynamic-field storage Randy L Tice
2026-10-06 15:54 ` [PATCH v4 1/1] " Randy L Tice
2026-10-06 16:51 ` [PATCH v4 0/1] " Stephen Hemminger
2026-10-08 16:15 ` Randy Tice (rtice)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox