> -----Original Message-----
> From: Morten Brørup <m...@smartsharesystems.com>
> Sent: Wednesday, 17 September 2025 17:46
> To: Shani Peretz <shper...@nvidia.com>; dev@dpdk.org
> Cc: step...@networkplumber.org; bruce.richard...@intel.com;
> ajit.khapa...@broadcom.com; jer...@marvell.com;
> konstantin.v.anan...@yandex.ru; david.march...@redhat.com;
> maxime.coque...@redhat.com; gak...@marvell.com; Slava Ovsiienko
> <viachesl...@nvidia.com>; NBU-Contact-Thomas Monjalon (EXTERNAL)
> <tho...@monjalon.net>; Andrew Rybchenko
> <andrew.rybche...@oktetlabs.ru>
> Subject: RE: [PATCH v2 1/4] mbuf: record mbuf operations history
> 
> External email: Use caution opening links or attachments
> 
> 
> > From: Shani Peretz [mailto:shper...@nvidia.com]
> > Sent: Tuesday, 16 September 2025 17.12
> >
> > This feature is designed to monitor the lifecycle of mbufs as they
> > move between the applicationand the PMD.
> >
> > It will allow us to track the operations and transitions of each mbuf
> > throughout the system, helping in debugging and understanding objects
> > flow.
> >
> > The implementation uses a dynamic field to store a 64-bit history
> > value in each mbuf. Each operation is represented by a 4-bit value,
> > allowing up to 16 operations to be tracked per mbuf. The dynamic field
> > is automatically initialized when the first mbuf pool is created.
> >
> > Signed-off-by: Shani Peretz <shper...@nvidia.com>
> 
> Looking really good.
> Some comments inline below.
> 
> > ---
> >  config/meson.build          |   1 +
> >  lib/ethdev/rte_ethdev.h     |  15 +++
> >  lib/mbuf/meson.build        |   2 +
> >  lib/mbuf/rte_mbuf.c         |  10 +-
> >  lib/mbuf/rte_mbuf.h         |  23 ++++-
> >  lib/mbuf/rte_mbuf_dyn.h     |   7 ++
> >  lib/mbuf/rte_mbuf_history.c | 181
> > ++++++++++++++++++++++++++++++++++++
> >  lib/mbuf/rte_mbuf_history.h | 154 ++++++++++++++++++++++++++++++
> >  meson_options.txt           |   2 +
> >  9 files changed, 392 insertions(+), 3 deletions(-)  create mode
> > 100644 lib/mbuf/rte_mbuf_history.c  create mode 100644
> > lib/mbuf/rte_mbuf_history.h
> >
> > diff --git a/config/meson.build b/config/meson.build index
> > 55497f0bf5..d1f21f3115 100644
> > --- a/config/meson.build
> > +++ b/config/meson.build
> > @@ -379,6 +379,7 @@ if get_option('mbuf_refcnt_atomic')
> >      dpdk_conf.set('RTE_MBUF_REFCNT_ATOMIC', true)  endif
> > dpdk_conf.set10('RTE_IOVA_IN_MBUF', get_option('enable_iova_as_pa'))
> > +dpdk_conf.set10('RTE_MBUF_HISTORY_DEBUG',
> > get_option('enable_mbuf_history'))
> >
> >  compile_time_cpuflags = []
> >  subdir(arch_subdir)
> > diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h index
> > d23c143eed..d0f6cd2582 100644
> > --- a/lib/ethdev/rte_ethdev.h
> > +++ b/lib/ethdev/rte_ethdev.h
> > @@ -6336,6 +6336,10 @@ rte_eth_rx_burst(uint16_t port_id, uint16_t
> > queue_id,
> >
> >       nb_rx = p->rx_pkt_burst(qd, rx_pkts, nb_pkts);
> >
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +     rte_mbuf_history_bulk(rx_pkts, nb_rx, RTE_MBUF_APP_RX); #endif
> > +
> >  #ifdef RTE_ETHDEV_RXTX_CALLBACKS
> >       {
> >               void *cb;
> > @@ -6688,8 +6692,19 @@ rte_eth_tx_burst(uint16_t port_id, uint16_t
> > queue_id,
> >       }
> >  #endif
> >
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +     uint16_t requested_pkts = nb_pkts;
> > +     rte_mbuf_history_bulk(tx_pkts, nb_pkts, RTE_MBUF_PMD_TX); #endif
> > +
> >       nb_pkts = p->tx_pkt_burst(qd, tx_pkts, nb_pkts);
> >
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +     if (requested_pkts > nb_pkts)
> > +             rte_mbuf_history_bulk(tx_pkts + nb_pkts,
> > +                                  requested_pkts - nb_pkts,
> > RTE_MBUF_BUSY_TX);
> > +#endif
> > +
> >       rte_ethdev_trace_tx_burst(port_id, queue_id, (void **)tx_pkts,
> > nb_pkts);
> >       return nb_pkts;
> >  }
> > diff --git a/lib/mbuf/meson.build b/lib/mbuf/meson.build index
> > 0435c5e628..2c840ee2f2 100644
> > --- a/lib/mbuf/meson.build
> > +++ b/lib/mbuf/meson.build
> > @@ -6,6 +6,7 @@ sources = files(
> >          'rte_mbuf_ptype.c',
> >          'rte_mbuf_pool_ops.c',
> >          'rte_mbuf_dyn.c',
> > +        'rte_mbuf_history.c',
> >  )
> >  headers = files(
> >          'rte_mbuf.h',
> > @@ -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
> > 9e7731a8a2..362dd252bc 100644
> > --- a/lib/mbuf/rte_mbuf.c
> > +++ b/lib/mbuf/rte_mbuf.c
> > @@ -281,6 +281,10 @@ rte_pktmbuf_pool_create(const char *name,
> > unsigned int n,
> >       unsigned int cache_size, uint16_t priv_size, uint16_t
> > data_room_size,
> >       int socket_id)
> >  {
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +             if (rte_mbuf_history_init() < 0)
> > +                     RTE_LOG(ERR, MBUF, "Failed to enable mbuf
> > history\n");
> > +#endif
> 
> rte_mbuf_history_init() must be called from all mbuf library functions to
> create a pktmbuf_pool.
> 
Will update

> >       return rte_pktmbuf_pool_create_by_ops(name, n, cache_size,
> > priv_size,
> >                       data_room_size, socket_id, NULL);  } @@ -516,8
> > +520,12 @@ void rte_pktmbuf_free_bulk(struct rte_mbuf **mbufs,
> > unsigned int count)
> >               } while (m != NULL);
> >       }
> >
> > -     if (nb_pending > 0)
> > +     if (nb_pending > 0) {
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +             rte_mbuf_history_bulk(pending, nb_pending,
> > +RTE_MBUF_FREE); #endif
> >               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..8126f09de4 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,9 @@ 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);
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +     rte_mbuf_history_mark(ret.m, RTE_MBUF_ALLOC); #endif
> >       return ret.m;
> >  }
> >
> > @@ -642,9 +646,14 @@ 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]);
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +                     rte_mbuf_history_mark(mbufs[idx],
> > +RTE_MBUF_ALLOC); #endif
> > +             }
> > +     }
> >       return rc;
> >  }
> >
> > @@ -667,6 +676,9 @@ rte_mbuf_raw_free(struct rte_mbuf *m)  {
> >       __rte_mbuf_raw_sanity_check(m);
> >       rte_mempool_put(m->pool, m);
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +     rte_mbuf_history_mark(m, RTE_MBUF_FREE); #endif
> >  }
> >
> >  /**
> > @@ -701,6 +713,9 @@ 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);
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +             rte_mbuf_history_mark(mbufs[idx], RTE_MBUF_FREE); #endif
> >       }
> >
> >       rte_mempool_put_bulk(mp, (void **)mbufs, count); @@ -1013,6
> > +1028,10 @@ static inline int rte_pktmbuf_alloc_bulk(struct
> > rte_mempool *pool,
> >       if (unlikely(rc))
> >               return rc;
> >
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +     rte_mbuf_history_bulk(mbufs, count, RTE_MBUF_ALLOC); #endif
> > +
> >       /* 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..8ae31b4e65 100644
> > --- a/lib/mbuf/rte_mbuf_dyn.h
> > +++ b/lib/mbuf/rte_mbuf_dyn.h
> > @@ -240,6 +240,13 @@ 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 to maintain
> > performance
> > + * while providing debugging capabilities.
> > + */
> > +#define RTE_MBUF_DYNFIELD_HISTORY_NAME
> "rte_mbuf_dynfield_history"
> 
> Suggest you also define the mbuf history type here:
> typedef uint64_t rte_mbuf_history_t;
> 
> Or even better, to support cloned mbufs accessed simultaneously by multiple
> lcores:
> typedef RTE_ATOMIC(uint64_t) rte_mbuf_history_t;

