DPDK patches and discussions
 help / color / mirror / Atom feed
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>


  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).