]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bng_en: add HW stats infra and structured ethtool ops
authorBhargava Marreddy <bhargava.marreddy@broadcom.com>
Mon, 6 Apr 2026 18:04:16 +0000 (23:34 +0530)
committerJakub Kicinski <kuba@kernel.org>
Sun, 12 Apr 2026 18:09:37 +0000 (11:09 -0700)
Implement the hardware-level statistics foundation and modern structured
ethtool operations.

1. Infrastructure: Add HWRM firmware wrappers (FUNC_QSTATS_EXT,
   PORT_QSTATS_EXT, and PORT_QSTATS) to query ring and port counters.
2. Structured ops: Implement .get_eth_phy_stats, .get_eth_mac_stats,
   .get_eth_ctrl_stats, .get_pause_stats, and .get_rmon_stats.

Stats are initially reported as 0; accumulation logic is added
in a subsequent patch.

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

index 424bf2d6721ce45e53226930c071bab6231f6908..87226d5bf15cc204826a64891d74d145e78c7286 100644 (file)
@@ -46,6 +46,161 @@ static void bnge_get_drvinfo(struct net_device *dev,
        strscpy(info->bus_info, pci_name(bd->pdev), sizeof(info->bus_info));
 }
 
+static void bnge_get_eth_phy_stats(struct net_device *dev,
+                                  struct ethtool_eth_phy_stats *phy_stats)
+{
+       struct bnge_net *bn = netdev_priv(dev);
+       u64 *rx;
+
+       if (!(bn->flags & BNGE_FLAG_PORT_STATS_EXT))
+               return;
+
+       rx = bn->rx_port_stats_ext.sw_stats;
+       phy_stats->SymbolErrorDuringCarrier =
+               *(rx + BNGE_RX_STATS_EXT_OFFSET(rx_pcs_symbol_err));
+}
+
+static void bnge_get_eth_mac_stats(struct net_device *dev,
+                                  struct ethtool_eth_mac_stats *mac_stats)
+{
+       struct bnge_net *bn = netdev_priv(dev);
+       u64 *rx, *tx;
+
+       if (!(bn->flags & BNGE_FLAG_PORT_STATS))
+               return;
+
+       rx = bn->port_stats.sw_stats;
+       tx = bn->port_stats.sw_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+
+       mac_stats->FramesReceivedOK =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_good_frames);
+       mac_stats->FramesTransmittedOK =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_good_frames);
+       mac_stats->FrameCheckSequenceErrors =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_fcs_err_frames);
+       mac_stats->AlignmentErrors =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_align_err_frames);
+       mac_stats->OutOfRangeLengthField =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_oor_len_frames);
+       mac_stats->OctetsReceivedOK = BNGE_GET_RX_PORT_STATS64(rx, rx_bytes);
+       mac_stats->OctetsTransmittedOK = BNGE_GET_TX_PORT_STATS64(tx, tx_bytes);
+       mac_stats->MulticastFramesReceivedOK =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_mcast_frames);
+       mac_stats->BroadcastFramesReceivedOK =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_bcast_frames);
+       mac_stats->MulticastFramesXmittedOK =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_mcast_frames);
+       mac_stats->BroadcastFramesXmittedOK =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_bcast_frames);
+       mac_stats->FrameTooLongErrors =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_ovrsz_frames);
+}
+
+static void bnge_get_eth_ctrl_stats(struct net_device *dev,
+                                   struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+       struct bnge_net *bn = netdev_priv(dev);
+       u64 *rx;
+
+       if (!(bn->flags & BNGE_FLAG_PORT_STATS))
+               return;
+
+       rx = bn->port_stats.sw_stats;
+       ctrl_stats->MACControlFramesReceived =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_ctrl_frames);
+}
+
+static void bnge_get_pause_stats(struct net_device *dev,
+                                struct ethtool_pause_stats *pause_stats)
+{
+       struct bnge_net *bn = netdev_priv(dev);
+       u64 *rx, *tx;
+
+       if (!(bn->flags & BNGE_FLAG_PORT_STATS))
+               return;
+
+       rx = bn->port_stats.sw_stats;
+       tx = bn->port_stats.sw_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+
+       pause_stats->rx_pause_frames =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_pause_frames);
+       pause_stats->tx_pause_frames =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_pause_frames);
+}
+
+static const struct ethtool_rmon_hist_range bnge_rmon_ranges[] = {
+       {    0,    64 },
+       {   65,   127 },
+       {  128,   255 },
+       {  256,   511 },
+       {  512,  1023 },
+       { 1024,  1518 },
+       { 1519,  2047 },
+       { 2048,  4095 },
+       { 4096,  9216 },
+       { 9217, 16383 },
+       {}
+};
+
+static void bnge_get_rmon_stats(struct net_device *dev,
+                               struct ethtool_rmon_stats *rmon_stats,
+                               const struct ethtool_rmon_hist_range **ranges)
+{
+       struct bnge_net *bn = netdev_priv(dev);
+       u64 *rx, *tx;
+
+       if (!(bn->flags & BNGE_FLAG_PORT_STATS))
+               return;
+
+       rx = bn->port_stats.sw_stats;
+       tx = bn->port_stats.sw_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+
+       rmon_stats->jabbers = BNGE_GET_RX_PORT_STATS64(rx, rx_jbr_frames);
+       rmon_stats->oversize_pkts =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_ovrsz_frames);
+       rmon_stats->undersize_pkts =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_undrsz_frames);
+
+       rmon_stats->hist[0] = BNGE_GET_RX_PORT_STATS64(rx, rx_64b_frames);
+       rmon_stats->hist[1] = BNGE_GET_RX_PORT_STATS64(rx, rx_65b_127b_frames);
+       rmon_stats->hist[2] = BNGE_GET_RX_PORT_STATS64(rx, rx_128b_255b_frames);
+       rmon_stats->hist[3] = BNGE_GET_RX_PORT_STATS64(rx, rx_256b_511b_frames);
+       rmon_stats->hist[4] =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_512b_1023b_frames);
+       rmon_stats->hist[5] =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_1024b_1518b_frames);
+       rmon_stats->hist[6] =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_1519b_2047b_frames);
+       rmon_stats->hist[7] =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_2048b_4095b_frames);
+       rmon_stats->hist[8] =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_4096b_9216b_frames);
+       rmon_stats->hist[9] =
+               BNGE_GET_RX_PORT_STATS64(rx, rx_9217b_16383b_frames);
+
+       rmon_stats->hist_tx[0] = BNGE_GET_TX_PORT_STATS64(tx, tx_64b_frames);
+       rmon_stats->hist_tx[1] =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_65b_127b_frames);
+       rmon_stats->hist_tx[2] =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_128b_255b_frames);
+       rmon_stats->hist_tx[3] =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_256b_511b_frames);
+       rmon_stats->hist_tx[4] =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_512b_1023b_frames);
+       rmon_stats->hist_tx[5] =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_1024b_1518b_frames);
+       rmon_stats->hist_tx[6] =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_1519b_2047b_frames);
+       rmon_stats->hist_tx[7] =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_2048b_4095b_frames);
+       rmon_stats->hist_tx[8] =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_4096b_9216b_frames);
+       rmon_stats->hist_tx[9] =
+               BNGE_GET_TX_PORT_STATS64(tx, tx_9217b_16383b_frames);
+
+       *ranges = bnge_rmon_ranges;
+}
+
 static void bnge_get_pauseparam(struct net_device *dev,
                                struct ethtool_pauseparam *epause)
 {
@@ -116,6 +271,11 @@ static const struct ethtool_ops bnge_ethtool_ops = {
        .nway_reset             = bnge_nway_reset,
        .get_pauseparam         = bnge_get_pauseparam,
        .set_pauseparam         = bnge_set_pauseparam,
+       .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,
+       .get_pause_stats        = bnge_get_pause_stats,
+       .get_rmon_stats         = bnge_get_rmon_stats,
 };
 
 void bnge_set_ethtool_ops(struct net_device *dev)
index 83c0b16c3e81f80ca15a567e2fab7756de118b98..eb11800f5573f723f34fa5961ea1b40e5325e89f 100644 (file)
@@ -14,6 +14,7 @@
 #include "bnge_hwrm_lib.h"
 #include "bnge_rmem.h"
 #include "bnge_resc.h"
+#include "bnge_netdev.h"
 
 static const u16 bnge_async_events_arr[] = {
        ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE,
@@ -594,7 +595,7 @@ int bnge_hwrm_func_qcaps(struct bnge_dev *bd)
        struct hwrm_func_qcaps_output *resp;
        struct hwrm_func_qcaps_input *req;
        struct bnge_pf_info *pf = &bd->pf;
-       u32 flags;
+       u32 flags, flags_ext;
        int rc;
 
        rc = bnge_hwrm_req_init(bd, req, HWRM_FUNC_QCAPS);
@@ -612,6 +613,12 @@ int bnge_hwrm_func_qcaps(struct bnge_dev *bd)
                bd->flags |= BNGE_EN_ROCE_V1;
        if (flags & FUNC_QCAPS_RESP_FLAGS_ROCE_V2_SUPPORTED)
                bd->flags |= BNGE_EN_ROCE_V2;
+       if (flags & FUNC_QCAPS_RESP_FLAGS_EXT_STATS_SUPPORTED)
+               bd->fw_cap |= BNGE_FW_CAP_EXT_STATS_SUPPORTED;
+
+       flags_ext = le32_to_cpu(resp->flags_ext);
+       if (flags_ext & FUNC_QCAPS_RESP_FLAGS_EXT_EXT_HW_STATS_SUPPORTED)
+               bd->fw_cap |= BNGE_FW_CAP_EXT_HW_STATS_SUPPORTED;
 
        pf->fw_fid = le16_to_cpu(resp->fid);
        pf->port_id = le16_to_cpu(resp->port_id);
@@ -1479,3 +1486,145 @@ int bnge_hwrm_vnic_set_tpa(struct bnge_dev *bd, struct bnge_vnic_info *vnic,
 
        return bnge_hwrm_req_send(bd, req);
 }
+
+int bnge_hwrm_func_qstat_ext(struct bnge_dev *bd, struct bnge_stats_mem *stats)
+{
+       struct hwrm_func_qstats_ext_output *resp;
+       struct hwrm_func_qstats_ext_input *req;
+       __le64 *hw_masks;
+       int rc;
+
+       if (!(bd->fw_cap & BNGE_FW_CAP_EXT_HW_STATS_SUPPORTED))
+               return -EOPNOTSUPP;
+
+       rc = bnge_hwrm_req_init(bd, req, HWRM_FUNC_QSTATS_EXT);
+       if (rc)
+               return rc;
+
+       req->fid = cpu_to_le16(0xffff);
+       req->flags = FUNC_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK;
+
+       resp = bnge_hwrm_req_hold(bd, req);
+       rc = bnge_hwrm_req_send(bd, req);
+       if (!rc) {
+               hw_masks = &resp->rx_ucast_pkts;
+               bnge_copy_hw_masks(stats->hw_masks, hw_masks, stats->len / 8);
+       }
+       bnge_hwrm_req_drop(bd, req);
+       return rc;
+}
+
+int bnge_hwrm_port_qstats_ext(struct bnge_dev *bd, u8 flags)
+{
+       struct hwrm_queue_pri2cos_qcfg_output *resp_qc;
+       struct bnge_net *bn = netdev_priv(bd->netdev);
+       struct hwrm_queue_pri2cos_qcfg_input *req_qc;
+       struct hwrm_port_qstats_ext_output *resp_qs;
+       struct hwrm_port_qstats_ext_input *req_qs;
+       struct bnge_pf_info *pf = &bd->pf;
+       u32 tx_stat_size;
+       int rc;
+
+       if (!(bn->flags & BNGE_FLAG_PORT_STATS_EXT))
+               return 0;
+
+       if (flags && !(bd->fw_cap & BNGE_FW_CAP_EXT_HW_STATS_SUPPORTED))
+               return -EOPNOTSUPP;
+
+       rc = bnge_hwrm_req_init(bd, req_qs, HWRM_PORT_QSTATS_EXT);
+       if (rc)
+               return rc;
+
+       req_qs->flags = flags;
+       req_qs->port_id = cpu_to_le16(pf->port_id);
+       req_qs->rx_stat_size = cpu_to_le16(sizeof(struct rx_port_stats_ext));
+       req_qs->rx_stat_host_addr =
+               cpu_to_le64(bn->rx_port_stats_ext.hw_stats_map);
+       tx_stat_size = bn->tx_port_stats_ext.hw_stats ?
+                      sizeof(struct tx_port_stats_ext) : 0;
+       req_qs->tx_stat_size = cpu_to_le16(tx_stat_size);
+       req_qs->tx_stat_host_addr =
+               cpu_to_le64(bn->tx_port_stats_ext.hw_stats_map);
+       resp_qs = bnge_hwrm_req_hold(bd, req_qs);
+       rc = bnge_hwrm_req_send(bd, req_qs);
+       if (!rc) {
+               bn->fw_rx_stats_ext_size =
+                       le16_to_cpu(resp_qs->rx_stat_size) / 8;
+               bn->fw_tx_stats_ext_size = tx_stat_size ?
+                       le16_to_cpu(resp_qs->tx_stat_size) / 8 : 0;
+       } else {
+               bn->fw_rx_stats_ext_size = 0;
+               bn->fw_tx_stats_ext_size = 0;
+       }
+       bnge_hwrm_req_drop(bd, req_qs);
+
+       if (flags)
+               return rc;
+
+       if (bn->fw_tx_stats_ext_size <=
+           offsetof(struct tx_port_stats_ext, pfc_pri0_tx_duration_us) / 8) {
+               bn->pri2cos_valid = false;
+               return rc;
+       }
+
+       rc = bnge_hwrm_req_init(bd, req_qc, HWRM_QUEUE_PRI2COS_QCFG);
+       if (rc)
+               return rc;
+
+       req_qc->flags = cpu_to_le32(QUEUE_PRI2COS_QCFG_REQ_FLAGS_IVLAN);
+
+       resp_qc = bnge_hwrm_req_hold(bd, req_qc);
+       rc = bnge_hwrm_req_send(bd, req_qc);
+       if (!rc) {
+               u8 *pri2cos;
+               int i, j;
+
+               pri2cos = &resp_qc->pri0_cos_queue_id;
+               for (i = 0; i < 8; i++) {
+                       u8 queue_id = pri2cos[i];
+                       u8 queue_idx;
+
+                       /* Per port queue IDs start from 0, 10, 20, etc */
+                       queue_idx = queue_id % 10;
+                       if (queue_idx >= BNGE_MAX_QUEUE) {
+                               bn->pri2cos_valid = false;
+                               rc = -EINVAL;
+                               goto drop_req;
+                       }
+                       for (j = 0; j < bd->max_q; j++) {
+                               if (bd->q_ids[j] == queue_id)
+                                       bn->pri2cos_idx[i] = queue_idx;
+                       }
+               }
+               bn->pri2cos_valid = true;
+       }
+drop_req:
+       bnge_hwrm_req_drop(bd, req_qc);
+       return rc;
+}
+
+int bnge_hwrm_port_qstats(struct bnge_dev *bd, u8 flags)
+{
+       struct bnge_net *bn = netdev_priv(bd->netdev);
+       struct hwrm_port_qstats_input *req;
+       struct bnge_pf_info *pf = &bd->pf;
+       int rc;
+
+       if (!(bn->flags & BNGE_FLAG_PORT_STATS))
+               return 0;
+
+       if (flags && !(bd->fw_cap & BNGE_FW_CAP_EXT_HW_STATS_SUPPORTED))
+               return -EOPNOTSUPP;
+
+       rc = bnge_hwrm_req_init(bd, req, HWRM_PORT_QSTATS);
+       if (rc)
+               return rc;
+
+       req->flags = flags;
+       req->port_id = cpu_to_le16(pf->port_id);
+       req->tx_stat_host_addr = cpu_to_le64(bn->port_stats.hw_stats_map +
+                                            BNGE_TX_PORT_STATS_BYTE_OFFSET);
+       req->rx_stat_host_addr = cpu_to_le64(bn->port_stats.hw_stats_map);
+
+       return bnge_hwrm_req_send(bd, req);
+}
index 86ca3ac2244b7787dc2b88002e27d7e209b03f85..3501de7a89b9193aa2de2cb198580224f9bb87bb 100644 (file)
@@ -62,4 +62,7 @@ int bnge_hwrm_phy_qcaps(struct bnge_dev *bd);
 int bnge_hwrm_set_link_setting(struct bnge_net *bn, bool set_pause);
 int bnge_hwrm_set_pause(struct bnge_net *bn);
 int bnge_hwrm_shutdown_link(struct bnge_dev *bd);
+int bnge_hwrm_port_qstats(struct bnge_dev *bd, u8 flags);
+int bnge_hwrm_port_qstats_ext(struct bnge_dev *bd, u8 flags);
+int bnge_hwrm_func_qstat_ext(struct bnge_dev *bd, struct bnge_stats_mem *stats);
 #endif /* _BNGE_HWRM_LIB_H_ */
index 90458b323120966202bc5fd715bbe53f9ed2722a..290bf1cd96c9b7c8878686333b08bfc7a8cc0a05 100644 (file)
@@ -39,6 +39,10 @@ static void bnge_free_stats_mem(struct bnge_net *bn,
 {
        struct bnge_dev *bd = bn->bd;
 
+       kfree(stats->hw_masks);
+       stats->hw_masks = NULL;
+       kfree(stats->sw_stats);
+       stats->sw_stats = NULL;
        if (stats->hw_stats) {
                dma_free_coherent(bd->dev, stats->len, stats->hw_stats,
                                  stats->hw_stats_map);
@@ -47,7 +51,7 @@ static void bnge_free_stats_mem(struct bnge_net *bn,
 }
 
 static int bnge_alloc_stats_mem(struct bnge_net *bn,
-                               struct bnge_stats_mem *stats)
+                               struct bnge_stats_mem *stats, bool alloc_masks)
 {
        struct bnge_dev *bd = bn->bd;
 
@@ -56,7 +60,20 @@ static int bnge_alloc_stats_mem(struct bnge_net *bn,
        if (!stats->hw_stats)
                return -ENOMEM;
 
+       stats->sw_stats = kzalloc(stats->len, GFP_KERNEL);
+       if (!stats->sw_stats)
+               goto stats_mem_err;
+
+       if (alloc_masks) {
+               stats->hw_masks = kzalloc(stats->len, GFP_KERNEL);
+               if (!stats->hw_masks)
+                       goto stats_mem_err;
+       }
        return 0;
+
+stats_mem_err:
+       bnge_free_stats_mem(bn, stats);
+       return -ENOMEM;
 }
 
 static void bnge_free_ring_stats(struct bnge_net *bn)
@@ -75,6 +92,107 @@ static void bnge_free_ring_stats(struct bnge_net *bn)
        }
 }
 
+static void bnge_fill_masks(u64 *mask_arr, u64 mask, int count)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               mask_arr[i] = mask;
+}
+
+void bnge_copy_hw_masks(u64 *mask_arr, __le64 *hw_mask_arr, int count)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               mask_arr[i] = le64_to_cpu(hw_mask_arr[i]);
+}
+
+static void bnge_init_stats(struct bnge_net *bn)
+{
+       struct bnge_napi *bnapi = bn->bnapi[0];
+       struct bnge_nq_ring_info *nqr;
+       struct bnge_stats_mem *stats;
+       struct bnge_dev *bd = bn->bd;
+       __le64 *rx_stats, *tx_stats;
+       int rc, rx_count, tx_count;
+       u64 *rx_masks, *tx_masks;
+       u8 flags;
+
+       nqr = &bnapi->nq_ring;
+       stats = &nqr->stats;
+       rc = bnge_hwrm_func_qstat_ext(bd, stats);
+       if (rc) {
+               u64 mask = (1ULL << 48) - 1;
+
+               bnge_fill_masks(stats->hw_masks, mask, stats->len / 8);
+       }
+
+       if (bn->flags & BNGE_FLAG_PORT_STATS) {
+               stats = &bn->port_stats;
+               rx_stats = stats->hw_stats;
+               rx_masks = stats->hw_masks;
+               rx_count = sizeof(struct rx_port_stats) / 8;
+               tx_stats = rx_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+               tx_masks = rx_masks + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+               tx_count = sizeof(struct tx_port_stats) / 8;
+
+               flags = PORT_QSTATS_REQ_FLAGS_COUNTER_MASK;
+               rc = bnge_hwrm_port_qstats(bd, flags);
+               if (rc) {
+                       u64 mask = (1ULL << 40) - 1;
+
+                       bnge_fill_masks(rx_masks, mask, rx_count);
+                       bnge_fill_masks(tx_masks, mask, tx_count);
+               } else {
+                       bnge_copy_hw_masks(rx_masks, rx_stats, rx_count);
+                       bnge_copy_hw_masks(tx_masks, tx_stats, tx_count);
+               }
+               bnge_hwrm_port_qstats(bd, 0);
+       }
+
+       if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) {
+               stats = &bn->rx_port_stats_ext;
+               rx_stats = stats->hw_stats;
+               rx_masks = stats->hw_masks;
+               rx_count = sizeof(struct rx_port_stats_ext) / 8;
+               stats = &bn->tx_port_stats_ext;
+               tx_stats = stats->hw_stats;
+               tx_masks = stats->hw_masks;
+               tx_count = sizeof(struct tx_port_stats_ext) / 8;
+
+               flags = PORT_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK;
+               rc = bnge_hwrm_port_qstats_ext(bd, flags);
+               if (rc) {
+                       u64 mask = (1ULL << 40) - 1;
+
+                       bnge_fill_masks(rx_masks, mask, rx_count);
+                       if (tx_stats)
+                               bnge_fill_masks(tx_masks, mask, tx_count);
+               } else {
+                       bnge_copy_hw_masks(rx_masks, rx_stats, rx_count);
+                       if (tx_stats)
+                               bnge_copy_hw_masks(tx_masks, tx_stats,
+                                                  tx_count);
+               }
+               bnge_hwrm_port_qstats_ext(bd, 0);
+       }
+}
+
+static void bnge_free_port_ext_stats(struct bnge_net *bn)
+{
+       bn->flags &= ~BNGE_FLAG_PORT_STATS_EXT;
+       bnge_free_stats_mem(bn, &bn->rx_port_stats_ext);
+       bnge_free_stats_mem(bn, &bn->tx_port_stats_ext);
+}
+
+static void bnge_free_port_stats(struct bnge_net *bn)
+{
+       bn->flags &= ~BNGE_FLAG_PORT_STATS;
+       bnge_free_stats_mem(bn, &bn->port_stats);
+       bnge_free_port_ext_stats(bn);
+}
+
 static int bnge_alloc_ring_stats(struct bnge_net *bn)
 {
        struct bnge_dev *bd = bn->bd;
@@ -88,12 +206,13 @@ static int bnge_alloc_ring_stats(struct bnge_net *bn)
                struct bnge_nq_ring_info *nqr = &bnapi->nq_ring;
 
                nqr->stats.len = size;
-               rc = bnge_alloc_stats_mem(bn, &nqr->stats);
+               rc = bnge_alloc_stats_mem(bn, &nqr->stats, !i);
                if (rc)
                        goto err_free_ring_stats;
 
                nqr->hw_stats_ctx_id = INVALID_STATS_CTX_ID;
        }