Yes make sense I'll add this

> 
> <feature creep>
> It could be uint32_t on 32 bit architectures, for performance.
> </feature creep>
> 
> > +
> >  /*
> >   * 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.c
> > b/lib/mbuf/rte_mbuf_history.c new file mode 100644 index
> > 0000000000..5be56289ca
> > --- /dev/null
> > +++ b/lib/mbuf/rte_mbuf_history.c
> > @@ -0,0 +1,181 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2024 NVIDIA Corporation & Affiliates  */
> > +
> > +#include <rte_mbuf_history.h>
> > +#include <rte_mbuf_dyn.h>
> > +#include <rte_log.h>
> > +#include <rte_errno.h>
> > +#include <eal_export.h>
> > +#include <rte_mempool.h>
> > +#include <rte_tailq.h>
> > +#include <stdio.h>
> > +#include <string.h>
> > +#include <inttypes.h>
> > +
> > +/* Global offset for the history field */ int
> > +rte_mbuf_history_field_offset = -1;
> > +RTE_EXPORT_SYMBOL(rte_mbuf_history_field_offset);
> > +
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +/* Dynamic field definition for mbuf history */ static const struct
> > +rte_mbuf_dynfield mbuf_dynfield_history = {
> > +     .name = RTE_MBUF_DYNFIELD_HISTORY_NAME,
> > +     .size = sizeof(uint64_t),
> > +     .align = RTE_ALIGN(sizeof(uint64_t), 8), };
> > +
> > +/* Context structure for combined statistics counting and mbuf
> > +history
> > printing */
> > +struct count_and_print_ctx {
> > +     uint64_t *stats;
> > +     FILE *f;
> > +};
> > +
> > +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 *mbuf = (struct rte_mbuf *)obj;
> > +
> > +     if (obj == NULL || ctx == NULL || ctx->stats == NULL || ctx->f
> > + ==
> > NULL)
> > +             return;
> > +
> > +     /* Get mbuf history */
> > +     uint64_t history = rte_mbuf_history_get(mbuf);
> > +
> > +     ctx->stats[0]++; /* n_total */
> > +
> > +     if (history == 0) {
> > +             ctx->stats[1]++; /* n_never */
> > +             return;
> > +     }
> > +
> > +     /* Extract the most recent operation */
> > +     uint64_t op = history & RTE_MBUF_HISTORY_MASK;
> > +
> > +     switch (op) {
> > +     case RTE_MBUF_FREE:
> > +             ctx->stats[2]++; /* n_free */
> > +             break;
> > +     case RTE_MBUF_PMD_FREE:
> > +             ctx->stats[3]++; /* n_pmd_free */
> > +             break;
> > +     case RTE_MBUF_PMD_TX:
> > +             ctx->stats[4]++; /* n_pmd_tx */
> > +             break;
> > +     case RTE_MBUF_APP_RX:
> > +             ctx->stats[5]++; /* n_app_rx */
> > +             break;
> > +     case RTE_MBUF_PMD_ALLOC:
> > +             ctx->stats[6]++; /* n_pmd_alloc */
> > +             break;
> > +     case RTE_MBUF_ALLOC:
> > +             ctx->stats[7]++; /* n_alloc */
> > +             break;
> > +     case RTE_MBUF_BUSY_TX:
> > +             ctx->stats[8]++; /* n_busy_tx */
> > +             break;
> > +     default:
> > +             break;
> > +     }
> 
> Intead of using stats[9], use stats[RTE_MBUF_HISTORY_OP_MAX].
> Then your switch/case can be replaced by a simple one-liner:
> ctx->stats[op]++;
> 
> This can also handle future history operation types not yet defined in the
> enumeration of operations.

