mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Tariq Toukan <tariqt@nvidia.com>
To: netdev-bot+sashiko@kernel.org
Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@kernel.org,
	kuba@kernel.org, netdev@vger.kernel.org, pabeni@redhat.com,
	ast@kernel.org, bpf@vger.kernel.org, cjubran@nvidia.com,
	cratiu@nvidia.com, daniel@iogearbox.net, dtatulea@nvidia.com,
	gal@nvidia.com, hawk@kernel.org, john.fastabend@gmail.com,
	kees@kernel.org, leon@kernel.org, linux-kernel@vger.kernel.org,
	linux-rdma@vger.kernel.org, lirongqing@baidu.com,
	mbloch@nvidia.com, richardcochran@gmail.com, saeedm@nvidia.com,
	shshitrit@nvidia.com, sdf@fomichev.me, toke@redhat.com,
	toke@kernel.org, amorenoz@redhat.com, mschmidt@redhat.com
Subject: Re: [PATCH net-next 1/2] net/mlx5e: Batch NAPI RCU grace periods on channel close
Date: Thu, 15 Oct 2026 13:53:53 +0300	[thread overview]
Message-ID: <662109f7-44aa-452b-986d-74ed819a3afb@nvidia.com> (raw)
In-Reply-To: <179181290697.434549.8876520561458529007@kernel.org>



