Implement the ndo_get_stats64 callback to report aggregate network
statistics. The driver gathers these by accumulating the per-ring
counters into the provided rtnl_link_stats64 structure.
Signed-off-by: Bhargava Marreddy <bhargava.marreddy@broadcom.com>
Reviewed-by: Vikas Gupta <vikas.gupta@broadcom.com>
Reviewed-by: Ajit Kumar Khaparde <ajit.khaparde@broadcom.com>
---
.../net/ethernet/broadcom/bnge/bnge_netdev.c | 111 +++++++++++++++++-
.../net/ethernet/broadcom/bnge/bnge_netdev.h | 2 +
2 files changed, 112 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
index 2b772956df8..7de82a0ce9a 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
@@ -468,7 +468,8 @@ static void bnge_sp_task(struct work_struct *work)
static bool bnge_drv_busy(struct bnge_net *bn)
{
- return test_bit(BNGE_STATE_IN_SP_TASK, &bn->state);
+ return test_bit(BNGE_STATE_IN_SP_TASK, &bn->state) ||
+ test_bit(BNGE_STATE_READ_STATS, &bn->state);
}
static void bnge_free_nq_desc_arr(struct bnge_nq_ring_info *nqr)
@@ -2934,6 +2935,106 @@ static int bnge_shutdown_nic(struct bnge_net *bn)
return 0;
}
+static void bnge_get_ring_stats(struct bnge_dev *bd,
+ struct rtnl_link_stats64 *stats)
+{
+ struct bnge_net *bn = netdev_priv(bd->netdev);
+ int i;
+
+ for (i = 0; i < bd->nq_nr_rings; i++) {
+ struct bnge_napi *bnapi = bn->bnapi[i];
+ struct bnge_nq_ring_info *nqr = &bnapi->nq_ring;
+ u64 *sw = nqr->stats.sw_stats;
+
+ stats->rx_packets += BNGE_GET_RING_STATS64(sw, rx_ucast_pkts);
+ stats->rx_packets += BNGE_GET_RING_STATS64(sw, rx_mcast_pkts);
+ stats->rx_packets += BNGE_GET_RING_STATS64(sw, rx_bcast_pkts);
+
+ stats->tx_packets += BNGE_GET_RING_STATS64(sw, tx_ucast_pkts);
+ stats->tx_packets += BNGE_GET_RING_STATS64(sw, tx_mcast_pkts);
+ stats->tx_packets += BNGE_GET_RING_STATS64(sw, tx_bcast_pkts);
+
+ stats->rx_bytes += BNGE_GET_RING_STATS64(sw, rx_ucast_bytes);
+ stats->rx_bytes += BNGE_GET_RING_STATS64(sw, rx_mcast_bytes);
+ stats->rx_bytes += BNGE_GET_RING_STATS64(sw, rx_bcast_bytes);
+
+ stats->tx_bytes += BNGE_GET_RING_STATS64(sw, tx_ucast_bytes);
+ stats->tx_bytes += BNGE_GET_RING_STATS64(sw, tx_mcast_bytes);
+ stats->tx_bytes += BNGE_GET_RING_STATS64(sw, tx_bcast_bytes);
+
+ stats->rx_missed_errors +=
+ BNGE_GET_RING_STATS64(sw, rx_discard_pkts);
+
+ stats->multicast += BNGE_GET_RING_STATS64(sw, rx_mcast_pkts);
+
+ stats->tx_dropped += BNGE_GET_RING_STATS64(sw, tx_error_pkts);
+
+ stats->rx_dropped +=
+ nqr->sw_stats->rx.rx_netpoll_discards +
+ nqr->sw_stats->rx.rx_oom_discards;
+ }
+}
+
+static void bnge_add_prev_stats(struct bnge_net *bn,
+ struct rtnl_link_stats64 *stats)
+{
+ struct rtnl_link_stats64 *prev_stats = &bn->net_stats_prev;
+
+ stats->rx_packets += prev_stats->rx_packets;
+ stats->tx_packets += prev_stats->tx_packets;
+ stats->rx_bytes += prev_stats->rx_bytes;
+ stats->tx_bytes += prev_stats->tx_bytes;
+ stats->rx_missed_errors += prev_stats->rx_missed_errors;
+ stats->multicast += prev_stats->multicast;
+ stats->rx_dropped += prev_stats->rx_dropped;
+ stats->tx_dropped += prev_stats->tx_dropped;
+}
+
+static void bnge_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_dev *bd = bn->bd;
+
+ set_bit(BNGE_STATE_READ_STATS, &bn->state);
+ /* Make sure bnge_close_core() sees that we are reading stats before
+ * we check the BNGE_STATE_OPEN flag.
+ */
+ smp_mb__after_atomic();
+ if (!test_bit(BNGE_STATE_OPEN, &bd->state)) {
+ clear_bit(BNGE_STATE_READ_STATS, &bn->state);
+ *stats = bn->net_stats_prev;
+ return;
+ }
+
+ bnge_get_ring_stats(bd, stats);
+ bnge_add_prev_stats(bn, stats);
+
+ if (bn->flags & BNGE_FLAG_PORT_STATS) {
+ u64 *rx = bn->port_stats.sw_stats;
+ u64 *tx = bn->port_stats.sw_stats +
+ BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+
+ stats->rx_crc_errors =
+ BNGE_GET_RX_PORT_STATS64(rx, rx_fcs_err_frames);
+ stats->rx_frame_errors =
+ BNGE_GET_RX_PORT_STATS64(rx, rx_align_err_frames);
+ stats->rx_length_errors =
+ BNGE_GET_RX_PORT_STATS64(rx, rx_undrsz_frames) +
+ BNGE_GET_RX_PORT_STATS64(rx, rx_ovrsz_frames) +
+ BNGE_GET_RX_PORT_STATS64(rx, rx_runt_frames);
+ stats->rx_errors =
+ BNGE_GET_RX_PORT_STATS64(rx, rx_false_carrier_frames) +
+ BNGE_GET_RX_PORT_STATS64(rx, rx_jbr_frames);
+ stats->collisions =
+ BNGE_GET_TX_PORT_STATS64(tx, tx_total_collisions);
+ stats->tx_fifo_errors =
+ BNGE_GET_TX_PORT_STATS64(tx, tx_fifo_underruns);
+ stats->tx_errors = BNGE_GET_TX_PORT_STATS64(tx, tx_err);
+ }
+ clear_bit(BNGE_STATE_READ_STATS, &bn->state);
+}
+
static void bnge_close_core(struct bnge_net *bn)
{
struct bnge_dev *bd = bn->bd;
@@ -2949,6 +3050,13 @@ static void bnge_close_core(struct bnge_net *bn)
timer_delete_sync(&bn->timer);
bnge_shutdown_nic(bn);
bnge_disable_napi(bn);
+
+ /* Save ring stats before shutdown */
+ if (bn->bnapi) {
+ bnge_get_ring_stats(bd, &bn->net_stats_prev);
+ bnge_get_ring_err_stats(bn, &bn->ring_err_stats_prev);
+ }
+
bnge_free_all_rings_bufs(bn);
bnge_free_irq(bn);
bnge_del_napi(bn);
@@ -2998,6 +3106,7 @@ static const struct net_device_ops bnge_netdev_ops = {
.ndo_open = bnge_open,
.ndo_stop = bnge_close,
.ndo_start_xmit = bnge_start_xmit,
+ .ndo_get_stats64 = bnge_get_stats64,
.ndo_features_check = bnge_features_check,
};
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h
index f28edf94257..775438bccd0 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.h
@@ -291,6 +291,7 @@ struct bnge_sw_stats {
enum bnge_net_state {
BNGE_STATE_NAPI_DISABLED,
BNGE_STATE_IN_SP_TASK,
+ BNGE_STATE_READ_STATS,
};
#define BNGE_TIMER_INTERVAL HZ
@@ -382,6 +383,7 @@ struct bnge_net {
u64 flags;
+ struct rtnl_link_stats64 net_stats_prev;
struct bnge_total_ring_err_stats ring_err_stats_prev;
struct bnge_stats_mem port_stats;
--
2.47.3
On Thu, 26 Feb 2026 23:51:33 +0530 Bhargava Marreddy wrote:
> + set_bit(BNGE_STATE_READ_STATS, &bn->state);
> + /* Make sure bnge_close_core() sees that we are reading stats before
> + * we check the BNGE_STATE_OPEN flag.
> + */
> + smp_mb__after_atomic();
> + if (!test_bit(BNGE_STATE_OPEN, &bd->state)) {
> + clear_bit(BNGE_STATE_READ_STATS, &bn->state);
> + *stats = bn->net_stats_prev;
> + return;
> + }
There can be multiple concurrent readers. Two enter, first one gets
stalled second one exits clearing the bit, close frees the memory,
second reader proceeds to hit the NULL pointers.
Again, please don't try to invent synchronization primitives.
You can probably use RCU here.
--
pw-bot: cr
On Fri, Feb 27, 2026 at 6:52 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Thu, 26 Feb 2026 23:51:33 +0530 Bhargava Marreddy wrote:
> > + set_bit(BNGE_STATE_READ_STATS, &bn->state);
> > + /* Make sure bnge_close_core() sees that we are reading stats before
> > + * we check the BNGE_STATE_OPEN flag.
> > + */
> > + smp_mb__after_atomic();
> > + if (!test_bit(BNGE_STATE_OPEN, &bd->state)) {
> > + clear_bit(BNGE_STATE_READ_STATS, &bn->state);
> > + *stats = bn->net_stats_prev;
> > + return;
> > + }
>
> There can be multiple concurrent readers. Two enter, first one gets
> stalled second one exits clearing the bit, close frees the memory,
> second reader proceeds to hit the NULL pointers.
>
> Again, please don't try to invent synchronization primitives.
> You can probably use RCU here.
Thanks Jakub.
RCU is a good idea to handle the race.
I will switch to it in the next spin and drop the BNGE_STATE_READ_STATS bit.
Thanks,
Bhargava Marreddy
> --
> pw-bot: cr
© 2016 - 2026 Red Hat, Inc.