All of lore.kernel.org
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: mengyuanlou@net-swift.com
Cc: netdev@vger.kernel.org, jiawenwu@trustnetic.com,
	duanqiangwen@net-swift.com, linglingzhang@net-swift.com,
	andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
	kuba@kernel.org, pabeni@redhat.com, jacob.e.keller@intel.com,
	richardcochran@gmail.com
Subject: Re: [PATCH net v6] net: Wangxun: protect ring accesses with RCU
Date: Sat, 19 Sep 2026 07:16:47 +0000	[thread overview]
Message-ID: <178980220740.22033.15257166624913083817@kernel.org> (raw)
In-Reply-To: <20260915065731.6007-1-mengyuanlou@net-swift.com>

Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider:

Critical: 0 · High: 0 (1) · Medium: 3 (3) · Low: 0

- [Medium] The commit message describes only four annotation-level
  changes (add __rcu, add rcu_read_lock() sections, use…
- [Medium] The patch adds rtnl_lock()/rtnl_unlock() around
  wx_init_interrupt_scheme() in four probe routines (ngbe_probe,…
- [Medium] The three ethtool set_ringparam paths annotate the ring-array
  dereferences with rcu_dereference_protected(...,…

Pre-existing issues:
- [High] wxvf_suspend() tears down the whole interrupt scheme
  (wx_clear_interrupt_scheme() -> wx_free_q_vectors() ->…
- [Medium] wxvf_resume() ignores the return value of
  wx_init_interrupt_scheme() (the call this patch wraps in…
- [Medium] ngbe_setup_tc() and txgbe_setup_tc() close (or reset) the
  device and destroy the existing interrupt scheme, then call…
- [Medium] ngbe_resume() stores the result of wx_init_interrupt_scheme()
  (and possibly of ngbe_open()) in `err` and correctly skips…

--

net: Wangxun: protect ring accesses with RCU

This converts the wx->tx_ring[]/wx->rx_ring[] arrays in the Wangxun drivers
to __rcu, wraps the lockless readers in rcu_read_lock() sections with
rcu_dereference() and NULL guards, publishes and clears the slots with
rcu_assign_pointer(), and uses rcu_dereference_protected() in the control
paths.  A few call sites also gain rtnl_lock()/rtnl_unlock().

> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> index 940d2e59876c7..414523fe83cb4 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c

[ ... ]

> @@ -805,9 +807,13 @@ static int wx_set_ringparam_vf(struct net_device *netdev,
>  
>  	if (!netif_running(wx->netdev)) {
>  		for (i = 0; i < wx->num_tx_queues; i++)
> -			wx->tx_ring[i]->count = new_tx_count;
> +			rcu_dereference_protected(wx->tx_ring[i],
> +						  lockdep_is_held(&wx->reset_lock))->count =
> +				new_tx_count;
>  		for (i = 0; i < wx->num_rx_queues; i++)
> -			wx->rx_ring[i]->count = new_rx_count;
> +			rcu_dereference_protected(wx->rx_ring[i],
> +						  lockdep_is_held(&wx->reset_lock))->count =
> +				new_rx_count;
>  		wx->tx_ring_count = new_tx_count;
>  		wx->rx_ring_count = new_rx_count;
>  

[Severity: Medium]
Does wx->reset_lock actually exclude any writer of wx->tx_ring[] and
wx->rx_ring[]?

As far as I can tell the only writers of those slots are the
rcu_assign_pointer() calls in wx_alloc_q_vector() and wx_free_q_vector(),
reached through wx_init_interrupt_scheme()/wx_clear_interrupt_scheme() from
probe, PM, ndo_setup_tc and the SR-IOV reinit path.  All of those hold RTNL
and none of them take reset_lock.

The same function then calls wx_set_ring(), which declares the opposite lock
for the identical slots:

	if (new_tx_count != wx->tx_ring_count) {
		for (i = 0; i < wx->num_tx_queues; i++) {
			memcpy(&temp_ring[i],
			       rcu_dereference_protected(wx->tx_ring[i],
							 lockdep_rtnl_is_held()),

ngbe_set_ringparam() and txgbe_set_ringparam() carry the same reset_lock
annotation.  Would it be better to annotate these as RTNL (or
RTNL-or-reset_lock with the rule spelled out next to the array definition in
struct wx), so that the assertion can actually catch an unsynchronized
caller?

The commit message also states:

    4. Use rcu_dereference_protected() in control paths guarded by locks
       (e.g., RTNL or reset_lock).

which reads as if either lock were sufficient for these arrays.

> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> index ed5aad7857bd9..bbd670504b276 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> @@ -1700,6 +1700,7 @@ netdev_tx_t wx_xmit_frame(struct sk_buff *skb,
>  	unsigned int r_idx = skb->queue_mapping;
>  	struct wx *wx = netdev_priv(netdev);
>  	struct wx_ring *tx_ring;
> +	u16 num_tx_queues;
>  
>  	if (!netif_carrier_ok(netdev)) {
>  		dev_kfree_skb_any(skb);
> @@ -1712,9 +1713,18 @@ netdev_tx_t wx_xmit_frame(struct sk_buff *skb,
>  	if (skb_put_padto(skb, 17))
>  		return NETDEV_TX_OK;
>  
> -	if (r_idx >= wx->num_tx_queues)
> -		r_idx = r_idx % wx->num_tx_queues;
> -	tx_ring = wx->tx_ring[r_idx];
> +	num_tx_queues = READ_ONCE(wx->num_tx_queues);
> +	if (unlikely(!num_tx_queues)) {
> +		dev_kfree_skb_any(skb);
> +		return NETDEV_TX_OK;
> +	}
> +	if (r_idx >= num_tx_queues)
> +		r_idx = r_idx % num_tx_queues;
> +	tx_ring = rcu_dereference_bh(wx->tx_ring[r_idx]);
> +	if (unlikely(!tx_ring)) {
> +		dev_kfree_skb_any(skb);
> +		return NETDEV_TX_OK;
> +	}
>  

[Severity: Medium]
Should the commit message mention this part?  The message describes only
annotation work, but this hunk changes the behaviour of ndo_start_xmit in two
ways.

First, the new zero-queue guard removes a divide by zero.  Before the patch
the code did:

	if (r_idx >= wx->num_tx_queues)
		r_idx = r_idx % wx->num_tx_queues;

and wx_free_q_vectors() zeroes the counter while the netdev is still
registered:

	wx->num_tx_queues = 0;
	wx->num_rx_queues = 0;
	wx->num_q_vectors = 0;

so a transmit racing with wx_clear_interrupt_scheme() (ndo_setup_tc,
shutdown/suspend, wxvf_suspend()) could hit "% 0".  With a Fixes: tag on the
patch, would it help a backporter to have the divide error named explicitly?

Second, both new paths free the skb and return NETDEV_TX_OK without bumping
any tx_dropped/tx_errors counter, so those transmits disappear silently.  Is
that intentional?

[ ... ]

> @@ -2245,10 +2261,10 @@ static void wx_free_q_vector(struct wx *wx, int v_idx)
>  	struct wx_ring *ring;
>  
>  	wx_for_each_ring(ring, q_vector->tx)
> -		wx->tx_ring[ring->queue_index] = NULL;
> +		rcu_assign_pointer(wx->tx_ring[ring->queue_index], NULL);
>  
>  	wx_for_each_ring(ring, q_vector->rx)
> -		wx->rx_ring[ring->queue_index] = NULL;
> +		rcu_assign_pointer(wx->rx_ring[ring->queue_index], NULL);
>  
>  	wx->q_vector[v_idx] = NULL;
>  	netif_napi_del(&q_vector->napi);

[ ... ]

> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
> index 9454e90258d8e..9d06003259dba 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
> @@ -1359,8 +1360,8 @@ struct wx {
>  	u32 tx_ring_count;
>  	u32 rx_ring_count;
>  
> -	struct wx_ring *tx_ring[64] ____cacheline_aligned_in_smp;
> -	struct wx_ring *rx_ring[64];
> +	struct wx_ring __rcu *tx_ring[64] ____cacheline_aligned_in_smp;
> +	struct wx_ring __rcu *rx_ring[64];
>  	struct wx_q_vector *q_vector[64];
>  	int num_rx_pools;
>  	int num_rx_queues_per_pool;

Would it be worth adding a short comment here naming the lock that writers of
these two arrays must hold?  Right now the only documentation of that rule is
the two disagreeing rcu_dereference_protected() annotations discussed above.

> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
> index 26de78e9a69ef..93a4d7632ea8e 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
> @@ -17,7 +17,9 @@ int wxvf_suspend(struct device *dev_d)
>  	struct wx *wx = pci_get_drvdata(pdev);
>  
>  	netif_device_detach(wx->netdev);
> +	rtnl_lock();
>  	wx_clear_interrupt_scheme(wx);
> +	rtnl_unlock();
>  	pci_disable_device(pdev);
>  
>  	return 0;

[Severity: Medium]
Could the commit message cover the new rtnl_lock()/rtnl_unlock() calls?  They
appear here, in wxvf_resume() below, and around wx_init_interrupt_scheme() in
ngbe_probe(), txgbe_probe(), ngbevf_probe() and txgbevf_probe(), while the
message only describes RCU annotation work.

For the four probe routines the reason seems to be the new
lockdep_rtnl_is_held() assertions in wx_cache_ring_rss()/wx_cache_ring_vmdq().
The wxvf_suspend() case is less clear, since wx_clear_interrupt_scheme() ->
wx_free_q_vector() only uses rcu_assign_pointer(), which has no lockdep
requirement.  What does RTNL protect there?

[Severity: High]
This isn't a bug introduced by this patch, but since these are the lines being
touched: does wxvf_suspend() leak the per-ring resources when the VF interface
is up?

The PF shutdown path calls ngbe_close() when netif_running(), and
wxvf_close() shows the expected order:

	wxvf_down(wx);
	wx_free_irq(wx);
	wx_free_resources(wx);

wxvf_suspend() does none of that; it goes straight to
wx_clear_interrupt_scheme(), which reaches wx_free_q_vectors() ->
wx_free_q_vector():

	wx->q_vector[v_idx] = NULL;
	netif_napi_del(&q_vector->napi);
	kfree_rcu(q_vector, rcu);

The struct wx_ring objects are embedded in that q_vector, and they hold the
only pointers to tx_buffer_info/rx_buffer_info, the dma_alloc_coherent()
descriptor rings and the page_pool, so those allocations become unreachable.

The MSI-X handlers requested by wx_request_msix_irqs_vf() use dev_id ==
q_vector and are never freed here, and the DIM works are never
disable_work_sync()'d, so can a late interrupt or a pending DIM work touch the
freed q_vector across the grace period and across pci_free_irq_vectors()?

And on the wxvf_close() following a resume, would wx_free_irq() call
free_irq() on vectors that were never requested?

> @@ -36,8 +38,10 @@ int wxvf_resume(struct device *dev_d)
>  	struct wx *wx = pci_get_drvdata(pdev);
>  
>  	pci_set_master(pdev);
> +	rtnl_lock();
>  	wx_init_interrupt_scheme(wx);
>  	netif_device_attach(wx->netdev);
> +	rtnl_unlock();
>  
>  	return 0;
>  }

[Severity: Medium]
This is a pre-existing issue, but the call being wrapped here still has its
return value dropped.

If wx_init_interrupt_scheme() fails, wx_set_interrupt_capability() or the
wx_alloc_q_vectors() error path has already run:

err_out:
	wx->num_tx_queues = 0;
	wx->num_rx_queues = 0;
	wx->num_q_vectors = 0;

	while (v_idx--)
		wx_free_q_vector(wx, v_idx);

so every ring slot is NULL and the queue counts are zero.  Should
netif_device_attach() still run in that case, re-waking the transmit queues
and arming the watchdog on a device with no rings, IRQs or NAPI contexts?

And should the function still return 0, telling the PM core the resume
succeeded?  Every subsequent transmit would take the new
"!num_tx_queues"/"!tx_ring" drop paths in wx_xmit_frame().

[ ... ]

> diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> index 855dc963c610c..c670f4eb2759a 100644
> --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> @@ -866,7 +869,9 @@ static int ngbe_probe(struct pci_dev *pdev,
>  
>  	ngbe_init_service(wx);
>  
> +	rtnl_lock();
>  	err = wx_init_interrupt_scheme(wx);
> +	rtnl_unlock();
>  	if (err)
>  		goto err_cancel_service;
>  

[Severity: Medium]
This is a pre-existing issue and not part of the diff, but two other callers
of the same function do not check it at all.  ngbe_setup_tc() ends with:

	wx_init_interrupt_scheme(wx);

	if (netif_running(dev))
		ngbe_open(dev);

	return 0;

and txgbe_setup_tc() is identical.  Both have already destroyed the previous
interrupt scheme at that point, and wx_init_interrupt_scheme() can fail:

	ret = wx_set_interrupt_capability(wx);
	if (ret) {
		wx_err(wx, "Allocate irq vectors for failed.\n");
		return ret;
	}

Should ndo_setup_tc and ethtool -L callers be told the reconfiguration
succeeded when the device is left with num_tx_queues/num_rx_queues == 0 and
all ring slots NULL?

[Severity: Medium]
This is a pre-existing issue as well, but ngbe_resume() in the same file
tracks the error and then discards it:

	err = wx_init_interrupt_scheme(wx);
	if (!err && netif_running(netdev))
		err = ngbe_open(netdev);
	if (!err)
		netif_device_attach(netdev);
	rtnl_unlock();

	return 0;

Should the final statement be "return err", so the PM core is not told the
resume succeeded while the netdev stays detached?

> diff --git a/drivers/net/ethernet/wangxun/ngbevf/ngbevf_main.c b/drivers/net/ethernet/wangxun/ngbevf/ngbevf_main.c
> index d79cf8d8484f9..aac88c77d3b85 100644
> --- a/drivers/net/ethernet/wangxun/ngbevf/ngbevf_main.c
> +++ b/drivers/net/ethernet/wangxun/ngbevf/ngbevf_main.c
> @@ -202,7 +202,9 @@ static int ngbevf_probe(struct pci_dev *pdev,
>  	ether_addr_copy(netdev->perm_addr, wx->mac.addr);
>  
>  	wxvf_init_service(wx);
> +	rtnl_lock();
>  	err = wx_init_interrupt_scheme(wx);
> +	rtnl_unlock();
>  	if (err)
>  		goto err_free_sw_init;
>  

[ ... ]

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260915065731.6007-1-mengyuanlou%40net-swift.com

  reply	other threads:[~2026-09-19  7:16 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-15  6:57 [PATCH net v6] net: Wangxun: protect ring accesses with RCU Mengyuan Lou
2026-09-19  7:16 ` netdev-bot+sashiko [this message]
2026-09-22  0:05 ` Jakub Kicinski
2026-09-22  8:48   ` mengyuanlou
2026-09-23  0:19     ` Jakub Kicinski

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=178980220740.22033.15257166624913083817@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=duanqiangwen@net-swift.com \
    --cc=edumazet@google.com \
    --cc=jacob.e.keller@intel.com \
    --cc=jiawenwu@trustnetic.com \
    --cc=kuba@kernel.org \
    --cc=linglingzhang@net-swift.com \
    --cc=mengyuanlou@net-swift.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=richardcochran@gmail.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.