+
        return 0;
 
 err_free_ring_stats:
@@ -101,6 +220,51 @@ err_free_ring_stats:
        return rc;
 }
 
+static void bnge_alloc_port_ext_stats(struct bnge_net *bn)
+{
+       struct bnge_dev *bd = bn->bd;
+       int rc;
+
+       if (!(bd->fw_cap & BNGE_FW_CAP_EXT_STATS_SUPPORTED))
+               return;
+
+       if (!bn->rx_port_stats_ext.hw_stats) {
+               bn->rx_port_stats_ext.len = sizeof(struct rx_port_stats_ext);
+               rc = bnge_alloc_stats_mem(bn, &bn->rx_port_stats_ext, true);
+               /* Extended stats are optional */
+               if (rc)
+                       return;
+       }
+
+       if (!bn->tx_port_stats_ext.hw_stats) {
+               bn->tx_port_stats_ext.len = sizeof(struct tx_port_stats_ext);
+               rc = bnge_alloc_stats_mem(bn, &bn->tx_port_stats_ext, true);
+               /* Extended stats are optional */
+               if (rc) {
+                       bnge_free_port_ext_stats(bn);
+                       return;
+               }
+       }
+       bn->flags |= BNGE_FLAG_PORT_STATS_EXT;
+}
+
+static int bnge_alloc_port_stats(struct bnge_net *bn)
+{
+       int rc;
+
+       if (!bn->port_stats.hw_stats) {
+               bn->port_stats.len = BNGE_PORT_STATS_SIZE;
+               rc = bnge_alloc_stats_mem(bn, &bn->port_stats, true);
+               if (rc)
+                       return rc;
+
+               bn->flags |= BNGE_FLAG_PORT_STATS;
+       }
+
+       bnge_alloc_port_ext_stats(bn);
+       return 0;
+}
+
 void __bnge_queue_sp_work(struct bnge_net *bn)
 {
        queue_work(bn->bnge_pf_wq, &bn->sp_task);
@@ -1028,6 +1192,8 @@ static int bnge_alloc_core(struct bnge_net *bn)
        if (rc)
                goto err_free_core;
 
+       bnge_init_stats(bn);
+
        rc = bnge_alloc_vnics(bn);
        if (rc)
                goto err_free_core;
@@ -2904,15 +3070,21 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)
        if (rc)
                goto err_free_workq;
 
