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:
Jakub Kicinski 2025-09-26 16:48:02 -07:00
commit 55f5a5a7c1
20 changed files with 352 additions and 25 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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) \

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()