[PATCH net-next 3/4] eth: mpnic: count Tx queue stops and wakes

From: Daniel Zahka

Date: Fri Oct 09 2026 - 07:51:54 EST


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@xxxxxxxxx>
---
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