+       rc = bnge_alloc_port_stats(bn);
+       if (rc)
+               goto err_free_workq;
+
        netdev->request_ops_lock = true;
        rc = register_netdev(netdev);
        if (rc) {
                dev_err(bd->dev, "Register netdev failed rc: %d\n", rc);
-               goto err_free_workq;
+               goto err_free_port_stats;
        }
 
        return 0;
 
+err_free_port_stats:
+       bnge_free_port_stats(bn);
 err_free_workq:
        destroy_workqueue(bn->bnge_pf_wq);
 err_netdev:
@@ -2934,6 +3106,8 @@ void bnge_netdev_free(struct bnge_dev *bd)
        bn->sp_event = 0;
        destroy_workqueue(bn->bnge_pf_wq);
 
+       bnge_free_port_stats(bn);
+
        free_netdev(netdev);
        bd->netdev = NULL;
 }
index 5636eb371e243fe23084bdbaef62ac70852e3186..15ededb801f69e70d773da44e80abe2b3b7eb643 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/bnge/hsi.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/refcount.h>
+#include <linux/u64_stats_sync.h>
 #include "bnge_db.h"
 #include "bnge_hw_def.h"
 #include "bnge_link.h"
@@ -224,6 +225,42 @@ struct bnge_tpa_info {
 #define BNGE_NQ_HDL_IDX(hdl)   ((hdl) & BNGE_NQ_HDL_IDX_MASK)
 #define BNGE_NQ_HDL_TYPE(hdl)  (((hdl) & BNGE_NQ_HDL_TYPE_MASK) >>     \
                                 BNGE_NQ_HDL_TYPE_SHIFT)
+#define BNGE_GET_RING_STATS64(sw, counter)             \
+       (*((sw) + offsetof(struct ctx_hw_stats, counter) / 8))
+
+#define BNGE_GET_RX_PORT_STATS64(sw, counter)          \
+       (*((sw) + offsetof(struct rx_port_stats, counter) / 8))
+
+#define BNGE_GET_TX_PORT_STATS64(sw, counter)          \
+       (*((sw) + offsetof(struct tx_port_stats, counter) / 8))
+
+#define BNGE_PORT_STATS_SIZE                           \
+       (sizeof(struct rx_port_stats) + sizeof(struct tx_port_stats) + 1024)
+
+#define BNGE_TX_PORT_STATS_BYTE_OFFSET                 \
+       (sizeof(struct rx_port_stats) + 512)
+
+#define BNGE_RX_STATS_OFFSET(counter)                  \
+       (offsetof(struct rx_port_stats, counter) / 8)
+
+#define BNGE_TX_STATS_OFFSET(counter)                  \
+       ((offsetof(struct tx_port_stats, counter) +     \
+         BNGE_TX_PORT_STATS_BYTE_OFFSET) / 8)
+
+#define BNGE_RX_STATS_EXT_OFFSET(counter)              \
+       (offsetof(struct rx_port_stats_ext, counter) / 8)
+
+#define BNGE_TX_STATS_EXT_OFFSET(counter)              \
+       (offsetof(struct tx_port_stats_ext, counter) / 8)
+
+struct bnge_stats_mem {
+       u64             *sw_stats;
+       u64             *hw_masks;
+       void            *hw_stats;
+       dma_addr_t      hw_stats_map;
+       u32             len;
+       struct u64_stats_sync   syncp;
+};
 
 enum bnge_net_state {
        BNGE_STATE_NAPI_DISABLED,
@@ -231,6 +268,11 @@ enum bnge_net_state {
 
 #define BNGE_TIMER_INTERVAL    HZ
 
+enum bnge_net_flag {
+       BNGE_FLAG_PORT_STATS            = BIT(0),
+       BNGE_FLAG_PORT_STATS_EXT        = BIT(1),
+};
+
 enum bnge_sp_event {
        BNGE_LINK_CHNG_SP_EVENT,
        BNGE_LINK_SPEED_CHNG_SP_EVENT,
@@ -309,6 +351,17 @@ struct bnge_net {
        unsigned long           sp_event;
 
        struct bnge_ethtool_link_info   eth_link_info;
+
+       u64                     flags;
+
+       struct bnge_stats_mem   port_stats;
+       struct bnge_stats_mem   rx_port_stats_ext;
+       struct bnge_stats_mem   tx_port_stats_ext;
+       u16                     fw_rx_stats_ext_size;
+       u16                     fw_tx_stats_ext_size;
+
+       u8                      pri2cos_idx[8];
+       bool                    pri2cos_valid;
 };
 
 #define BNGE_DEFAULT_RX_RING_SIZE      511
@@ -374,14 +427,6 @@ void bnge_set_ring_params(struct bnge_dev *bd);
        bnge_writeq(bd, (db)->db_key64 | DBR_TYPE_NQ_ARM |      \
                    DB_RING_IDX(db, idx), (db)->doorbell)
 
-struct bnge_stats_mem {
-       u64             *sw_stats;
-       u64             *hw_masks;
-       void            *hw_stats;
-       dma_addr_t      hw_stats_map;
-       int             len;
-};
-
 struct nqe_cn {
        __le16  type;
        #define NQ_CN_TYPE_MASK                 0x3fUL
@@ -588,4 +633,5 @@ u8 *__bnge_alloc_rx_frag(struct bnge_net *bn, dma_addr_t *mapping,
 int bnge_alloc_rx_netmem(struct bnge_net *bn, struct bnge_rx_ring_info *rxr,
                         u16 prod, gfp_t gfp);
 void __bnge_queue_sp_work(struct bnge_net *bn);
+void bnge_copy_hw_masks(u64 *mask_arr, __le64 *hw_mask_arr, int count);
 #endif /* _BNGE_NETDEV_H_ */