Stephen, Please see inline, look for #RT: Thanks, -rt From: Stephen Hemminger <[email protected]> Date: Tuesday, October 6, 2026 at 12:51 PM To: Randy Tice (rtice) <[email protected]> Cc: [email protected] <[email protected]>; Morten Brørup <[email protected]>; Nithin Dabilpuram <[email protected]>; Harman Kalra <[email protected]> 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 <[email protected]> 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.

