DPDK patches and discussions
 help / color / mirror / Atom feed
From: Thomas Monjalon <thomas@monjalon.net>
To: dev@dpdk.org
Cc: shperetz@nvidia.com, viacheslavo@nvidia.com,
	bruce.richardson@intel.com, mb@smartsharesystems.com,
	stephen@networkplumber.org
Subject: [PATCH v4 3/7] mbuf: record mbuf operations history
Date: Mon, 13 Oct 2025 23:16:03 +0200	[thread overview]
Message-ID: <20251013212019.892032-4-thomas@monjalon.net> (raw)
In-Reply-To: <20251013212019.892032-1-thomas@monjalon.net>

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
+ */
+
+#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)
+
+/* 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)
+{
+	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;
+
+	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);
 }
 
 /**
@@ -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);
 	}
 
 	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 */
+	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 */
+};
+
+/**
+ * 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)
+{
+#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)
+{
+#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


  parent reply	other threads:[~2025-10-13 21:20 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   ` Thomas Monjalon [this message]
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
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=20251013212019.892032-4-thomas@monjalon.net \
    --to=thomas@monjalon.net \
    --cc=bruce.richardson@intel.com \
    --cc=dev@dpdk.org \
    --cc=mb@smartsharesystems.com \
    --cc=shperetz@nvidia.com \
    --cc=stephen@networkplumber.org \
    --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).