* [PATCH net-next 0/4] eth: mpnic: add basic netdev statistics
@ 2026-10-09 11:35 Daniel Zahka
2026-10-09 11:35 ` [PATCH net-next 1/4] eth: mpnic: count packets, bytes, and drops per ring Daniel Zahka
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Daniel Zahka @ 2026-10-09 11:35 UTC (permalink / raw)
To: Alexander Duyck, Jakub Kicinski, kernel-team, Andrew Lunn,
David S. Miller, Paolo Abeni, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Eric Dumazet, Eric Dumazet
Cc: netdev, linux-kernel, bpf
Add basic netdev statistics and report them via ndo_get_stats64 and
netdev_stat_ops.
Track per queue netdev statistics such as Tx/Rx: packets, bytes, drops,
etc. The driver keeps per-queue state for statistics that is updated
mostly from the NAPI poll and ndo_start_xmit paths. The stats are made
available through the usual driver APIs.
When the netif is transitioned down, the per-queue stat counters are
folded into a global per-device set of counters, and then when the netif
is brought back up, the per-queue counters start counting again from 0.
Extra care is needed for implementing ndo_get_stats64 since the instance
lock is not taken. When ndo_get_stats64 races against ndo_stop and
ndo_open, the reader can attempt to read per-queue stats while the queue
count is changing. To deal with this, the writer publishes the
aggregated base stats and the active queue counts in a single RCU
versioned object.
Signed-off-by: Daniel Zahka <daniel.zahka@gmail.com>
---
Daniel Zahka (4):
eth: mpnic: count packets, bytes, and drops per ring
eth: mpnic: report per-queue stats
eth: mpnic: count Tx queue stops and wakes
eth: mpnic: count Rx allocation failures
drivers/net/ethernet/meta/mpnic/mpnic_netdev.c | 182 ++++++++++++++++++++++++
drivers/net/ethernet/meta/mpnic/mpnic_netdev.h | 29 ++++
drivers/net/ethernet/meta/mpnic/mpnic_txrx.c | 185 ++++++++++++++++++++++---
drivers/net/ethernet/meta/mpnic/mpnic_txrx.h | 30 ++++
4 files changed, 408 insertions(+), 18 deletions(-)
---
base-commit: d8674294aefef02266c4d47ad10131f1bffbe534
change-id: 20261002-mpnic-counters3-d08883b96372
Best regards,
--
Daniel Zahka <daniel.zahka@gmail.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next 1/4] eth: mpnic: count packets, bytes, and drops per ring
2026-10-09 11:35 [PATCH net-next 0/4] eth: mpnic: add basic netdev statistics Daniel Zahka
@ 2026-10-09 11:35 ` Daniel Zahka
2026-10-10 11:52 ` netdev-bot+sashiko
2026-10-09 11:35 ` [PATCH net-next 2/4] eth: mpnic: report per-queue stats Daniel Zahka
` (2 subsequent siblings)
3 siblings, 1 reply; 6+ messages in thread
From: Daniel Zahka @ 2026-10-09 11:35 UTC (permalink / raw)
To: Alexander Duyck, Jakub Kicinski, kernel-team, Andrew Lunn,
David S. Miller, Paolo Abeni, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Eric Dumazet, Eric Dumazet
Cc: netdev, linux-kernel, bpf
Keep packet, byte and drop counters on each Tx work, Tx completion, and
Rx completion queue. Report them to the core via the rtnl_link_stats64
interface.
Rx completion queues also count errors. Frames the device marked with an
uncorrectable error are counted as errors, and frames that fail for any
other reason are counted as drops.
All packets cleaned from the TWQ during mpnic_flush() are counted as
drops, though completions for some of them may have been DMA'd into
the completion ring between the time mpnic_poll() last ran and before
mpnic_wait_all_queues_idle() returns. This is done for code simplicity.
An RCU approach is used because ndo_get_stats64 is not called with the
instance lock.
Signed-off-by: Daniel Zahka <daniel.zahka@gmail.com>
---
drivers/net/ethernet/meta/mpnic/mpnic_netdev.c | 76 +++++++++++++++
drivers/net/ethernet/meta/mpnic/mpnic_netdev.h | 26 +++++
drivers/net/ethernet/meta/mpnic/mpnic_txrx.c | 125 +++++++++++++++++++++++--
drivers/net/ethernet/meta/mpnic/mpnic_txrx.h | 24 +++++
4 files changed, 243 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
index bd10df4a9d46..5164475a0f94 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
@@ -28,6 +28,8 @@ static int mpnic_open(struct net_device *netdev)
if (err)
goto err_free_resources;
+ mpnic_stats_attach_rings(mpn);
+
mpnic_enable(mpn);
mpnic_fill(mpn);
mpnic_napi_enable(mpn);
@@ -60,6 +62,8 @@ static int mpnic_stop(struct net_device *netdev)
mpnic_wait_all_queues_idle(mpn->mpd);
mpnic_flush(mpn);
+ mpnic_stats_fold_rings(mpn);
+
mpnic_reset_netif_queues(mpn);
mpnic_free_resources(mpn);
mpnic_free_napi_vectors(mpn);
@@ -67,11 +71,79 @@ static int mpnic_stop(struct net_device *netdev)
return 0;
}
+static void
+mpnic_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats64)
+{
+ struct mpnic_net *mpn = netdev_priv(dev);
+ u64 bytes, packets, dropped, errors;
+ struct mpnic_queue_stats *stats;
+ struct mpnic_base_stats *base;
+ unsigned int start, i;
+
+ rcu_read_lock();
+
+ base = rcu_dereference(mpn->stats.base);
+
+ stats64->tx_bytes = base->tx.bytes;
+ stats64->tx_packets = base->tx.packets;
+ stats64->tx_dropped = base->tx.dropped;
+
+ stats64->rx_bytes = base->rx.bytes;
+ stats64->rx_packets = base->rx.packets;
+ stats64->rx_dropped = base->rx.dropped;
+ stats64->rx_errors = base->rx.errors;
+
+ for (i = 0; i < base->num_tx_queues; i++) {
+ struct mpnic_ring *txr = base->txr[i];
+ struct mpnic_q_triad *qt;
+
+ qt = container_of(txr, struct mpnic_q_triad, sub0);
+
+ stats = &qt->cmpl.stats;
+ do {
+ start = u64_stats_fetch_begin(&stats->syncp);
+ bytes = u64_stats_read(&stats->tcq.bytes);
+ packets = u64_stats_read(&stats->tcq.packets);
+ } while (u64_stats_fetch_retry(&stats->syncp, start));
+
+ stats = &txr->stats;
+ do {
+ start = u64_stats_fetch_begin(&stats->syncp);
+ dropped = u64_stats_read(&stats->twq.dropped);
+ } while (u64_stats_fetch_retry(&stats->syncp, start));
+
+ stats64->tx_bytes += bytes;
+ stats64->tx_packets += packets;
+ stats64->tx_dropped += dropped;
+ }
+
+ for (i = 0; i < base->num_rx_queues; i++) {
+ struct mpnic_ring *rxr = base->rxr[i];
+
+ stats = &rxr->stats;
+ do {
+ start = u64_stats_fetch_begin(&stats->syncp);
+ bytes = u64_stats_read(&stats->rcq.bytes);
+ packets = u64_stats_read(&stats->rcq.packets);
+ dropped = u64_stats_read(&stats->rcq.dropped);
+ errors = u64_stats_read(&stats->rcq.errors);
+ } while (u64_stats_fetch_retry(&stats->syncp, start));
+
+ stats64->rx_bytes += bytes;
+ stats64->rx_packets += packets;
+ stats64->rx_dropped += dropped;
+ stats64->rx_errors += errors;
+ }
+
+ rcu_read_unlock();
+}
+
static const struct net_device_ops mpnic_netdev_ops = {
.ndo_open = mpnic_open,
.ndo_stop = mpnic_stop,
.ndo_validate_addr = eth_validate_addr,
.ndo_start_xmit = mpnic_xmit_frame,
+ .ndo_get_stats64 = mpnic_get_stats64,
};
/**
@@ -110,6 +182,10 @@ struct net_device *mpnic_netdev_alloc(struct mpnic_dev *mpd)
mpn->netdev = netdev;
mpn->mpd = mpd;
+ mpn->stats.buf[0].txr = mpn->tx;
+ mpn->stats.buf[0].rxr = mpn->rx;
+ RCU_INIT_POINTER(mpn->stats.base, &mpn->stats.buf[0]);
+
mpn->txq_size = MPNIC_TXQ_SIZE_DEFAULT;
mpn->hpq_size = MPNIC_HPQ_SIZE_DEFAULT;
mpn->ppq_size = MPNIC_PPQ_SIZE_DEFAULT;
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h
index ccb0929f9180..a61249d12a04 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h
@@ -4,11 +4,32 @@
#ifndef _MPNIC_NETDEV_H_
#define _MPNIC_NETDEV_H_
+#include <linux/netdevice.h>
+#include <linux/rcupdate.h>
#include <linux/types.h>
#include "mpnic.h"
#include "mpnic_txrx.h"
+struct mpnic_base_stats {
+ struct {
+ u64 packets;
+ u64 bytes;
+ u64 dropped;
+ } tx;
+ struct {
+ u64 packets;
+ u64 bytes;
+ u64 dropped;
+ u64 errors;
+ } rx;
+
+ unsigned int num_tx_queues;
+ unsigned int num_rx_queues;
+ struct mpnic_ring **txr;
+ struct mpnic_ring **rxr;
+};
+
struct mpnic_net {
struct mpnic_ring *tx[MPNIC_MAX_TXQS];
struct mpnic_ring *rx[MPNIC_MAX_RXQS];
@@ -26,6 +47,11 @@ struct mpnic_net {
u16 num_napi;
u16 num_tx_queues;
u16 num_rx_queues;
+
+ struct {
+ struct mpnic_base_stats __rcu *base;
+ struct mpnic_base_stats buf[2];
+ } stats;
};
struct net_device *mpnic_netdev_alloc(struct mpnic_dev *mpd);
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
index 5495e9a9aa65..4bf640494115 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
@@ -6,6 +6,7 @@
#include <linux/iopoll.h>
#include <linux/pci.h>
#include <linux/slab.h>
+#include <net/netdev_lock.h>
#include <net/page_pool/helpers.h>
#include "mpnic.h"
@@ -228,6 +229,10 @@ static netdev_tx_t mpnic_xmit_frame_ring(struct sk_buff *skb,
err_drop:
mpnic_tx_flush_doorbell(ring);
+ u64_stats_update_begin(&ring->stats.syncp);
+ u64_stats_inc(&ring->stats.twq.dropped);
+ u64_stats_update_end(&ring->stats.syncp);
+
return NETDEV_TX_OK;
}
@@ -239,10 +244,12 @@ netdev_tx_t mpnic_xmit_frame(struct sk_buff *skb, struct net_device *dev)
}
static void mpnic_clean_twq0(struct mpnic_napi_vector *nv, int napi_budget,
- struct mpnic_ring *ring, bool discard,
+ struct mpnic_q_triad *qt, bool discard,
unsigned int hw_head)
{
u64 total_bytes = 0, total_packets = 0;
+ struct mpnic_ring *ring = &qt->sub0;
+ struct mpnic_ring *cmpl = &qt->cmpl;
unsigned int head = ring->head;
struct netdev_queue *txq;
unsigned int clean_desc;
@@ -288,8 +295,19 @@ static void mpnic_clean_twq0(struct mpnic_napi_vector *nv, int napi_budget,
ring->head = head;
- if (discard)
+ if (discard) {
+ preempt_disable();
+ u64_stats_update_begin(&ring->stats.syncp);
+ u64_stats_add(&ring->stats.twq.dropped, total_packets);
+ u64_stats_update_end(&ring->stats.syncp);
+ preempt_enable();
return;
+ }
+
+ u64_stats_update_begin(&cmpl->stats.syncp);
+ u64_stats_add(&cmpl->stats.tcq.bytes, total_bytes);
+ u64_stats_add(&cmpl->stats.tcq.packets, total_packets);
+ u64_stats_update_end(&cmpl->stats.syncp);
txq = mpnic_txring_txq(nv->napi.dev, ring);
netif_txq_completed_wake(txq, total_packets, total_bytes,
@@ -346,7 +364,7 @@ static void mpnic_clean_tcq(struct mpnic_napi_vector *nv,
cmpl->head = head;
if (head0 >= 0)
- mpnic_clean_twq0(nv, napi_budget, &qt->sub0, false, head0);
+ mpnic_clean_twq0(nv, napi_budget, qt, false, head0);
}
static void mpnic_bd_prep(struct mpnic_ring *bdq, u32 idx, struct page *page)
@@ -560,9 +578,9 @@ static void mpnic_put_pkt_buff(struct mpnic_pkt_ctxt *ctxt, bool napi)
static int mpnic_clean_rcq(struct mpnic_napi_vector *nv,
struct mpnic_q_triad *qt, int budget)
{
+ unsigned int packets = 0, bytes = 0, dropped = 0, errors = 0;
struct mpnic_ring *rcq = &qt->cmpl;
struct mpnic_rcq_state *state;
- unsigned int packets = 0;
__le64 *raw_rcd, done;
u32 head = rcq->head;
@@ -591,16 +609,25 @@ static int mpnic_clean_rcq(struct mpnic_napi_vector *nv,
break;
case MPNIC_RCD_TYPE_META: {
struct sk_buff *skb = NULL;
+ u32 pkt_bytes = 0;
if (likely(!(rcd &
MPNIC_RCD_META_UNCORRECTABLE_ERR_MASK) &&
- !state->pkt.add_frag_failed))
+ !state->pkt.add_frag_failed)) {
+ pkt_bytes = xdp_get_buff_len(&state->pkt.buff);
skb = xdp_build_skb_from_buff(&state->pkt.buff);
+ }
- if (likely(skb))
+ if (likely(skb)) {
napi_gro_receive(&nv->napi, skb);
- else
+ bytes += pkt_bytes;
+ } else {
mpnic_put_pkt_buff(&state->pkt, true);
+ if (rcd & MPNIC_RCD_META_UNCORRECTABLE_ERR_MASK)
+ errors++;
+ else
+ dropped++;
+ }
state->pkt.buff.data_hard_start = NULL;
packets++;
@@ -619,6 +646,13 @@ static int mpnic_clean_rcq(struct mpnic_napi_vector *nv,
rcq->head = head;
+ u64_stats_update_begin(&rcq->stats.syncp);
+ u64_stats_add(&rcq->stats.rcq.packets, packets - dropped - errors);
+ u64_stats_add(&rcq->stats.rcq.bytes, bytes);
+ u64_stats_add(&rcq->stats.rcq.dropped, dropped);
+ u64_stats_add(&rcq->stats.rcq.errors, errors);
+ u64_stats_update_end(&rcq->stats.syncp);
+
/* Allocate buffers, force dma_wmb(), and then start writing tails */
mpnic_fill_qt_bdqs(qt);
@@ -661,6 +695,80 @@ static irqreturn_t mpnic_msix_clean_rings(int __always_unused irq, void *data)
return IRQ_HANDLED;
}
+static void mpnic_aggregate_ring_twq_counters(struct mpnic_base_stats *base,
+ struct mpnic_ring *twq)
+{
+ base->tx.dropped += u64_stats_read(&twq->stats.twq.dropped);
+}
+
+static void mpnic_aggregate_ring_tcq_counters(struct mpnic_base_stats *base,
+ struct mpnic_ring *tcq)
+{
+ base->tx.packets += u64_stats_read(&tcq->stats.tcq.packets);
+ base->tx.bytes += u64_stats_read(&tcq->stats.tcq.bytes);
+}
+
+static void mpnic_aggregate_ring_rcq_counters(struct mpnic_base_stats *base,
+ struct mpnic_ring *rcq)
+{
+ base->rx.packets += u64_stats_read(&rcq->stats.rcq.packets);
+ base->rx.bytes += u64_stats_read(&rcq->stats.rcq.bytes);
+ base->rx.dropped += u64_stats_read(&rcq->stats.rcq.dropped);
+ base->rx.errors += u64_stats_read(&rcq->stats.rcq.errors);
+}
+
+static struct mpnic_base_stats *mpnic_base_stats_prepare(struct mpnic_net *mpn)
+{
+ struct mpnic_base_stats *old, *new;
+
+ old = netdev_lock_dereference(mpn->stats.base, mpn->netdev);
+ new = &mpn->stats.buf[old == &mpn->stats.buf[0]];
+ *new = *old;
+
+ return new;
+}
+
+void mpnic_stats_fold_rings(struct mpnic_net *mpn)
+{
+ struct mpnic_base_stats *new;
+ int i, j, t;
+
+ new = mpnic_base_stats_prepare(mpn);
+ new->num_tx_queues = 0;
+ new->num_rx_queues = 0;
+
+ for (i = 0; i < mpn->num_napi; i++) {
+ struct mpnic_napi_vector *nv = mpn->napi[i];
+
+ if (!nv)
+ continue;
+
+ for (t = 0; t < nv->txt_count; t++) {
+ struct mpnic_q_triad *qt = &nv->qt[t];
+
+ mpnic_aggregate_ring_twq_counters(new, &qt->sub0);
+ mpnic_aggregate_ring_tcq_counters(new, &qt->cmpl);
+ }
+
+ for (j = 0; j < nv->rxt_count; j++, t++)
+ mpnic_aggregate_ring_rcq_counters(new, &nv->qt[t].cmpl);
+ }
+
+ rcu_assign_pointer(mpn->stats.base, new);
+ synchronize_net();
+}
+
+void mpnic_stats_attach_rings(struct mpnic_net *mpn)
+{
+ struct mpnic_base_stats *new;
+
+ new = mpnic_base_stats_prepare(mpn);
+ new->num_tx_queues = mpn->num_tx_queues;
+ new->num_rx_queues = mpn->num_rx_queues;
+ rcu_assign_pointer(mpn->stats.base, new);
+ synchronize_net();
+}
+
static void mpnic_free_napi_vector(struct mpnic_net *mpn,
struct mpnic_napi_vector *nv)
{
@@ -692,6 +800,7 @@ static void mpnic_ring_init(struct mpnic_ring *ring, u32 __iomem *doorbell,
{
ring->doorbell = doorbell;
ring->q_idx = q_idx;
+ u64_stats_init(&ring->stats.syncp);
}
static int mpnic_alloc_napi_vector(struct mpnic_dev *mpd,
@@ -1326,7 +1435,7 @@ void mpnic_flush(struct mpnic_net *mpn)
struct netdev_queue *txq;
/* Clean the work queue of unprocessed work */
- mpnic_clean_twq0(nv, 0, &qt->sub0, true, qt->sub0.tail);
+ mpnic_clean_twq0(nv, 0, qt, true, qt->sub0.tail);
txq = netdev_get_tx_queue(mpn->netdev, qt->sub0.q_idx);
netdev_tx_reset_queue(txq);
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h
index 767d87a36c35..7b44cf700e5b 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h
@@ -8,6 +8,7 @@
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/types.h>
+#include <linux/u64_stats_sync.h>
#include <net/netdev_queues.h>
#include <net/xdp.h>
@@ -84,6 +85,25 @@ struct mpnic_rcq_state {
struct mpnic_pg_ctxt payld;
};
+struct mpnic_queue_stats {
+ union {
+ struct {
+ u64_stats_t dropped;
+ } twq;
+ struct {
+ u64_stats_t packets;
+ u64_stats_t bytes;
+ } tcq;
+ struct {
+ u64_stats_t packets;
+ u64_stats_t bytes;
+ u64_stats_t dropped;
+ u64_stats_t errors;
+ } rcq;
+ };
+ struct u64_stats_sync syncp;
+};
+
struct mpnic_ring {
union {
struct mpnic_rcq_state *state; /* RCQ */
@@ -110,6 +130,8 @@ struct mpnic_ring {
s32 deferred_meta;
};
+ struct mpnic_queue_stats stats;
+
/* Slow path fields follow */
dma_addr_t dma; /* Phys addr of descriptor memory */
size_t size; /* Size of descriptor ring in memory */
@@ -142,6 +164,8 @@ struct mpnic_napi_vector {
netdev_tx_t mpnic_xmit_frame(struct sk_buff *skb, struct net_device *dev);
int mpnic_alloc_napi_vectors(struct mpnic_net *mpn);
void mpnic_free_napi_vectors(struct mpnic_net *mpn);
+void mpnic_stats_attach_rings(struct mpnic_net *mpn);
+void mpnic_stats_fold_rings(struct mpnic_net *mpn);
int mpnic_alloc_resources(struct mpnic_net *mpn);
void mpnic_free_resources(struct mpnic_net *mpn);
int mpnic_set_netif_queues(struct mpnic_net *mpn);
--
2.52.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH net-next 2/4] eth: mpnic: report per-queue stats
2026-10-09 11:35 [PATCH net-next 0/4] eth: mpnic: add basic netdev statistics Daniel Zahka
2026-10-09 11:35 ` [PATCH net-next 1/4] eth: mpnic: count packets, bytes, and drops per ring Daniel Zahka
@ 2026-10-09 11:35 ` Daniel Zahka
2026-10-09 11:35 ` [PATCH net-next 3/4] eth: mpnic: count Tx queue stops and wakes Daniel Zahka
2026-10-09 11:35 ` [PATCH net-next 4/4] eth: mpnic: count Rx allocation failures Daniel Zahka
3 siblings, 0 replies; 6+ messages in thread
From: Daniel Zahka @ 2026-10-09 11:35 UTC (permalink / raw)
To: Alexander Duyck, Jakub Kicinski, kernel-team, Andrew Lunn,
David S. Miller, Paolo Abeni, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Eric Dumazet, Eric Dumazet
Cc: netdev, linux-kernel, bpf
Expose the per-ring packet and byte counters through the queue stats
API. Counters for deactivated rings are reported through the base stats.
Signed-off-by: Daniel Zahka <daniel.zahka@gmail.com>
---
drivers/net/ethernet/meta/mpnic/mpnic_netdev.c | 73 ++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
index 5164475a0f94..c2f4a326a8f0 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
@@ -6,6 +6,7 @@
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/types.h>
+#include <net/netdev_lock.h>
#include "mpnic.h"
#include "mpnic_netdev.h"
@@ -146,6 +147,77 @@ static const struct net_device_ops mpnic_netdev_ops = {
.ndo_get_stats64 = mpnic_get_stats64,
};
+static void mpnic_get_queue_stats_rx(struct net_device *dev, int idx,
+ struct netdev_queue_stats_rx *rx)
+{
+ struct mpnic_net *mpn = netdev_priv(dev);
+ struct mpnic_ring *rxr = mpn->rx[idx];
+ struct mpnic_queue_stats *stats;
+ unsigned int start;
+ u64 bytes, packets;
+
+ if (!rxr)
+ return;
+
+ stats = &rxr->stats;
+ do {
+ start = u64_stats_fetch_begin(&stats->syncp);
+ bytes = u64_stats_read(&stats->rcq.bytes);
+ packets = u64_stats_read(&stats->rcq.packets);
+ } while (u64_stats_fetch_retry(&stats->syncp, start));
+
+ rx->bytes = bytes;
+ rx->packets = packets;
+}
+
+static void mpnic_get_queue_stats_tx(struct net_device *dev, int idx,
+ struct netdev_queue_stats_tx *tx)
+{
+ struct mpnic_net *mpn = netdev_priv(dev);
+ struct mpnic_ring *txr = mpn->tx[idx];
+ struct mpnic_queue_stats *stats;
+ struct mpnic_q_triad *qt;
+ unsigned int start;
+ u64 bytes, packets;
+
+ if (!txr)
+ return;
+
+ qt = container_of(txr, struct mpnic_q_triad, sub0);
+
+ stats = &qt->cmpl.stats;
+ do {
+ start = u64_stats_fetch_begin(&stats->syncp);
+ bytes = u64_stats_read(&stats->tcq.bytes);
+ packets = u64_stats_read(&stats->tcq.packets);
+ } while (u64_stats_fetch_retry(&stats->syncp, start));
+
+ tx->bytes = bytes;
+ tx->packets = packets;
+}
+
+static void mpnic_get_base_stats(struct net_device *dev,
+ struct netdev_queue_stats_rx *rx,
+ struct netdev_queue_stats_tx *tx)
+{
+ struct mpnic_net *mpn = netdev_priv(dev);
+ struct mpnic_base_stats *base;
+
+ base = netdev_lock_dereference(mpn->stats.base, dev);
+
+ tx->bytes = base->tx.bytes;
+ tx->packets = base->tx.packets;
+
+ rx->bytes = base->rx.bytes;
+ rx->packets = base->rx.packets;
+}
+
+static const struct netdev_stat_ops mpnic_stat_ops = {
+ .get_queue_stats_rx = mpnic_get_queue_stats_rx,
+ .get_queue_stats_tx = mpnic_get_queue_stats_tx,
+ .get_base_stats = mpnic_get_base_stats,
+};
+
/**
* mpnic_netdev_free - Free the netdev associated with mpnic
* @mpd: Driver specific structure to free netdev from
@@ -176,6 +248,7 @@ struct net_device *mpnic_netdev_alloc(struct mpnic_dev *mpd)
mpd->netdev = netdev;
netdev->netdev_ops = &mpnic_netdev_ops;
+ netdev->stat_ops = &mpnic_stat_ops;
netdev->request_ops_lock = true;
mpn = netdev_priv(netdev);
--
2.52.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH net-next 3/4] eth: mpnic: count Tx queue stops and wakes
2026-10-09 11:35 [PATCH net-next 0/4] eth: mpnic: add basic netdev statistics Daniel Zahka
2026-10-09 11:35 ` [PATCH net-next 1/4] eth: mpnic: count packets, bytes, and drops per ring Daniel Zahka
2026-10-09 11:35 ` [PATCH net-next 2/4] eth: mpnic: report per-queue stats Daniel Zahka
@ 2026-10-09 11:35 ` Daniel Zahka
2026-10-09 11:35 ` [PATCH net-next 4/4] eth: mpnic: count Rx allocation failures Daniel Zahka
3 siblings, 0 replies; 6+ messages in thread
From: Daniel Zahka @ 2026-10-09 11:35 UTC (permalink / raw)
To: Alexander Duyck, Jakub Kicinski, kernel-team, Andrew Lunn,
David S. Miller, Paolo Abeni, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Eric Dumazet, Eric Dumazet
Cc: netdev, linux-kernel, bpf
Count how often a Tx queue is stopped for lack of descriptors and woken
again by completions, and report the counts through the queue stats API.
Stops are counted on the TWQ from the xmit path and wakes on the TCQ
from mpnic_poll(), keeping a single writer context for each.
Signed-off-by: Daniel Zahka <daniel.zahka@gmail.com>
---
drivers/net/ethernet/meta/mpnic/mpnic_netdev.c | 13 ++++++++-
drivers/net/ethernet/meta/mpnic/mpnic_netdev.h | 2 ++
drivers/net/ethernet/meta/mpnic/mpnic_txrx.c | 37 +++++++++++++++++++-------
drivers/net/ethernet/meta/mpnic/mpnic_txrx.h | 2 ++
4 files changed, 44 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
index c2f4a326a8f0..9e262f939c79 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
@@ -176,9 +176,9 @@ static void mpnic_get_queue_stats_tx(struct net_device *dev, int idx,
struct mpnic_net *mpn = netdev_priv(dev);
struct mpnic_ring *txr = mpn->tx[idx];
struct mpnic_queue_stats *stats;
+ u64 bytes, packets, stop, wake;
struct mpnic_q_triad *qt;
unsigned int start;
- u64 bytes, packets;
if (!txr)
return;
@@ -190,10 +190,19 @@ static void mpnic_get_queue_stats_tx(struct net_device *dev, int idx,
start = u64_stats_fetch_begin(&stats->syncp);
bytes = u64_stats_read(&stats->tcq.bytes);
packets = u64_stats_read(&stats->tcq.packets);
+ wake = u64_stats_read(&stats->tcq.wake);
+ } while (u64_stats_fetch_retry(&stats->syncp, start));
+
+ stats = &txr->stats;
+ do {
+ start = u64_stats_fetch_begin(&stats->syncp);
+ stop = u64_stats_read(&stats->twq.stop);
} while (u64_stats_fetch_retry(&stats->syncp, start));
tx->bytes = bytes;
tx->packets = packets;
+ tx->stop = stop;
+ tx->wake = wake;
}
static void mpnic_get_base_stats(struct net_device *dev,
@@ -207,6 +216,8 @@ static void mpnic_get_base_stats(struct net_device *dev,
tx->bytes = base->tx.bytes;
tx->packets = base->tx.packets;
+ tx->stop = base->tx.stop;
+ tx->wake = base->tx.wake;
rx->bytes = base->rx.bytes;
rx->packets = base->rx.packets;
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h
index a61249d12a04..0d1ee66dd86c 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h
@@ -16,6 +16,8 @@ struct mpnic_base_stats {
u64 packets;
u64 bytes;
u64 dropped;
+ u64 stop;
+ u64 wake;
} tx;
struct {
u64 packets;
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
index 4bf640494115..35129fc149e2 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
@@ -68,6 +68,23 @@ static struct netdev_queue *mpnic_txring_txq(const struct net_device *dev,
return netdev_get_tx_queue(dev, ring->q_idx);
}
+static bool
+mpnic_maybe_stop_tx(const struct net_device *dev, struct mpnic_ring *ring)
+{
+ int res;
+
+ res = netif_txq_maybe_stop(mpnic_txring_txq(dev, ring),
+ mpnic_desc_unused(ring), MPNIC_MAX_SKB_DESC,
+ MPNIC_TX_DESC_WAKEUP);
+ if (!res) {
+ u64_stats_update_begin(&ring->stats.syncp);
+ u64_stats_inc(&ring->stats.twq.stop);
+ u64_stats_update_end(&ring->stats.syncp);
+ }
+
+ return !res;
+}
+
static void mpnic_tx_doorbell(struct mpnic_ring *ring, __le64 *meta)
{
*meta |= cpu_to_le64(MPNIC_TWD_FLAG_REQ_COMPLETION);
@@ -164,9 +181,7 @@ mpnic_tx_map(struct mpnic_ring *ring, struct sk_buff *skb, __le64 *meta)
ring->tail = tail;
/* Verify there is room for another packet */
- netif_txq_maybe_stop(mpnic_txring_txq(skb->dev, ring),
- mpnic_desc_unused(ring), MPNIC_MAX_SKB_DESC,
- MPNIC_TX_DESC_WAKEUP);
+ mpnic_maybe_stop_tx(skb->dev, ring);
if (__netdev_tx_sent_queue(mpnic_txring_txq(skb->dev, ring),
MPNIC_XMIT_CB(skb)->bytecount,
@@ -204,9 +219,7 @@ static netdev_tx_t mpnic_xmit_frame_ring(struct sk_buff *skb,
if (skb_put_padto(skb, MPNIC_MIN_FRAME_LEN))
goto err_drop;
- if (!netif_txq_maybe_stop(mpnic_txring_txq(skb->dev, ring),
- mpnic_desc_unused(ring), MPNIC_MAX_SKB_DESC,
- MPNIC_TX_DESC_WAKEUP)) {
+ if (mpnic_maybe_stop_tx(skb->dev, ring)) {
mpnic_tx_flush_doorbell(ring);
return NETDEV_TX_BUSY;
}
@@ -310,9 +323,13 @@ static void mpnic_clean_twq0(struct mpnic_napi_vector *nv, int napi_budget,
u64_stats_update_end(&cmpl->stats.syncp);
txq = mpnic_txring_txq(nv->napi.dev, ring);
- netif_txq_completed_wake(txq, total_packets, total_bytes,
- mpnic_desc_unused(ring),
- MPNIC_TX_DESC_WAKEUP);
+ if (!netif_txq_completed_wake(txq, total_packets, total_bytes,
+ mpnic_desc_unused(ring),
+ MPNIC_TX_DESC_WAKEUP)) {
+ u64_stats_update_begin(&cmpl->stats.syncp);
+ u64_stats_inc(&cmpl->stats.tcq.wake);
+ u64_stats_update_end(&cmpl->stats.syncp);
+ }
}
static void mpnic_commit_cq_head(struct mpnic_ring *cmpl)
@@ -699,6 +716,7 @@ static void mpnic_aggregate_ring_twq_counters(struct mpnic_base_stats *base,
struct mpnic_ring *twq)
{
base->tx.dropped += u64_stats_read(&twq->stats.twq.dropped);
+ base->tx.stop += u64_stats_read(&twq->stats.twq.stop);
}
static void mpnic_aggregate_ring_tcq_counters(struct mpnic_base_stats *base,
@@ -706,6 +724,7 @@ static void mpnic_aggregate_ring_tcq_counters(struct mpnic_base_stats *base,
{
base->tx.packets += u64_stats_read(&tcq->stats.tcq.packets);
base->tx.bytes += u64_stats_read(&tcq->stats.tcq.bytes);
+ base->tx.wake += u64_stats_read(&tcq->stats.tcq.wake);
}
static void mpnic_aggregate_ring_rcq_counters(struct mpnic_base_stats *base,
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h
index 7b44cf700e5b..764e78918b8f 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h
@@ -89,10 +89,12 @@ struct mpnic_queue_stats {
union {
struct {
u64_stats_t dropped;
+ u64_stats_t stop;
} twq;
struct {
u64_stats_t packets;
u64_stats_t bytes;
+ u64_stats_t wake;
} tcq;
struct {
u64_stats_t packets;
--
2.52.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH net-next 4/4] eth: mpnic: count Rx allocation failures
2026-10-09 11:35 [PATCH net-next 0/4] eth: mpnic: add basic netdev statistics Daniel Zahka
` (2 preceding siblings ...)
2026-10-09 11:35 ` [PATCH net-next 3/4] eth: mpnic: count Tx queue stops and wakes Daniel Zahka
@ 2026-10-09 11:35 ` Daniel Zahka
3 siblings, 0 replies; 6+ messages in thread
From: Daniel Zahka @ 2026-10-09 11:35 UTC (permalink / raw)
To: Alexander Duyck, Jakub Kicinski, kernel-team, Andrew Lunn,
David S. Miller, Paolo Abeni, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Eric Dumazet, Eric Dumazet
Cc: netdev, linux-kernel, bpf
Count both skb allocation failures and page pool allocation failures per
queue, and report them through the queue stats API. The bdq allocation
failures are added to the skb allocation failures when reporting to core
and folded into base stats during aggregation, because the qstats API
doesn't support reporting them separately.
Page pool allocation failures are also counted when the BDQs are first
filled in ndo_open, which runs in process context. mpnic_fill() disables
BH around the fill, so the u64_stats update happens with preemption
disabled.
Signed-off-by: Daniel Zahka <daniel.zahka@gmail.com>
---
drivers/net/ethernet/meta/mpnic/mpnic_netdev.c | 24 ++++++++++++++++++++++-
drivers/net/ethernet/meta/mpnic/mpnic_netdev.h | 1 +
drivers/net/ethernet/meta/mpnic/mpnic_txrx.c | 27 +++++++++++++++++++++++---
drivers/net/ethernet/meta/mpnic/mpnic_txrx.h | 4 ++++
4 files changed, 52 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
index 9e262f939c79..7fdf57f70151 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.c
@@ -147,14 +147,29 @@ static const struct net_device_ops mpnic_netdev_ops = {
.ndo_get_stats64 = mpnic_get_stats64,
};
+static u64 mpnic_bdq_alloc_failed(struct mpnic_ring *bdq)
+{
+ struct mpnic_queue_stats *stats = &bdq->stats;
+ unsigned int start;
+ u64 alloc_failed;
+
+ do {
+ start = u64_stats_fetch_begin(&stats->syncp);
+ alloc_failed = u64_stats_read(&stats->bdq.alloc_failed);
+ } while (u64_stats_fetch_retry(&stats->syncp, start));
+
+ return alloc_failed;
+}
+
static void mpnic_get_queue_stats_rx(struct net_device *dev, int idx,
struct netdev_queue_stats_rx *rx)
{
struct mpnic_net *mpn = netdev_priv(dev);
struct mpnic_ring *rxr = mpn->rx[idx];
+ u64 bytes, packets, alloc_failed;
struct mpnic_queue_stats *stats;
+ struct mpnic_q_triad *qt;
unsigned int start;
- u64 bytes, packets;
if (!rxr)
return;
@@ -164,10 +179,16 @@ static void mpnic_get_queue_stats_rx(struct net_device *dev, int idx,
start = u64_stats_fetch_begin(&stats->syncp);
bytes = u64_stats_read(&stats->rcq.bytes);
packets = u64_stats_read(&stats->rcq.packets);
+ alloc_failed = u64_stats_read(&stats->rcq.alloc_failed);
} while (u64_stats_fetch_retry(&stats->syncp, start));
+ qt = container_of(rxr, struct mpnic_q_triad, cmpl);
+ alloc_failed += mpnic_bdq_alloc_failed(&qt->sub0);
+ alloc_failed += mpnic_bdq_alloc_failed(&qt->sub1);
+
rx->bytes = bytes;
rx->packets = packets;
+ rx->alloc_fail = alloc_failed;
}
static void mpnic_get_queue_stats_tx(struct net_device *dev, int idx,
@@ -221,6 +242,7 @@ static void mpnic_get_base_stats(struct net_device *dev,
rx->bytes = base->rx.bytes;
rx->packets = base->rx.packets;
+ rx->alloc_fail = base->rx.alloc_failed;
}
static const struct netdev_stat_ops mpnic_stat_ops = {
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h
index 0d1ee66dd86c..6711766ebc3c 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_netdev.h
@@ -23,6 +23,7 @@ struct mpnic_base_stats {
u64 packets;
u64 bytes;
u64 dropped;
+ u64 alloc_failed;
u64 errors;
} rx;
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
index 35129fc149e2..91b897d766a8 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
@@ -409,8 +409,12 @@ static unsigned int __mpnic_fill_bdq(struct mpnic_ring *bdq)
struct page *page;
page = page_pool_dev_alloc_pages(bdq->page_pool);
- if (!page)
+ if (!page) {
+ u64_stats_update_begin(&bdq->stats.syncp);
+ u64_stats_inc(&bdq->stats.bdq.alloc_failed);
+ u64_stats_update_end(&bdq->stats.syncp);
break;
+ }
bdq->rx_buf[i] = page;
mpnic_bd_prep(bdq, i, page);
@@ -598,6 +602,7 @@ static int mpnic_clean_rcq(struct mpnic_napi_vector *nv,
unsigned int packets = 0, bytes = 0, dropped = 0, errors = 0;
struct mpnic_ring *rcq = &qt->cmpl;
struct mpnic_rcq_state *state;
+ unsigned int alloc_failed = 0;
__le64 *raw_rcd, done;
u32 head = rcq->head;
@@ -633,6 +638,7 @@ static int mpnic_clean_rcq(struct mpnic_napi_vector *nv,
!state->pkt.add_frag_failed)) {
pkt_bytes = xdp_get_buff_len(&state->pkt.buff);
skb = xdp_build_skb_from_buff(&state->pkt.buff);
+ alloc_failed += !skb;
}
if (likely(skb)) {
@@ -667,6 +673,7 @@ static int mpnic_clean_rcq(struct mpnic_napi_vector *nv,
u64_stats_add(&rcq->stats.rcq.packets, packets - dropped - errors);
u64_stats_add(&rcq->stats.rcq.bytes, bytes);
u64_stats_add(&rcq->stats.rcq.dropped, dropped);
+ u64_stats_add(&rcq->stats.rcq.alloc_failed, alloc_failed);
u64_stats_add(&rcq->stats.rcq.errors, errors);
u64_stats_update_end(&rcq->stats.syncp);
@@ -727,12 +734,19 @@ static void mpnic_aggregate_ring_tcq_counters(struct mpnic_base_stats *base,
base->tx.wake += u64_stats_read(&tcq->stats.tcq.wake);
}
+static void mpnic_aggregate_ring_bdq_counters(struct mpnic_base_stats *base,
+ struct mpnic_ring *bdq)
+{
+ base->rx.alloc_failed += u64_stats_read(&bdq->stats.bdq.alloc_failed);
+}
+
static void mpnic_aggregate_ring_rcq_counters(struct mpnic_base_stats *base,
struct mpnic_ring *rcq)
{
base->rx.packets += u64_stats_read(&rcq->stats.rcq.packets);
base->rx.bytes += u64_stats_read(&rcq->stats.rcq.bytes);
base->rx.dropped += u64_stats_read(&rcq->stats.rcq.dropped);
+ base->rx.alloc_failed += u64_stats_read(&rcq->stats.rcq.alloc_failed);
base->rx.errors += u64_stats_read(&rcq->stats.rcq.errors);
}
@@ -769,8 +783,13 @@ void mpnic_stats_fold_rings(struct mpnic_net *mpn)
mpnic_aggregate_ring_tcq_counters(new, &qt->cmpl);
}
- for (j = 0; j < nv->rxt_count; j++, t++)
- mpnic_aggregate_ring_rcq_counters(new, &nv->qt[t].cmpl);
+ for (j = 0; j < nv->rxt_count; j++, t++) {
+ struct mpnic_q_triad *qt = &nv->qt[t];
+
+ mpnic_aggregate_ring_bdq_counters(new, &qt->sub0);
+ mpnic_aggregate_ring_bdq_counters(new, &qt->sub1);
+ mpnic_aggregate_ring_rcq_counters(new, &qt->cmpl);
+ }
}
rcu_assign_pointer(mpn->stats.base, new);
@@ -1496,7 +1515,9 @@ void mpnic_fill(struct mpnic_net *mpn)
state->hdr.idx = UINT_MAX;
state->payld.idx = UINT_MAX;
+ local_bh_disable();
mpnic_fill_qt_bdqs(qt);
+ local_bh_enable();
}
}
}
diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h
index 764e78918b8f..52492e3f484f 100644
--- a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h
+++ b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.h
@@ -100,8 +100,12 @@ struct mpnic_queue_stats {
u64_stats_t packets;
u64_stats_t bytes;
u64_stats_t dropped;
+ u64_stats_t alloc_failed;
u64_stats_t errors;
} rcq;
+ struct {
+ u64_stats_t alloc_failed;
+ } bdq;
};
struct u64_stats_sync syncp;
};
--
2.52.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net-next 1/4] eth: mpnic: count packets, bytes, and drops per ring
2026-10-09 11:35 ` [PATCH net-next 1/4] eth: mpnic: count packets, bytes, and drops per ring Daniel Zahka
@ 2026-10-10 11:52 ` netdev-bot+sashiko
0 siblings, 0 replies; 6+ messages in thread
From: netdev-bot+sashiko @ 2026-10-10 11:52 UTC (permalink / raw)
To: daniel.zahka
Cc: alexanderduyck, kuba, kernel-team, andrew+netdev, davem, pabeni,
ast, daniel, hawk, john.fastabend, sdf, edumazet, netdev,
linux-kernel, bpf
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 1 · Low: 0
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] mpnic_clean_rcq()
(drivers/net/ethernet/meta/mpnic/mpnic_txrx.c) leaves good Rx frames…
--
eth: mpnic: count packets, bytes, and drops per ring
This commit adds packet, byte and drop counters to the Tx work, Tx
completion and Rx completion rings, and reports them through
ndo_get_stats64. Rx frames that the device flagged with an uncorrectable
error count as errors, and any other Rx failure counts as a drop.
> diff --git a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
> index 5495e9a9aa651..4bf6404941158 100644
> --- a/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
> +++ b/drivers/net/ethernet/meta/mpnic/mpnic_txrx.c
[ ... ]
> @@ -591,16 +609,25 @@ static int mpnic_clean_rcq(struct mpnic_napi_vector *nv,
> break;
> case MPNIC_RCD_TYPE_META: {
> struct sk_buff *skb = NULL;
> + u32 pkt_bytes = 0;
>
> if (likely(!(rcd &
> MPNIC_RCD_META_UNCORRECTABLE_ERR_MASK) &&
> - !state->pkt.add_frag_failed))
> + !state->pkt.add_frag_failed)) {
> + pkt_bytes = xdp_get_buff_len(&state->pkt.buff);
> skb = xdp_build_skb_from_buff(&state->pkt.buff);
> + }
>
> - if (likely(skb))
> + if (likely(skb)) {
> napi_gro_receive(&nv->napi, skb);
> - else
> + bytes += pkt_bytes;
> + } else {
> mpnic_put_pkt_buff(&state->pkt, true);
> + if (rcd & MPNIC_RCD_META_UNCORRECTABLE_ERR_MASK)
> + errors++;
> + else
> + dropped++;
> + }
>
> state->pkt.buff.data_hard_start = NULL;
> packets++;
[ ... ]
> @@ -619,6 +646,13 @@ static int mpnic_clean_rcq(struct mpnic_napi_vector *nv,
>
> rcq->head = head;
>
> + u64_stats_update_begin(&rcq->stats.syncp);
> + u64_stats_add(&rcq->stats.rcq.packets, packets - dropped - errors);
> + u64_stats_add(&rcq->stats.rcq.bytes, bytes);
> + u64_stats_add(&rcq->stats.rcq.dropped, dropped);
> + u64_stats_add(&rcq->stats.rcq.errors, errors);
> + u64_stats_update_end(&rcq->stats.syncp);
> +
[Severity: Medium]
Should rx_packets and rx_bytes still count frames that the device
reported as good but the driver then dropped?
In the MPNIC_RCD_TYPE_META branch, two cases reach dropped++ while
MPNIC_RCD_META_UNCORRECTABLE_ERR_MASK is clear:
- mpnic_add_rx_frag() set state->pkt.add_frag_failed
- xdp_build_skb_from_buff() returned NULL
Subtracting dropped here takes those frames out of rcq.packets. Their
bytes are lost as well, because bytes += pkt_bytes only runs when an skb
was built. When xdp_build_skb_from_buff() fails, pkt_bytes has already
been computed and is then thrown away.
mpnic_get_stats64() and mpnic_aggregate_ring_rcq_counters() pass these
values directly into rx_packets and rx_bytes. The rtnl_link_stats64
kernel-doc in include/uapi/linux/if_link.h says:
* @rx_packets: Number of good packets received by the interface.
* For hardware interfaces counts all good packets received from the device
* by the host, including packets which host had to drop at various stages
* of processing (even in the driver).
...
* @rx_bytes: Number of good received bytes, corresponding to @rx_packets.
Leaving out frames the hardware flagged as uncorrectable matches this,
since they go to rx_errors. Good frames dropped for lack of resources go
to rx_dropped, but by this text they should also be counted in
rx_packets and rx_bytes.
Could this subtract only errors from packets, and add pkt_bytes in the
dropped case too?
The later commits in the series ("eth: mpnic: report per-queue stats",
"eth: mpnic: count Tx queue stops and wakes" and "eth: mpnic: count Rx
allocation failures") keep both the packets - dropped - errors
subtraction and the byte counting that only happens on success. So the
problem is still there at the end of the series.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261009-mpnic-counters3-v1-0-7dc7644cc500%40gmail.com
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-10 11:52 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-09 11:35 [PATCH net-next 0/4] eth: mpnic: add basic netdev statistics Daniel Zahka
2026-10-09 11:35 ` [PATCH net-next 1/4] eth: mpnic: count packets, bytes, and drops per ring Daniel Zahka
2026-10-10 11:52 ` netdev-bot+sashiko
2026-10-09 11:35 ` [PATCH net-next 2/4] eth: mpnic: report per-queue stats Daniel Zahka
2026-10-09 11:35 ` [PATCH net-next 3/4] eth: mpnic: count Tx queue stops and wakes Daniel Zahka
2026-10-09 11:35 ` [PATCH net-next 4/4] eth: mpnic: count Rx allocation failures Daniel Zahka
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox