Skip to contentSkip to conversation

eth: fbnic: Extend hw stats support

PATCH Applied Jakub Kicinski 3 participants 14 messages Aug 25, 2025 – Aug 28, 2025

Subsystems (3)
lore
Share & tools
lore

Jakub Kicinski

author maintainer
#0 lore raw

v2 cover eth: fbnic: Extend hw stats support

Mohsin says:

Extend hardware stats support for fbnic by adding the ability to reset hardware stats when the device experience a reset due to a PCI error and include MAC stats in the hardware stats reset. Additionally, expand hardware stats coverage to include FEC, PHY, and Pause stats.

v2:
- [patch 3] make ASSERT_RTNL() conditional, at init stats are flushed without rtnl_lock, before netdev is allocated
v1: https://lore.kernel.org/[email protected]

Mohsin Bashir (6):
eth: fbnic: Move hw_stats_lock out of fbnic_dev
eth: fbnic: Reset hw stats upon PCI error
eth: fbnic: Reset MAC stats
eth: fbnic: Fetch PHY stats from device
eth: fbnic: Read PHY stats via the ethtool API
eth: fbnic: Add pause stats support

 drivers/net/ethernet/meta/fbnic/fbnic.h       |  3 -
 drivers/net/ethernet/meta/fbnic/fbnic_csr.h   | 19 ++++++
 .../net/ethernet/meta/fbnic/fbnic_hw_stats.h  | 28 ++++++++
 drivers/net/ethernet/meta/fbnic/fbnic_mac.h   |  6 ++
 .../net/ethernet/meta/fbnic/fbnic_ethtool.c   | 63 +++++++++++++++++-
 .../net/ethernet/meta/fbnic/fbnic_hw_stats.c  | 66 ++++++++++++++++---
 drivers/net/ethernet/meta/fbnic/fbnic_mac.c   | 57 ++++++++++++++++
 .../net/ethernet/meta/fbnic/fbnic_netdev.c    | 12 ++--
 drivers/net/ethernet/meta/fbnic/fbnic_pci.c   |  7 +-
 9 files changed, 240 insertions(+), 21 deletions(-)
Signature · 1 line
2.51.0
author maintainer
#1 lore raw

PATCH v2 1/6 eth: fbnic: Move hw_stats_lock out of fbnic_dev

From: Mohsin Bashir <[email protected]>

Move hw_stats_lock out of fbnic_dev to a more appropriate struct fbnic_hw_stats since the only use of this lock is to protect access to the hardware stats. While at it, enclose the lock and stats initialization in a single init call.

---
 drivers/net/ethernet/meta/fbnic/fbnic.h       |  3 ---
 .../net/ethernet/meta/fbnic/fbnic_hw_stats.h  |  5 ++++
 .../net/ethernet/meta/fbnic/fbnic_ethtool.c   |  4 ++--
 .../net/ethernet/meta/fbnic/fbnic_hw_stats.c  | 23 ++++++++++++-------
 .../net/ethernet/meta/fbnic/fbnic_netdev.c    | 12 +++++-----
 drivers/net/ethernet/meta/fbnic/fbnic_pci.c   |  3 +--
 6 files changed, 29 insertions(+), 21 deletions(-)
drivers/net/ethernet/meta/fbnic/fbnic.h +0 -3
index c376e06880c9..311c7dda911a 100644
@@ -84,9 +84,6 @@ struct fbnic_dev {
 	/* Local copy of hardware statistics */
 	struct fbnic_hw_stats hw_stats;
 
-	/* Lock protecting access to hw_stats */
-	spinlock_t hw_stats_lock;
-
 	struct fbnic_fw_log fw_log;
 };
 
drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h +5 -0
index 4fe239717497..2fc25074a5e6 100644
@@ -5,6 +5,7 @@
 #define _FBNIC_HW_STATS_H_
 
 #include <linux/ethtool.h>
+#include <linux/spinlock.h>
 
 #include "fbnic_csr.h"
 
@@ -122,11 +123,15 @@ struct fbnic_hw_stats {
 	struct fbnic_rxb_stats rxb;
 	struct fbnic_hw_q_stats hw_q[FBNIC_MAX_QUEUES];
 	struct fbnic_pcie_stats pcie;
+
+	/* Lock protecting the access to hw stats */
+	spinlock_t lock;
 };
 
 u64 fbnic_stat_rd64(struct fbnic_dev *fbd, u32 reg, u32 offset);
 
 void fbnic_reset_hw_stats(struct fbnic_dev *fbd);
+void fbnic_init_hw_stats(struct fbnic_dev *fbd);
 void fbnic_get_hw_q_stats(struct fbnic_dev *fbd,
 			  struct fbnic_hw_q_stats *hw_q);
 void fbnic_get_hw_stats32(struct fbnic_dev *fbd);
drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c +2 -2
index a758f510f886..e6a60d7ea864 100644
@@ -518,7 +518,7 @@ static void fbnic_get_ethtool_stats(struct net_device *dev,
 
 	fbnic_get_hw_stats(fbn->fbd);
 
-	spin_lock(&fbd->hw_stats_lock);
+	spin_lock(&fbd->hw_stats.lock);
 	fbnic_report_hw_stats(fbnic_gstrings_hw_stats, &fbd->hw_stats,
 			      FBNIC_HW_FIXED_STATS_LEN, &data);
 
@@ -555,7 +555,7 @@ static void fbnic_get_ethtool_stats(struct net_device *dev,
 		fbnic_report_hw_stats(fbnic_gstrings_hw_q_stats, hw_q,
 				      FBNIC_HW_Q_STATS_LEN, &data);
 	}
-	spin_unlock(&fbd->hw_stats_lock);
+	spin_unlock(&fbd->hw_stats.lock);
 
 	for (i = 0; i < FBNIC_MAX_XDPQS; i++)
 		fbnic_get_xdp_queue_stats(fbn->tx[i + FBNIC_MAX_TXQS], &data);
drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.c +15 -8
index 4223d8100e64..77182922f018 100644
@@ -421,9 +421,9 @@ static void fbnic_get_hw_rxq_stats32(struct fbnic_dev *fbd,
 void fbnic_get_hw_q_stats(struct fbnic_dev *fbd,
 			  struct fbnic_hw_q_stats *hw_q)
 {
-	spin_lock(&fbd->hw_stats_lock);
+	spin_lock(&fbd->hw_stats.lock);
 	fbnic_get_hw_rxq_stats32(fbd, hw_q);
-	spin_unlock(&fbd->hw_stats_lock);
+	spin_unlock(&fbd->hw_stats.lock);
 }
 
 static void fbnic_reset_pcie_stats_asic(struct fbnic_dev *fbd,
@@ -512,14 +512,21 @@ static void fbnic_get_pcie_stats_asic64(struct fbnic_dev *fbd,
 
 void fbnic_reset_hw_stats(struct fbnic_dev *fbd)
 {
-	spin_lock(&fbd->hw_stats_lock);
+	spin_lock(&fbd->hw_stats.lock);
 	fbnic_reset_tmi_stats(fbd, &fbd->hw_stats.tmi);
 	fbnic_reset_tti_stats(fbd, &fbd->hw_stats.tti);
 	fbnic_reset_rpc_stats(fbd, &fbd->hw_stats.rpc);
 	fbnic_reset_rxb_stats(fbd, &fbd->hw_stats.rxb);
 	fbnic_reset_hw_rxq_stats(fbd, fbd->hw_stats.hw_q);
 	fbnic_reset_pcie_stats_asic(fbd, &fbd->hw_stats.pcie);
-	spin_unlock(&fbd->hw_stats_lock);
+	spin_unlock(&fbd->hw_stats.lock);
+}
+
+void fbnic_init_hw_stats(struct fbnic_dev *fbd)
+{
+	spin_lock_init(&fbd->hw_stats.lock);
+
+	fbnic_reset_hw_stats(fbd);
 }
 
 static void __fbnic_get_hw_stats32(struct fbnic_dev *fbd)
@@ -533,19 +540,19 @@ static void __fbnic_get_hw_stats32(struct fbnic_dev *fbd)
 
 void fbnic_get_hw_stats32(struct fbnic_dev *fbd)
 {
-	spin_lock(&fbd->hw_stats_lock);
+	spin_lock(&fbd->hw_stats.lock);
 	__fbnic_get_hw_stats32(fbd);
-	spin_unlock(&fbd->hw_stats_lock);
+	spin_unlock(&fbd->hw_stats.lock);
 }
 
 void fbnic_get_hw_stats(struct fbnic_dev *fbd)
 {
-	spin_lock(&fbd->hw_stats_lock);
+	spin_lock(&fbd->hw_stats.lock);
 	__fbnic_get_hw_stats32(fbd);
 
 	fbnic_get_tmi_stats(fbd, &fbd->hw_stats.tmi);
 	fbnic_get_tti_stats(fbd, &fbd->hw_stats.tti);
 	fbnic_get_rxb_stats(fbd, &fbd->hw_stats.rxb);
 	fbnic_get_pcie_stats_asic64(fbd, &fbd->hw_stats.pcie);
-	spin_unlock(&fbd->hw_stats_lock);
+	spin_unlock(&fbd->hw_stats.lock);
 }
drivers/net/ethernet/meta/fbnic/fbnic_netdev.c +6 -6
index b8b684ad376b..98602bc3b39f 100644
@@ -424,12 +424,12 @@ static void fbnic_get_stats64(struct net_device *dev,
 	tx_dropped = stats->dropped;
 
 	/* Record drops from Tx HW Datapath */
-	spin_lock(&fbd->hw_stats_lock);
+	spin_lock(&fbd->hw_stats.lock);
 	tx_dropped += fbd->hw_stats.tmi.drop.frames.value +
 		      fbd->hw_stats.tti.cm_drop.frames.value +
 		      fbd->hw_stats.tti.frame_drop.frames.value +
 		      fbd->hw_stats.tti.tbi_drop.frames.value;
-	spin_unlock(&fbd->hw_stats_lock);
+	spin_unlock(&fbd->hw_stats.lock);
 
 	stats64->tx_bytes = tx_bytes;
 	stats64->tx_packets = tx_packets;
@@ -460,7 +460,7 @@ static void fbnic_get_stats64(struct net_device *dev,
 	rx_packets = stats->packets;
 	rx_dropped = stats->dropped;
 
-	spin_lock(&fbd->hw_stats_lock);
+	spin_lock(&fbd->hw_stats.lock);
 	/* Record drops for the host FIFOs.
 	 * 4: network to Host,	6: BMC to Host
 	 * Exclude the BMC and MC FIFOs as those stats may contain drops
@@ -480,7 +480,7 @@ static void fbnic_get_stats64(struct net_device *dev,
 		/* Report packets with errors */
 		rx_errors += fbd->hw_stats.hw_q[i].rde_pkt_err.value;
 	}
-	spin_unlock(&fbd->hw_stats_lock);
+	spin_unlock(&fbd->hw_stats.lock);
 
 	stats64->rx_bytes = rx_bytes;
 	stats64->rx_packets = rx_packets;
@@ -608,12 +608,12 @@ static void fbnic_get_queue_stats_rx(struct net_device *dev, int idx,
 
 	fbnic_get_hw_q_stats(fbd, fbd->hw_stats.hw_q);
 
-	spin_lock(&fbd->hw_stats_lock);
+	spin_lock(&fbd->hw_stats.lock);
 	rx->hw_drop_overruns = fbd->hw_stats.hw_q[idx].rde_pkt_cq_drop.value +
 			       fbd->hw_stats.hw_q[idx].rde_pkt_bdq_drop.value;
 	rx->hw_drops = fbd->hw_stats.hw_q[idx].rde_pkt_err.value +
 		       rx->hw_drop_overruns;
-	spin_unlock(&fbd->hw_stats_lock);
+	spin_unlock(&fbd->hw_stats.lock);
 }
 
 static void fbnic_get_queue_stats_tx(struct net_device *dev, int idx,
drivers/net/ethernet/meta/fbnic/fbnic_pci.c +1 -2
index b70e4cadb37b..8190f49e1426 100644
@@ -304,10 +304,9 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	fbnic_devlink_register(fbd);
 	fbnic_dbg_fbd_init(fbd);
-	spin_lock_init(&fbd->hw_stats_lock);
 
 	/* Capture snapshot of hardware stats so netdev can calculate delta */
-	fbnic_reset_hw_stats(fbd);
+	fbnic_init_hw_stats(fbd);
 
 	fbnic_hwmon_register(fbd);
 
Signature · 1 line
2.51.0
Reviewed-by on v2 1/6
#2 lore raw

Re: [PATCH net-next v2 1/6] eth: fbnic: Move hw_stats_lock out of fbnic_dev

Quoted 10 lines · Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Move hw_stats_lock out of fbnic_dev to a more appropriate struct fbnic_hw_stats since the only use of this lock is to protect access to the hardware stats. While at it, enclose the lock and stats initialization in a single init call.

---

author maintainer
#3 lore raw

PATCH v2 2/6 eth: fbnic: Reset hw stats upon PCI error

In reply to Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Upon experiencing a PCI error, fbnic reset the device to recover from the failure. Reset the hardware stats as part of the device reset to ensure accurate stats reporting.

Note that the reset is not really resetting the aggregate value to 0, which may result in a spike for a system collecting deltas in stats. Rather, the reset re-latches the current value as previous, in case HW got reset.

---
 drivers/net/ethernet/meta/fbnic/fbnic_pci.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/ethernet/meta/fbnic/fbnic_pci.c +2 -0
index 8190f49e1426..953297f667a2 100644
@@ -491,6 +491,8 @@ static void __fbnic_pm_attach(struct device *dev)
 	struct net_device *netdev = fbd->netdev;
 	struct fbnic_net *fbn;
 
+	fbnic_reset_hw_stats(fbd);
+
 	if (fbnic_init_failure(fbd))
 		return;
 
Signature · 1 line
2.51.0
Reviewed-by on v2 2/6
#4 lore raw

Re: [PATCH net-next v2 2/6] eth: fbnic: Reset hw stats upon PCI error

Quoted 11 lines · Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Upon experiencing a PCI error, fbnic reset the device to recover from the failure. Reset the hardware stats as part of the device reset to ensure accurate stats reporting.

Note that the reset is not really resetting the aggregate value to 0, which may result in a spike for a system collecting deltas in stats. Rather, the reset re-latches the current value as previous, in case HW got reset.

Good. This means stats should stay relatively stable across reset, while preventing glitches if the HW stat itself reset. Makes sense.

Quoted 19 lines
---
 drivers/net/ethernet/meta/fbnic/fbnic_pci.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/net/ethernet/meta/fbnic/fbnic_pci.c +2 -0
index 8190f49e1426..953297f667a2 100644
@@ -491,6 +491,8 @@ static void __fbnic_pm_attach(struct device *dev)
 	struct net_device *netdev = fbd->netdev;
 	struct fbnic_net *fbn;
 
+	fbnic_reset_hw_stats(fbd);
+
 	if (fbnic_init_failure(fbd))
 		return;
 
author maintainer
#5 lore raw

PATCH v2 3/6 eth: fbnic: Reset MAC stats

In reply to Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Reset the MAC stats as part of the hardware stats reset to ensure consistency. Currently, hardware stats are reset during device bring-up and upon experiencing PCI errors; however, MAC stats are being skipped during these resets.

When fbnic_reset_hw_stats() is called upon recovering from PCI error, MAC stats are accessed outside the rtnl_lock. The only other access to MAC stats is via the ethtool API, which is protected by rtnl_lock. This can result in concurrent access to MAC stats and a potential race. Protect the fbnic_reset_hw_stats() call in __fbnic_pm_attach() with rtnl_lock to avoid this.

Note that fbnic_reset_hw_mac_stats() is called outside the hardware stats lock which protects access to the fbnic_hw_stats. This is intentional because MAC stats are fetched from the device outside this lock and are exclusively read via the ethtool API.

---
 .../net/ethernet/meta/fbnic/fbnic_hw_stats.c  | 22 +++++++++++++++++++
 drivers/net/ethernet/meta/fbnic/fbnic_pci.c   |  2 ++
 2 files changed, 24 insertions(+)
drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.c +22 -0
index 77182922f018..8aa9a0e286bb 100644
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) Meta Platforms, Inc. and affiliates. */
 
+#include <linux/rtnetlink.h>
+
 #include "fbnic.h"
 
 static void fbnic_hw_stat_rst32(struct fbnic_dev *fbd, u32 reg,
@@ -510,6 +512,16 @@ static void fbnic_get_pcie_stats_asic64(struct fbnic_dev *fbd,
 			   &pcie->ob_rd_no_np_cred);
 }
 
+static void fbnic_reset_hw_mac_stats(struct fbnic_dev *fbd,
+				     struct fbnic_mac_stats *mac_stats)
+{
+	const struct fbnic_mac *mac = fbd->mac;
+
+	mac->get_eth_mac_stats(fbd, true, &mac_stats->eth_mac);
+	mac->get_eth_ctrl_stats(fbd, true, &mac_stats->eth_ctrl);
+	mac->get_rmon_stats(fbd, true, &mac_stats->rmon);
+}
+
 void fbnic_reset_hw_stats(struct fbnic_dev *fbd)
 {
 	spin_lock(&fbd->hw_stats.lock);
@@ -520,6 +532,16 @@ void fbnic_reset_hw_stats(struct fbnic_dev *fbd)
 	fbnic_reset_hw_rxq_stats(fbd, fbd->hw_stats.hw_q);
 	fbnic_reset_pcie_stats_asic(fbd, &fbd->hw_stats.pcie);
 	spin_unlock(&fbd->hw_stats.lock);
+
+	/* Once registered, the only other access to MAC stats is via the
+	 * ethtool API which is protected by the rtnl_lock. The call to
+	 * fbnic_reset_hw_stats() during PCI recovery is also protected
+	 * by the rtnl_lock hence, we don't need the spinlock to access
+	 * the MAC stats.
+	 */
+	if (fbd->netdev)
+		ASSERT_RTNL();
+	fbnic_reset_hw_mac_stats(fbd, &fbd->hw_stats.mac);
 }
 
 void fbnic_init_hw_stats(struct fbnic_dev *fbd)
drivers/net/ethernet/meta/fbnic/fbnic_pci.c +2 -0
index 953297f667a2..ef7928b18ac0 100644
@@ -491,7 +491,9 @@ static void __fbnic_pm_attach(struct device *dev)
 	struct net_device *netdev = fbd->netdev;
 	struct fbnic_net *fbn;
 
+	rtnl_lock();
 	fbnic_reset_hw_stats(fbd);
+	rtnl_unlock();
 
 	if (fbnic_init_failure(fbd))
 		return;
Signature · 1 line
2.51.0
Reviewed-by on v2 3/6
#6 lore raw

Re: [PATCH net-next v2 3/6] eth: fbnic: Reset MAC stats

Quoted 14 lines · Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Reset the MAC stats as part of the hardware stats reset to ensure consistency. Currently, hardware stats are reset during device bring-up and upon experiencing PCI errors; however, MAC stats are being skipped during these resets.

When fbnic_reset_hw_stats() is called upon recovering from PCI error, MAC stats are accessed outside the rtnl_lock. The only other access to MAC stats is via the ethtool API, which is protected by rtnl_lock. This can result in concurrent access to MAC stats and a potential race. Protect the fbnic_reset_hw_stats() call in __fbnic_pm_attach() with rtnl_lock to avoid this.

Quoted 8 lines

Note that fbnic_reset_hw_mac_stats() is called outside the hardware stats lock which protects access to the fbnic_hw_stats. This is intentional because MAC stats are fetched from the device outside this lock and are exclusively read via the ethtool API.

---

author maintainer
#7 lore raw

PATCH v2 4/6 eth: fbnic: Fetch PHY stats from device

In reply to Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Add support to fetch PHY stats consisting of PCS and FEC stats from the device. When reading the stats counters, the lo part is read first, which latches the hi part to ensure consistent reading of the stats counter.

FEC and PCS stats can wrap depending on the access frequency. To prevent wrapping, fetch these stats periodically under the service task. Also to maintain consistency fetch these stats along with other 32b stats under __fbnic_get_hw_stats32().

---
 drivers/net/ethernet/meta/fbnic/fbnic_csr.h   | 15 ++++++
 .../net/ethernet/meta/fbnic/fbnic_hw_stats.h  | 16 +++++++
 drivers/net/ethernet/meta/fbnic/fbnic_mac.h   |  4 ++
 .../net/ethernet/meta/fbnic/fbnic_hw_stats.c  | 20 ++++++++
 drivers/net/ethernet/meta/fbnic/fbnic_mac.c   | 46 +++++++++++++++++++
 5 files changed, 101 insertions(+)
drivers/net/ethernet/meta/fbnic/fbnic_csr.h +15 -0
index a81db842aa53..69cb73ca8bca 100644
@@ -790,6 +790,21 @@ enum {
 #define FBNIC_CSR_END_PCS		0x10668 /* CSR section delimiter */
 
 #define FBNIC_CSR_START_RSFEC		0x10800 /* CSR section delimiter */
+
+/* We have 4 RSFEC engines present in our part, however we are only using 1.
+ * As such only CCW(0) and NCCW(0) will never be non-zero and the other
+ * registers can be ignored.
+ */
+#define FBNIC_RSFEC_CCW_LO(n)	(0x10802 + 8 * (n))	/* 0x42008 + 32*n */
+#define FBNIC_RSFEC_CCW_HI(n)	(0x10803 + 8 * (n))	/* 0x4200c + 32*n */
+#define FBNIC_RSFEC_NCCW_LO(n)	(0x10804 + 8 * (n))	/* 0x42010 + 32*n */
+#define FBNIC_RSFEC_NCCW_HI(n)	(0x10805 + 8 * (n))	/* 0x42014 + 32*n */
+
+#define FBNIC_PCS_MAX_LANES			4
+#define FBNIC_PCS_SYMBLERR_LO(n) \
+				(0x10880 + 2 * (n))	/* 0x42200 + 8*n */
+#define FBNIC_PCS_SYMBLERR_HI(n) \
+				(0x10881 + 2 * (n))	/* 0x42204 + 8*n */
 #define FBNIC_CSR_END_RSFEC		0x108c8 /* CSR section delimiter */
 
 /* MAC MAC registers (ASIC only) */
drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h +16 -0
index 2fc25074a5e6..baffae1868a6 100644
@@ -23,6 +23,16 @@ struct fbnic_hw_stat {
 	struct fbnic_stat_counter bytes;
 };
 
+struct fbnic_fec_stats {
+	struct fbnic_stat_counter corrected_blocks, uncorrectable_blocks;
+};
+
+struct fbnic_pcs_stats {
+	struct {
+		struct fbnic_stat_counter lanes[FBNIC_PCS_MAX_LANES];
+	} SymbolErrorDuringCarrier;
+};
+
 /* Note: not updated by fbnic_get_hw_stats() */
 struct fbnic_eth_ctrl_stats {
 	struct fbnic_stat_counter MACControlFramesTransmitted;
@@ -56,6 +66,11 @@ struct fbnic_eth_mac_stats {
 	struct fbnic_stat_counter FrameTooLongErrors;
 };
 
+struct fbnic_phy_stats {
+	struct fbnic_fec_stats fec;
+	struct fbnic_pcs_stats pcs;
+};
+
 struct fbnic_mac_stats {
 	struct fbnic_eth_mac_stats eth_mac;
 	struct fbnic_eth_ctrl_stats eth_ctrl;
@@ -116,6 +131,7 @@ struct fbnic_pcie_stats {
 };
 
 struct fbnic_hw_stats {
+	struct fbnic_phy_stats phy;
 	struct fbnic_mac_stats mac;
 	struct fbnic_tmi_stats tmi;
 	struct fbnic_tti_stats tti;
drivers/net/ethernet/meta/fbnic/fbnic_mac.h +4 -0
index 86fa06da2b3e..92dd6efb920a 100644
@@ -79,6 +79,10 @@ struct fbnic_mac {
 	bool (*pcs_get_link)(struct fbnic_dev *fbd);
 	int (*pcs_get_link_event)(struct fbnic_dev *fbd);
 
+	void (*get_fec_stats)(struct fbnic_dev *fbd, bool reset,
+			      struct fbnic_fec_stats *fec_stats);
+	void (*get_pcs_stats)(struct fbnic_dev *fbd, bool reset,
+			      struct fbnic_pcs_stats *pcs_stats);
 	void (*get_eth_mac_stats)(struct fbnic_dev *fbd, bool reset,
 				  struct fbnic_eth_mac_stats *mac_stats);
 	void (*get_eth_ctrl_stats)(struct fbnic_dev *fbd, bool reset,
drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.c +20 -0
index 8aa9a0e286bb..9e7becba7386 100644
@@ -512,6 +512,24 @@ static void fbnic_get_pcie_stats_asic64(struct fbnic_dev *fbd,
 			   &pcie->ob_rd_no_np_cred);
 }
 
+static void fbnic_reset_phy_stats(struct fbnic_dev *fbd,
+				  struct fbnic_phy_stats *phy_stats)
+{
+	const struct fbnic_mac *mac = fbd->mac;
+
+	mac->get_fec_stats(fbd, true, &phy_stats->fec);
+	mac->get_pcs_stats(fbd, true, &phy_stats->pcs);
+}
+
+static void fbnic_get_phy_stats32(struct fbnic_dev *fbd,
+				  struct fbnic_phy_stats *phy_stats)
+{
+	const struct fbnic_mac *mac = fbd->mac;
+
+	mac->get_fec_stats(fbd, false, &phy_stats->fec);
+	mac->get_pcs_stats(fbd, false, &phy_stats->pcs);
+}
+
 static void fbnic_reset_hw_mac_stats(struct fbnic_dev *fbd,
 				     struct fbnic_mac_stats *mac_stats)
 {
@@ -525,6 +543,7 @@ static void fbnic_reset_hw_mac_stats(struct fbnic_dev *fbd,
 void fbnic_reset_hw_stats(struct fbnic_dev *fbd)
 {
 	spin_lock(&fbd->hw_stats.lock);
+	fbnic_reset_phy_stats(fbd, &fbd->hw_stats.phy);
 	fbnic_reset_tmi_stats(fbd, &fbd->hw_stats.tmi);
 	fbnic_reset_tti_stats(fbd, &fbd->hw_stats.tti);
 	fbnic_reset_rpc_stats(fbd, &fbd->hw_stats.rpc);
@@ -553,6 +572,7 @@ void fbnic_init_hw_stats(struct fbnic_dev *fbd)
 
 static void __fbnic_get_hw_stats32(struct fbnic_dev *fbd)
 {
+	fbnic_get_phy_stats32(fbd, &fbd->hw_stats.phy);
 	fbnic_get_tmi_stats32(fbd, &fbd->hw_stats.tmi);
 	fbnic_get_tti_stats32(fbd, &fbd->hw_stats.tti);
 	fbnic_get_rpc_stats32(fbd, &fbd->hw_stats.rpc);
drivers/net/ethernet/meta/fbnic/fbnic_mac.c +46 -0

58 lines not on this page · raw message

Signature · 1 line
2.51.0
Reviewed-by on v2 4/6
#8 lore raw

Re: [PATCH net-next v2 4/6] eth: fbnic: Fetch PHY stats from device

Quoted 6 lines · Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Add support to fetch PHY stats consisting of PCS and FEC stats from the device. When reading the stats counters, the lo part is read first, which latches the hi part to ensure consistent reading of the stats counter.

Quoted 8 lines

FEC and PCS stats can wrap depending on the access frequency. To prevent wrapping, fetch these stats periodically under the service task. Also to maintain consistency fetch these stats along with other 32b stats under __fbnic_get_hw_stats32().

---

author maintainer
#9 lore raw

PATCH v2 5/6 eth: fbnic: Read PHY stats via the ethtool API

In reply to Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Provide support to read PHY stats (FEC and PCS) via the ethtool API.

]# ethtool -I --show-fec eth0
FEC parameters for eth0:
Supported/Configured FEC encodings: RS
Active FEC encoding: RS
Statistics:
corrected_blocks: 0
uncorrectable_blocks: 0

]# ethtool -S eth0 --groups eth-phy
Standard stats for eth0:
eth-phy-SymbolErrorDuringCarrier: 0

---
 .../net/ethernet/meta/fbnic/fbnic_ethtool.c   | 42 +++++++++++++++++++
 1 file changed, 42 insertions(+)
drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c +42 -0
index e6a60d7ea864..4194b30f1074 100644
@@ -1641,6 +1641,46 @@ static void fbnic_set_counter(u64 *stat, struct fbnic_stat_counter *counter)
 		*stat = counter->value;
 }
 
+static void
+fbnic_get_fec_stats(struct net_device *netdev,
+		    struct ethtool_fec_stats *fec_stats)
+{
+	struct fbnic_net *fbn = netdev_priv(netdev);
+	struct fbnic_phy_stats *phy_stats;
+	struct fbnic_dev *fbd = fbn->fbd;
+
+	fbnic_get_hw_stats32(fbd);
+	phy_stats = &fbd->hw_stats.phy;
+
+	spin_lock(&fbd->hw_stats.lock);
+	fec_stats->corrected_blocks.total =
+		phy_stats->fec.corrected_blocks.value;
+	fec_stats->uncorrectable_blocks.total =
+		phy_stats->fec.uncorrectable_blocks.value;
+	spin_unlock(&fbd->hw_stats.lock);
+}
+
+static void
+fbnic_get_eth_phy_stats(struct net_device *netdev,
+			struct ethtool_eth_phy_stats *eth_phy_stats)
+{
+	struct fbnic_net *fbn = netdev_priv(netdev);
+	struct fbnic_phy_stats *phy_stats;
+	struct fbnic_dev *fbd = fbn->fbd;
+	u64 total = 0;
+	int i;
+
+	fbnic_get_hw_stats32(fbd);
+	phy_stats = &fbd->hw_stats.phy;
+
+	spin_lock(&fbd->hw_stats.lock);
+	for (i = 0; i < FBNIC_PCS_MAX_LANES; i++)
+		total += phy_stats->pcs.SymbolErrorDuringCarrier.lanes[i].value;
+
+	eth_phy_stats->SymbolErrorDuringCarrier = total;
+	spin_unlock(&fbd->hw_stats.lock);
+}
+
 static void
 fbnic_get_eth_mac_stats(struct net_device *netdev,
 			struct ethtool_eth_mac_stats *eth_mac_stats)
@@ -1782,7 +1822,9 @@ static const struct ethtool_ops fbnic_ethtool_ops = {
 	.get_ts_info			= fbnic_get_ts_info,
 	.get_ts_stats			= fbnic_get_ts_stats,
 	.get_link_ksettings		= fbnic_phylink_ethtool_ksettings_get,
+	.get_fec_stats			= fbnic_get_fec_stats,
 	.get_fecparam			= fbnic_phylink_get_fecparam,
+	.get_eth_phy_stats		= fbnic_get_eth_phy_stats,
 	.get_eth_mac_stats		= fbnic_get_eth_mac_stats,
 	.get_eth_ctrl_stats		= fbnic_get_eth_ctrl_stats,
 	.get_rmon_stats			= fbnic_get_rmon_stats,
Signature · 1 line
2.51.0
Reviewed-by on v2 5/6
#10 lore raw

Re: [PATCH net-next v2 5/6] eth: fbnic: Read PHY stats via the ethtool API

Quoted 12 lines · Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Provide support to read PHY stats (FEC and PCS) via the ethtool API.

]# ethtool -I --show-fec eth0
FEC parameters for eth0:
Supported/Configured FEC encodings: RS
Active FEC encoding: RS
Statistics:
corrected_blocks: 0
uncorrectable_blocks: 0

Quoted 7 lines

]# ethtool -S eth0 --groups eth-phy
Standard stats for eth0:
eth-phy-SymbolErrorDuringCarrier: 0

---

author maintainer
#11 lore raw

PATCH v2 6/6 eth: fbnic: Add pause stats support

In reply to Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Add support to read pause stats for fbnic. Unlike FEC and PCS stats, pause stats won't wrap, do not fetch them under the service task. Since, they are exclusively accessed via the ethtool API, don't include them in fbnic_get_hw_stats().

]# ethtool -I -a eth0
Pause parameters for eth0:
Autonegotiate: on
RX: off
TX: off
Statistics:
tx_pause_frames: 0
rx_pause_frames: 0

---
 drivers/net/ethernet/meta/fbnic/fbnic_csr.h     |  4 ++++
 .../net/ethernet/meta/fbnic/fbnic_hw_stats.h    |  7 +++++++
 drivers/net/ethernet/meta/fbnic/fbnic_mac.h     |  2 ++
 drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c | 17 +++++++++++++++++
 .../net/ethernet/meta/fbnic/fbnic_hw_stats.c    |  1 +
 drivers/net/ethernet/meta/fbnic/fbnic_mac.c     | 11 +++++++++++
 6 files changed, 42 insertions(+)
drivers/net/ethernet/meta/fbnic/fbnic_csr.h +4 -0
index 69cb73ca8bca..e2fffe1597e9 100644
@@ -844,6 +844,10 @@ enum {
 #define FBNIC_CSR_END_SIG		0x1184e /* CSR section delimiter */
 
 #define FBNIC_CSR_START_MAC_STAT	0x11a00
+#define FBNIC_MAC_STAT_RX_XOFF_STB_L	0x11a00		/* 0x46800 */
+#define FBNIC_MAC_STAT_RX_XOFF_STB_H	0x11a01		/* 0x46804 */
+#define FBNIC_MAC_STAT_TX_XOFF_STB_L	0x11a04		/* 0x46810 */
+#define FBNIC_MAC_STAT_TX_XOFF_STB_H	0x11a05		/* 0x46814 */
 #define FBNIC_MAC_STAT_RX_BYTE_COUNT_L	0x11a08		/* 0x46820 */
 #define FBNIC_MAC_STAT_RX_BYTE_COUNT_H	0x11a09		/* 0x46824 */
 #define FBNIC_MAC_STAT_RX_ALIGN_ERROR_L	0x11a0a		/* 0x46828 */
drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.h +7 -0
index baffae1868a6..aa3f429a9aed 100644
@@ -50,6 +50,12 @@ struct fbnic_rmon_stats {
 	struct fbnic_stat_counter hist_tx[ETHTOOL_RMON_HIST_MAX];
 };
 
+/* Note: not updated by fbnic_get_hw_stats() */
+struct fbnic_pause_stats {
+	struct fbnic_stat_counter tx_pause_frames;
+	struct fbnic_stat_counter rx_pause_frames;
+};
+
 struct fbnic_eth_mac_stats {
 	struct fbnic_stat_counter FramesTransmittedOK;
 	struct fbnic_stat_counter FramesReceivedOK;
@@ -73,6 +79,7 @@ struct fbnic_phy_stats {
 
 struct fbnic_mac_stats {
 	struct fbnic_eth_mac_stats eth_mac;
+	struct fbnic_pause_stats pause;
 	struct fbnic_eth_ctrl_stats eth_ctrl;
 	struct fbnic_rmon_stats rmon;
 };
drivers/net/ethernet/meta/fbnic/fbnic_mac.h +2 -0
index 92dd6efb920a..ede5ff0dae22 100644
@@ -85,6 +85,8 @@ struct fbnic_mac {
 			      struct fbnic_pcs_stats *pcs_stats);
 	void (*get_eth_mac_stats)(struct fbnic_dev *fbd, bool reset,
 				  struct fbnic_eth_mac_stats *mac_stats);
+	void (*get_pause_stats)(struct fbnic_dev *fbd, bool reset,
+				struct fbnic_pause_stats *pause_stats);
 	void (*get_eth_ctrl_stats)(struct fbnic_dev *fbd, bool reset,
 				   struct fbnic_eth_ctrl_stats *ctrl_stats);
 	void (*get_rmon_stats)(struct fbnic_dev *fbd, bool reset,
drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c +17 -0
index 4194b30f1074..b4ff98ee2051 100644
@@ -1641,6 +1641,22 @@ static void fbnic_set_counter(u64 *stat, struct fbnic_stat_counter *counter)
 		*stat = counter->value;
 }
 
+static void
+fbnic_get_pause_stats(struct net_device *netdev,
+		      struct ethtool_pause_stats *pause_stats)
+{
+	struct fbnic_net *fbn = netdev_priv(netdev);
+	struct fbnic_mac_stats *mac_stats;
+	struct fbnic_dev *fbd = fbn->fbd;
+
+	mac_stats = &fbd->hw_stats.mac;
+
+	fbd->mac->get_pause_stats(fbd, false, &mac_stats->pause);
+
+	pause_stats->tx_pause_frames = mac_stats->pause.tx_pause_frames.value;
+	pause_stats->rx_pause_frames = mac_stats->pause.rx_pause_frames.value;
+}
+
 static void
 fbnic_get_fec_stats(struct net_device *netdev,
 		    struct ethtool_fec_stats *fec_stats)
@@ -1801,6 +1817,7 @@ static const struct ethtool_ops fbnic_ethtool_ops = {
 	.set_coalesce			= fbnic_set_coalesce,
 	.get_ringparam			= fbnic_get_ringparam,
 	.set_ringparam			= fbnic_set_ringparam,
+	.get_pause_stats		= fbnic_get_pause_stats,
 	.get_pauseparam			= fbnic_phylink_get_pauseparam,
 	.set_pauseparam			= fbnic_phylink_set_pauseparam,
 	.get_strings			= fbnic_get_strings,
drivers/net/ethernet/meta/fbnic/fbnic_hw_stats.c +1 -0
index 9e7becba7386..8b9b2076beec 100644
@@ -536,6 +536,7 @@ static void fbnic_reset_hw_mac_stats(struct fbnic_dev *fbd,
 	const struct fbnic_mac *mac = fbd->mac;
 
 	mac->get_eth_mac_stats(fbd, true, &mac_stats->eth_mac);
+	mac->get_pause_stats(fbd, true, &mac_stats->pause);
 	mac->get_eth_ctrl_stats(fbd, true, &mac_stats->eth_ctrl);
 	mac->get_rmon_stats(fbd, true, &mac_stats->rmon);
 }
drivers/net/ethernet/meta/fbnic/fbnic_mac.c +11 -0
index ffdaebd4002a..8f998d26b9a3 100644
@@ -709,6 +709,16 @@ fbnic_mac_get_eth_mac_stats(struct fbnic_dev *fbd, bool reset,
 			    MAC_STAT_TX_BROADCAST);
 }
 
+static void
+fbnic_mac_get_pause_stats(struct fbnic_dev *fbd, bool reset,
+			  struct fbnic_pause_stats *pause_stats)
+{
+	fbnic_mac_stat_rd64(fbd, reset, pause_stats->tx_pause_frames,
+			    MAC_STAT_TX_XOFF_STB);
+	fbnic_mac_stat_rd64(fbd, reset, pause_stats->rx_pause_frames,
+			    MAC_STAT_RX_XOFF_STB);
+}
+
 static void
 fbnic_mac_get_eth_ctrl_stats(struct fbnic_dev *fbd, bool reset,
 			     struct fbnic_eth_ctrl_stats *ctrl_stats)
@@ -856,6 +866,7 @@ static const struct fbnic_mac fbnic_mac_asic = {
 	.get_fec_stats = fbnic_mac_get_fec_stats,
 	.get_pcs_stats = fbnic_mac_get_pcs_stats,
 	.get_eth_mac_stats = fbnic_mac_get_eth_mac_stats,
+	.get_pause_stats = fbnic_mac_get_pause_stats,
 	.get_eth_ctrl_stats = fbnic_mac_get_eth_ctrl_stats,
 	.get_rmon_stats = fbnic_mac_get_rmon_stats,
 	.link_down = fbnic_mac_link_down_asic,
Signature · 1 line
2.51.0
Reviewed-by on v2 6/6
#12 lore raw

Re: [PATCH net-next v2 6/6] eth: fbnic: Add pause stats support

Quoted 7 lines · Jakub Kicinski

From: Mohsin Bashir <[email protected]>

Add support to read pause stats for fbnic. Unlike FEC and PCS stats, pause stats won't wrap, do not fetch them under the service task. Since, they are exclusively accessed via the ethtool API, don't include them in fbnic_get_hw_stats().

Quoted 9 lines

]# ethtool -I -a eth0
Pause parameters for eth0:
Autonegotiate: on
RX: off
TX: off
Statistics:
tx_pause_frames: 0
rx_pause_frames: 0

Quoted 3 lines

---

#13 lore raw
In reply to Jakub Kicinski
This series was applied to netdev/net-next.git (main) by Jakub Kicinski <[email protected]>:

Hello:

This series was applied to netdev/net-next.git (main) by Jakub Kicinski <[email protected]>:

Quoted 8 lines · On Mon, 25 Aug 2025 13:02:00 -0700 you wrote:

Mohsin says:

Extend hardware stats support for fbnic by adding the ability to reset hardware stats when the device experience a reset due to a PCI error and include MAC stats in the hardware stats reset. Additionally, expand hardware stats coverage to include FEC, PHY, and Pause stats.

[...]

Here is the summary with links:
- [net-next,v2,1/6] eth: fbnic: Move hw_stats_lock out of fbnic_dev

    https://git.kernel.org/netdev/net-next/c/2ee5c8c0c28e
  - [net-next,v2,2/6] eth: fbnic: Reset hw stats upon PCI error
    https://git.kernel.org/netdev/net-next/c/b1161b1863c5
  - [net-next,v2,3/6] eth: fbnic: Reset MAC stats
    https://git.kernel.org/netdev/net-next/c/bcf54e5d7cd0
  - [net-next,v2,4/6] eth: fbnic: Fetch PHY stats from device
    https://git.kernel.org/netdev/net-next/c/df4c5d9a290e
  - [net-next,v2,5/6] eth: fbnic: Read PHY stats via the ethtool API
    https://git.kernel.org/netdev/net-next/c/33c493791bc0
  - [net-next,v2,6/6] eth: fbnic: Add pause stats support
    https://git.kernel.org/netdev/net-next/c/e9faf4db5f26

You are awesome, thank you!

Signature · 2 lines
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html