]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ethtool: Embed FEC hist ranges as buffer in struct
authorEric Joyner <eric.joyner@amd.com>
Thu, 23 Jul 2026 04:13:42 +0000 (21:13 -0700)
committerJakub Kicinski <kuba@kernel.org>
Tue, 28 Jul 2026 01:23:07 +0000 (18:23 -0700)
When a driver's .get_fec_stats() handler is called and the driver
supports FEC histogram stats, the driver supplies the histogram bin
ranges via a pointer.  This pointer is assigned while under the netdev
ops lock in fec_prepare_data(), but the actual data is only read after
the lock is released; so this allows the driver to change the ranges
(e.g. from another .get_fec_stats() call) while the current call chain
is reading them in fec_fill_reply().

Fix this by adding an ethtool core-owned buffer, ranges_buf, to struct
ethtool_fec_hist. Drivers whose ranges are built dynamically (currently
just mlx5) fill ranges_buf and then point the existing ranges pointer at
it, giving ethtool a consistent copy that stays valid after the netdev
ops lock is dropped and later in fec_fill_reply(). Drivers whose ranges
are compile-time constants (bnxt, netdevsim) are unaffected by the
potential race and keep setting the existing ranges pointer to their
constant array, without making copies.

Fixes: cc2f08129925 ("ethtool: add FEC bins histogram report")
Signed-off-by: Eric Joyner <eric.joyner@amd.com>
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Link: https://patch.msgid.link/20260723041342.39238-1-eric.joyner@amd.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
include/linux/ethtool.h

index d507289096c2067a65119b5f818c91eee874e86e..6867a5aed42c086870fb446925b4ed6fb4ac7f05 100644 (file)
@@ -984,7 +984,6 @@ 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;
 };
 
 static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv)
index c1acb9012d3f9edc121b803aa8957994cc583c24..7d47a1da8b6b64e5d87a33765cf90ad1415da0ad 100644 (file)
@@ -6415,14 +6415,8 @@ int mlx5e_priv_init(struct mlx5e_priv *priv,
        if (!priv->channel_stats)
                goto err_free_tx_rates;
 
-       priv->fec_ranges = kzalloc_objs(*priv->fec_ranges, ETHTOOL_FEC_HIST_MAX);
-       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:
@@ -6447,7 +6441,6 @@ 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);
index de38b60806c264fa35040c3d7141f60212fd2100..e7e6db7f6bf175e26b243a8f43ac676d1e4ff04d 100644 (file)
@@ -1550,7 +1550,7 @@ static bool fec_rs_validate_hist_type(int mode, int hist_type)
 
 static u8
 fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode,
-                            const struct ethtool_fec_hist_range **ranges)
+                            struct ethtool_fec_hist_range *ranges)
 {
        struct mlx5_core_dev *mdev = priv->mdev;
        u32 out[MLX5_ST_SZ_DW(pphcr_reg)] = {0};
@@ -1558,8 +1558,6 @@ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode,
        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;
@@ -1575,12 +1573,11 @@ fec_rs_histogram_fill_ranges(struct mlx5e_priv *priv, int mode,
        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[i].high = MLX5_GET(bin_range_layout, bin_range,
+                                         high_val);
+               ranges[i].low = MLX5_GET(bin_range_layout, bin_range,
+                                        low_val);
        }
-       *ranges = priv->fec_ranges;
 
        return num_of_bins;
 }
@@ -1622,10 +1619,12 @@ static void fec_set_histograms_stats(struct mlx5e_priv *priv, int mode,
        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)
+                       fec_rs_histogram_fill_ranges(priv, mode, hist->ranges_buf);
+               if (num_of_bins) {
+                       hist->ranges = hist->ranges_buf;
                        return fec_rs_histogram_fill_stats(priv, num_of_bins,
                                                           hist);
+               }
                break;
        default:
                return;
index 5d491a98265ef92fdb588ad19b31ff8106a6dae5..12683b5d125e41342e5d1324ce476e8a4da8e791 100644 (file)
@@ -562,6 +562,7 @@ struct ethtool_fec_hist {
                u64 per_lane[ETHTOOL_MAX_LANES];
        } values[ETHTOOL_FEC_HIST_MAX];
        const struct ethtool_fec_hist_range *ranges;
+       struct ethtool_fec_hist_range ranges_buf[ETHTOOL_FEC_HIST_MAX];
 };
 /**
  * struct ethtool_fec_stats - statistics for IEEE 802.3 FEC