[PATCH 6/8] net/bonding: add extended statistics

Stephen Hemminger stephen at networkplumber.org
Sun Aug 30 22:23:48 CEST 2026


The bonding device reported only the sum of its members, so there was
no way to see how traffic was distributed across them.
This patch adds xstats which reports per-member packets/bytes/errors.

Also drop RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS since driver never
implemented per-queue stats.

Signed-off-by: Stephen Hemminger <stephen at networkplumber.org>
---
 doc/guides/rel_notes/release_26_11.rst |   6 ++
 drivers/net/bonding/rte_eth_bond_pmd.c | 113 ++++++++++++++++++++++++-
 2 files changed, 117 insertions(+), 2 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 87c7e81bde..83d70872af 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -55,6 +55,12 @@ New Features
      Also, make sure to start the actual text at the margin.
      =======================================================
 
+* **Added extended statistics to bonding PMD.**
+
+  Extended statistics now report the packets, bytes and errors
+  of each member as ``rx_memberN_*`` and ``tx_memberN_*``.
+  The per-queue entries, which were always zero, are no longer reported.
+
 
 Removed Items
 -------------
diff --git a/drivers/net/bonding/rte_eth_bond_pmd.c b/drivers/net/bonding/rte_eth_bond_pmd.c
index 0e18ded4a5..46bc1db120 100644
--- a/drivers/net/bonding/rte_eth_bond_pmd.c
+++ b/drivers/net/bonding/rte_eth_bond_pmd.c
@@ -2764,6 +2764,111 @@ bond_ethdev_stats_reset(struct rte_eth_dev *dev)
 	return err;
 }
 
