Merge branch 'eth-fbnic-extend-hw-stats-support'

Jakub Kicinski says:

====================
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.

v1: https://lore.kernel.org/20250822164731.1461754-1-kuba@kernel.org
====================

Link: https://patch.msgid.link/20250825200206.2357713-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2025-08-27 18:56:27 -07:00
9 changed files with 240 additions and 21 deletions

View File

@@ -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;
};

View File

@@ -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) */
@@ -829,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 */

View File

@@ -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);
@@ -1641,6 +1641,62 @@ 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)
{
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)
@@ -1761,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,
@@ -1782,7 +1839,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,

View File

@@ -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,
@@ -421,9 +423,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,
@@ -510,20 +512,68 @@ 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)
{
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);
}
void fbnic_reset_hw_stats(struct fbnic_dev *fbd)
{
spin_lock(&fbd->hw_stats_lock);
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);
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);
/* 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)
{
spin_lock_init(&fbd->hw_stats.lock);
fbnic_reset_hw_stats(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);
@@ -533,19 +583,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);
}

View File

@@ -5,6 +5,7 @@
#define _FBNIC_HW_STATS_H_
#include <linux/ethtool.h>
#include <linux/spinlock.h>
#include "fbnic_csr.h"
@@ -22,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;
@@ -39,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;
@@ -55,8 +72,14 @@ 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_pause_stats pause;
struct fbnic_eth_ctrl_stats eth_ctrl;
struct fbnic_rmon_stats rmon;
};
@@ -115,6 +138,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;
@@ -122,11 +146,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);

View File

@@ -631,6 +631,50 @@ static void fbnic_mac_link_up_asic(struct fbnic_dev *fbd,
wr32(fbd, FBNIC_MAC_COMMAND_CONFIG, cmd_cfg);
}
static void
fbnic_pcs_rsfec_stat_rd32(struct fbnic_dev *fbd, u32 reg, bool reset,
struct fbnic_stat_counter *stat)
{
u32 pcs_rsfec_stat;
/* The PCS/RFSEC registers are only 16b wide each. So what we will
* have after the 64b read is 0x0000xxxx0000xxxx. To make it usable
* as a full stat we will shift the upper bits into the lower set of
* 0s and then mask off the math at 32b.
*
* Read ordering must be lower reg followed by upper reg.
*/
pcs_rsfec_stat = rd32(fbd, reg) & 0xffff;
pcs_rsfec_stat |= rd32(fbd, reg + 1) << 16;
/* RFSEC registers clear themselves upon being read so there is no
* need to store the old_reg_value.
*/
if (!reset)
stat->value += pcs_rsfec_stat;
}
static void
fbnic_mac_get_fec_stats(struct fbnic_dev *fbd, bool reset,
struct fbnic_fec_stats *s)
{
fbnic_pcs_rsfec_stat_rd32(fbd, FBNIC_RSFEC_CCW_LO(0), reset,
&s->corrected_blocks);
fbnic_pcs_rsfec_stat_rd32(fbd, FBNIC_RSFEC_NCCW_LO(0), reset,
&s->uncorrectable_blocks);
}
static void
fbnic_mac_get_pcs_stats(struct fbnic_dev *fbd, bool reset,
struct fbnic_pcs_stats *s)
{
int i;
for (i = 0; i < FBNIC_PCS_MAX_LANES; i++)
fbnic_pcs_rsfec_stat_rd32(fbd, FBNIC_PCS_SYMBLERR_LO(i), reset,
&s->SymbolErrorDuringCarrier.lanes[i]);
}
static void
fbnic_mac_get_eth_mac_stats(struct fbnic_dev *fbd, bool reset,
struct fbnic_eth_mac_stats *mac_stats)
@@ -665,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)
@@ -809,7 +863,10 @@ static const struct fbnic_mac fbnic_mac_asic = {
.pcs_disable = fbnic_pcs_disable_asic,
.pcs_get_link = fbnic_pcs_get_link_asic,
.pcs_get_link_event = fbnic_pcs_get_link_event_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,

View File

@@ -79,8 +79,14 @@ 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_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,

View File

@@ -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,

View File

@@ -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);
@@ -492,6 +491,10 @@ 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;