]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bng_en: add support for ethtool -S stats display
authorBhargava Marreddy <bhargava.marreddy@broadcom.com>
Mon, 6 Apr 2026 18:04:20 +0000 (23:34 +0530)
committerJakub Kicinski <kuba@kernel.org>
Sun, 12 Apr 2026 18:09:37 +0000 (11:09 -0700)
Implement the legacy ethtool statistics interface (get_sset_count,
get_strings, get_ethtool_stats) to expose hardware counters not
available through standard kernel stats APIs.

Ex:
a) Per-queue ring stats
     rxq0_ucast_packets: 2
     rxq0_mcast_packets: 0
     rxq0_bcast_packets: 15
     rxq0_ucast_bytes: 120
     rxq0_mcast_bytes: 0
     rxq0_bcast_bytes: 900
     txq0_ucast_packets: 0
     txq0_mcast_packets: 0
     txq0_bcast_packets: 0
     txq0_ucast_bytes: 0
     txq0_mcast_bytes: 0
     txq0_bcast_bytes: 0

b) Per-queue TPA(LRO/GRO) stats
     rxq4_tpa_eligible_pkt: 0
     rxq4_tpa_eligible_bytes: 0
     rxq4_tpa_pkt: 0
     rxq4_tpa_bytes: 0
     rxq4_tpa_errors: 0
     rxq4_tpa_events: 0

c) Port level stats
     rxp_good_vlan_frames: 0
     rxp_mtu_err_frames: 0
     rxp_tagged_frames: 0
     rxp_double_tagged_frames: 0
     rxp_pfc_ena_frames_pri0: 0
     rxp_pfc_ena_frames_pri1: 0
     rxp_pfc_ena_frames_pri2: 0
     rxp_pfc_ena_frames_pri3: 0
     rxp_pfc_ena_frames_pri4: 0
     rxp_pfc_ena_frames_pri5: 0
     rxp_pfc_ena_frames_pri6: 0
     rxp_pfc_ena_frames_pri7: 0
     rxp_eee_lpi_events: 0
     rxp_eee_lpi_duration: 0
     rxp_runt_bytes: 0
     rxp_runt_frames: 0
     txp_good_vlan_frames: 0
     txp_jabber_frames: 0
     txp_fcs_err_frames: 0
     txp_pfc_ena_frames_pri0: 0
     txp_pfc_ena_frames_pri1: 0
     txp_pfc_ena_frames_pri2: 0
     txp_pfc_ena_frames_pri3: 0
     txp_pfc_ena_frames_pri4: 0
     txp_pfc_ena_frames_pri5: 0
     txp_pfc_ena_frames_pri6: 0
     txp_pfc_ena_frames_pri7: 0
     txp_eee_lpi_events: 0
     txp_eee_lpi_duration: 0
     txp_xthol_frames: 0

d) Per-priority stats
     rx_bytes_pri0: 4182650
     rx_bytes_pri1: 4182650
     rx_bytes_pri2: 4182650
     rx_bytes_pri3: 4182650
     rx_bytes_pri4: 4182650
     rx_bytes_pri5: 4182650
     rx_bytes_pri6: 4182650
     rx_bytes_pri7: 4182650

Signed-off-by: Bhargava Marreddy <bhargava.marreddy@broadcom.com>
Reviewed-by: Vikas Gupta <vikas.gupta@broadcom.com>
Link: https://patch.msgid.link/20260406180420.279470-11-bhargava.marreddy@broadcom.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/broadcom/bnge/bnge_ethtool.c

index 87226d5bf15cc204826a64891d74d145e78c7286..2467e44de291a1eb3f8ca2665cda63b0e3a2a686 100644 (file)
@@ -35,6 +35,271 @@ static int bnge_nway_reset(struct net_device *dev)
        return rc;
 }
 
