Netdev List
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: alexander.duyck@gmail.com
Cc: netdev@vger.kernel.org, andrew+netdev@lunn.ch,
	davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
	pabeni@redhat.com, kernel-team@meta.com, horms@kernel.org
Subject: Re: [net PATCH 1/4] fbnic: reset num_napi when the IRQ vectors are freed
Date: Sat, 05 Sep 2026 20:37:27 +0000	[thread overview]
Message-ID: <178864064783.219967.6355835755160509945@kernel.org> (raw)
In-Reply-To: <178838831069.11136.15113954049469090479.stgit@ahduyck-xeon-server.home.arpa>

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

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

- [Medium] The patch encodes "datapath torn down" as `fbn->num_napi == 0`
  while deliberately leaving `netif_running(netdev)` true…

Pre-existing issues:
- [High] __fbnic_pm_attach()
  (drivers/net/ethernet/meta/fbnic/fbnic_pci.c) drops RTNL after…

--

fbnic: reset num_napi when the IRQ vectors are freed

This clears fbn->num_napi in fbnic_pm_suspend() after the datapath has
been torn down and the IRQs are about to be freed, and also clears it in
__fbnic_pm_resume() when __fbnic_open() fails, so the count-bounded
consumers such as fbnic_dbg_down() no longer walk freed napi vectors.

> diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
> index 8b9bc9e8ea56..c6698e3002a1 100644
> --- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
> +++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
> @@ -434,6 +434,7 @@ static int fbnic_pm_suspend(struct device *dev)
>  {
>  	struct fbnic_dev *fbd = dev_get_drvdata(dev);
>  	struct net_device *netdev = fbd->netdev;
> +	struct fbnic_net *fbn;
>  
>  	if (fbnic_init_failure(fbd))
>  		goto null_uc_addr;
> @@ -441,11 +442,16 @@ static int fbnic_pm_suspend(struct device *dev)
>  	rtnl_lock();
>  	netdev_lock(netdev);
>  
> +	fbn = netdev_priv(netdev);
> +
>  	netif_device_detach(netdev);
>  
>  	if (netif_running(netdev))
>  		netdev->netdev_ops->ndo_stop(netdev);
>  
> +	/* The IRQs are about to be freed, so drop the napi vector count */
> +	fbn->num_napi = 0;
> +

[Severity: Medium]
This makes num_napi == 0 the marker for "datapath torn down" while
netif_running(netdev) is deliberately left true (ndo_stop is called
directly, so __LINK_STATE_START stays set).  Do the netdev queue
management ops cope with that combination?

fbnic_queue_mem_alloc() in fbnic_txrx.c uses netif_running() as the test
for "the napi vectors exist" and then divides by the count:

	if (!netif_running(dev))
		return fbnic_alloc_qt_page_pools(fbn, qt, idx);

	real = container_of(fbn->rx[idx], struct fbnic_q_triad, cmpl);
	nv = fbn->napi[idx % fbn->num_napi];

fbnic_queue_start() and fbnic_queue_stop() do the same modulo with no
guard at all:

	real = container_of(fbn->rx[idx], struct fbnic_q_triad, cmpl);
	nv = fbn->napi[idx % fbn->num_napi];

With num_napi == 0, is idx % fbn->num_napi a divide error oops?

These ops look reachable in that state.  fbnic_netdev_alloc() sets
netdev->queue_mgmt_ops = &fbnic_queue_mgmt_ops, so a memory provider can
be bound to an fbnic Rx queue (io_uring zcrx via netif_mp_open_rxq(), or
devmem).  On teardown of the io_uring fd or the netlink socket,
__netif_mp_close_rxq() -> netdev_rx_queue_reconfig() in
net/core/netdev_rx_queue.c calls ndo_queue_mem_alloc unconditionally and
ndo_queue_stop/ndo_queue_start under netif_running(dev), holding only the
netdev instance lock and doing no netif_device_present() check, unlike
the netdev-genl bind entry points.  real_num_rx_queues is not reset by
ndo_stop, so the index checks still pass.

The window is not short: userspace is not frozen during PCIe error
recovery, and the state persists indefinitely when __fbnic_open() fails
on resume or when fbnic_err_slot_reset() returns
PCI_ERS_RESULT_DISCONNECT so .resume and __fbnic_pm_attach() never run.

For completeness, the same sequence already oopsed before this patch,
since fbnic_free_napi_vector() sets fbn->napi[i] = NULL and
fbnic_remove_rx_ring() sets fbn->rx[q_idx] = NULL, so the pre-patch
crash was a NULL dereference rather than a divide error.  The question is
whether the torn-down state can be made unambiguous for all consumers,
for example by gating the queue management ops on
netif_device_present() or on an explicit datapath-up flag, rather than
only fixing the count-bounded consumers.

>  	netdev_unlock(netdev);
>  	rtnl_unlock();
>  
> @@ -508,16 +514,20 @@ static int __fbnic_pm_resume(struct device *dev)
>  	if (fbnic_init_failure(fbd))
>  		return 0;
>  
> +	rtnl_lock();
> +	netdev_lock(netdev);
> +
>  	fbn = netdev_priv(netdev);
>  
>  	/* Reset the queues if needed */
>  	fbnic_reset_queues(fbn, fbn->num_tx_queues, fbn->num_rx_queues);
>  
> -	rtnl_lock();
> -	netdev_lock(netdev);
> -
> -	if (netif_running(netdev))
> +	if (netif_running(netdev)) {
>  		err = __fbnic_open(fbn);
> +		/* On failure the vectors are freed, so drop the count */
> +		if (err)
> +			fbn->num_napi = 0;
> +	}
>  
>  	netdev_unlock(netdev);
>  	rtnl_unlock();

[Severity: High]
This isn't a bug introduced by this patch, but while the resume path is
being reworked to take rtnl_lock() plus netdev_lock() around the
netif_running() test, __fbnic_pm_attach() in the same file still does the
equivalent test with neither lock held:

	rtnl_lock();
	fbnic_reset_hw_stats(fbd);
	rtnl_unlock();
	...
	fbn = netdev_priv(netdev);

	if (netif_running(netdev))
		fbnic_up(fbn);

	netif_device_attach(netdev);

Can a close slip in between the netif_running() test and fbnic_up()?
__dev_close_many() in net/core/dev.c explicitly allows ndo_stop after a
detach:

		/*
		 *	Call the device specific close. This cannot fail.
		 *	Only if device is UP
		 *
		 *	We allow it to be called even after a DETACH hot-plug
		 *	event.
		 */
		if (ops->ndo_stop)
			ops->ndo_stop(dev);

so fbnic_stop() -> fbnic_free_napi_vectors() can NULL and kfree() every
entry of fbn->napi[] while fbn->num_napi keeps the normal queue count.
fbnic_up() -> fbnic_napi_enable() then walks that array:

	for (i = 0; i < fbn->num_napi; i++) {
		struct fbnic_napi_vector *nv = fbn->napi[i];

		napi_enable_locked(&nv->napi);

Is that a NULL dereference, or a use-after-free of the vector, when the
close won the race?

The path is fbnic_err_slot_reset() succeeding -> fbnic_err_resume() ->
__fbnic_pm_attach(), which runs from the PCI recovery context with
userspace not frozen, and that context takes only device_lock, never
rtnl or netdev->lock, so an "ip link set dev X down" on another CPU is
free to run concurrently.  Would it make sense to serialize the
netif_running() check and the dependent fbnic_up() under the same
rtnl_lock() and netdev_lock() pair now used by suspend and resume?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/178838795612.11136.15512516427273655916.stgit%40ahduyck-xeon-server.home.arpa

  reply	other threads:[~2026-09-05 20:37 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-02 22:31 [net PATCH 0/4] eth: fbnic: a collection of fixes Alexander Duyck
2026-09-02 22:31 ` [net PATCH 1/4] fbnic: reset num_napi when the IRQ vectors are freed Alexander Duyck
2026-09-05 20:37   ` netdev-bot+sashiko [this message]
2026-09-02 22:31 ` [net PATCH 2/4] fbnic: Set AW_FLUSH_MODE alongside AW_FLUSH when flushing the mailbox Alexander Duyck
2026-09-05 20:37   ` netdev-bot+sashiko
2026-09-02 22:32 ` [net PATCH 3/4] fbnic: Handle FW mailbox completions flagged with an error Alexander Duyck
2026-09-05 20:37   ` netdev-bot+sashiko
2026-09-02 22:32 ` [net PATCH 4/4] net: ethtool: keep rtnl_lock for the ioctl self test Alexander Duyck
2026-09-05 20:37   ` netdev-bot+sashiko
2026-09-08  9:41 ` [net PATCH 0/4] eth: fbnic: a collection of fixes Paolo Abeni
2026-09-08  9:41 ` Paolo Abeni
2026-09-08 17:01   ` Alexander Duyck
2026-09-08 17:24     ` Paolo Abeni

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=178864064783.219967.6355835755160509945@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=alexander.duyck@gmail.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=kernel-team@meta.com \
    --cc=kuba@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@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