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

ethtool: add FEC bins histogram report

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. Drivers can leave bin
counter uninitialized if per-lane values are provided. In this case the
core will recalculate summ for the bin.

Signed-off-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Link: https://patch.msgid.link/20250924124037.1508846-2-vadim.fedorenko@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Vadim Fedorenko and committed by
Jakub Kicinski
cc2f0812 fbb8bc40

+186 -13
+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;
+2 -1
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
+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);