+static const char * const bnge_ring_q_stats_str[] = {
+       "ucast_packets",
+       "mcast_packets",
+       "bcast_packets",
+       "ucast_bytes",
+       "mcast_bytes",
+       "bcast_bytes",
+};
+
+static const char * const bnge_ring_tpa2_stats_str[] = {
+       "tpa_eligible_pkt",
+       "tpa_eligible_bytes",
+       "tpa_pkt",
+       "tpa_bytes",
+       "tpa_errors",
+       "tpa_events",
+};
+
+#define BNGE_RX_PORT_STATS_ENTRY(suffix)       \
+       { BNGE_RX_STATS_OFFSET(rx_##suffix), "rxp_" __stringify(suffix) }
+
+#define BNGE_TX_PORT_STATS_ENTRY(suffix)       \
+       { BNGE_TX_STATS_OFFSET(tx_##suffix), "txp_" __stringify(suffix) }
+
+#define BNGE_RX_STATS_EXT_ENTRY(counter)       \
+       { BNGE_RX_STATS_EXT_OFFSET(counter), __stringify(counter) }
+
+#define BNGE_TX_STATS_EXT_ENTRY(counter)       \
+       { BNGE_TX_STATS_EXT_OFFSET(counter), __stringify(counter) }
+
+#define BNGE_RX_STATS_EXT_PFC_ENTRY(n)                         \
+       BNGE_RX_STATS_EXT_ENTRY(pfc_pri##n##_rx_duration_us),   \
+       BNGE_RX_STATS_EXT_ENTRY(pfc_pri##n##_rx_transitions)
+
+#define BNGE_TX_STATS_EXT_PFC_ENTRY(n)                         \
+       BNGE_TX_STATS_EXT_ENTRY(pfc_pri##n##_tx_duration_us),   \
+       BNGE_TX_STATS_EXT_ENTRY(pfc_pri##n##_tx_transitions)
+
+#define BNGE_RX_STATS_EXT_PFC_ENTRIES                          \
+       BNGE_RX_STATS_EXT_PFC_ENTRY(0),                         \
+       BNGE_RX_STATS_EXT_PFC_ENTRY(1),                         \
+       BNGE_RX_STATS_EXT_PFC_ENTRY(2),                         \
+       BNGE_RX_STATS_EXT_PFC_ENTRY(3),                         \
+       BNGE_RX_STATS_EXT_PFC_ENTRY(4),                         \
+       BNGE_RX_STATS_EXT_PFC_ENTRY(5),                         \
+       BNGE_RX_STATS_EXT_PFC_ENTRY(6),                         \
+       BNGE_RX_STATS_EXT_PFC_ENTRY(7)
+
+#define BNGE_TX_STATS_EXT_PFC_ENTRIES                          \
+       BNGE_TX_STATS_EXT_PFC_ENTRY(0),                         \
+       BNGE_TX_STATS_EXT_PFC_ENTRY(1),                         \
+       BNGE_TX_STATS_EXT_PFC_ENTRY(2),                         \
+       BNGE_TX_STATS_EXT_PFC_ENTRY(3),                         \
+       BNGE_TX_STATS_EXT_PFC_ENTRY(4),                         \
+       BNGE_TX_STATS_EXT_PFC_ENTRY(5),                         \
+       BNGE_TX_STATS_EXT_PFC_ENTRY(6),                         \
+       BNGE_TX_STATS_EXT_PFC_ENTRY(7)
+
+#define BNGE_RX_STATS_EXT_COS_ENTRY(n)                         \
+       BNGE_RX_STATS_EXT_ENTRY(rx_bytes_cos##n),               \
+       BNGE_RX_STATS_EXT_ENTRY(rx_packets_cos##n)
+
+#define BNGE_TX_STATS_EXT_COS_ENTRY(n)                         \
+       BNGE_TX_STATS_EXT_ENTRY(tx_bytes_cos##n),               \
+       BNGE_TX_STATS_EXT_ENTRY(tx_packets_cos##n)
+
+#define BNGE_RX_STATS_EXT_COS_ENTRIES                          \
+       BNGE_RX_STATS_EXT_COS_ENTRY(0),                         \
+       BNGE_RX_STATS_EXT_COS_ENTRY(1),                         \
+       BNGE_RX_STATS_EXT_COS_ENTRY(2),                         \
+       BNGE_RX_STATS_EXT_COS_ENTRY(3),                         \
+       BNGE_RX_STATS_EXT_COS_ENTRY(4),                         \
+       BNGE_RX_STATS_EXT_COS_ENTRY(5),                         \
+       BNGE_RX_STATS_EXT_COS_ENTRY(6),                         \
+       BNGE_RX_STATS_EXT_COS_ENTRY(7)                          \
+
+#define BNGE_TX_STATS_EXT_COS_ENTRIES                          \
+       BNGE_TX_STATS_EXT_COS_ENTRY(0),                         \
+       BNGE_TX_STATS_EXT_COS_ENTRY(1),                         \
+       BNGE_TX_STATS_EXT_COS_ENTRY(2),                         \
+       BNGE_TX_STATS_EXT_COS_ENTRY(3),                         \
+       BNGE_TX_STATS_EXT_COS_ENTRY(4),                         \
+       BNGE_TX_STATS_EXT_COS_ENTRY(5),                         \
+       BNGE_TX_STATS_EXT_COS_ENTRY(6),                         \
+       BNGE_TX_STATS_EXT_COS_ENTRY(7)                          \
+
+#define BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(n)                 \
+       BNGE_RX_STATS_EXT_ENTRY(rx_discard_bytes_cos##n),       \
+       BNGE_RX_STATS_EXT_ENTRY(rx_discard_packets_cos##n)
+
+#define BNGE_RX_STATS_EXT_DISCARD_COS_ENTRIES                          \
+       BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(0),                         \
+       BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(1),                         \
+       BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(2),                         \
+       BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(3),                         \
+       BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(4),                         \
+       BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(5),                         \
+       BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(6),                         \
+       BNGE_RX_STATS_EXT_DISCARD_COS_ENTRY(7)
+
+#define BNGE_RX_STATS_PRI_ENTRY(counter, n)            \
+       { BNGE_RX_STATS_EXT_OFFSET(counter##_cos0),     \
+         __stringify(counter##_pri##n) }
+
+#define BNGE_TX_STATS_PRI_ENTRY(counter, n)            \
+       { BNGE_TX_STATS_EXT_OFFSET(counter##_cos0),     \
+         __stringify(counter##_pri##n) }
+
+#define BNGE_RX_STATS_PRI_ENTRIES(counter)             \
+       BNGE_RX_STATS_PRI_ENTRY(counter, 0),            \
+       BNGE_RX_STATS_PRI_ENTRY(counter, 1),            \
+       BNGE_RX_STATS_PRI_ENTRY(counter, 2),            \
+       BNGE_RX_STATS_PRI_ENTRY(counter, 3),            \
+       BNGE_RX_STATS_PRI_ENTRY(counter, 4),            \
+       BNGE_RX_STATS_PRI_ENTRY(counter, 5),            \
+       BNGE_RX_STATS_PRI_ENTRY(counter, 6),            \
+       BNGE_RX_STATS_PRI_ENTRY(counter, 7)
+
+#define BNGE_TX_STATS_PRI_ENTRIES(counter)             \
+       BNGE_TX_STATS_PRI_ENTRY(counter, 0),            \
+       BNGE_TX_STATS_PRI_ENTRY(counter, 1),            \
+       BNGE_TX_STATS_PRI_ENTRY(counter, 2),            \
+       BNGE_TX_STATS_PRI_ENTRY(counter, 3),            \
+       BNGE_TX_STATS_PRI_ENTRY(counter, 4),            \
+       BNGE_TX_STATS_PRI_ENTRY(counter, 5),            \
+       BNGE_TX_STATS_PRI_ENTRY(counter, 6),            \
+       BNGE_TX_STATS_PRI_ENTRY(counter, 7)
+
+#define NUM_RING_Q_HW_STATS            ARRAY_SIZE(bnge_ring_q_stats_str)
+
+static const struct {
+       long offset;
+       char string[ETH_GSTRING_LEN];
+} bnge_tx_port_stats_ext_arr[] = {
+       BNGE_TX_STATS_EXT_COS_ENTRIES,
+       BNGE_TX_STATS_EXT_PFC_ENTRIES,
+};
+
+static const struct {
+       long base_off;
+       char string[ETH_GSTRING_LEN];
+} bnge_rx_bytes_pri_arr[] = {
+       BNGE_RX_STATS_PRI_ENTRIES(rx_bytes),
+};
+
+static const struct {
+       long base_off;
+       char string[ETH_GSTRING_LEN];
+} bnge_rx_pkts_pri_arr[] = {
+       BNGE_RX_STATS_PRI_ENTRIES(rx_packets),
+};
+
+static const struct {
+       long base_off;
+       char string[ETH_GSTRING_LEN];
+} bnge_tx_bytes_pri_arr[] = {
+       BNGE_TX_STATS_PRI_ENTRIES(tx_bytes),
+};
+
+static const struct {
+       long base_off;
+       char string[ETH_GSTRING_LEN];
+} bnge_tx_pkts_pri_arr[] = {
+       BNGE_TX_STATS_PRI_ENTRIES(tx_packets),
+};
+
+static const struct {
+       long offset;
+       char string[ETH_GSTRING_LEN];
+} bnge_port_stats_arr[] = {
+       BNGE_RX_PORT_STATS_ENTRY(good_vlan_frames),
+       BNGE_RX_PORT_STATS_ENTRY(mtu_err_frames),
+       BNGE_RX_PORT_STATS_ENTRY(tagged_frames),
+       BNGE_RX_PORT_STATS_ENTRY(double_tagged_frames),
+       BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri0),
+       BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri1),
+       BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri2),
+       BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri3),
+       BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri4),
+       BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri5),
+       BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri6),
+       BNGE_RX_PORT_STATS_ENTRY(pfc_ena_frames_pri7),
+       BNGE_RX_PORT_STATS_ENTRY(eee_lpi_events),
+       BNGE_RX_PORT_STATS_ENTRY(eee_lpi_duration),
+       BNGE_RX_PORT_STATS_ENTRY(runt_bytes),
+       BNGE_RX_PORT_STATS_ENTRY(runt_frames),
+
+       BNGE_TX_PORT_STATS_ENTRY(good_vlan_frames),
+       BNGE_TX_PORT_STATS_ENTRY(jabber_frames),
+       BNGE_TX_PORT_STATS_ENTRY(fcs_err_frames),
+       BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri0),
+       BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri1),
+       BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri2),
+       BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri3),
+       BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri4),
+       BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri5),
+       BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri6),
+       BNGE_TX_PORT_STATS_ENTRY(pfc_ena_frames_pri7),
+       BNGE_TX_PORT_STATS_ENTRY(eee_lpi_events),
+       BNGE_TX_PORT_STATS_ENTRY(eee_lpi_duration),
+       BNGE_TX_PORT_STATS_ENTRY(xthol_frames),
+};
+
+static const struct {
+       long offset;
+       char string[ETH_GSTRING_LEN];
+} bnge_port_stats_ext_arr[] = {
+       BNGE_RX_STATS_EXT_ENTRY(continuous_pause_events),
+       BNGE_RX_STATS_EXT_ENTRY(resume_pause_events),
+       BNGE_RX_STATS_EXT_ENTRY(continuous_roce_pause_events),
+       BNGE_RX_STATS_EXT_ENTRY(resume_roce_pause_events),
+       BNGE_RX_STATS_EXT_COS_ENTRIES,
+       BNGE_RX_STATS_EXT_PFC_ENTRIES,
+       BNGE_RX_STATS_EXT_ENTRY(rx_bits),
+       BNGE_RX_STATS_EXT_ENTRY(rx_buffer_passed_threshold),
+       BNGE_RX_STATS_EXT_DISCARD_COS_ENTRIES,
+       BNGE_RX_STATS_EXT_ENTRY(rx_filter_miss),
+};
+
+static int bnge_get_num_tpa_ring_stats(struct bnge_dev *bd)
+{
+       if (BNGE_SUPPORTS_TPA(bd))
+               return BNGE_NUM_TPA_RING_STATS;
+       return 0;
+}
+
+#define BNGE_NUM_PORT_STATS ARRAY_SIZE(bnge_port_stats_arr)
+#define BNGE_NUM_STATS_PRI                     \
+       (ARRAY_SIZE(bnge_rx_bytes_pri_arr) +    \
+        ARRAY_SIZE(bnge_rx_pkts_pri_arr) +     \
+        ARRAY_SIZE(bnge_tx_bytes_pri_arr) +    \
+        ARRAY_SIZE(bnge_tx_pkts_pri_arr))
+
+static int bnge_get_num_ring_stats(struct bnge_dev *bd)
+{
+       int rx, tx;
+
+       rx = NUM_RING_Q_HW_STATS + bnge_get_num_tpa_ring_stats(bd);
+       tx = NUM_RING_Q_HW_STATS;
+       return rx * bd->rx_nr_rings +
+              tx * bd->tx_nr_rings_per_tc;
+}
+
+static u32 bnge_get_num_stats(struct bnge_net *bn)
+{
+       u32 num_stats = bnge_get_num_ring_stats(bn->bd);
+       u32 len;
+
+       if (bn->flags & BNGE_FLAG_PORT_STATS)
+               num_stats += BNGE_NUM_PORT_STATS;
+
+       if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) {
+               len = min_t(u32, bn->fw_rx_stats_ext_size,
+                           ARRAY_SIZE(bnge_port_stats_ext_arr));
+               num_stats += len;
+               len = min_t(u32, bn->fw_tx_stats_ext_size,
+                           ARRAY_SIZE(bnge_tx_port_stats_ext_arr));
+               num_stats += len;
+               if (bn->pri2cos_valid)
+                       num_stats += BNGE_NUM_STATS_PRI;
+       }
+
+       return num_stats;
+}
+
 static void bnge_get_drvinfo(struct net_device *dev,
                             struct ethtool_drvinfo *info)
 {
@@ -46,6 +311,219 @@ static void bnge_get_drvinfo(struct net_device *dev,
        strscpy(info->bus_info, pci_name(bd->pdev), sizeof(info->bus_info));
 }
 
+static int bnge_get_sset_count(struct net_device *dev, int sset)
+{
+       struct bnge_net *bn = netdev_priv(dev);
+
+       switch (sset) {
+       case ETH_SS_STATS:
+               return bnge_get_num_stats(bn);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static bool is_rx_ring(struct bnge_dev *bd, u16 ring_num)
+{
+       return ring_num < bd->rx_nr_rings;
+}
+
+static bool is_tx_ring(struct bnge_dev *bd, u16 ring_num)
+{
+       u16 tx_base = 0;
+
+       if (!(bd->flags & BNGE_EN_SHARED_CHNL))
+               tx_base = bd->rx_nr_rings;
+
+       return ring_num >= tx_base && ring_num < (tx_base + bd->tx_nr_rings_per_tc);
+}
+
+static void bnge_get_ethtool_stats(struct net_device *dev,
+                                  struct ethtool_stats *stats, u64 *buf)
+{
+       struct bnge_net *bn = netdev_priv(dev);
+       struct bnge_dev *bd = bn->bd;
+       u32 tpa_stats;
+       u32 i, j = 0;
+
+       if (!bn->bnapi) {
+               j += bnge_get_num_ring_stats(bd);
+               goto skip_ring_stats;
+       }
+
+       tpa_stats = bnge_get_num_tpa_ring_stats(bd);
+       for (i = 0; i < bd->nq_nr_rings; i++) {
+               struct bnge_napi *bnapi = bn->bnapi[i];
+               struct bnge_nq_ring_info *nqr;
+               u64 *sw_stats;
+               int k;
+
+               nqr = &bnapi->nq_ring;
+               sw_stats = nqr->stats.sw_stats;
+
+               if (is_rx_ring(bd, i)) {
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_ucast_pkts);
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_mcast_pkts);
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_bcast_pkts);
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_ucast_bytes);
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_mcast_bytes);
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, rx_bcast_bytes);
+               }
+               if (is_tx_ring(bd, i)) {
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_ucast_pkts);
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_mcast_pkts);
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_bcast_pkts);
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_ucast_bytes);
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_mcast_bytes);
+                       buf[j++] = BNGE_GET_RING_STATS64(sw_stats, tx_bcast_bytes);
+               }
+               if (!tpa_stats || !is_rx_ring(bd, i))
+                       continue;
+
+               k = BNGE_NUM_RX_RING_STATS + BNGE_NUM_TX_RING_STATS;
+               for (; k < BNGE_NUM_RX_RING_STATS + BNGE_NUM_TX_RING_STATS +
+                          tpa_stats; j++, k++)
+                       buf[j] = sw_stats[k];
+       }
+
+skip_ring_stats:
+       if (bn->flags & BNGE_FLAG_PORT_STATS) {
+               u64 *port_stats = bn->port_stats.sw_stats;
+
+               for (i = 0; i < BNGE_NUM_PORT_STATS; i++, j++)
+                       buf[j] = *(port_stats + bnge_port_stats_arr[i].offset);
+       }
+       if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) {
+               u64 *rx_port_stats_ext = bn->rx_port_stats_ext.sw_stats;
+               u64 *tx_port_stats_ext = bn->tx_port_stats_ext.sw_stats;
+               u32 len;
+
+               len = min_t(u32, bn->fw_rx_stats_ext_size,
+                           ARRAY_SIZE(bnge_port_stats_ext_arr));
+               for (i = 0; i < len; i++, j++) {
+                       buf[j] = *(rx_port_stats_ext +
+                                  bnge_port_stats_ext_arr[i].offset);
+               }
+               len = min_t(u32, bn->fw_tx_stats_ext_size,
+                           ARRAY_SIZE(bnge_tx_port_stats_ext_arr));
+               for (i = 0; i < len; i++, j++) {
+                       buf[j] = *(tx_port_stats_ext +
+                                  bnge_tx_port_stats_ext_arr[i].offset);
+               }
+               if (bn->pri2cos_valid) {
+                       for (i = 0; i < 8; i++, j++) {
+                               long n = bnge_rx_bytes_pri_arr[i].base_off +
+                                        bn->pri2cos_idx[i];
+
+                               buf[j] = *(rx_port_stats_ext + n);
+                       }
+                       for (i = 0; i < 8; i++, j++) {
+                               long n = bnge_rx_pkts_pri_arr[i].base_off +
+                                        bn->pri2cos_idx[i];
+
+                               buf[j] = *(rx_port_stats_ext + n);
+                       }
+                       for (i = 0; i < 8; i++, j++) {
+                               long n = bnge_tx_bytes_pri_arr[i].base_off +
+                                        bn->pri2cos_idx[i];
+
+                               buf[j] = *(tx_port_stats_ext + n);
+                       }
+                       for (i = 0; i < 8; i++, j++) {
+                               long n = bnge_tx_pkts_pri_arr[i].base_off +
+                                        bn->pri2cos_idx[i];
+
+                               buf[j] = *(tx_port_stats_ext + n);
+                       }
+               }
+       }
+}
+
+static void bnge_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
+{
+       struct bnge_net *bn = netdev_priv(dev);
+       struct bnge_dev *bd = bn->bd;
+       u32 i, j, num_str;
+       const char *str;
+
+       switch (stringset) {
+       case ETH_SS_STATS:
+               for (i = 0; i < bd->nq_nr_rings; i++) {
+                       if (is_rx_ring(bd, i))
+                               for (j = 0; j < NUM_RING_Q_HW_STATS; j++) {
+                                       str = bnge_ring_q_stats_str[j];
+                                       ethtool_sprintf(&buf, "rxq%d_%s", i,
+                                                       str);
+                               }
+                       if (is_tx_ring(bd, i))
+                               for (j = 0; j < NUM_RING_Q_HW_STATS; j++) {
+                                       str = bnge_ring_q_stats_str[j];
+                                       ethtool_sprintf(&buf, "txq%d_%s", i,
+                                                       str);
+                               }
+                       num_str = bnge_get_num_tpa_ring_stats(bd);
+                       if (!num_str || !is_rx_ring(bd, i))
+                               continue;
+
+                       for (j = 0; j < num_str; j++) {
+                               str = bnge_ring_tpa2_stats_str[j];
+                               ethtool_sprintf(&buf, "rxq%d_%s", i, str);
+                       }
+               }
+
+               if (bn->flags & BNGE_FLAG_PORT_STATS)
+                       for (i = 0; i < BNGE_NUM_PORT_STATS; i++) {
+                               str = bnge_port_stats_arr[i].string;
+                               ethtool_puts(&buf, str);
+                       }
+
+               if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) {
+                       u32 len;
+
+                       len = min_t(u32, bn->fw_rx_stats_ext_size,
+                                   ARRAY_SIZE(bnge_port_stats_ext_arr));
+                       for (i = 0; i < len; i++) {
+                               str = bnge_port_stats_ext_arr[i].string;
+                               ethtool_puts(&buf, str);
+                       }
+
+                       len = min_t(u32, bn->fw_tx_stats_ext_size,
+                                   ARRAY_SIZE(bnge_tx_port_stats_ext_arr));
+                       for (i = 0; i < len; i++) {
+                               str = bnge_tx_port_stats_ext_arr[i].string;
+                               ethtool_puts(&buf, str);
+                       }
+
+                       if (bn->pri2cos_valid) {
+                               for (i = 0; i < 8; i++) {
+                                       str = bnge_rx_bytes_pri_arr[i].string;
+                                       ethtool_puts(&buf, str);
+                               }
+
+                               for (i = 0; i < 8; i++) {
+                                       str = bnge_rx_pkts_pri_arr[i].string;
+                                       ethtool_puts(&buf, str);
+                               }
+
+                               for (i = 0; i < 8; i++) {
+                                       str = bnge_tx_bytes_pri_arr[i].string;
+                                       ethtool_puts(&buf, str);
+                               }
+
+                               for (i = 0; i < 8; i++) {
+                                       str = bnge_tx_pkts_pri_arr[i].string;
+                                       ethtool_puts(&buf, str);
+                               }
+                       }
+               }
+               break;
+       default:
+               netdev_err(bd->netdev, "%s invalid request %x\n",
+                          __func__, stringset);
+               break;
+       }
+}
+
 static void bnge_get_eth_phy_stats(struct net_device *dev,
                                   struct ethtool_eth_phy_stats *phy_stats)
 {
@@ -271,6 +749,9 @@ static const struct ethtool_ops bnge_ethtool_ops = {
        .nway_reset             = bnge_nway_reset,
        .get_pauseparam         = bnge_get_pauseparam,
        .set_pauseparam         = bnge_set_pauseparam,
+       .get_sset_count         = bnge_get_sset_count,
+       .get_strings            = bnge_get_strings,
+       .get_ethtool_stats      = bnge_get_ethtool_stats,
        .get_eth_phy_stats      = bnge_get_eth_phy_stats,
        .get_eth_mac_stats      = bnge_get_eth_mac_stats,
        .get_eth_ctrl_stats     = bnge_get_eth_ctrl_stats,