From: "Morten Brørup" <mb@smartsharesystems.com>
To: "Thomas Monjalon" <thomas@monjalon.net>, <dev@dpdk.org>
Cc: <shperetz@nvidia.com>, <viacheslavo@nvidia.com>,
<bruce.richardson@intel.com>, <stephen@networkplumber.org>
Subject: RE: [PATCH v5 3/7] mbuf: record mbuf operations history
Date: Tue, 14 Oct 2025 11:59:40 +0200 [thread overview]
Message-ID: <98CBD80474FA8B44BF855DF32C47DC35F654C7@smartserver.smartshare.dk> (raw)
In-Reply-To: <20251014070517.922137-4-thomas@monjalon.net>
> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> Sent: Tuesday, 14 October 2025 08.59
>
> From: Shani Peretz <shperetz@nvidia.com>
>
> This feature is designed to monitor the lifecycle of mbufs
> as they move between the application and the PMD.
> It will allow to track the operations and transitions
> of each mbuf throughout the system, helping in debugging
> and understanding objects flow.
>
> As a debug feature impacting the data path, it is disabled by default.
>
> The implementation uses a dynamic field to store a 64-bit
> atomic history value in each mbuf. Each operation is represented
> by a 4-bit value, allowing up to 16 operations to be tracked.
> Atomic operations ensure thread safety for cloned mbufs accessed
> by multiple lcores. The dynamic field is automatically initialized
> when the first mbuf pool is created.
>
> Some operations done in the mbuf library are marked.
> More operations from other libraries or the application can be marked.
>
> Signed-off-by: Shani Peretz <shperetz@nvidia.com>
> Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
> ---
> config/rte_config.h | 1 +
> doc/guides/howto/debug_troubleshoot.rst | 6 +
> doc/guides/prog_guide/mbuf_lib.rst | 7 +
> doc/guides/rel_notes/release_25_11.rst | 6 +
> lib/mbuf/mbuf_history.c | 227 ++++++++++++++++++++++++
> lib/mbuf/meson.build | 2 +
> lib/mbuf/rte_mbuf.c | 6 +-
> lib/mbuf/rte_mbuf.h | 13 +-
> lib/mbuf/rte_mbuf_dyn.h | 18 ++
> lib/mbuf/rte_mbuf_history.h | 203 +++++++++++++++++++++
> 10 files changed, 486 insertions(+), 3 deletions(-)
> create mode 100644 lib/mbuf/mbuf_history.c
> create mode 100644 lib/mbuf/rte_mbuf_history.h
>
> diff --git a/config/rte_config.h b/config/rte_config.h
> index 05344e2619..aee137717b 100644
> --- a/config/rte_config.h
> +++ b/config/rte_config.h
> @@ -64,6 +64,7 @@
>
> /* mbuf defines */
> #define RTE_MBUF_DEFAULT_MEMPOOL_OPS "ring_mp_mc"
> +/* RTE_MBUF_HISTORY_DEBUG is not set */
>
> /* ether defines */
> #define RTE_MAX_QUEUES_PER_PORT 1024
> diff --git a/doc/guides/howto/debug_troubleshoot.rst
> b/doc/guides/howto/debug_troubleshoot.rst
> index df69fa8bcc..16feeb1e54 100644
> --- a/doc/guides/howto/debug_troubleshoot.rst
> +++ b/doc/guides/howto/debug_troubleshoot.rst
> @@ -217,6 +217,12 @@ Memory objects close to NUMA
> :numref:`dtg_mempool`.
> desired value and frequently uses ``rte_pktmbuf_prefree_seg`` and
> does
> not release MBUF back to mempool.
>
> + The mbuf lifecycle can be tracked
> + by defining the compilation flag ``RTE_MBUF_HISTORY_DEBUG``.
> + Then the libraries will mark the mbufs,
> + and more marks can be added in the application.
> + Some dump functions must be used to collect the history.
> +
> #. Lower performance between the pipeline processing stages can be
>
> * The NUMA instance for packets or objects from NIC, mempool, and
> ring
> diff --git a/doc/guides/prog_guide/mbuf_lib.rst
> b/doc/guides/prog_guide/mbuf_lib.rst
> index badc0f3501..ae72eecc61 100644
> --- a/doc/guides/prog_guide/mbuf_lib.rst
> +++ b/doc/guides/prog_guide/mbuf_lib.rst
> @@ -273,6 +273,13 @@ perform sanity checks (such as buffer corruption,
> bad type, and so on).
> When ``RTE_ENABLE_ASSERT`` is enabled,
> more basic checks are done in many functions.
>
> +When ``RTE_MBUF_HISTORY_DEBUG`` is enabled,
> +the mbuf lifecycle is tracked.
> +More marks can be added by the application
> +by calling functions like ``rte_mbuf_history_mark_bulk()``.
> +Then the history can be stored in a file
> +by calling functions like ``rte_mbuf_history_dump_all()``.
> +
>
> Use Cases
> ---------
> diff --git a/doc/guides/rel_notes/release_25_11.rst
> b/doc/guides/rel_notes/release_25_11.rst
> index c3b94e1896..6854090096 100644
> --- a/doc/guides/rel_notes/release_25_11.rst
> +++ b/doc/guides/rel_notes/release_25_11.rst
> @@ -60,6 +60,12 @@ New Features
> Added Ethernet link speed for 800 Gb/s as it is well standardized in
> IEEE,
> and some devices already support this speed.
>
> +* **Added mbuf tracking for debug.**
> +
> + Added history dynamic field in mbuf (disabled by default)
> + to store successive states of the mbuf lifecycle.
> + Some functions were added to dump statistics.
> +
> * **Updated NXP DPAA2 ethernet driver.**
>
> * Enabled software taildrop for ordered queues.
> diff --git a/lib/mbuf/mbuf_history.c b/lib/mbuf/mbuf_history.c
> new file mode 100644
> index 0000000000..e3203c0d5c
> --- /dev/null
> +++ b/lib/mbuf/mbuf_history.c
> @@ -0,0 +1,227 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2024 NVIDIA Corporation & Affiliates
Suggest: 2024 -> 2025
Also in other files.
> + */
> +
> +#include <rte_errno.h>
> +#include <eal_export.h>
> +#include <rte_bitops.h>
> +#include <rte_mempool.h>
> +
> +#include "rte_mbuf_history.h"
> +#include "rte_mbuf_dyn.h"
> +#include "rte_mbuf.h"
> +#include "mbuf_log.h"
> +
> +/* Dynamic field offset */
> +RTE_EXPORT_SYMBOL(rte_mbuf_history_field_offset);
> +int rte_mbuf_history_field_offset = -1;
> +
> +#ifdef RTE_MBUF_HISTORY_DEBUG
> +
> +#define HISTORY_LAST_MASK (RTE_BIT64(RTE_MBUF_HISTORY_BITS) - 1)
Various places in the code has something like:
+ last_op = history & HISTORY_LAST_MASK;
+ RTE_ASSERT(last_op < RTE_MBUF_HISTORY_OP_MAX);
Suggest replacing those by adding a static_assert here instead:
static_assert(RTE_MBUF_HISTORY_OP_MAX == HISTORY_LAST_MASK + 1, "Op size mismatch")
> +
> +/* Dynamic field definition for mbuf history */
> +static const struct rte_mbuf_dynfield mbuf_dynfield_history = {
> + .name = RTE_MBUF_DYNFIELD_HISTORY_NAME,
> + .size = sizeof(rte_mbuf_history_t),
> + .align = RTE_ALIGN(sizeof(rte_mbuf_history_t), 8),
> +};
> +
> +/* Context structure for statistics counting and history printing */
> +struct count_and_print_ctx {
> + uint64_t *stats;
> + FILE *f;
> +};
> +
> +static uint64_t
> +mbuf_history_get(const struct rte_mbuf *m)
> +{
> + if (rte_mbuf_history_field_offset < 0 || m == NULL)
> + return 0;
> +
> + rte_mbuf_history_t *history = RTE_MBUF_DYNFIELD(m,
> + rte_mbuf_history_field_offset, rte_mbuf_history_t *);
> +
> + return rte_atomic_load_explicit(history,
> rte_memory_order_acquire);
> +}
> +
> +static int
> +mbuf_history_print(FILE *f, const struct rte_mbuf *m, uint64_t
> history)
> +{
> + return fprintf(f, "mbuf %p: %016" PRIx64 "\n", m, history);
> +}
> +
> +static void
> +mbuf_history_count_stats_and_print(struct rte_mempool *mp
> __rte_unused,
> + void *opaque, void *obj, unsigned obj_idx __rte_unused)
Fix:
unsigned -> unsigned int
> +{
> + struct count_and_print_ctx *ctx = (struct count_and_print_ctx
> *)opaque;
> + struct rte_mbuf *m = (struct rte_mbuf *)obj;
> + uint64_t history, last_op;
Suggest using the enum type for operation variables:
uint64_t history;
enum rte_mbuf_history_op last_op;
Also elsewhere in the code.
> +
> + if (obj == NULL || ctx == NULL || ctx->stats == NULL || ctx->f ==
> NULL)
> + return;
> +
> + history = mbuf_history_get(m);
> + /* Extract the most recent operation */
> + last_op = history & HISTORY_LAST_MASK;
> + RTE_ASSERT(last_op < RTE_MBUF_HISTORY_OP_MAX);
> +
> + ctx->stats[last_op]++;
> + ctx->stats[RTE_MBUF_HISTORY_OP_MAX]++; /* total */
> +
> + if (history != 0)
> + mbuf_history_print(ctx->f, m, history);
> +}
> +
> +static void
> +mbuf_history_get_stats(struct rte_mempool *mp, FILE *f)
> +{
> + uint64_t stats[RTE_MBUF_HISTORY_OP_MAX + 1] = {0};
> + struct count_and_print_ctx ctx = {
> + .stats = stats,
> + .f = f
> + };
> +
> + if (mp->elt_size < sizeof(struct rte_mbuf)) {
> + MBUF_LOG(ERR, "Invalid mempool element size (less than
> mbuf)");
> + return;
> + }
> +
> + if (f == NULL)
> + return;
> +
> + /* Output mempool header */
> + fprintf(f, "mempool <%s>@%p\n", mp->name, mp);
> +
> + /* Single pass: collect statistics and print mbuf history */
> + rte_mempool_obj_iter(mp, mbuf_history_count_stats_and_print,
> &ctx);
> +
> + /* Calculate total allocated mbufs */
> + uint64_t total_allocated =
> + stats[RTE_MBUF_HISTORY_OP_LIB_ALLOC] +
> + stats[RTE_MBUF_HISTORY_OP_PMD_ALLOC] +
> + stats[RTE_MBUF_HISTORY_OP_APP_ALLOC] +
> + stats[RTE_MBUF_HISTORY_OP_RX] +
> + stats[RTE_MBUF_HISTORY_OP_TX] +
> + stats[RTE_MBUF_HISTORY_OP_PREP_TX] +
> + stats[RTE_MBUF_HISTORY_OP_BUSY_TX] +
> + stats[RTE_MBUF_HISTORY_OP_ENQUEUE] +
> + stats[RTE_MBUF_HISTORY_OP_DEQUEUE];
> +
> + /* Print statistics summary */
> + fprintf(f, " populated = %u\n", mp->populated_size);
> + fprintf(f, " never allocated = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_NEVER]);
> + fprintf(f, " lib free = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_LIB_FREE]);
> + fprintf(f, " PMD free = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_PMD_FREE]);
> + fprintf(f, " app free = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_APP_FREE]);
> + fprintf(f, " allocated = %" PRIu64 "\n", total_allocated);
> + fprintf(f, " lib alloc = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_LIB_ALLOC]);
> + fprintf(f, " PMD alloc = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_PMD_ALLOC]);
> + fprintf(f, " app alloc = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_APP_ALLOC]);
> + fprintf(f, " Rx = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_RX]);
> + fprintf(f, " Tx = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_TX]);
> + fprintf(f, " prep Tx = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_PREP_TX]);
> + fprintf(f, " busy Tx = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_BUSY_TX]);
> + fprintf(f, " enqueue = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_ENQUEUE]);
> + fprintf(f, " dequeue = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_DEQUEUE]);
> + fprintf(f, " user defined 1 = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_USR1]);
> + fprintf(f, " user defined 2 = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_USR2]);
> + fprintf(f, " counted total = %" PRIu64 "\n",
> stats[RTE_MBUF_HISTORY_OP_MAX]);
> +}
> +
> +static void
> +mbuf_history_get_stats_walking(struct rte_mempool *mp, void *arg)
> +{
> + if (mp->elt_size < sizeof(struct rte_mbuf))
> + return; /* silently ignore while walking in all mempools */
> +
> + mbuf_history_get_stats(mp, arg);
> +}
> +
> +#endif /* RTE_MBUF_HISTORY_DEBUG */
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_mbuf_history_dump, 25.11)
> +void rte_mbuf_history_dump(FILE *f, const struct rte_mbuf *m)
> +{
> +#ifndef RTE_MBUF_HISTORY_DEBUG
> + RTE_SET_USED(f);
> + RTE_SET_USED(m);
> + MBUF_LOG(INFO, "mbuf history recorder is not enabled");
> +#else
> + if (f == NULL) {
> + MBUF_LOG(ERR, "Invalid mbuf dump file");
> + return;
> + }
> + if (m == NULL) {
> + MBUF_LOG(ERR, "Invalid mbuf pointer");
> + return;
> + }
> + if (rte_mbuf_history_field_offset < 0) {
> + MBUF_LOG(WARNING, "mbuf history not initialized, call
> rte_mbuf_history_init()");
> + return;
> + }
> + mbuf_history_print(f, m, mbuf_history_get(m));
> +#endif
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_mbuf_history_dump_mempool, 25.11)
> +void rte_mbuf_history_dump_mempool(FILE *f, struct rte_mempool *mp)
> +{
> +#ifndef RTE_MBUF_HISTORY_DEBUG
> + RTE_SET_USED(f);
> + RTE_SET_USED(mp);
> + MBUF_LOG(INFO, "mbuf history recorder is not enabled");
> +#else
> + if (f == NULL) {
> + MBUF_LOG(ERR, "Invalid mbuf dump file");
> + return;
> + }
> + if (mp == NULL) {
> + MBUF_LOG(ERR, "Invalid mempool pointer");
> + return;
> + }
> + if (rte_mbuf_history_field_offset < 0) {
> + MBUF_LOG(WARNING, "mbuf history not initialized, call
> rte_mbuf_history_init()");
> + return;
> + }
> + mbuf_history_get_stats(mp, f);
> +#endif
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_mbuf_history_dump_all, 25.11)
> +void rte_mbuf_history_dump_all(FILE *f)
> +{
> +#ifndef RTE_MBUF_HISTORY_DEBUG
> + RTE_SET_USED(f);
> + MBUF_LOG(INFO, "mbuf history recorder is not enabled");
> +#else
> + if (f == NULL) {
> + MBUF_LOG(ERR, "Invalid mbuf dump file");
> + return;
> + }
> + if (rte_mbuf_history_field_offset < 0) {
> + MBUF_LOG(WARNING, "mbuf history not initialized, call
> rte_mbuf_history_init()");
> + return;
> + }
> + fprintf(f, "mbuf history statistics:\n");
> + rte_mempool_walk(mbuf_history_get_stats_walking, f);
> +#endif
> +}
> +
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_mbuf_history_init, 25.11)
> +void rte_mbuf_history_init(void)
> +{
> +#ifdef RTE_MBUF_HISTORY_DEBUG
> + if (rte_mbuf_history_field_offset >= 0) {
> + /* already initialized */
> + return;
> + }
> +
> + rte_mbuf_history_field_offset =
> rte_mbuf_dynfield_register(&mbuf_dynfield_history);
> + if (rte_mbuf_history_field_offset < 0) {
> + MBUF_LOG(ERR, "Failed to register mbuf history dynamic
> field: %s\n",
> + rte_strerror(rte_errno));
> + }
> +#endif
> +}
> diff --git a/lib/mbuf/meson.build b/lib/mbuf/meson.build
> index 0435c5e628..a394db2b19 100644
> --- a/lib/mbuf/meson.build
> +++ b/lib/mbuf/meson.build
> @@ -2,6 +2,7 @@
> # Copyright(c) 2017 Intel Corporation
>
> sources = files(
> + 'mbuf_history.c',
> 'rte_mbuf.c',
> 'rte_mbuf_ptype.c',
> 'rte_mbuf_pool_ops.c',
> @@ -13,5 +14,6 @@ headers = files(
> 'rte_mbuf_ptype.h',
> 'rte_mbuf_pool_ops.h',
> 'rte_mbuf_dyn.h',
> + 'rte_mbuf_history.h',
> )
> deps += ['mempool']
> diff --git a/lib/mbuf/rte_mbuf.c b/lib/mbuf/rte_mbuf.c
> index 22d7cfc475..d96c2196a3 100644
> --- a/lib/mbuf/rte_mbuf.c
> +++ b/lib/mbuf/rte_mbuf.c
> @@ -41,6 +41,8 @@ rte_pktmbuf_pool_init(struct rte_mempool *mp, void
> *opaque_arg)
> sizeof(struct rte_pktmbuf_pool_private));
> RTE_ASSERT(mp->elt_size >= sizeof(struct rte_mbuf));
>
> + rte_mbuf_history_init();
> +
> /* if no structure is provided, assume no mbuf private area */
> user_mbp_priv = opaque_arg;
> if (user_mbp_priv == NULL) {
> @@ -515,8 +517,10 @@ void rte_pktmbuf_free_bulk(struct rte_mbuf
> **mbufs, unsigned int count)
> } while (m != NULL);
> }
>
> - if (nb_pending > 0)
> + if (nb_pending > 0) {
> + rte_mbuf_history_mark_bulk(pending, nb_pending,
> RTE_MBUF_HISTORY_OP_LIB_FREE);
> rte_mempool_put_bulk(pending[0]->pool, (void **)pending,
> nb_pending);
> + }
> }
>
> /* Creates a shallow copy of mbuf */
> diff --git a/lib/mbuf/rte_mbuf.h b/lib/mbuf/rte_mbuf.h
> index 06ab7502a5..a6c6b21253 100644
> --- a/lib/mbuf/rte_mbuf.h
> +++ b/lib/mbuf/rte_mbuf.h
> @@ -40,6 +40,7 @@
> #include <rte_branch_prediction.h>
> #include <rte_mbuf_ptype.h>
> #include <rte_mbuf_core.h>
> +#include <rte_mbuf_history.h>
>
> #ifdef __cplusplus
> extern "C" {
> @@ -607,6 +608,7 @@ static inline struct rte_mbuf
> *rte_mbuf_raw_alloc(struct rte_mempool *mp)
> if (rte_mempool_get(mp, &ret.ptr) < 0)
> return NULL;
> __rte_mbuf_raw_sanity_check(ret.m);
> + rte_mbuf_history_mark(ret.m, RTE_MBUF_HISTORY_OP_LIB_ALLOC);
> return ret.m;
> }
>
> @@ -642,9 +644,12 @@ static __rte_always_inline int
> rte_mbuf_raw_alloc_bulk(struct rte_mempool *mp, struct rte_mbuf
> **mbufs, unsigned int count)
> {
> int rc = rte_mempool_get_bulk(mp, (void **)mbufs, count);
> - if (likely(rc == 0))
> - for (unsigned int idx = 0; idx < count; idx++)
> + if (likely(rc == 0)) {
> + for (unsigned int idx = 0; idx < count; idx++) {
> __rte_mbuf_raw_sanity_check(mbufs[idx]);
> + rte_mbuf_history_mark(mbufs[idx],
> RTE_MBUF_HISTORY_OP_LIB_ALLOC);
> + }
> + }
> return rc;
> }
>
> @@ -667,6 +672,7 @@ rte_mbuf_raw_free(struct rte_mbuf *m)
> {
> __rte_mbuf_raw_sanity_check(m);
> rte_mempool_put(m->pool, m);
> + rte_mbuf_history_mark(m, RTE_MBUF_HISTORY_OP_LIB_FREE);
Fix: For improved race protection, mark the mbuf before actually freeing it, like this:
__rte_mbuf_raw_sanity_check(m);
+ rte_mbuf_history_mark(m, RTE_MBUF_HISTORY_OP_LIB_FREE);
rte_mempool_put(m->pool, m);
> }
>
> /**
> @@ -701,6 +707,7 @@ rte_mbuf_raw_free_bulk(struct rte_mempool *mp,
> struct rte_mbuf **mbufs, unsigned
> RTE_ASSERT(m != NULL);
> RTE_ASSERT(m->pool == mp);
> __rte_mbuf_raw_sanity_check(m);
> + rte_mbuf_history_mark(mbufs[idx],
> RTE_MBUF_HISTORY_OP_LIB_FREE);
> }
Fix: The loop is normally omitted, so use rte_mbuf_history_mark_bulk() here instead of rte_mbuf_history_mark() inside the loop.
It also makes the code easier to read.
>
> rte_mempool_put_bulk(mp, (void **)mbufs, count);
> @@ -1013,6 +1020,8 @@ static inline int rte_pktmbuf_alloc_bulk(struct
> rte_mempool *pool,
> if (unlikely(rc))
> return rc;
>
> + rte_mbuf_history_mark_bulk(mbufs, count,
> RTE_MBUF_HISTORY_OP_LIB_ALLOC);
> +
> /* To understand duff's device on loop unwinding optimization,
> see
> * https://en.wikipedia.org/wiki/Duff's_device.
> * Here while() loop is used rather than do() while{} to avoid
> extra
> diff --git a/lib/mbuf/rte_mbuf_dyn.h b/lib/mbuf/rte_mbuf_dyn.h
> index 865c90f579..dc1d4f146a 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_stdatomic.h>
>
> #ifdef __cplusplus
> extern "C" {
> @@ -240,6 +241,23 @@ void rte_mbuf_dyn_dump(FILE *out);
> * and parameters together.
> */
>
> +/**
> + * The mbuf history dynamic field provides lifecycle tracking
> + * for mbuf objects through the system.
> + * It records a fixed set of predefined operations for debugging.
> + */
> +#define RTE_MBUF_DYNFIELD_HISTORY_NAME "rte_mbuf_dynfield_history"
> +
> +/**
> + * Type for mbuf history dynamic field.
> + *
> + * Use atomic operations to support cloned mbufs
> + * accessed simultaneously by multiple lcores.
> + *
> + * The size is 64-bit for better performance on modern systems.
> + */
> +typedef RTE_ATOMIC(uint64_t) rte_mbuf_history_t;
> +
> /*
> * The metadata dynamic field provides some extra packet information
> * to interact with RTE Flow engine. The metadata in sent mbufs can be
> diff --git a/lib/mbuf/rte_mbuf_history.h b/lib/mbuf/rte_mbuf_history.h
> new file mode 100644
> index 0000000000..915fb13dac
> --- /dev/null
> +++ b/lib/mbuf/rte_mbuf_history.h
> @@ -0,0 +1,203 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2024 NVIDIA Corporation & Affiliates
> + */
> +
> +#ifndef _RTE_MBUF_HISTORY_H_
> +#define _RTE_MBUF_HISTORY_H_
> +
> +/**
> + * @file
> + *
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * These functions allow to track history of mbuf objects using a
> dynamic field.
> + *
> + * It tracks the lifecycle of mbuf objects through the system
> + * with a fixed set of predefined events to maintain performance.
> + *
> + * The history is stored as an atomic value (64-bit) in a dynamic
> field of the mbuf,
> + * with each event encoded in 4 bits, allowing up to 16 events to be
> tracked.
> + * Atomic operations ensure thread safety for cloned mbufs accessed by
> multiple lcores.
> + */
> +
> +#include <rte_common.h>
> +#include <rte_debug.h>
> +
> +#include <rte_mbuf_dyn.h>
> +
> +/* Forward declaration to avoid circular dependency. */
> +struct rte_mbuf;
> +struct rte_mempool;
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/**
> + * Number of bits for each history operation.
> + */
> +#define RTE_MBUF_HISTORY_BITS 4
> +
> +/**
> + * Maximum number of history operations that can be stored.
> + */
> +#define RTE_MBUF_HISTORY_MAX (sizeof(rte_mbuf_history_t) * 8 /
> RTE_MBUF_HISTORY_BITS)
> +
> +/**
> + * History operation types.
> + */
> +enum rte_mbuf_history_op {
> + RTE_MBUF_HISTORY_OP_NEVER = 0, /**< Initial state - never
> allocated */
> + RTE_MBUF_HISTORY_OP_LIB_FREE = 1, /**< Freed back to pool */
> + RTE_MBUF_HISTORY_OP_PMD_FREE = 2, /**< Freed by PMD */
> + RTE_MBUF_HISTORY_OP_APP_FREE = 3, /**< Freed by application */
> + RTE_MBUF_HISTORY_OP_LIB_ALLOC = 4, /**< Allocation in mbuf
> library */
> + RTE_MBUF_HISTORY_OP_PMD_ALLOC = 5, /**< Allocated by PMD for Rx
> */
> + RTE_MBUF_HISTORY_OP_APP_ALLOC = 6, /**< Allocated by application
> */
> + RTE_MBUF_HISTORY_OP_RX = 7, /**< Received */
> + RTE_MBUF_HISTORY_OP_TX = 8, /**< Sent */
> + RTE_MBUF_HISTORY_OP_PREP_TX = 9, /**< Being prepared before Tx
> */
> + RTE_MBUF_HISTORY_OP_BUSY_TX = 10, /**< Returned due to Tx busy
> */
Suggest:
RTE_MBUF_HISTORY_OP_PREP_TX -> RTE_MBUF_HISTORY_OP_TX_PREP
RTE_MBUF_HISTORY_OP_BUSY_TX -> RTE_MBUF_HISTORY_OP_TX_BUSY
> + RTE_MBUF_HISTORY_OP_ENQUEUE = 11, /**< Enqueued for processing
> */
> + RTE_MBUF_HISTORY_OP_DEQUEUE = 12, /**< Dequeued for processing
> */
> + /* 13, reserved for future */
> + RTE_MBUF_HISTORY_OP_USR2 = 14, /**< Application-defined
> event 2 */
> + RTE_MBUF_HISTORY_OP_USR1 = 15, /**< Application-defined
> event 1 */
> + RTE_MBUF_HISTORY_OP_MAX = 16, /**< Maximum number of
> operation types */
> +};
Suggest adding:
static_assert(RTE_MBUF_HISTORY_OP_MAX == 1 << RTE_MBUF_HISTORY_BITS, "Enum vs bitsize mismatch");
> +
> +/**
> + * Global offset for the history dynamic field (set during
> initialization).
> + */
> +extern int rte_mbuf_history_field_offset;
> +
> +/**
> + * Initialize the mbuf history system.
> + *
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * This function registers the dynamic field for mbuf history
> tracking.
> + * It should be called once during application initialization.
> + *
> + * Note: This function is called by rte_pktmbuf_pool_create,
> + * so explicit invocation is usually not required.
> + */
> +__rte_experimental
> +void rte_mbuf_history_init(void);
> +
> +/**
> + * Mark an mbuf with a history event.
> + *
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * @param m
> + * Pointer to the mbuf.
> + * @param op
> + * The operation to record.
> + */
> +static inline void rte_mbuf_history_mark(struct rte_mbuf *m, uint32_t
> op)
Fix:
uint32_t op -> enum rte_mbuf_history_op op
> +{
> +#ifndef RTE_MBUF_HISTORY_DEBUG
> + RTE_SET_USED(m);
> + RTE_SET_USED(op);
> +#else
> + RTE_ASSERT(rte_mbuf_history_field_offset >= 0);
> + RTE_ASSERT(op < RTE_MBUF_HISTORY_OP_MAX);
> + if (unlikely(m == NULL))
> + return;
> +
> + rte_mbuf_history_t *history_field = RTE_MBUF_DYNFIELD(m,
> + rte_mbuf_history_field_offset, rte_mbuf_history_t *);
> + uint64_t old_history = rte_atomic_load_explicit(history_field,
> + rte_memory_order_acquire);
> + uint64_t new_history;
> + do {
> + new_history = (old_history << RTE_MBUF_HISTORY_BITS) | op;
> + } while
> (unlikely(!rte_atomic_compare_exchange_weak_explicit(history_field,
> + &old_history, new_history,
> + rte_memory_order_release,
> rte_memory_order_acquire)));
> +#endif
> +}
> +
> +/**
> + * Mark multiple mbufs with a history event.
> + *
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * @param mbufs
> + * Array of mbuf pointers.
> + * @param n
> + * Number of mbufs to mark.
> + * @param op
> + * The operation to record.
> + */
> +static inline void rte_mbuf_history_mark_bulk(struct rte_mbuf * const
> *mbufs,
> + uint32_t n, uint32_t op)
Fix:
uint32_t n -> unsigned int count (for consistency)
uint32_t op -> enum rte_mbuf_history_op op
> +{
> +#ifndef RTE_MBUF_HISTORY_DEBUG
> + RTE_SET_USED(mbufs);
> + RTE_SET_USED(n);
> + RTE_SET_USED(op);
> +#else
> + RTE_ASSERT(rte_mbuf_history_field_offset >= 0);
> + RTE_ASSERT(op < RTE_MBUF_HISTORY_OP_MAX);
> + if (unlikely(mbufs == NULL))
> + return;
> +
> + while (n--)
> + rte_mbuf_history_mark(*mbufs++, op);
> +#endif
> +}
> +
> +/**
> + * Dump mbuf history for a single mbuf to a file.
> + *
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * @param f
> + * File pointer to write the history to.
> + * @param m
> + * Pointer to the mbuf to dump history for.
> + */
> +__rte_experimental
> +void rte_mbuf_history_dump(FILE *f, const struct rte_mbuf *m);
> +
> +/**
> + * Dump mbuf history statistics for a single mempool to a file.
> + *
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * @param f
> + * File pointer to write the history statistics to.
> + * @param mp
> + * Pointer to the mempool to dump history for.
> + */
> +__rte_experimental
> +void rte_mbuf_history_dump_mempool(FILE *f, struct rte_mempool *mp);
> +
> +/**
> + * Dump mbuf history statistics for all mempools to a file.
> + *
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice.
> + *
> + * @param f
> + * File pointer to write the history statistics to.
> + */
> +__rte_experimental
> +void rte_mbuf_history_dump_all(FILE *f);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* _RTE_MBUF_HISTORY_H_ */
> --
> 2.51.0
With fixes (suggestions optional),
Acked-by: Morten Brørup <mb@smartsharesystems.com>
next prev parent reply other threads:[~2025-10-14 9:59 UTC|newest]
Thread overview: 78+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-16 7:29 [RFC PATCH 0/5] Introduce mempool object new debug capabilities Shani Peretz
2025-06-16 7:29 ` [RFC PATCH 1/5] mempool: record mempool objects operations history Shani Peretz
2025-06-16 7:29 ` [RFC PATCH 2/5] drivers: add mempool history compilation flag Shani Peretz
2025-06-16 7:29 ` [RFC PATCH 3/5] net/mlx5: mark an operation in mempool object's history Shani Peretz
2025-06-16 7:29 ` [RFC PATCH 4/5] app/testpmd: add testpmd command to dump mempool history Shani Peretz
2025-06-16 7:29 ` [RFC PATCH 5/5] usertool: add a script to parse mempool history dump Shani Peretz
2025-06-16 15:30 ` [RFC PATCH 0/5] Introduce mempool object new debug capabilities Stephen Hemminger
2025-06-19 12:57 ` Morten Brørup
2025-07-07 5:46 ` Shani Peretz
2025-07-07 5:45 ` Shani Peretz
2025-07-07 12:10 ` Morten Brørup
2025-07-19 14:39 ` Morten Brørup
2025-08-25 11:27 ` Slava Ovsiienko
2025-09-01 15:34 ` Morten Brørup
2025-09-16 15:12 ` [PATCH v2 0/4] add mbuf " Shani Peretz
2025-09-16 15:12 ` [PATCH v2 1/4] mbuf: record mbuf operations history Shani Peretz
2025-09-16 21:17 ` Stephen Hemminger
2025-09-16 21:33 ` Thomas Monjalon
2025-09-17 1:22 ` Morten Brørup
2025-09-17 14:46 ` Morten Brørup
2025-09-19 9:14 ` Shani Peretz
2025-09-16 15:12 ` [PATCH v2 2/4] net/mlx5: mark an operation in mbuf's history Shani Peretz
2025-09-16 21:14 ` Stephen Hemminger
2025-09-16 21:31 ` Thomas Monjalon
2025-09-17 15:04 ` Stephen Hemminger
2025-09-16 15:12 ` [PATCH v2 3/4] app/testpmd: add testpmd command to dump mbuf history Shani Peretz
2025-09-16 15:12 ` [PATCH v2 4/4] usertool: add a script to parse mbuf history dump Shani Peretz
2025-09-30 23:25 ` [PATCH v3 0/5] add mbuf debug capabilities Thomas Monjalon
2025-09-30 23:25 ` [PATCH v3 1/5] mbuf: move header include for logs Thomas Monjalon
2025-09-30 23:25 ` [PATCH v3 2/5] mbuf: record mbuf operations history Thomas Monjalon
2025-10-01 0:12 ` Thomas Monjalon
2025-10-02 7:37 ` Morten Brørup
2025-10-02 21:23 ` Thomas Monjalon
2025-10-13 18:39 ` Thomas Monjalon
2025-10-13 20:08 ` Morten Brørup
2025-10-13 21:07 ` Thomas Monjalon
2025-10-14 10:04 ` Morten Brørup
2025-09-30 23:25 ` [PATCH v3 3/5] ethdev: mark mbufs in burst functions Thomas Monjalon
2025-10-02 7:44 ` Morten Brørup
2025-10-13 15:32 ` Thomas Monjalon
2025-09-30 23:25 ` [PATCH v3 4/5] app/testpmd: add commands to dump mbuf history Thomas Monjalon
2025-10-01 8:20 ` Stephen Hemminger
2025-10-13 15:31 ` Thomas Monjalon
2025-09-30 23:25 ` [PATCH v3 5/5] usertools/mbuf: parse mbuf history dump Thomas Monjalon
2025-10-02 8:07 ` Robin Jarry
2025-10-13 21:16 ` [PATCH v4 0/7] add mbuf debug capabilities Thomas Monjalon
2025-10-13 21:16 ` [PATCH v4 1/7] doc: explain debug options in mbuf guide Thomas Monjalon
2025-10-13 21:16 ` [PATCH v4 2/7] mbuf: move header include for logs Thomas Monjalon
2025-10-13 21:16 ` [PATCH v4 3/7] mbuf: record mbuf operations history Thomas Monjalon
2025-10-13 21:16 ` [PATCH v4 4/7] ethdev: mark mbufs in burst functions Thomas Monjalon
2025-10-13 21:16 ` [PATCH v4 5/7] app/testpmd: use space separator in dump commands Thomas Monjalon
2025-10-13 21:16 ` [PATCH v4 6/7] app/testpmd: add commands to dump mbuf history Thomas Monjalon
2025-10-13 21:16 ` [PATCH v4 7/7] usertools/mbuf: parse mbuf history dump Thomas Monjalon
2025-10-14 6:58 ` [PATCH v5 0/7] add mbuf debug capabilities Thomas Monjalon
2025-10-14 6:58 ` [PATCH v5 1/7] doc: explain debug options in mbuf guide Thomas Monjalon
2025-10-14 6:58 ` [PATCH v5 2/7] mbuf: move header include for logs Thomas Monjalon
2025-10-14 8:47 ` Morten Brørup
2025-10-14 6:58 ` [PATCH v5 3/7] mbuf: record mbuf operations history Thomas Monjalon
2025-10-14 9:59 ` Morten Brørup [this message]
2025-10-14 12:03 ` Thomas Monjalon
2025-10-14 12:31 ` Thomas Monjalon
2025-10-14 6:58 ` [PATCH v5 4/7] ethdev: mark mbufs in burst functions Thomas Monjalon
2025-10-14 6:58 ` [PATCH v5 5/7] app/testpmd: use space separator in dump commands Thomas Monjalon
2025-10-14 6:58 ` [PATCH v5 6/7] app/testpmd: add commands to dump mbuf history Thomas Monjalon
2025-10-14 8:45 ` Morten Brørup
2025-10-14 9:43 ` Thomas Monjalon
2025-10-14 9:48 ` Bruce Richardson
2025-10-14 9:55 ` Thomas Monjalon
2025-10-14 6:58 ` [PATCH v5 7/7] usertools/mbuf: parse mbuf history dump Thomas Monjalon
2025-10-14 12:33 ` [PATCH v6 0/7] add mbuf debug capabilities Thomas Monjalon
2025-10-14 12:33 ` [PATCH v6 1/7] doc: explain debug options in mbuf guide Thomas Monjalon
2025-10-14 12:33 ` [PATCH v6 2/7] mbuf: move header include for logs Thomas Monjalon
2025-10-14 12:33 ` [PATCH v6 3/7] mbuf: record mbuf operations history Thomas Monjalon
2025-10-14 12:33 ` [PATCH v6 4/7] ethdev: mark mbufs in burst functions Thomas Monjalon
2025-10-14 12:33 ` [PATCH v6 5/7] app/testpmd: use space separator in dump commands Thomas Monjalon
2025-10-14 12:33 ` [PATCH v6 6/7] app/testpmd: add commands to dump mbuf history Thomas Monjalon
2025-10-14 12:33 ` [PATCH v6 7/7] usertools/mbuf: parse mbuf history dump Thomas Monjalon
2025-10-14 14:03 ` Robin Jarry
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=98CBD80474FA8B44BF855DF32C47DC35F654C7@smartserver.smartshare.dk \
--to=mb@smartsharesystems.com \
--cc=bruce.richardson@intel.com \
--cc=dev@dpdk.org \
--cc=shperetz@nvidia.com \
--cc=stephen@networkplumber.org \
--cc=thomas@monjalon.net \
--cc=viacheslavo@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).