[PATCH net-next] r8169: report per-queue statistics through netdev qstats
From: Gustavo Arantes
Date: Fri Apr 17 2026 - 22:13:02 EST
r8169 maintains synchronized per-CPU software counters for packet and byte
accounting, but does not expose them through the netdev qstats interface.
Add netdev_stat_ops callbacks and report the existing software counters
through queue 0 for both Rx and Tx. Provide zero base stats so device-scope
qstats report the packet and byte counters as supported and match the
existing RTNL statistics.
Signed-off-by: Gustavo Arantes <dev.gustavoa@xxxxxxxxx>
---
drivers/net/ethernet/realtek/r8169_main.c | 70 +++++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 791277e750ba..9d833b446383 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -5175,6 +5175,75 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
pm_runtime_put_noidle(&pdev->dev);
}
+static void rtl8169_fetch_sw_stats(struct net_device *dev,
+ struct netdev_queue_stats_rx *rx,
+ struct netdev_queue_stats_tx *tx)
+{
+ const struct pcpu_sw_netstats *stats;
+ unsigned int start;
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
+
+ stats = per_cpu_ptr(dev->tstats, cpu);
+ do {
+ start = u64_stats_fetch_begin(&stats->syncp);
+ rx_packets = u64_stats_read(&stats->rx_packets);
+ rx_bytes = u64_stats_read(&stats->rx_bytes);
+ tx_packets = u64_stats_read(&stats->tx_packets);
+ tx_bytes = u64_stats_read(&stats->tx_bytes);
+ } while (u64_stats_fetch_retry(&stats->syncp, start));
+
+ rx->packets += rx_packets;
+ rx->bytes += rx_bytes;
+ tx->packets += tx_packets;
+ tx->bytes += tx_bytes;
+ }
+}
+
+static void rtl8169_get_queue_stats_rx(struct net_device *dev, int idx,
+ struct netdev_queue_stats_rx *rx)
+{
+ struct netdev_queue_stats_tx tx = {};
+
+ if (idx)
+ return;
+
+ rx->packets = 0;
+ rx->bytes = 0;
+ rtl8169_fetch_sw_stats(dev, rx, &tx);
+}
+
+static void rtl8169_get_queue_stats_tx(struct net_device *dev, int idx,
+ struct netdev_queue_stats_tx *tx)
+{
+ struct netdev_queue_stats_rx rx = {};
+
+ if (idx)
+ return;
+
+ tx->packets = 0;
+ tx->bytes = 0;
+ rtl8169_fetch_sw_stats(dev, &rx, tx);
+}
+
+static void rtl8169_get_base_stats(struct net_device *dev,
+ struct netdev_queue_stats_rx *rx,
+ struct netdev_queue_stats_tx *tx)
+{
+ rx->packets = 0;
+ rx->bytes = 0;
+ tx->packets = 0;
+ tx->bytes = 0;
+}
+
+static const struct netdev_stat_ops rtl8169_stat_ops = {
+ .get_queue_stats_rx = rtl8169_get_queue_stats_rx,
+ .get_queue_stats_tx = rtl8169_get_queue_stats_tx,
+ .get_base_stats = rtl8169_get_base_stats,
+};
+
static void rtl8169_net_suspend(struct rtl8169_private *tp)
{
netif_device_detach(tp->dev);
@@ -5615,6 +5684,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
SET_NETDEV_DEV(dev, &pdev->dev);
dev->netdev_ops = &rtl_netdev_ops;
+ dev->stat_ops = &rtl8169_stat_ops;
tp = netdev_priv(dev);
tp->dev = dev;
tp->pci_dev = pdev;
--
2.51.2