+#define BOND_MEMBER_STAT_PREFIX_LEN (sizeof("rx_member") - 1 + 5 + 1)
+
+struct bond_member_stats_name_off {
+	char name[RTE_ETH_XSTATS_NAME_SIZE - BOND_MEMBER_STAT_PREFIX_LEN];
+	size_t offset;
+};
+
+static const struct bond_member_stats_name_off bond_member_rxq_stats_strings[] = {
+	{ "packets", offsetof(struct rte_eth_stats, ipackets) },
+	{ "bytes", offsetof(struct rte_eth_stats, ibytes) },
+	{ "errors", offsetof(struct rte_eth_stats, ierrors) },
+};
+
+#define BOND_NB_MEMBER_RX_STATS RTE_DIM(bond_member_rxq_stats_strings)
+
+static const struct bond_member_stats_name_off bond_member_txq_stats_strings[] = {
+	{ "packets", offsetof(struct rte_eth_stats, opackets) },
+	{ "bytes", offsetof(struct rte_eth_stats, obytes) },
+	{ "errors", offsetof(struct rte_eth_stats, oerrors) },
+};
+
+#define BOND_NB_MEMBER_TX_STATS RTE_DIM(bond_member_txq_stats_strings)
+
+#define BOND_NB_MEMBER_STATS (BOND_NB_MEMBER_RX_STATS + BOND_NB_MEMBER_TX_STATS)
+
+static int
+bond_ethdev_xstats_get_names(struct rte_eth_dev *dev,
+			     struct rte_eth_xstat_name *xstats_names,
+			     unsigned int limit)
+{
+	struct bond_dev_private *internals = dev->data->dev_private;
+	unsigned int count = internals->member_count * BOND_NB_MEMBER_STATS;
+	unsigned int i, j;
+
+	if (xstats_names == NULL || limit < count)
+		return count;
+
+	count = 0;
+	for (i = 0; i < internals->member_count; i++) {
+		uint16_t member_id = internals->members[i].port_id;
+
+		for (j = 0; j < BOND_NB_MEMBER_RX_STATS; j++)
+			snprintf(xstats_names[count++].name,
+				 RTE_ETH_XSTATS_NAME_SIZE, "rx_member%u_%s",
+				 member_id, bond_member_rxq_stats_strings[j].name);
+
+		for (j = 0; j < BOND_NB_MEMBER_TX_STATS; j++)
+			snprintf(xstats_names[count++].name,
+				 RTE_ETH_XSTATS_NAME_SIZE, "tx_member%u_%s",
+				 member_id, bond_member_txq_stats_strings[j].name);
+	}
+
+	return count;
+}
+
+static int
+bond_ethdev_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstat *xstats,
+		       unsigned int n)
+{
+	const struct bond_dev_private *internals = dev->data->dev_private;
+	unsigned int count = internals->member_count * BOND_NB_MEMBER_STATS;
+	unsigned int i, j;
+
+	if (xstats == NULL || n < count)
+		return count;
+
+	count = 0;
+	for (i = 0; i < internals->member_count; i++) {
+		struct rte_eth_stats member_stats;
+		uint16_t member_id = internals->members[i].port_id;
+
+		/* If member query fails just report zero. */
+		if (rte_eth_stats_get(member_id, &member_stats) < 0)
+			memset(&member_stats, 0, sizeof(member_stats));
+
+		for (j = 0; j < BOND_NB_MEMBER_RX_STATS; j++) {
+			xstats[count].id = count;
+			xstats[count].value = *(const uint64_t *)((const char *)&member_stats +
+					bond_member_rxq_stats_strings[j].offset);
+			count++;
+		}
+
+		for (j = 0; j < BOND_NB_MEMBER_TX_STATS; j++) {
+			xstats[count].id = count;
+			xstats[count].value = *(const uint64_t *)((const char *)&member_stats +
+					bond_member_txq_stats_strings[j].offset);
+			count++;
+		}
+	}
+
+	return count;
+}
+
+static int
+bond_ethdev_xstats_reset(struct rte_eth_dev *dev)
+{
+	const struct bond_dev_private *internals = dev->data->dev_private;
+	uint16_t i;
+
+	for (i = 0; i < internals->member_count; i++)
+		rte_eth_stats_reset(internals->members[i].port_id);
+
+	return 0;
+}
+
 static int
 bond_ethdev_promiscuous_enable(struct rte_eth_dev *eth_dev)
 {
@@ -3710,6 +3815,8 @@ static const struct eth_dev_ops secondary_dev_ops = {
 	.dev_infos_get        = bond_ethdev_info,
 	.link_update          = bond_ethdev_link_update,
 	.stats_get            = bond_ethdev_stats_get,
+	.xstats_get           = bond_ethdev_xstats_get,
+	.xstats_get_names     = bond_ethdev_xstats_get_names,
 	.reta_query           = bond_ethdev_rss_reta_query,
 	.rss_hash_conf_get    = bond_ethdev_rss_hash_conf_get,
 	.eth_dev_priv_dump    = bond_ethdev_priv_dump,
@@ -3729,6 +3836,9 @@ const struct eth_dev_ops default_dev_ops = {
 	.link_update          = bond_ethdev_link_update,
 	.stats_get            = bond_ethdev_stats_get,
 	.stats_reset          = bond_ethdev_stats_reset,
+	.xstats_get           = bond_ethdev_xstats_get,
+	.xstats_get_names     = bond_ethdev_xstats_get_names,
+	.xstats_reset         = bond_ethdev_xstats_reset,
 	.promiscuous_enable   = bond_ethdev_promiscuous_enable,
 	.promiscuous_disable  = bond_ethdev_promiscuous_disable,
 	.allmulticast_enable  = bond_ethdev_allmulticast_enable,
@@ -3780,8 +3890,7 @@ bond_alloc(struct rte_vdev_device *dev, uint8_t mode)
 	}
 
 	eth_dev->dev_ops = &default_dev_ops;
-	eth_dev->data->dev_flags = RTE_ETH_DEV_INTR_LSC |
-					RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
+	eth_dev->data->dev_flags = RTE_ETH_DEV_INTR_LSC;
 
 	rte_spinlock_init(&internals->lock);
 	rte_spinlock_init(&internals->lsc_lock);
-- 
2.53.0



More information about the dev mailing list