On 12/10/2026 16:48, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential
> issue(s) to consider.
> 
> Critical: 1 · High: 0 · Medium: 1 · Low: 2
> 
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
> 
> - [Critical] Use-after-free in mlx5e_close_locked() (the ndo_stop path),
>    introduced by this patch.
> - [Medium] The commit message does not match the close-phase code, and
>    the close path's safety now rests on an undocumented, unenforced…
> - [Low] mlx5e_close_locked() now calls mlx5e_close_channels_pre_sync()
>    and mlx5e_close_channels_post_sync() directly instead of…
> - [Low] The patch changes how mlx5e_close_channel_post_sync()
>    (en_main.c:3032) decides whether to tear down a channel's XSK queues,…
> 
> --
> 
> net/mlx5e: Batch NAPI RCU grace periods on channel close
> 
> This splits mlx5e channel deactivate and close into pre_sync and post_sync
> phases, so one synchronize_net() covers all channels instead of one per
> queue. NAPI instances are unlinked with __netif_napi_del_locked(), and a
> single grace period is taken before the channels are freed. Single-queue
> paths are meant to keep their own synchronize_net() calls.
> 
>> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
>> index f90b5a34e097..fe4e5d0b2e9a 100644
>> --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
>> +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
> 
> [ ... ]
> 
>> @@ -2141,18 +2160,28 @@ int mlx5e_open_xdpsq(struct mlx5e_channel *c, struct mlx5e_params *params,
>>   	return err;
>>   }
>>   
>> -void mlx5e_close_xdpsq(struct mlx5e_xdpsq *sq)
>> +static void mlx5e_destroy_xdpsq(struct mlx5e_xdpsq *sq)
>>   {
>>   	struct mlx5e_channel *c = sq->channel;
>>   
>> -	clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
>> -	synchronize_net(); /* Sync with NAPI. */
>> -
>>   	mlx5e_destroy_sq(c->mdev, sq->sqn);
>>   	mlx5e_free_xdpsq_descs(sq);
>>   	mlx5e_free_xdpsq(sq);
>>   }
>>   
>> +void mlx5e_close_xdpsq_post_sync(struct mlx5e_xdpsq *sq)
>> +{
>> +	clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
>> +	mlx5e_destroy_xdpsq(sq);
>> +}
>> +
>> +void mlx5e_close_xdpsq(struct mlx5e_xdpsq *sq)
>> +{
>> +	clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
>> +	synchronize_net(); /* Sync with NAPI. */
>> +	mlx5e_destroy_xdpsq(sq);
>> +}
>> +
> 
> [Severity: Medium]
> The commit message says:
> 
>    First clear the ENABLED state on all queues, then wait for a single
>    grace period before continuing with the teardown.
> 
> and:
> 
>    Keep the existing per object synchronize_net() calls for single queue
>    paths.
> 
> Does that match the close phase?
> 
> mlx5e_close_xdpsq_post_sync() clears MLX5E_SQ_STATE_ENABLED after the
> shared grace period and then destroys the SQ right away. So there is no
> pre_sync clear for rq_xdpsq, the XDP_REDIRECT xdpsq or xsksq, and nothing
> waits after this clear_bit(). mlx5e_close_channel_pre_sync() only clears
> MLX5E_CHANNEL_STATE_XSK.
> 
> The other close-phase waits follow the same pattern:
> 
> - mlx5e_qos_close_queues_post_sync() removes the synchronize_rcu()
>    between unpublishing and freeing the array instead of batching it.
> 
> - The single-queue path mlx5e_close_xsk() goes from two synchronize_net()
>    calls to one. That particular reduction looks safe on its own, because
>    NAPI only touches xsksq under the xsk_open snapshot taken in the same
>    RCU section.
> 
> So these waits look removed rather than batched, on the assumption that
> NAPI is already disabled. That precondition only appears in a comment in
> mlx5e_qos_close_queues_post_sync():
> 
> 	/* Caller guarantees that NAPI is stopped or was never enabled. */
> 
> mlx5e_close_xdpsq_post_sync() is exported in en.h and also called from
> xsk/setup.c, but nothing documents or enforces that requirement.
> 
> Could the commit message describe this? Could the precondition also be
> documented on the exported post_sync helpers? As noted below for
> mlx5e_close_locked(), NAPI being disabled may not be enough on its own.
> 
> [ ... ]
> 
>> @@ -2936,35 +2993,56 @@ static void mlx5e_activate_channel(struct mlx5e_channel *c)
> 
> [ ... ]
> 
>> -static void mlx5e_close_channel(struct mlx5e_channel *c)
>> +static void mlx5e_close_channel_pre_sync(struct mlx5e_channel *c)
>>   {
>>   	if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state))
>> -		mlx5e_close_xsk(c);
>> -	mlx5e_close_queues(c);
>> -	mlx5e_qos_close_queues(c);
>> -	netif_napi_del_locked(&c->napi);
>> +		mlx5e_close_xsk_pre_sync(c);
>> +}
>> +
>> +static void mlx5e_close_channel_post_sync(struct mlx5e_channel *c)
>> +{
>> +	if (c->xskrq.xsk_pool)
>> +		mlx5e_close_xsk_post_sync(c);
> 
> [Severity: Low]
> This isn't a bug, but the test for tearing down the XSK queues changes
> here from test_bit(MLX5E_CHANNEL_STATE_XSK, c->state) to
> c->xskrq.xsk_pool. It is only correct because of the new error-path reset
> in mlx5e_open_xsk() in xsk/setup.c:
> 
> err_close_rx_cq:
> 	mlx5e_close_cq(&c->xskrq.cq);
> 
> 	c->xskrq.xsk_pool = NULL;
> 	c->xsksq.xsk_pool = NULL;
> 
> Without that reset, a failed mlx5e_xsk_enable_locked() on a live channel
> would leave the pool pointer set. Close would then tear down xskrq and
> xsksq a second time.
> 
> The two hunks depend on each other, but the commit message mentions
> neither. Could this be noted in the commit message, so a partial backport
> or a later edit to the error path doesn't bring back a double teardown?
> 
>> +	mlx5e_close_queues_post_sync(c);
>> +	mlx5e_qos_close_queues_post_sync(c);
>> +}
> 
> [ ... ]
> 
>> @@ -3060,33 +3138,72 @@ static int mlx5e_wait_channels_min_rx_wqes(struct mlx5e_channels *chs)
> 
> [ ... ]
> 
>> +static void mlx5e_close_channels_post_sync(struct mlx5e_channels *chs)
>>   {
>>   	int i;
>>   
>> -	ASSERT_RTNL();
>>   	if (chs->ptp) {
>> -		mlx5e_ptp_close(chs->ptp);
>> +		mlx5e_ptp_close_queues(chs->ptp);
>> +		__netif_napi_del_locked(&chs->ptp->napi);
>> +	}
>> +
>> +	for (i = 0; i < chs->num; i++) {
>> +		mlx5e_close_channel_post_sync(chs->c[i]);
>> +		__netif_napi_del_locked(&chs->c[i]->napi);
>> +	}
>> +
>> +	synchronize_net();
>> +
>> +	if (chs->ptp) {
>> +		kvfree(chs->ptp);
>>   		chs->ptp = NULL;
>>   	}
>> +
>>   	for (i = 0; i < chs->num; i++)
>> -		mlx5e_close_channel(chs->c[i]);
>> +		kvfree(chs->c[i]);
>>   
>>   	kfree(chs->c);
>>   	chs->num = 0;
>>   }
>>   
>> +void mlx5e_close_channels(struct mlx5e_channels *chs)
>> +{
>> +	ASSERT_RTNL();
>> +	mlx5e_close_channels_pre_sync(chs);
>> +	synchronize_net();
>> +	mlx5e_close_channels_post_sync(chs);
>> +}
> 
> [Severity: Low]
> mlx5e_close_locked() now calls mlx5e_close_channels_pre_sync() and
> mlx5e_close_channels_post_sync() directly. That means the ASSERT_RTNL(),
> which now lives only in this wrapper, no longer runs on the ndo_stop path.
> 
> This isn't a functional bug, since mlx5e_close() and mlx5e_rep_close()
> both run under RTNL. Would it be worth keeping the assertion in
> mlx5e_close_locked() or in the split helpers?
> 
> [ ... ]
> 
>> @@ -3611,8 +3741,14 @@ int mlx5e_close_locked(struct net_device *netdev)
>>   	clear_bit(MLX5E_STATE_OPENED, &priv->state);
>>   
>>   	netif_carrier_off(priv->netdev);
>> -	mlx5e_deactivate_priv_channels(priv);
>> -	mlx5e_close_channels(&priv->channels);
>> +
>> +	mlx5e_deactivate_priv_channels_pre_sync(priv);
>> +	mlx5e_close_channels_pre_sync(&priv->channels);
>> +
>> +	synchronize_net();
>> +
>> +	mlx5e_deactivate_priv_channels_post_sync(priv);
>> +	mlx5e_close_channels_post_sync(&priv->channels);
> 
> [Severity: Critical]
> In mlx5e_close_locked(), the only grace period now runs before
> napi_disable_locked(). What waits for a NAPI poll that is still running
> between napi_disable_locked() and the frees in
> mlx5e_close_channels_post_sync()?
> 
> The sequence becomes:
> 
> mlx5e_close_locked()
>      synchronize_net()
>      mlx5e_deactivate_priv_channels_post_sync()
>          mlx5e_deactivate_channel_post_sync()
>              napi_disable_locked(&c->napi)
>      mlx5e_close_channels_post_sync()
>          mlx5e_close_channel_post_sync()
>              mlx5e_close_queues_post_sync()
>                  frees sq->dim, rq->dim, c->async_icosq, c->xdpsq
>              mlx5e_qos_close_queues_post_sync()
>                  kfree(sq), kvfree(qos_sqs), no grace period
>          synchronize_net()
>          kvfree(chs->c[i])
> 
> napi_disable_locked() only waits for NAPIF_STATE_SCHED to be released.
> Documentation/networking/napi.rst notes that napi_disable() does not wait
> for the poll method to exit.
> 
> mlx5e_napi_poll() keeps using queue memory after napi_complete_done(),
> inside rcu_read_lock():
> 
> mlx5e_napi_poll() {
>      ...
> 	qos_sqs = rcu_dereference(c->qos_sqs);
>      ...
> 	if (unlikely(!napi_complete_done(napi, work_done)))
> 		goto out;
>      ...
> 	for (i = 0; i < c->num_tc; i++) {
> 		mlx5e_handle_tx_dim(&c->sq[i]);
> 		mlx5e_cq_arm(&c->sq[i].cq);
>      ...
> 			if (sq) {
> 				mlx5e_handle_tx_dim(sq);
> 				mlx5e_cq_arm(&sq->cq);
>      ...
> 	mlx5e_handle_rx_dim(rq);
>      ...
> }
> 
> The NAPI CPU could be delayed after napi_complete_done(), for example a
> preempted threaded NAPI on PREEMPT_RT, or vCPU preemption. In that case,
> can this tail touch the QoS SQs, the qos_sqs array, the DIM structs,
> async_icosq and xdpsq after they have been freed? net_dim() could also
> schedule dim->work on freed memory after cancel_work_sync() has already
> run.
> 
> Before this patch, mlx5e_qos_close_queues() did synchronize_rcu() between
> rcu_replace_pointer() and the frees. The per-object synchronize_net()
> calls also gave a grace period after each earlier channel's
> napi_disable_locked().
> 
> The synchronize_net() in mlx5e_close_channels_post_sync() runs after the
> queues are already freed, so it only covers the mlx5e_channel and
> mlx5e_ptp structs.
> 
> The other paths call mlx5e_deactivate_priv_channels() and then
> mlx5e_close_channels(), so they still get one grace period after
> napi_disable_locked(). Is merging the two into a single wait safe for
> mlx5e_close_locked()?
> 

We'll fix.

pw-bot: cr

>>   
>>   	return 0;
>>   }
> 


  reply	other threads:[~2026-10-15 10:54 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-11 13:32 [PATCH net-next 0/2] net/mlx5e: Batch RCU grace periods on channel open and close Tariq Toukan
2026-10-11 13:32 ` [PATCH net-next 1/2] net/mlx5e: Batch NAPI RCU grace periods on channel close Tariq Toukan
2026-10-12 13:48   ` netdev-bot+sashiko
2026-10-15 10:53     ` Tariq Toukan [this message]
2026-10-11 13:32 ` [PATCH net-next 2/2] net/mlx5e: Batch ICOSQ trigger RCU synchronization Tariq Toukan
2026-10-12 10:40 ` [PATCH net-next 0/2] net/mlx5e: Batch RCU grace periods on channel open and close Toke Høiland-Jørgensen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=662109f7-44aa-452b-986d-74ed819a3afb@nvidia.com \
    --to=tariqt@nvidia.com \
    --cc=amorenoz@redhat.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=cjubran@nvidia.com \
    --cc=cratiu@nvidia.com \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=dtatulea@nvidia.com \
    --cc=edumazet@kernel.org \
    --cc=gal@nvidia.com \
    --cc=hawk@kernel.org \
    --cc=john.fastabend@gmail.com \
    --cc=kees@kernel.org \
    --cc=kuba@kernel.org \
    --cc=leon@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=lirongqing@baidu.com \
    --cc=mbloch@nvidia.com \
    --cc=mschmidt@redhat.com \
    --cc=netdev-bot+sashiko@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=richardcochran@gmail.com \
    --cc=saeedm@nvidia.com \
    --cc=sdf@fomichev.me \
    --cc=shshitrit@nvidia.com \
    --cc=toke@kernel.org \
    --cc=toke@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®