Yes good idea I'll fix it

> 
> > +
> > +     /* Print the mbuf history value */
> > +     fprintf(ctx->f, "mbuf %p: %016" PRIX64 "\n", mbuf, history);
> > +
> > +}
> > +
> > +static void
> > +mbuf_history_get_stat(struct rte_mempool *mp, void *arg) {
> > +     FILE *f = (FILE *)arg;
> > +     uint64_t stats[9] = {0};
> > +
> > +     if (f == NULL)
> > +             return;
> 
> It would be nice if mempools can somehow be marked with the type of
> objects they hold, or at least can be marked that they hold mbufs.
> One simple way could be prefixing the mempool name with "MBUF_" when
> the mbuf library calls the mempool library to create a mempool.
> 
> Then this function can skip mempools holding other objects than mbufs.
> 
> > +
> > +     /* Output mempool header */
> > +     fprintf(f, "=== Mempool: %s ===\n", mp->name);
> > +
> > +     /* Create context structure for combined counting and printing */
> > +     struct count_and_print_ctx ctx = { .stats = stats, .f = f };
> > +
> > +     /* 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[3] + stats[4] + stats[5] +
> > +     stats[6] + stats[7] + stats[8];
> > +
> > +     /* Print statistics summary */
> > +     fprintf(f, "\n"
> > +             "Populated:       %u\n"
> > +             "Never allocated: %" PRIu64 "\n"
> > +             "Free:            %" PRIu64 "\n"
> > +             "Allocated:       %" PRIu64 "\n"
> > +             "PMD owned Tx:    %" PRIu64 "\n"
> > +             "PMD owned Rx:    %" PRIu64 "\n"
> > +             "App owned alloc: %" PRIu64 "\n"
> > +             "App owned Rx:    %" PRIu64 "\n"
> > +             "App owned busy:  %" PRIu64 "\n"
> > +             "Counted total:   %" PRIu64 "\n",
> > +             mp->populated_size, stats[1], stats[2], total_allocated,
> > +             stats[4], stats[6], stats[7], stats[5], stats[8],
> > stats[0]);
> > +
> > +     fprintf(f, "---\n");
> 
> Please follow the convention of other "dump" functions, e.g. like
> rte_mempool_dump():
> - Use hierarchical indentation
> - Use "    value=%" PRIu64 "\n".
> 
> > +}
> > +#endif
> > +
> > +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_mbuf_history_dump, 25.11)
> #if
> > +RTE_MBUF_HISTORY_DEBUG void rte_mbuf_history_dump(FILE *f) {
> > +     if (f == NULL) {
> > +             RTE_LOG(ERR, MBUF, "Invalid file pointer\n");
> > +             return;
> > +     }
> > +
> > +     fprintf(f, "=== MBUF History Statistics ===\n");
> > +     fprintf(f, "Dumping complete mbuf history for all
> > mempools...\n");
> > +
> > +     /* Check if mbuf history is initialized */
> > +     if (rte_mbuf_history_field_offset == -1) {
> > +             fprintf(f, "WARNING: MBUF history not initialized. Call
> > rte_mbuf_history_init() first.\n\n");
> > +             return;
> > +     }
> > +
> > +     /* Use rte_mempool_walk to iterate over all mempools */
> > +     rte_mempool_walk(mbuf_history_get_stat, f); }
> > +
> > +int rte_mbuf_history_init(void)
> > +{
> > +     if (rte_mbuf_history_field_offset != -1) {
> > +             /* Already initialized */
> > +             return 0;
> > +     }
> > +
> > +     rte_mbuf_history_field_offset =
> > rte_mbuf_dynfield_register(&mbuf_dynfield_history);
> > +     if (rte_mbuf_history_field_offset < 0) {
> > +             RTE_LOG(ERR, MBUF, "Failed to register mbuf history
> > + dynamic
> > field: %s\n",
> > +                     rte_strerror(rte_errno));
> > +             return -1;
> > +     }
> > +     return 0;
> > +}
> > +#else
> > +void rte_mbuf_history_dump(FILE *f)
> > +{
> > +     RTE_SET_USED(f);
> > +     RTE_LOG(INFO, MBUF, "Mbuf history recorder is not supported\n");
> > +}
> > +
> > +int rte_mbuf_history_init(void)
> > +{
> > +     rte_errno = ENOTSUP;
> > +     return -1;
> > +}
> > +#endif
> > +RTE_EXPORT_SYMBOL(rte_mbuf_history_init);
> > +RTE_EXPORT_SYMBOL(rte_mbuf_history_dump);
> > diff --git a/lib/mbuf/rte_mbuf_history.h b/lib/mbuf/rte_mbuf_history.h
> > new file mode 100644 index 0000000000..4448ad1557
> > --- /dev/null
> > +++ b/lib/mbuf/rte_mbuf_history.h
> > @@ -0,0 +1,154 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(c) 2024 NVIDIA Corporation & Affiliates  */
> > +
> > +#ifndef _RTE_MBUF_HISTORY_H_
> > +#define _RTE_MBUF_HISTORY_H_
> > +
> > +/**
> > + * @file
> > + * MBUF History
> > + *
> > + * This module provides history tracking for mbuf objects using
> > dynamic fields.
> > + * 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 a 64-bit value in the mbuf dynamic field
> > area,
> > + * with each event encoded in 4 bits, allowing up to 16 events to be
> > tracked.
> > + */
> > +
> > +#include <stdint.h>
> > +#include <rte_mbuf_dyn.h>
> > +#include <rte_common.h>
> > +#include <rte_branch_prediction.h>
> > +#include "mbuf_log.h"
> > +
> > +/* Forward declaration to avoid circular dependency */ struct
> > +rte_mbuf;
> > +
> > +#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_OPS       16
> 
> Suggest:
> #define RTE_MBUF_HISTORY_MAX_OPS (sizeof(rte_mbuf_history_t) * 8 /
> RTE_MBUF_HISTORY_BITS)
> 
> > +
> > +/**
> > + * Mask for extracting the most recent operation from history  */
> > +#define RTE_MBUF_HISTORY_MASK          ((1ULL <<
> > RTE_MBUF_HISTORY_BITS) - 1)
> > +
> > +/**
> > + * History operation types
> > + */
> > +enum rte_mbuf_history_op {
> 
> These values should be prefixed RTE_MBUF_HISTORY_OP_ or
> RTE_MBUF_HISTORY_ or something similar.
> Also, naming should be hierarchical. Suggest:
> 
Make sense, I'll rename it
> > +     RTE_MBUF_NEVER = 0,        /* Initial state - never allocated */
> 
> RTE_MBUF_HISTORY_OP_NEVER
> 
> > +     RTE_MBUF_FREE = 1,         /* Freed back to pool */
> 
> RTE_MBUF_HISTORY_OP_FREE
> 
> > +     RTE_MBUF_PMD_FREE = 2,     /* Freed by PMD back to pool*/
> 
> RTE_MBUF_HISTORY_OP_PMD_FREE
> 
> > +     RTE_MBUF_PMD_TX = 3,       /* Sent to PMD for Tx */
> 
> RTE_MBUF_HISTORY_OP_PMD_TX
> 
> > +     RTE_MBUF_APP_RX = 4,       /* Returned to application on Rx */
> 
> RTE_MBUF_HISTORY_OP_PMD_RX
> 
> > +     RTE_MBUF_PMD_ALLOC = 5,    /* Allocated by PMD for Rx */
> 
> RTE_MBUF_HISTORY_OP_PMD_RX_ALLOC
> 
> > +     RTE_MBUF_ALLOC = 6,        /* Allocated by application */
> 
> RTE_MBUF_HISTORY_OP_ALLOC
> 
> > +     RTE_MBUF_BUSY_TX = 7,      /* Returned to app due to Tx busy */
> 
> RTE_MBUF_HISTORY_OP_PMD_TX_BUSY
> 
> > +     RTE_MBUF_USR3 = 13,        /* Application-defined event 3 */
> > +     RTE_MBUF_USR2 = 14,        /* Application-defined event 2 */
> > +     RTE_MBUF_USR1 = 15,        /* Application-defined event 1 */
> > +     RTE_MBUF_MAX = 16,          /* Maximum trace operation value */
> > +};
> > +
> > +
> > +/**
> > + * Global offset for the history field (set during initialization)
> > +*/ extern int rte_mbuf_history_field_offset;
> > +
> > +/**
> > + * Initialize the mbuf history system
> > + *
> > + * 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 unless initializing
> > manually.
> > + *
> > + * @return
> > + *   0 on success, -1 on failure with rte_errno set
> > + */
> > +int rte_mbuf_history_init(void);
> > +
> > +#if RTE_MBUF_HISTORY_DEBUG
> > +/**
> > + * Get the history value from an mbuf
> > + *
> > + * @param m
> > + *   Pointer to the mbuf
> > + * @return
> > + *   The history value, or 0 if history is not available
> > + */
> > +static inline uint64_t rte_mbuf_history_get(const struct rte_mbuf *m)
> > +{
> > +     if (unlikely(m == NULL || rte_mbuf_history_field_offset == -1))
> > +             return 0;
> 
> These are fast path, so don't validate params at runtime. Suggest:
>     RTE_ASSERT(rte_mbuf_history_field_offset != -1);
>         if (unlikely(m == NULL))
>                 return 0;
> 
> > +
> > +     return *RTE_MBUF_DYNFIELD(m, rte_mbuf_history_field_offset,
> > uint64_t *);
> 
> Mbufs may be cloned, so this should be an atomic operation.
> 
> > +}
> > +
> > +/**
> > + * Mark an mbuf with a history event
> > + *
> > + * @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)
> > +{
> > +     if (unlikely(m == NULL || op >= RTE_MBUF_MAX ||
> > rte_mbuf_history_field_offset == -1))
> > +             return;
> 
> These are fast path, so don't validate params at runtime. Suggest:
>     RTE_ASSERT(rte_mbuf_history_field_offset != -1);
>     RTE_ASSERT(op < RTE_MBUF_HISTORY_OP_MAX);
>         if (unlikely(m == NULL))
>                 return 0;
> 
Sure
> > +
> > +     uint64_t *history = RTE_MBUF_DYNFIELD(m,
> > rte_mbuf_history_field_offset, uint64_t *);
> > +     *history = (*history << RTE_MBUF_HISTORY_BITS) | op;
> 
> Mbufs may be cloned, so this should be an atomic operation.
> 
> > +}
> > +
> > +/**
> > + * Mark multiple mbufs with a history event
> > + *
> > + * @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_bulk(struct rte_mbuf * const
> > *mbufs,
> > +                                     uint32_t n, uint32_t op)
> 
> rte_mbuf_history_bulk() -> rte_mbuf_history_mark_bulk()
> 
> > +{
> > +     if (unlikely(mbufs == NULL || op >= RTE_MBUF_MAX ||
> > rte_mbuf_history_field_offset == -1))
> > +             return;
> 
> These are fast path, so don't validate params at runtime. Suggest:
>     RTE_ASSERT(rte_mbuf_history_field_offset != -1);
>     RTE_ASSERT(mbufs != NULL);
>     RTE_ASSERT(op < RTE_MBUF_HISTORY_OP_MAX);
> 
> > +
> > +     while (n--)
> > +             rte_mbuf_history_mark(*mbufs++, op); } #endif
> > +
> > +/**
> > + * Dump mbuf history statistics for all mempools to a file
> > + *
> > + * @param f
> > + *   File pointer to write the history statistics to
> > + */
> > +__rte_experimental
> > +void rte_mbuf_history_dump(FILE *f);
> 
> <feature creep>
> It would be nice to expose three dump functions:
> 1) dump the history of one mbuf:
> void rte_mbuf_history_dump(FILE *f, const struct rte_mbuf * mbuf);
> 2) dump the history of one mbuf pool, and void
> rte_mbuf_history_dump_mempool(FILE *f, const struct rte_mempool * mp);
> 3) dump the history of all mbuf pools.
> void rte_mbuf_history_dump_all(FILE *f); <feature creep>
> 
> > +
> > +#ifdef __cplusplus
> > +}
> > +#endif
> > +
> > +#endif /* _RTE_MBUF_HISTORY_H_ */
> > diff --git a/meson_options.txt b/meson_options.txt index
> > e49b2fc089..48f6d4a9a5 100644
> > --- a/meson_options.txt
> > +++ b/meson_options.txt
> > @@ -16,6 +16,8 @@ option('drivers_install_subdir', type: 'string',
> > value: 'dpdk/pmds-<VERSION>', d
> >         'Subdirectory of libdir where to install PMDs. Defaults to
> > using a versioned subdirectory.')  option('enable_docs', type:
> > 'boolean', value: false, description:
> >         'build documentation')
> > +option('enable_mbuf_history', type: 'boolean', value: false,
> > description:
> > +       'Enable mbuf history tracking for debugging purposes. This
> > + will
> > track mbufs allocation and free operations. Default is false.')
> > option('enable_apps', type: 'string', value: '', description:
> >         'Comma-separated list of apps to build. If unspecified, build
> > all apps.')  option('enable_deprecated_libs', type: 'string', value:
> > '',
> > description:
> > --
> > 2.34.1

Thanks for the detailed review! I'll fix it and send an update!



Reply via email to