Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+352 -25
+29
Documentation/netlink/specs/ethtool.yaml
··· 1220 1220 type: nest 1221 1221 nested-attributes: tunnel-udp 1222 1222 - 1223 + name: fec-hist 1224 + attr-cnt-name: --ethtool-a-fec-hist-cnt 1225 + attributes: 1226 + - 1227 + name: pad 1228 + type: pad 1229 + - 1230 + name: bin-low 1231 + type: u32 1232 + doc: Low bound of FEC bin (inclusive) 1233 + - 1234 + name: bin-high 1235 + type: u32 1236 + doc: High bound of FEC bin (inclusive) 1237 + - 1238 + name: bin-val 1239 + type: uint 1240 + doc: Error count in the bin (optional if per-lane values exist) 1241 + - 1242 + name: bin-val-per-lane 1243 + type: binary 1244 + sub-type: u64 1245 + doc: An array of per-lane error counters in the bin (optional) 1246 + - 1223 1247 name: fec-stat 1224 1248 attr-cnt-name: __ethtool-a-fec-stat-cnt 1225 1249 attributes: ··· 1266 1242 name: corr-bits 1267 1243 type: binary 1268 1244 sub-type: u64 1245 + - 1246 + name: hist 1247 + type: nest 1248 + multi-attr: True 1249 + nested-attributes: fec-hist 1269 1250 - 1270 1251 name: fec 1271 1252 attr-cnt-name: __ethtool-a-fec-cnt
+5
Documentation/networking/ethtool-netlink.rst
··· 1541 1541 .. kernel-doc:: include/linux/ethtool.h 1542 1542 :identifiers: ethtool_fec_stats 1543 1543 1544 + Statistics may have FEC bins histogram attribute ``ETHTOOL_A_FEC_STAT_HIST`` 1545 + as defined in IEEE 802.3ck-2022 and 802.3df-2024. Nested attributes will have 1546 + the range of FEC errors in the bin (inclusive) and the amount of error events 1547 + in the bin. 1548 + 1544 1549 FEC_SET 1545 1550 ======= 1546 1551
+2 -1
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
··· 3208 3208 } 3209 3209 3210 3210 static void bnxt_get_fec_stats(struct net_device *dev, 3211 - struct ethtool_fec_stats *fec_stats) 3211 + struct ethtool_fec_stats *fec_stats, 3212 + struct ethtool_fec_hist *hist) 3212 3213 { 3213 3214 struct bnxt *bp = netdev_priv(dev); 3214 3215 u64 *rx;
+2 -1
drivers/net/ethernet/fungible/funeth/funeth_ethtool.c
··· 930 930 } 931 931 932 932 static void fun_get_fec_stats(struct net_device *netdev, 933 - struct ethtool_fec_stats *stats) 933 + struct ethtool_fec_stats *stats, 934 + struct ethtool_fec_hist *hist) 934 935 { 935 936 const struct funeth_priv *fp = netdev_priv(netdev); 936 937
+2 -1
drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c
··· 1659 1659 } 1660 1660 1661 1661 static void hns3_get_fec_stats(struct net_device *netdev, 1662 - struct ethtool_fec_stats *fec_stats) 1662 + struct ethtool_fec_stats *fec_stats, 1663 + struct ethtool_fec_hist *hist) 1663 1664 { 1664 1665 struct hnae3_handle *handle = hns3_get_handle(netdev); 1665 1666 struct hnae3_ae_dev *ae_dev = hns3_get_ae_dev(handle);
+3 -1
drivers/net/ethernet/intel/ice/ice_ethtool.c
··· 4624 4624 * ice_get_fec_stats - returns FEC correctable, uncorrectable stats per netdev 4625 4625 * @netdev: network interface device structure 4626 4626 * @fec_stats: buffer to hold FEC statistics for given port 4627 + * @hist: buffer to put FEC histogram statistics for given port 4627 4628 * 4628 4629 */ 4629 4630 static void ice_get_fec_stats(struct net_device *netdev, 4630 - struct ethtool_fec_stats *fec_stats) 4631 + struct ethtool_fec_stats *fec_stats, 4632 + struct ethtool_fec_hist *hist) 4631 4633 { 4632 4634 struct ice_netdev_priv *np = netdev_priv(netdev); 4633 4635 struct ice_port_topology port_topology;
+2 -1
drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
··· 1283 1283 } 1284 1284 1285 1285 static void otx2_get_fec_stats(struct net_device *netdev, 1286 - struct ethtool_fec_stats *fec_stats) 1286 + struct ethtool_fec_stats *fec_stats, 1287 + struct ethtool_fec_hist *hist) 1287 1288 { 1288 1289 struct otx2_nic *pfvf = netdev_priv(netdev); 1289 1290 struct cgx_fw_data *rsp;
+1
drivers/net/ethernet/mellanox/mlx5/core/en.h
··· 956 956 struct mlx5e_mqprio_rl *mqprio_rl; 957 957 struct dentry *dfs_root; 958 958 struct mlx5_devcom_comp_dev *devcom; 959 + struct ethtool_fec_hist_range *fec_ranges; 959 960 }; 960 961 961 962 struct mlx5e_dev {
+3 -2
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
··· 1927 1927 } 1928 1928 1929 1929 static void mlx5e_get_fec_stats(struct net_device *netdev, 1930 - struct ethtool_fec_stats *fec_stats) 1930 + struct ethtool_fec_stats *fec_stats, 1931 + struct ethtool_fec_hist *hist) 1931 1932 { 1932 1933 struct mlx5e_priv *priv = netdev_priv(netdev); 1933 1934 1934 - mlx5e_stats_fec_get(priv, fec_stats); 1935 + mlx5e_stats_fec_get(priv, fec_stats, hist); 1935 1936 } 1936 1937 1937 1938 static int mlx5e_get_fecparam(struct net_device *netdev,
+8
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
··· 6279 6279 if (!priv->channel_stats) 6280 6280 goto err_free_tx_rates; 6281 6281 6282 + priv->fec_ranges = kcalloc(ETHTOOL_FEC_HIST_MAX, 6283 + sizeof(*priv->fec_ranges), GFP_KERNEL); 6284 + if (!priv->fec_ranges) 6285 + goto err_free_channel_stats; 6286 + 6282 6287 return 0; 6283 6288 6289 + err_free_channel_stats: 6290 + kfree(priv->channel_stats); 6284 6291 err_free_tx_rates: 6285 6292 kfree(priv->tx_rates); 6286 6293 err_free_txq2sq_stats: ··· 6311 6304 if (!priv->mdev) 6312 6305 return; 6313 6306 6307 + kfree(priv->fec_ranges); 6314 6308 for (i = 0; i < priv->stats_nch; i++) 6315 6309 kvfree(priv->channel_stats[i]); 6316 6310 kfree(priv->channel_stats);
+121 -8
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
··· 1446 1446 } 1447 1447 1448 1448 static void fec_set_block_stats(struct mlx5e_priv *priv, 1449 + int mode, 1449 1450 struct ethtool_fec_stats *fec_stats) 1450 1451 { 1451 1452 struct mlx5_core_dev *mdev = priv->mdev; 1452 1453 u32 out[MLX5_ST_SZ_DW(ppcnt_reg)] = {}; 1453 1454 u32 in[MLX5_ST_SZ_DW(ppcnt_reg)] = {}; 1454 1455 int sz = MLX5_ST_SZ_BYTES(ppcnt_reg); 1455 - int mode = fec_active_mode(mdev); 1456 - 1457 - if (mode == MLX5E_FEC_NOFEC) 1458 - return; 1459 1456 1460 1457 MLX5_SET(ppcnt_reg, in, local_port, 1); 1461 1458 MLX5_SET(ppcnt_reg, in, grp, MLX5_PHYSICAL_LAYER_COUNTERS_GROUP); ··· 1491 1494 phy_corrected_bits); 1492 1495 } 1493 1496 1494 - void mlx5e_stats_fec_get(struct mlx5e_priv *priv, 1495 - struct ethtool_fec_stats *fec_stats) 1497 + #define MLX5_RS_HISTOGRAM_ENTRIES \ 1498 + (MLX5_FLD_SZ_BYTES(rs_histogram_cntrs, hist) / \ 1499 + MLX5_FLD_SZ_BYTES(rs_histogram_cntrs, hist[0])) 1500 + 1501 + enum { 1502 + MLX5E_HISTOGRAM_FEC_RS_544_514 = 1, 1503 + MLX5E_HISTOGRAM_FEC_LLRS = 2, 1504 + MLX5E_HISTOGRAM_FEC_RS_528_514 = 3, 1505 + }; 1506 + 1507 + static bool fec_rs_validate_hist_type(int mode, int hist_type) 1496 1508 { 1497 - if (!MLX5_CAP_PCAM_FEATURE(priv->mdev, ppcnt_statistical_group)) 1509 + switch (mode) { 1510 + case MLX5E_FEC_RS_528_514: 1511 + return hist_type == MLX5E_HISTOGRAM_FEC_RS_528_514; 1512 + case MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD: 1513 + case MLX5E_FEC_RS_544_514: 1514 + return hist_type == MLX5E_HISTOGRAM_FEC_RS_544_514; 1515 + case MLX5E_FEC_LLRS_272_257_1: 1516 + return hist_type == MLX5E_HISTOGRAM_FEC_LLRS; 1517 + default: 1518 + break; 1519 + } 1520 + 1521 + return false; 1522 + } 1523 + 1524 + static u8 1525 + fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode, 1526 + const struct ethtool_fec_hist_range **ranges) 1527 + { 1528 + struct mlx5_core_dev *mdev = priv->mdev; 1529 + u32 out[MLX5_ST_SZ_DW(pphcr_reg)] = {0}; 1530 + u32 in[MLX5_ST_SZ_DW(pphcr_reg)] = {0}; 1531 + int sz = MLX5_ST_SZ_BYTES(pphcr_reg); 1532 + u8 hist_type, num_of_bins; 1533 + 1534 + memset(priv->fec_ranges, 0, 1535 + ETHTOOL_FEC_HIST_MAX * sizeof(*priv->fec_ranges)); 1536 + MLX5_SET(pphcr_reg, in, local_port, 1); 1537 + if (mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPHCR, 0, 0)) 1538 + return 0; 1539 + 1540 + hist_type = MLX5_GET(pphcr_reg, out, active_hist_type); 1541 + if (!fec_rs_validate_hist_type(mode, hist_type)) 1542 + return 0; 1543 + 1544 + num_of_bins = MLX5_GET(pphcr_reg, out, num_of_bins); 1545 + if (WARN_ON_ONCE(num_of_bins > MLX5_RS_HISTOGRAM_ENTRIES)) 1546 + return 0; 1547 + 1548 + for (int i = 0; i < num_of_bins; i++) { 1549 + void *bin_range = MLX5_ADDR_OF(pphcr_reg, out, bin_range[i]); 1550 + 1551 + priv->fec_ranges[i].high = MLX5_GET(bin_range_layout, bin_range, 1552 + high_val); 1553 + priv->fec_ranges[i].low = MLX5_GET(bin_range_layout, bin_range, 1554 + low_val); 1555 + } 1556 + *ranges = priv->fec_ranges; 1557 + 1558 + return num_of_bins; 1559 + } 1560 + 1561 + static void fec_rs_histogram_fill_stats(struct mlx5e_priv *priv, 1562 + u8 num_of_bins, 1563 + struct ethtool_fec_hist *hist) 1564 + { 1565 + struct mlx5_core_dev *mdev = priv->mdev; 1566 + u32 out[MLX5_ST_SZ_DW(ppcnt_reg)] = {0}; 1567 + u32 in[MLX5_ST_SZ_DW(ppcnt_reg)] = {0}; 1568 + int sz = MLX5_ST_SZ_BYTES(ppcnt_reg); 1569 + void *rs_histogram_cntrs; 1570 + 1571 + MLX5_SET(ppcnt_reg, in, local_port, 1); 1572 + MLX5_SET(ppcnt_reg, in, grp, MLX5_RS_FEC_HISTOGRAM_GROUP); 1573 + if (mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPCNT, 0, 0)) 1574 + return; 1575 + 1576 + rs_histogram_cntrs = MLX5_ADDR_OF(ppcnt_reg, out, 1577 + counter_set.rs_histogram_cntrs); 1578 + /* Guaranteed that num_of_bins is less than MLX5E_FEC_RS_HIST_MAX 1579 + * by fec_rs_histogram_fill_ranges(). 1580 + */ 1581 + for (int i = 0; i < num_of_bins; i++) 1582 + hist->values[i].sum = MLX5_GET64(rs_histogram_cntrs, 1583 + rs_histogram_cntrs, 1584 + hist[i]); 1585 + } 1586 + 1587 + static void fec_set_histograms_stats(struct mlx5e_priv *priv, int mode, 1588 + struct ethtool_fec_hist *hist) 1589 + { 1590 + u8 num_of_bins; 1591 + 1592 + switch (mode) { 1593 + case MLX5E_FEC_RS_528_514: 1594 + case MLX5E_FEC_RS_544_514: 1595 + case MLX5E_FEC_LLRS_272_257_1: 1596 + case MLX5E_FEC_RS_544_514_INTERLEAVED_QUAD: 1597 + num_of_bins = 1598 + fec_rs_histogram_fill_ranges(priv, mode, &hist->ranges); 1599 + if (num_of_bins) 1600 + return fec_rs_histogram_fill_stats(priv, num_of_bins, 1601 + hist); 1602 + break; 1603 + default: 1604 + return; 1605 + } 1606 + } 1607 + 1608 + void mlx5e_stats_fec_get(struct mlx5e_priv *priv, 1609 + struct ethtool_fec_stats *fec_stats, 1610 + struct ethtool_fec_hist *hist) 1611 + { 1612 + int mode = fec_active_mode(priv->mdev); 1613 + 1614 + if (mode == MLX5E_FEC_NOFEC || 1615 + !MLX5_CAP_PCAM_FEATURE(priv->mdev, ppcnt_statistical_group)) 1498 1616 return; 1499 1617 1500 1618 fec_set_corrected_bits_total(priv, fec_stats); 1501 - fec_set_block_stats(priv, fec_stats); 1619 + fec_set_block_stats(priv, mode, fec_stats); 1620 + fec_set_histograms_stats(priv, mode, hist); 1502 1621 } 1503 1622 1504 1623 #define PPORT_ETH_EXT_OFF(c) \
+2 -1
drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
··· 117 117 void mlx5e_stats_pause_get(struct mlx5e_priv *priv, 118 118 struct ethtool_pause_stats *pause_stats); 119 119 void mlx5e_stats_fec_get(struct mlx5e_priv *priv, 120 - struct ethtool_fec_stats *fec_stats); 120 + struct ethtool_fec_stats *fec_stats, 121 + struct ethtool_fec_hist *hist); 121 122 122 123 void mlx5e_stats_eth_phy_get(struct mlx5e_priv *priv, 123 124 struct ethtool_eth_phy_stats *phy_stats);
+2 -1
drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c
··· 1718 1718 1719 1719 static void 1720 1720 fbnic_get_fec_stats(struct net_device *netdev, 1721 - struct ethtool_fec_stats *fec_stats) 1721 + struct ethtool_fec_stats *fec_stats, 1722 + struct ethtool_fec_hist *hist) 1722 1723 { 1723 1724 struct fbnic_net *fbn = netdev_priv(netdev); 1724 1725 struct fbnic_phy_stats *phy_stats;
+2 -1
drivers/net/ethernet/sfc/ethtool.c
··· 217 217 } 218 218 219 219 static void efx_ethtool_get_fec_stats(struct net_device *net_dev, 220 - struct ethtool_fec_stats *fec_stats) 220 + struct ethtool_fec_stats *fec_stats, 221 + struct ethtool_fec_hist *hist) 221 222 { 222 223 struct efx_nic *efx = efx_netdev_priv(net_dev); 223 224
+2 -1
drivers/net/ethernet/sfc/siena/ethtool.c
··· 217 217 } 218 218 219 219 static void efx_ethtool_get_fec_stats(struct net_device *net_dev, 220 - struct ethtool_fec_stats *fec_stats) 220 + struct ethtool_fec_stats *fec_stats, 221 + struct ethtool_fec_hist *hist) 221 222 { 222 223 struct efx_nic *efx = netdev_priv(net_dev); 223 224
+24 -1
drivers/net/netdevsim/ethtool.c
··· 165 165 return 0; 166 166 } 167 167 168 + static const struct ethtool_fec_hist_range netdevsim_fec_ranges[] = { 169 + { 0, 0}, 170 + { 1, 3}, 171 + { 4, 7}, 172 + { 0, 0} 173 + }; 174 + 168 175 static void 169 - nsim_get_fec_stats(struct net_device *dev, struct ethtool_fec_stats *fec_stats) 176 + nsim_get_fec_stats(struct net_device *dev, struct ethtool_fec_stats *fec_stats, 177 + struct ethtool_fec_hist *hist) 170 178 { 179 + struct ethtool_fec_hist_value *values = hist->values; 180 + 181 + hist->ranges = netdevsim_fec_ranges; 182 + 171 183 fec_stats->corrected_blocks.total = 123; 172 184 fec_stats->uncorrectable_blocks.total = 4; 185 + 186 + values[0].per_lane[0] = 125; 187 + values[0].per_lane[1] = 120; 188 + values[0].per_lane[2] = 100; 189 + values[0].per_lane[3] = 100; 190 + values[1].sum = 12; 191 + values[2].sum = 2; 192 + values[2].per_lane[0] = 2; 193 + values[2].per_lane[1] = 0; 194 + values[2].per_lane[2] = 0; 195 + values[2].per_lane[3] = 0; 173 196 } 174 197 175 198 static int nsim_get_ts_info(struct net_device *dev,
+24 -1
include/linux/ethtool.h
··· 492 492 }; 493 493 494 494 #define ETHTOOL_MAX_LANES 8 495 + /** 496 + * IEEE 802.3ck/df defines 16 bins for FEC histogram plus one more for 497 + * the end-of-list marker, total 17 items 498 + */ 499 + #define ETHTOOL_FEC_HIST_MAX 17 500 + /** 501 + * struct ethtool_fec_hist_range - error bits range for FEC histogram 502 + * statistics 503 + * @low: low bound of the bin (inclusive) 504 + * @high: high bound of the bin (inclusive) 505 + */ 506 + struct ethtool_fec_hist_range { 507 + u16 low; 508 + u16 high; 509 + }; 495 510 511 + struct ethtool_fec_hist { 512 + struct ethtool_fec_hist_value { 513 + u64 sum; 514 + u64 per_lane[ETHTOOL_MAX_LANES]; 515 + } values[ETHTOOL_FEC_HIST_MAX]; 516 + const struct ethtool_fec_hist_range *ranges; 517 + }; 496 518 /** 497 519 * struct ethtool_fec_stats - statistics for IEEE 802.3 FEC 498 520 * @corrected_blocks: number of received blocks corrected by FEC ··· 1236 1214 int (*set_link_ksettings)(struct net_device *, 1237 1215 const struct ethtool_link_ksettings *); 1238 1216 void (*get_fec_stats)(struct net_device *dev, 1239 - struct ethtool_fec_stats *fec_stats); 1217 + struct ethtool_fec_stats *fec_stats, 1218 + struct ethtool_fec_hist *hist); 1240 1219 int (*get_fecparam)(struct net_device *, 1241 1220 struct ethtool_fecparam *); 1242 1221 int (*set_fecparam)(struct net_device *,
+73 -2
net/ethtool/fec.c
··· 17 17 u64 stats[1 + ETHTOOL_MAX_LANES]; 18 18 u8 cnt; 19 19 } corr, uncorr, corr_bits; 20 + struct ethtool_fec_hist fec_stat_hist; 20 21 }; 21 22 22 23 #define FEC_REPDATA(__reply_base) \ ··· 114 113 struct ethtool_fec_stats stats; 115 114 116 115 ethtool_stats_init((u64 *)&stats, sizeof(stats) / 8); 117 - dev->ethtool_ops->get_fec_stats(dev, &stats); 116 + ethtool_stats_init((u64 *)data->fec_stat_hist.values, 117 + sizeof(data->fec_stat_hist.values) / 8); 118 + dev->ethtool_ops->get_fec_stats(dev, &stats, 119 + &data->fec_stat_hist); 118 120 119 121 fec_stats_recalc(&data->corr, &stats.corrected_blocks); 120 122 fec_stats_recalc(&data->uncorr, &stats.uncorrectable_blocks); ··· 161 157 len += nla_total_size(sizeof(u8)) + /* _FEC_AUTO */ 162 158 nla_total_size(sizeof(u32)); /* _FEC_ACTIVE */ 163 159 164 - if (req_base->flags & ETHTOOL_FLAG_STATS) 160 + if (req_base->flags & ETHTOOL_FLAG_STATS) { 165 161 len += 3 * nla_total_size_64bit(sizeof(u64) * 166 162 (1 + ETHTOOL_MAX_LANES)); 163 + /* add FEC bins information */ 164 + len += (nla_total_size(0) + /* _A_FEC_HIST */ 165 + nla_total_size(4) + /* _A_FEC_HIST_BIN_LOW */ 166 + nla_total_size(4) + /* _A_FEC_HIST_BIN_HI */ 167 + /* _A_FEC_HIST_BIN_VAL + per-lane values */ 168 + nla_total_size_64bit(sizeof(u64)) + 169 + nla_total_size_64bit(sizeof(u64) * ETHTOOL_MAX_LANES)) * 170 + ETHTOOL_FEC_HIST_MAX; 171 + } 167 172 168 173 return len; 174 + } 175 + 176 + static int fec_put_hist(struct sk_buff *skb, 177 + const struct ethtool_fec_hist *hist) 178 + { 179 + const struct ethtool_fec_hist_range *ranges = hist->ranges; 180 + const struct ethtool_fec_hist_value *values = hist->values; 181 + struct nlattr *nest; 182 + int i, j; 183 + u64 sum; 184 + 185 + if (!ranges) 186 + return 0; 187 + 188 + for (i = 0; i < ETHTOOL_FEC_HIST_MAX; i++) { 189 + if (i && !ranges[i].low && !ranges[i].high) 190 + break; 191 + 192 + if (WARN_ON_ONCE(values[i].sum == ETHTOOL_STAT_NOT_SET && 193 + values[i].per_lane[0] == ETHTOOL_STAT_NOT_SET)) 194 + break; 195 + 196 + nest = nla_nest_start(skb, ETHTOOL_A_FEC_STAT_HIST); 197 + if (!nest) 198 + return -EMSGSIZE; 199 + 200 + if (nla_put_u32(skb, ETHTOOL_A_FEC_HIST_BIN_LOW, 201 + ranges[i].low) || 202 + nla_put_u32(skb, ETHTOOL_A_FEC_HIST_BIN_HIGH, 203 + ranges[i].high)) 204 + goto err_cancel_hist; 205 + sum = 0; 206 + for (j = 0; j < ETHTOOL_MAX_LANES; j++) { 207 + if (values[i].per_lane[j] == ETHTOOL_STAT_NOT_SET) 208 + break; 209 + sum += values[i].per_lane[j]; 210 + } 211 + if (nla_put_uint(skb, ETHTOOL_A_FEC_HIST_BIN_VAL, 212 + values[i].sum == ETHTOOL_STAT_NOT_SET ? 213 + sum : values[i].sum)) 214 + goto err_cancel_hist; 215 + if (j && nla_put_64bit(skb, ETHTOOL_A_FEC_HIST_BIN_VAL_PER_LANE, 216 + sizeof(u64) * j, 217 + values[i].per_lane, 218 + ETHTOOL_A_FEC_HIST_PAD)) 219 + goto err_cancel_hist; 220 + 221 + nla_nest_end(skb, nest); 222 + } 223 + 224 + return 0; 225 + 226 + err_cancel_hist: 227 + nla_nest_cancel(skb, nest); 228 + return -EMSGSIZE; 169 229 } 170 230 171 231 static int fec_put_stats(struct sk_buff *skb, const struct fec_reply_data *data) ··· 249 181 nla_put_64bit(skb, ETHTOOL_A_FEC_STAT_CORR_BITS, 250 182 sizeof(u64) * data->corr_bits.cnt, 251 183 data->corr_bits.stats, ETHTOOL_A_FEC_STAT_PAD)) 184 + goto err_cancel; 185 + 186 + if (fec_put_hist(skb, &data->fec_stat_hist)) 252 187 goto err_cancel; 253 188 254 189 nla_nest_end(skb, nest);
+33 -2
tools/testing/selftests/drivers/net/stats.py
··· 57 57 ksft_true(data['stats'], "driver does not report stats") 58 58 59 59 60 + def check_fec_hist(cfg) -> None: 61 + """ 62 + Check that drivers which support FEC histogram statistics report 63 + reasonable values. 64 + """ 65 + 66 + try: 67 + data = ethnl.fec_get({"header": {"dev-index": cfg.ifindex, 68 + "flags": {'stats'}}}) 69 + except NlError as e: 70 + if e.error == errno.EOPNOTSUPP: 71 + raise KsftSkipEx("FEC not supported by the device") from e 72 + raise 73 + if 'stats' not in data: 74 + raise KsftSkipEx("FEC stats not supported by the device") 75 + if 'hist' not in data['stats']: 76 + raise KsftSkipEx("FEC histogram not supported by the device") 77 + 78 + hist = data['stats']['hist'] 79 + for fec_bin in hist: 80 + for key in ['bin-low', 'bin-high', 'bin-val']: 81 + ksft_in(key, fec_bin, 82 + "Drivers should always report FEC bin range and value") 83 + ksft_ge(fec_bin['bin-high'], fec_bin['bin-low'], 84 + "FEC bin range should be valid") 85 + if 'bin-val-per-lane' in fec_bin: 86 + ksft_eq(sum(fec_bin['bin-val-per-lane']), fec_bin['bin-val'], 87 + "FEC bin value should be equal to sum of per-plane values") 88 + 89 + 60 90 def pkt_byte_sum(cfg) -> None: 61 91 """ 62 92 Check that qstat and interface stats match in value. ··· 309 279 """ Ksft boiler plate main """ 310 280 311 281 with NetDrvEnv(__file__, queue_count=100) as cfg: 312 - ksft_run([check_pause, check_fec, pkt_byte_sum, qstat_by_ifindex, 313 - check_down, procfs_hammer, procfs_downup_hammer], 282 + ksft_run([check_pause, check_fec, check_fec_hist, pkt_byte_sum, 283 + qstat_by_ifindex, check_down, procfs_hammer, 284 + procfs_downup_hammer], 314 285 args=(cfg, )) 315 286 ksft_exit() 316 287