mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
Merge branch 'net-mlx5e-fix-crashes-in-dynamic-per-channel-stats-and-hv-vhca-agent'
Tariq Toukan says:
====================
net/mlx5e: Fix crashes in dynamic per-channel stats and HV VHCA agent
Since per-channel stats were converted to be allocated and published
lazily at first channel open in commit fa691d0c9c ("net/mlx5e:
Allocate per-channel stats dynamically at first usage"),
priv->channel_stats[] and priv->stats_nch are filled in
incrementally during interface bring-up. This opened a window in
which the various stats readers - most of them reachable from
userspace via netlink/netdev stats queries - can race with
mlx5e_open_channel() on another CPU and observe partially
initialized state. The HV VHCA stats agent, which is created
before the channels are opened, hits related problems of its own.
This series by Feng fixes the resulting crashes.
V3: https://lore.kernel.org/all/20260622083646.593220-1-tariqt@nvidia.com/
V2: https://lore.kernel.org/all/20260617140127.573117-1-tariqt@nvidia.com/
====================
Link: https://patch.msgid.link/20260630115151.729219-1-tariqt@nvidia.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
commit
d645674342
|
|
@ -987,6 +987,18 @@ struct mlx5e_priv {
|
|||
struct ethtool_fec_hist_range *fec_ranges;
|
||||
};
|
||||
|
||||
static inline u16 mlx5e_stats_nch_read(const struct mlx5e_priv *priv)
|
||||
{
|
||||
/* Pairs with smp_store_release in mlx5e_stats_nch_write(). */
|
||||
return smp_load_acquire(&priv->stats_nch);
|
||||
}
|
||||
|
||||
static inline void mlx5e_stats_nch_write(struct mlx5e_priv *priv, u16 n)
|
||||
{
|
||||
/* Pairs with smp_load_acquire in mlx5e_stats_nch_read(). */
|
||||
smp_store_release(&priv->stats_nch, n);
|
||||
}
|
||||
|
||||
struct mlx5e_dev {
|
||||
struct net_device *netdev;
|
||||
struct devlink_port dl_port;
|
||||
|
|
|
|||
|
|
@ -33,9 +33,10 @@ mlx5e_hv_vhca_fill_ring_stats(struct mlx5e_priv *priv, int ch,
|
|||
static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data,
|
||||
int buf_len)
|
||||
{
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
int ch, i = 0;
|
||||
|
||||
for (ch = 0; ch < priv->stats_nch; ch++) {
|
||||
for (ch = 0; ch < nch; ch++) {
|
||||
void *buf = data + i;
|
||||
|
||||
if (WARN_ON_ONCE(buf +
|
||||
|
|
@ -49,9 +50,16 @@ static void mlx5e_hv_vhca_fill_stats(struct mlx5e_priv *priv, void *data,
|
|||
}
|
||||
|
||||
static int mlx5e_hv_vhca_stats_buf_size(struct mlx5e_priv *priv)
|
||||
{
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
|
||||
return sizeof(struct mlx5e_hv_vhca_per_ring_stats) * nch;
|
||||
}
|
||||
|
||||
static int mlx5e_hv_vhca_stats_buf_max_size(struct mlx5e_priv *priv)
|
||||
{
|
||||
return (sizeof(struct mlx5e_hv_vhca_per_ring_stats) *
|
||||
priv->stats_nch);
|
||||
max(priv->max_nch, priv->stats_nch));
|
||||
}
|
||||
|
||||
static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
|
||||
|
|
@ -67,7 +75,7 @@ static void mlx5e_hv_vhca_stats_work(struct work_struct *work)
|
|||
sagent = container_of(dwork, struct mlx5e_hv_vhca_stats_agent, work);
|
||||
priv = container_of(sagent, struct mlx5e_priv, stats_agent);
|
||||
buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
|
||||
agent = sagent->agent;
|
||||
agent = READ_ONCE(sagent->agent);
|
||||
buf = sagent->buf;
|
||||
|
||||
memset(buf, 0, buf_len);
|
||||
|
|
@ -100,7 +108,7 @@ static void mlx5e_hv_vhca_stats_control(struct mlx5_hv_vhca_agent *agent,
|
|||
sagent = &priv->stats_agent;
|
||||
|
||||
block->version = MLX5_HV_VHCA_STATS_VERSION;
|
||||
block->rings = priv->stats_nch;
|
||||
block->rings = mlx5e_stats_nch_read(priv);
|
||||
|
||||
if (!block->command) {
|
||||
cancel_delayed_work_sync(&priv->stats_agent.work);
|
||||
|
|
@ -122,18 +130,21 @@ static void mlx5e_hv_vhca_stats_cleanup(struct mlx5_hv_vhca_agent *agent)
|
|||
|
||||
void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
|
||||
{
|
||||
int buf_len = mlx5e_hv_vhca_stats_buf_size(priv);
|
||||
int buf_len = mlx5e_hv_vhca_stats_buf_max_size(priv);
|
||||
struct mlx5_hv_vhca_agent *agent;
|
||||
|
||||
priv->stats_agent.buf = kvzalloc(buf_len, GFP_KERNEL);
|
||||
if (!priv->stats_agent.buf)
|
||||
return;
|
||||
|
||||
INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
|
||||
|
||||
agent = mlx5_hv_vhca_agent_create(priv->mdev->hv_vhca,
|
||||
MLX5_HV_VHCA_AGENT_STATS,
|
||||
mlx5e_hv_vhca_stats_control, NULL,
|
||||
mlx5e_hv_vhca_stats_cleanup,
|
||||
priv);
|
||||
priv,
|
||||
&priv->stats_agent.agent);
|
||||
|
||||
if (IS_ERR_OR_NULL(agent)) {
|
||||
if (IS_ERR(agent))
|
||||
|
|
@ -142,18 +153,20 @@ void mlx5e_hv_vhca_stats_create(struct mlx5e_priv *priv)
|
|||
agent);
|
||||
|
||||
kvfree(priv->stats_agent.buf);
|
||||
return;
|
||||
priv->stats_agent.buf = NULL;
|
||||
}
|
||||
|
||||
priv->stats_agent.agent = agent;
|
||||
INIT_DELAYED_WORK(&priv->stats_agent.work, mlx5e_hv_vhca_stats_work);
|
||||
}
|
||||
|
||||
void mlx5e_hv_vhca_stats_destroy(struct mlx5e_priv *priv)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(priv->stats_agent.agent))
|
||||
struct mlx5_hv_vhca_agent *agent;
|
||||
|
||||
agent = READ_ONCE(priv->stats_agent.agent);
|
||||
if (IS_ERR_OR_NULL(agent))
|
||||
return;
|
||||
|
||||
mlx5_hv_vhca_agent_destroy(priv->stats_agent.agent);
|
||||
mlx5_hv_vhca_agent_destroy(agent);
|
||||
WRITE_ONCE(priv->stats_agent.agent, NULL);
|
||||
kvfree(priv->stats_agent.buf);
|
||||
priv->stats_agent.buf = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2773,7 +2773,7 @@ static int mlx5e_channel_stats_alloc(struct mlx5e_priv *priv, int ix, int cpu)
|
|||
GFP_KERNEL, cpu_to_node(cpu));
|
||||
if (!priv->channel_stats[ix])
|
||||
return -ENOMEM;
|
||||
priv->stats_nch++;
|
||||
mlx5e_stats_nch_write(priv, priv->stats_nch + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -4040,9 +4040,10 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
|
|||
|
||||
void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s)
|
||||
{
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < priv->stats_nch; i++) {
|
||||
for (i = 0; i < nch; i++) {
|
||||
struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i];
|
||||
struct mlx5e_rq_stats *xskrq_stats = &channel_stats->xskrq;
|
||||
struct mlx5e_rq_stats *rq_stats = &channel_stats->rq;
|
||||
|
|
@ -5488,7 +5489,7 @@ static void mlx5e_get_queue_stats_rx(struct net_device *dev, int i,
|
|||
struct mlx5e_rq_stats *xskrq_stats;
|
||||
struct mlx5e_rq_stats *rq_stats;
|
||||
|
||||
if (mlx5e_is_uplink_rep(priv) || !priv->stats_nch)
|
||||
if (mlx5e_is_uplink_rep(priv) || !mlx5e_stats_nch_read(priv))
|
||||
return;
|
||||
|
||||
channel_stats = priv->channel_stats[i];
|
||||
|
|
@ -5512,7 +5513,7 @@ static void mlx5e_get_queue_stats_tx(struct net_device *dev, int i,
|
|||
struct mlx5e_priv *priv = netdev_priv(dev);
|
||||
struct mlx5e_sq_stats *sq_stats;
|
||||
|
||||
if (!priv->stats_nch)
|
||||
if (!mlx5e_stats_nch_read(priv))
|
||||
return;
|
||||
|
||||
/* no special case needed for ptp htb etc since txq2sq_stats is kept up
|
||||
|
|
@ -5538,6 +5539,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
|
|||
struct netdev_queue_stats_tx *tx)
|
||||
{
|
||||
struct mlx5e_priv *priv = netdev_priv(dev);
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
struct mlx5e_ptp *ptp_channel;
|
||||
int i, tc;
|
||||
|
||||
|
|
@ -5549,7 +5551,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
|
|||
rx->hw_gro_wire_packets = 0;
|
||||
rx->hw_gro_wire_bytes = 0;
|
||||
|
||||
for (i = priv->channels.params.num_channels; i < priv->stats_nch; i++) {
|
||||
for (i = priv->channels.params.num_channels; i < nch; i++) {
|
||||
struct netdev_queue_stats_rx rx_i = {0};
|
||||
|
||||
mlx5e_get_queue_stats_rx(dev, i, &rx_i);
|
||||
|
|
@ -5585,7 +5587,7 @@ static void mlx5e_get_base_stats(struct net_device *dev,
|
|||
tx->stop = 0;
|
||||
tx->wake = 0;
|
||||
|
||||
for (i = 0; i < priv->stats_nch; i++) {
|
||||
for (i = 0; i < nch; i++) {
|
||||
struct mlx5e_channel_stats *channel_stats = priv->channel_stats[i];
|
||||
|
||||
/* handle two cases:
|
||||
|
|
|
|||
|
|
@ -515,6 +515,7 @@ static void mlx5e_stats_update_stats_rq_page_pool(struct mlx5e_channel *c)
|
|||
static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw)
|
||||
{
|
||||
struct mlx5e_sw_stats *s = &priv->stats.sw;
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
int i;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
|
|
@ -522,7 +523,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw)
|
|||
for (i = 0; i < priv->channels.num; i++) /* for active channels only */
|
||||
mlx5e_stats_update_stats_rq_page_pool(priv->channels.c[i]);
|
||||
|
||||
for (i = 0; i < priv->stats_nch; i++) {
|
||||
for (i = 0; i < nch; i++) {
|
||||
struct mlx5e_channel_stats *channel_stats =
|
||||
priv->channel_stats[i];
|
||||
|
||||
|
|
@ -2614,7 +2615,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ptp) { return; }
|
|||
|
||||
static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels)
|
||||
{
|
||||
int max_nch = priv->stats_nch;
|
||||
int max_nch = mlx5e_stats_nch_read(priv);
|
||||
|
||||
return (NUM_RQ_STATS * max_nch) +
|
||||
(NUM_CH_STATS * max_nch) +
|
||||
|
|
@ -2627,8 +2628,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(channels)
|
|||
|
||||
static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels)
|
||||
{
|
||||
int max_nch = mlx5e_stats_nch_read(priv);
|
||||
bool is_xsk = priv->xsk.ever_used;
|
||||
int max_nch = priv->stats_nch;
|
||||
int i, j, tc;
|
||||
|
||||
for (i = 0; i < max_nch; i++)
|
||||
|
|
@ -2660,8 +2661,8 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(channels)
|
|||
|
||||
static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(channels)
|
||||
{
|
||||
int max_nch = mlx5e_stats_nch_read(priv);
|
||||
bool is_xsk = priv->xsk.ever_used;
|
||||
int max_nch = priv->stats_nch;
|
||||
int i, j, tc;
|
||||
|
||||
for (i = 0; i < max_nch; i++)
|
||||
|
|
|
|||
|
|
@ -135,10 +135,11 @@ void mlx5i_cleanup(struct mlx5e_priv *priv)
|
|||
|
||||
static void mlx5i_grp_sw_update_stats(struct mlx5e_priv *priv)
|
||||
{
|
||||
u16 nch = mlx5e_stats_nch_read(priv);
|
||||
struct rtnl_link_stats64 s = {};
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < priv->stats_nch; i++) {
|
||||
for (i = 0; i < nch; i++) {
|
||||
struct mlx5e_channel_stats *channel_stats;
|
||||
struct mlx5e_rq_stats *rq_stats;
|
||||
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ mlx5_hv_vhca_control_agent_create(struct mlx5_hv_vhca *hv_vhca)
|
|||
return mlx5_hv_vhca_agent_create(hv_vhca, MLX5_HV_VHCA_AGENT_CONTROL,
|
||||
NULL,
|
||||
mlx5_hv_vhca_control_agent_invalidate,
|
||||
NULL, NULL);
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void mlx5_hv_vhca_control_agent_destroy(struct mlx5_hv_vhca_agent *agent)
|
||||
|
|
@ -256,7 +256,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
|
|||
void (*invalidate)(struct mlx5_hv_vhca_agent*,
|
||||
u64 block_mask),
|
||||
void (*cleaup)(struct mlx5_hv_vhca_agent *agent),
|
||||
void *priv)
|
||||
void *priv,
|
||||
struct mlx5_hv_vhca_agent **ctx_update)
|
||||
{
|
||||
struct mlx5_hv_vhca_agent *agent;
|
||||
|
||||
|
|
@ -284,6 +285,9 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
|
|||
agent->invalidate = invalidate;
|
||||
agent->cleanup = cleaup;
|
||||
|
||||
if (ctx_update)
|
||||
WRITE_ONCE(*ctx_update, agent);
|
||||
|
||||
mutex_lock(&hv_vhca->agents_lock);
|
||||
hv_vhca->agents[type] = agent;
|
||||
mutex_unlock(&hv_vhca->agents_lock);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
|
|||
void (*invalidate)(struct mlx5_hv_vhca_agent*,
|
||||
u64 block_mask),
|
||||
void (*cleanup)(struct mlx5_hv_vhca_agent *agent),
|
||||
void *context);
|
||||
void *context,
|
||||
struct mlx5_hv_vhca_agent **ctx_update);
|
||||
|
||||
void mlx5_hv_vhca_agent_destroy(struct mlx5_hv_vhca_agent *agent);
|
||||
int mlx5_hv_vhca_agent_write(struct mlx5_hv_vhca_agent *agent,
|
||||
|
|
@ -84,7 +85,8 @@ mlx5_hv_vhca_agent_create(struct mlx5_hv_vhca *hv_vhca,
|
|||
void (*invalidate)(struct mlx5_hv_vhca_agent*,
|
||||
u64 block_mask),
|
||||
void (*cleanup)(struct mlx5_hv_vhca_agent *agent),
|
||||
void *context)
|
||||
void *context,
|
||||
struct mlx5_hv_vhca_agent **ctx_update)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user