mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
Merge branch 'add-fec-bins-histogram-report-via-ethtool'
Vadim Fedorenko says:
====================
add FEC bins histogram report via ethtool
IEEE 802.3ck-2022 defines counters for FEC bins and 802.3df-2024
clarifies it a bit further. Implement reporting interface through as
addition to FEC stats available in ethtool. NetDevSim driver has simple
implementation as an example while mlx5 has much more complex solution.
The example query is the same as usual FEC statistics while the answer
is a bit more verbose:
$ ynl --family ethtool --do fec-get \
--json '{"header":{"dev-index": 10, "flags": 4}}'
{'auto': 0,
'header': {'dev-index': 10, 'dev-name': 'eni10np1'},
'modes': {'bits': {}, 'nomask': True, 'size': 121},
'stats': {'corr-bits': [],
'corrected': [123],
'hist': [{'bin-high': 0,
'bin-low': 0,
'bin-val': 445,
'bin-val-per-lane': [125, 120, 100, 100]},
{'bin-high': 3, 'bin-low': 1, 'bin-val': 12},
{'bin-high': 7,
'bin-low': 4,
'bin-val': 2,
'bin-val-per-lane': [2, 0, 0, 0]}],
'uncorr': [4]}}
====================
Link: https://patch.msgid.link/20250924124037.1508846-1-vadim.fedorenko@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
55f5a5a7c1
|
|
@ -1219,6 +1219,30 @@ attribute-sets:
|
|||
name: udp-ports
|
||||
type: nest
|
||||
nested-attributes: tunnel-udp
|
||||
-
|
||||
name: fec-hist
|
||||
attr-cnt-name: --ethtool-a-fec-hist-cnt
|
||||
attributes:
|
||||
-
|
||||
name: pad
|
||||
type: pad
|
||||
-
|
||||
name: bin-low
|
||||
type: u32
|
||||
doc: Low bound of FEC bin (inclusive)
|
||||
-
|
||||
name: bin-high
|
||||
type: u32
|
||||
doc: High bound of FEC bin (inclusive)
|
||||
-
|
||||
name: bin-val
|
||||
type: uint
|
||||
doc: Error count in the bin (optional if per-lane values exist)
|
||||
-
|
||||
name: bin-val-per-lane
|
||||
type: binary
|
||||
sub-type: u64
|
||||
doc: An array of per-lane error counters in the bin (optional)
|
||||
-
|
||||
name: fec-stat
|
||||
attr-cnt-name: __ethtool-a-fec-stat-cnt
|
||||
|
|
@ -1242,6 +1266,11 @@ attribute-sets:
|
|||
name: corr-bits
|
||||
type: binary
|
||||
sub-type: u64
|
||||
-
|
||||
name: hist
|
||||
type: nest
|
||||
multi-attr: True
|
||||
nested-attributes: fec-hist
|
||||
-
|
||||
name: fec
|
||||
attr-cnt-name: __ethtool-a-fec-cnt
|
||||
|
|
|
|||
|
|
@ -1541,6 +1541,11 @@ Drivers fill in the statistics in the following structure:
|
|||
.. kernel-doc:: include/linux/ethtool.h
|
||||
:identifiers: ethtool_fec_stats
|
||||
|
||||
Statistics may have FEC bins histogram attribute ``ETHTOOL_A_FEC_STAT_HIST``
|
||||
as defined in IEEE 802.3ck-2022 and 802.3df-2024. Nested attributes will have
|
||||
the range of FEC errors in the bin (inclusive) and the amount of error events
|
||||
in the bin.
|
||||
|
||||
FEC_SET
|
||||
=======
|
||||
|
||||
|
|
|
|||
|
|
@ -3208,7 +3208,8 @@ static int bnxt_get_fecparam(struct net_device *dev,
|
|||
}
|
||||
|
||||
static void bnxt_get_fec_stats(struct net_device *dev,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
struct bnxt *bp = netdev_priv(dev);
|
||||
u64 *rx;
|
||||
|
|
|
|||
|
|
@ -930,7 +930,8 @@ static void fun_get_rmon_stats(struct net_device *netdev,
|
|||
}
|
||||
|
||||
static void fun_get_fec_stats(struct net_device *netdev,
|
||||
struct ethtool_fec_stats *stats)
|
||||
struct ethtool_fec_stats *stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
const struct funeth_priv *fp = netdev_priv(netdev);
|
||||
|
||||
|
|
|
|||
|
|
@ -1659,7 +1659,8 @@ static void hns3_set_msglevel(struct net_device *netdev, u32 msg_level)
|
|||
}
|
||||
|
||||
static void hns3_get_fec_stats(struct net_device *netdev,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
struct hnae3_handle *handle = hns3_get_handle(netdev);
|
||||
struct hnae3_ae_dev *ae_dev = hns3_get_ae_dev(handle);
|
||||
|
|
|
|||
|
|
@ -4624,10 +4624,12 @@ static int ice_get_port_fec_stats(struct ice_hw *hw, u16 pcs_quad, u16 pcs_port,
|
|||
* ice_get_fec_stats - returns FEC correctable, uncorrectable stats per netdev
|
||||
* @netdev: network interface device structure
|
||||
* @fec_stats: buffer to hold FEC statistics for given port
|
||||
* @hist: buffer to put FEC histogram statistics for given port
|
||||
*
|
||||
*/
|
||||
static void ice_get_fec_stats(struct net_device *netdev,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
struct ice_netdev_priv *np = netdev_priv(netdev);
|
||||
struct ice_port_topology port_topology;
|
||||
|
|
|
|||
|
|
@ -1283,7 +1283,8 @@ static int otx2_set_link_ksettings(struct net_device *netdev,
|
|||
}
|
||||
|
||||
static void otx2_get_fec_stats(struct net_device *netdev,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
struct otx2_nic *pfvf = netdev_priv(netdev);
|
||||
struct cgx_fw_data *rsp;
|
||||
|
|
|
|||
|
|
@ -956,6 +956,7 @@ struct mlx5e_priv {
|
|||
struct mlx5e_mqprio_rl *mqprio_rl;
|
||||
struct dentry *dfs_root;
|
||||
struct mlx5_devcom_comp_dev *devcom;
|
||||
struct ethtool_fec_hist_range *fec_ranges;
|
||||
};
|
||||
|
||||
struct mlx5e_dev {
|
||||
|
|
|
|||
|
|
@ -1927,11 +1927,12 @@ static int mlx5e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
|
|||
}
|
||||
|
||||
static void mlx5e_get_fec_stats(struct net_device *netdev,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
struct mlx5e_priv *priv = netdev_priv(netdev);
|
||||
|
||||
mlx5e_stats_fec_get(priv, fec_stats);
|
||||
mlx5e_stats_fec_get(priv, fec_stats, hist);
|
||||
}
|
||||
|
||||
static int mlx5e_get_fecparam(struct net_device *netdev,
|
||||
|
|
|
|||
|
|
@ -6279,8 +6279,15 @@ int mlx5e_priv_init(struct mlx5e_priv *priv,
|
|||
if (!priv->channel_stats)
|
||||
goto err_free_tx_rates;
|
||||
|
||||
priv->fec_ranges = kcalloc(ETHTOOL_FEC_HIST_MAX,
|
||||
sizeof(*priv->fec_ranges), GFP_KERNEL);
|
||||
if (!priv->fec_ranges)
|
||||
goto err_free_channel_stats;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_channel_stats:
|
||||
kfree(priv->channel_stats);
|
||||
err_free_tx_rates:
|
||||
kfree(priv->tx_rates);
|
||||
err_free_txq2sq_stats:
|
||||
|
|
@ -6304,6 +6311,7 @@ void mlx5e_priv_cleanup(struct mlx5e_priv *priv)
|
|||
if (!priv->mdev)
|
||||
return;
|
||||
|
||||
kfree(priv->fec_ranges);
|
||||
for (i = 0; i < priv->stats_nch; i++)
|
||||
kvfree(priv->channel_stats[i]);
|
||||
kfree(priv->channel_stats);
|
||||
|
|
|
|||
|
|
@ -1446,16 +1446,13 @@ static void fec_set_rs_stats(struct ethtool_fec_stats *fec_stats, u32 *ppcnt)
|
|||
}
|
||||
|
||||
static void fec_set_block_stats(struct mlx5e_priv *priv,
|
||||
int mode,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = priv->mdev;
|
||||
u32 out[MLX5_ST_SZ_DW(ppcnt_reg)] = {};
|
||||
u32 in[MLX5_ST_SZ_DW(ppcnt_reg)] = {};
|
||||
int sz = MLX5_ST_SZ_BYTES(ppcnt_reg);
|
||||
int mode = fec_active_mode(mdev);
|
||||
|
||||
if (mode == MLX5E_FEC_NOFEC)
|
||||
return;
|
||||
|
||||
MLX5_SET(ppcnt_reg, in, local_port, 1);
|
||||
MLX5_SET(ppcnt_reg, in, grp, MLX5_PHYSICAL_LAYER_COUNTERS_GROUP);
|
||||
|
|
@ -1494,14 +1491,130 @@ static void fec_set_corrected_bits_total(struct mlx5e_priv *priv,
|
|||
phy_corrected_bits);
|
||||
}
|
||||
|
||||
void mlx5e_stats_fec_get(struct mlx5e_priv *priv,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
#define MLX5_RS_HISTOGRAM_ENTRIES \
|
||||
(MLX5_FLD_SZ_BYTES(rs_histogram_cntrs, hist) / \
|
||||
MLX5_FLD_SZ_BYTES(rs_histogram_cntrs, hist[0]))
|
||||
|
||||
enum {
|
||||
MLX5E_HISTOGRAM_FEC_RS_544_514 = 1,
|
||||
MLX5E_HISTOGRAM_FEC_LLRS = 2,
|
||||
MLX5E_HISTOGRAM_FEC_RS_528_514 = 3,
|
||||
};
|
||||
|
||||
static bool fec_rs_validate_hist_type(int mode, int hist_type)
|
||||
{
|
||||
if (!MLX5_CAP_PCAM_FEATURE(priv->mdev, ppcnt_statistical_group))
|
||||
switch (mode) {
|
||||
case MLX5E_FEC_RS_528_514:
|
||||
return hist_type == MLX5E_HISTOGRAM_FEC_RS_528_514;
|
||||
case MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD:
|
||||
case MLX5E_FEC_RS_544_514:
|
||||
return hist_type == MLX5E_HISTOGRAM_FEC_RS_544_514;
|
||||
case MLX5E_FEC_LLRS_272_257_1:
|
||||
return hist_type == MLX5E_HISTOGRAM_FEC_LLRS;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static u8
|
||||
fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode,
|
||||
const struct ethtool_fec_hist_range **ranges)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = priv->mdev;
|
||||
u32 out[MLX5_ST_SZ_DW(pphcr_reg)] = {0};
|
||||
u32 in[MLX5_ST_SZ_DW(pphcr_reg)] = {0};
|
||||
int sz = MLX5_ST_SZ_BYTES(pphcr_reg);
|
||||
u8 hist_type, num_of_bins;
|
||||
|
||||
memset(priv->fec_ranges, 0,
|
||||
ETHTOOL_FEC_HIST_MAX * sizeof(*priv->fec_ranges));
|
||||
MLX5_SET(pphcr_reg, in, local_port, 1);
|
||||
if (mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPHCR, 0, 0))
|
||||
return 0;
|
||||
|
||||
hist_type = MLX5_GET(pphcr_reg, out, active_hist_type);
|
||||
if (!fec_rs_validate_hist_type(mode, hist_type))
|
||||
return 0;
|
||||
|
||||
num_of_bins = MLX5_GET(pphcr_reg, out, num_of_bins);
|
||||
if (WARN_ON_ONCE(num_of_bins > MLX5_RS_HISTOGRAM_ENTRIES))
|
||||
return 0;
|
||||
|
||||
for (int i = 0; i < num_of_bins; i++) {
|
||||
void *bin_range = MLX5_ADDR_OF(pphcr_reg, out, bin_range[i]);
|
||||
|
||||
priv->fec_ranges[i].high = MLX5_GET(bin_range_layout, bin_range,
|
||||
high_val);
|
||||
priv->fec_ranges[i].low = MLX5_GET(bin_range_layout, bin_range,
|
||||
low_val);
|
||||
}
|
||||
*ranges = priv->fec_ranges;
|
||||
|
||||
return num_of_bins;
|
||||
}
|
||||
|
||||
static void fec_rs_histogram_fill_stats(struct mlx5e_priv *priv,
|
||||
u8 num_of_bins,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
struct mlx5_core_dev *mdev = priv->mdev;
|
||||
u32 out[MLX5_ST_SZ_DW(ppcnt_reg)] = {0};
|
||||
u32 in[MLX5_ST_SZ_DW(ppcnt_reg)] = {0};
|
||||
int sz = MLX5_ST_SZ_BYTES(ppcnt_reg);
|
||||
void *rs_histogram_cntrs;
|
||||
|
||||
MLX5_SET(ppcnt_reg, in, local_port, 1);
|
||||
MLX5_SET(ppcnt_reg, in, grp, MLX5_RS_FEC_HISTOGRAM_GROUP);
|
||||
if (mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPCNT, 0, 0))
|
||||
return;
|
||||
|
||||
rs_histogram_cntrs = MLX5_ADDR_OF(ppcnt_reg, out,
|
||||
counter_set.rs_histogram_cntrs);
|
||||
/* Guaranteed that num_of_bins is less than MLX5E_FEC_RS_HIST_MAX
|
||||
* by fec_rs_histogram_fill_ranges().
|
||||
*/
|
||||
for (int i = 0; i < num_of_bins; i++)
|
||||
hist->values[i].sum = MLX5_GET64(rs_histogram_cntrs,
|
||||
rs_histogram_cntrs,
|
||||
hist[i]);
|
||||
}
|
||||
|
||||
static void fec_set_histograms_stats(struct mlx5e_priv *priv, int mode,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
u8 num_of_bins;
|
||||
|
||||
switch (mode) {
|
||||
case MLX5E_FEC_RS_528_514:
|
||||
case MLX5E_FEC_RS_544_514:
|
||||
case MLX5E_FEC_LLRS_272_257_1:
|
||||
case MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD:
|
||||
num_of_bins =
|
||||
fec_rs_histogram_fill_ranges(priv, mode, &hist->ranges);
|
||||
if (num_of_bins)
|
||||
return fec_rs_histogram_fill_stats(priv, num_of_bins,
|
||||
hist);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void mlx5e_stats_fec_get(struct mlx5e_priv *priv,
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
int mode = fec_active_mode(priv->mdev);
|
||||
|
||||
if (mode == MLX5E_FEC_NOFEC ||
|
||||
!MLX5_CAP_PCAM_FEATURE(priv->mdev, ppcnt_statistical_group))
|
||||
return;
|
||||
|
||||
fec_set_corrected_bits_total(priv, fec_stats);
|
||||
fec_set_block_stats(priv, fec_stats);
|
||||
fec_set_block_stats(priv, mode, fec_stats);
|
||||
fec_set_histograms_stats(priv, mode, hist);
|
||||
}
|
||||
|
||||
#define PPORT_ETH_EXT_OFF(c) \
|
||||
|
|
|
|||
|
|
@ -117,7 +117,8 @@ void mlx5e_stats_update_ndo_stats(struct mlx5e_priv *priv);
|
|||
void mlx5e_stats_pause_get(struct mlx5e_priv *priv,
|
||||
struct ethtool_pause_stats *pause_stats);
|
||||
void mlx5e_stats_fec_get(struct mlx5e_priv *priv,
|
||||
struct ethtool_fec_stats *fec_stats);
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist);
|
||||
|
||||
void mlx5e_stats_eth_phy_get(struct mlx5e_priv *priv,
|
||||
struct ethtool_eth_phy_stats *phy_stats);
|
||||
|
|
|
|||
|
|
@ -1718,7 +1718,8 @@ fbnic_get_pause_stats(struct net_device *netdev,
|
|||
|
||||
static void
|
||||
fbnic_get_fec_stats(struct net_device *netdev,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(netdev);
|
||||
struct fbnic_phy_stats *phy_stats;
|
||||
|
|
|
|||
|
|
@ -217,7 +217,8 @@ static int efx_ethtool_set_wol(struct net_device *net_dev,
|
|||
}
|
||||
|
||||
static void efx_ethtool_get_fec_stats(struct net_device *net_dev,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
struct efx_nic *efx = efx_netdev_priv(net_dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -217,7 +217,8 @@ static int efx_ethtool_set_wol(struct net_device *net_dev,
|
|||
}
|
||||
|
||||
static void efx_ethtool_get_fec_stats(struct net_device *net_dev,
|
||||
struct ethtool_fec_stats *fec_stats)
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
struct efx_nic *efx = netdev_priv(net_dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -165,11 +165,34 @@ nsim_set_fecparam(struct net_device *dev, struct ethtool_fecparam *fecparam)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_fec_hist_range netdevsim_fec_ranges[] = {
|
||||
{ 0, 0},
|
||||
{ 1, 3},
|
||||
{ 4, 7},
|
||||
{ 0, 0}
|
||||
};
|
||||
|
||||
static void
|
||||
nsim_get_fec_stats(struct net_device *dev, struct ethtool_fec_stats *fec_stats)
|
||||
nsim_get_fec_stats(struct net_device *dev, struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist)
|
||||
{
|
||||
struct ethtool_fec_hist_value *values = hist->values;
|
||||
|
||||
hist->ranges = netdevsim_fec_ranges;
|
||||
|
||||
fec_stats->corrected_blocks.total = 123;
|
||||
fec_stats->uncorrectable_blocks.total = 4;
|
||||
|
||||
values[0].per_lane[0] = 125;
|
||||
values[0].per_lane[1] = 120;
|
||||
values[0].per_lane[2] = 100;
|
||||
values[0].per_lane[3] = 100;
|
||||
values[1].sum = 12;
|
||||
values[2].sum = 2;
|
||||
values[2].per_lane[0] = 2;
|
||||
values[2].per_lane[1] = 0;
|
||||
values[2].per_lane[2] = 0;
|
||||
values[2].per_lane[3] = 0;
|
||||
}
|
||||
|
||||
static int nsim_get_ts_info(struct net_device *dev,
|
||||
|
|
|
|||
|
|
@ -492,7 +492,29 @@ struct ethtool_pause_stats {
|
|||
};
|
||||
|
||||
#define ETHTOOL_MAX_LANES 8
|
||||
/**
|
||||
* IEEE 802.3ck/df defines 16 bins for FEC histogram plus one more for
|
||||
* the end-of-list marker, total 17 items
|
||||
*/
|
||||
#define ETHTOOL_FEC_HIST_MAX 17
|
||||
/**
|
||||
* struct ethtool_fec_hist_range - error bits range for FEC histogram
|
||||
* statistics
|
||||
* @low: low bound of the bin (inclusive)
|
||||
* @high: high bound of the bin (inclusive)
|
||||
*/
|
||||
struct ethtool_fec_hist_range {
|
||||
u16 low;
|
||||
u16 high;
|
||||
};
|
||||
|
||||
struct ethtool_fec_hist {
|
||||
struct ethtool_fec_hist_value {
|
||||
u64 sum;
|
||||
u64 per_lane[ETHTOOL_MAX_LANES];
|
||||
} values[ETHTOOL_FEC_HIST_MAX];
|
||||
const struct ethtool_fec_hist_range *ranges;
|
||||
};
|
||||
/**
|
||||
* struct ethtool_fec_stats - statistics for IEEE 802.3 FEC
|
||||
* @corrected_blocks: number of received blocks corrected by FEC
|
||||
|
|
@ -1214,7 +1236,8 @@ struct ethtool_ops {
|
|||
int (*set_link_ksettings)(struct net_device *,
|
||||
const struct ethtool_link_ksettings *);
|
||||
void (*get_fec_stats)(struct net_device *dev,
|
||||
struct ethtool_fec_stats *fec_stats);
|
||||
struct ethtool_fec_stats *fec_stats,
|
||||
struct ethtool_fec_hist *hist);
|
||||
int (*get_fecparam)(struct net_device *,
|
||||
struct ethtool_fecparam *);
|
||||
int (*set_fecparam)(struct net_device *,
|
||||
|
|
|
|||
|
|
@ -561,12 +561,24 @@ enum {
|
|||
ETHTOOL_A_TUNNEL_INFO_MAX = (__ETHTOOL_A_TUNNEL_INFO_CNT - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
ETHTOOL_A_FEC_HIST_PAD = 1,
|
||||
ETHTOOL_A_FEC_HIST_BIN_LOW,
|
||||
ETHTOOL_A_FEC_HIST_BIN_HIGH,
|
||||
ETHTOOL_A_FEC_HIST_BIN_VAL,
|
||||
ETHTOOL_A_FEC_HIST_BIN_VAL_PER_LANE,
|
||||
|
||||
__ETHTOOL_A_FEC_HIST_CNT,
|
||||
ETHTOOL_A_FEC_HIST_MAX = (__ETHTOOL_A_FEC_HIST_CNT - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
ETHTOOL_A_FEC_STAT_UNSPEC,
|
||||
ETHTOOL_A_FEC_STAT_PAD,
|
||||
ETHTOOL_A_FEC_STAT_CORRECTED,
|
||||
ETHTOOL_A_FEC_STAT_UNCORR,
|
||||
ETHTOOL_A_FEC_STAT_CORR_BITS,
|
||||
ETHTOOL_A_FEC_STAT_HIST,
|
||||
|
||||
__ETHTOOL_A_FEC_STAT_CNT,
|
||||
ETHTOOL_A_FEC_STAT_MAX = (__ETHTOOL_A_FEC_STAT_CNT - 1)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ struct fec_reply_data {
|
|||
u64 stats[1 + ETHTOOL_MAX_LANES];
|
||||
u8 cnt;
|
||||
} corr, uncorr, corr_bits;
|
||||
struct ethtool_fec_hist fec_stat_hist;
|
||||
};
|
||||
|
||||
#define FEC_REPDATA(__reply_base) \
|
||||
|
|
@ -113,7 +114,10 @@ static int fec_prepare_data(const struct ethnl_req_info *req_base,
|
|||
struct ethtool_fec_stats stats;
|
||||
|
||||
ethtool_stats_init((u64 *)&stats, sizeof(stats) / 8);
|
||||
dev->ethtool_ops->get_fec_stats(dev, &stats);
|
||||
ethtool_stats_init((u64 *)data->fec_stat_hist.values,
|
||||
sizeof(data->fec_stat_hist.values) / 8);
|
||||
dev->ethtool_ops->get_fec_stats(dev, &stats,
|
||||
&data->fec_stat_hist);
|
||||
|
||||
fec_stats_recalc(&data->corr, &stats.corrected_blocks);
|
||||
fec_stats_recalc(&data->uncorr, &stats.uncorrectable_blocks);
|
||||
|
|
@ -157,13 +161,77 @@ static int fec_reply_size(const struct ethnl_req_info *req_base,
|
|||
len += nla_total_size(sizeof(u8)) + /* _FEC_AUTO */
|
||||
nla_total_size(sizeof(u32)); /* _FEC_ACTIVE */
|
||||
|
||||
if (req_base->flags & ETHTOOL_FLAG_STATS)
|
||||
if (req_base->flags & ETHTOOL_FLAG_STATS) {
|
||||
len += 3 * nla_total_size_64bit(sizeof(u64) *
|
||||
(1 + ETHTOOL_MAX_LANES));
|
||||
/* add FEC bins information */
|
||||
len += (nla_total_size(0) + /* _A_FEC_HIST */
|
||||
nla_total_size(4) + /* _A_FEC_HIST_BIN_LOW */
|
||||
nla_total_size(4) + /* _A_FEC_HIST_BIN_HI */
|
||||
/* _A_FEC_HIST_BIN_VAL + per-lane values */
|
||||
nla_total_size_64bit(sizeof(u64)) +
|
||||
nla_total_size_64bit(sizeof(u64) * ETHTOOL_MAX_LANES)) *
|
||||
ETHTOOL_FEC_HIST_MAX;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int fec_put_hist(struct sk_buff *skb,
|
||||
const struct ethtool_fec_hist *hist)
|
||||
{
|
||||
const struct ethtool_fec_hist_range *ranges = hist->ranges;
|
||||
const struct ethtool_fec_hist_value *values = hist->values;
|
||||
struct nlattr *nest;
|
||||
int i, j;
|
||||
u64 sum;
|
||||
|
||||
if (!ranges)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ETHTOOL_FEC_HIST_MAX; i++) {
|
||||
if (i && !ranges[i].low && !ranges[i].high)
|
||||
break;
|
||||
|
||||
if (WARN_ON_ONCE(values[i].sum == ETHTOOL_STAT_NOT_SET &&
|
||||
values[i].per_lane[0] == ETHTOOL_STAT_NOT_SET))
|
||||
break;
|
||||
|
||||
nest = nla_nest_start(skb, ETHTOOL_A_FEC_STAT_HIST);
|
||||
if (!nest)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (nla_put_u32(skb, ETHTOOL_A_FEC_HIST_BIN_LOW,
|
||||
ranges[i].low) ||
|
||||
nla_put_u32(skb, ETHTOOL_A_FEC_HIST_BIN_HIGH,
|
||||
ranges[i].high))
|
||||
goto err_cancel_hist;
|
||||
sum = 0;
|
||||
for (j = 0; j < ETHTOOL_MAX_LANES; j++) {
|
||||
if (values[i].per_lane[j] == ETHTOOL_STAT_NOT_SET)
|
||||
break;
|
||||
sum += values[i].per_lane[j];
|
||||
}
|
||||
if (nla_put_uint(skb, ETHTOOL_A_FEC_HIST_BIN_VAL,
|
||||
values[i].sum == ETHTOOL_STAT_NOT_SET ?
|
||||
sum : values[i].sum))
|
||||
goto err_cancel_hist;
|
||||
if (j && nla_put_64bit(skb, ETHTOOL_A_FEC_HIST_BIN_VAL_PER_LANE,
|
||||
sizeof(u64) * j,
|
||||
values[i].per_lane,
|
||||
ETHTOOL_A_FEC_HIST_PAD))
|
||||
goto err_cancel_hist;
|
||||
|
||||
nla_nest_end(skb, nest);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_cancel_hist:
|
||||
nla_nest_cancel(skb, nest);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static int fec_put_stats(struct sk_buff *skb, const struct fec_reply_data *data)
|
||||
{
|
||||
struct nlattr *nest;
|
||||
|
|
@ -183,6 +251,9 @@ static int fec_put_stats(struct sk_buff *skb, const struct fec_reply_data *data)
|
|||
data->corr_bits.stats, ETHTOOL_A_FEC_STAT_PAD))
|
||||
goto err_cancel;
|
||||
|
||||
if (fec_put_hist(skb, &data->fec_stat_hist))
|
||||
goto err_cancel;
|
||||
|
||||
nla_nest_end(skb, nest);
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,36 @@ def check_fec(cfg) -> None:
|
|||
ksft_true(data['stats'], "driver does not report stats")
|
||||
|
||||
|
||||
def check_fec_hist(cfg) -> None:
|
||||
"""
|
||||
Check that drivers which support FEC histogram statistics report
|
||||
reasonable values.
|
||||
"""
|
||||
|
||||
try:
|
||||
data = ethnl.fec_get({"header": {"dev-index": cfg.ifindex,
|
||||
"flags": {'stats'}}})
|
||||
except NlError as e:
|
||||
if e.error == errno.EOPNOTSUPP:
|
||||
raise KsftSkipEx("FEC not supported by the device") from e
|
||||
raise
|
||||
if 'stats' not in data:
|
||||
raise KsftSkipEx("FEC stats not supported by the device")
|
||||
if 'hist' not in data['stats']:
|
||||
raise KsftSkipEx("FEC histogram not supported by the device")
|
||||
|
||||
hist = data['stats']['hist']
|
||||
for fec_bin in hist:
|
||||
for key in ['bin-low', 'bin-high', 'bin-val']:
|
||||
ksft_in(key, fec_bin,
|
||||
"Drivers should always report FEC bin range and value")
|
||||
ksft_ge(fec_bin['bin-high'], fec_bin['bin-low'],
|
||||
"FEC bin range should be valid")
|
||||
if 'bin-val-per-lane' in fec_bin:
|
||||
ksft_eq(sum(fec_bin['bin-val-per-lane']), fec_bin['bin-val'],
|
||||
"FEC bin value should be equal to sum of per-plane values")
|
||||
|
||||
|
||||
def pkt_byte_sum(cfg) -> None:
|
||||
"""
|
||||
Check that qstat and interface stats match in value.
|
||||
|
|
@ -279,8 +309,9 @@ def main() -> None:
|
|||
""" Ksft boiler plate main """
|
||||
|
||||
with NetDrvEnv(__file__, queue_count=100) as cfg:
|
||||
ksft_run([check_pause, check_fec, pkt_byte_sum, qstat_by_ifindex,
|
||||
check_down, procfs_hammer, procfs_downup_hammer],
|
||||
ksft_run([check_pause, check_fec, check_fec_hist, pkt_byte_sum,
|
||||
qstat_by_ifindex, check_down, procfs_hammer,
|
||||
procfs_downup_hammer],
|
||||
args=(cfg, ))
|
||||
ksft_exit()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user