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 v2 3/6] eth: fbnic: use the Rx queue napi pointer to find the napi vector
Date: Thu, 17 Sep 2026 21:12:09 +0000 [thread overview]
Message-ID: <178967952944.22033.10329187569173798096@kernel.org> (raw)
In-Reply-To: <178942021136.7700.4391219358260544104.stgit@ahduyck-xeon-server.home.arpa>
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 1 (2) · Medium: 0 · Low: 0
- [High] The invariant the patch relies on - "The pointer is NULL exactly
while the datapath is down" - does not hold across PCIe…
Pre-existing issues:
- [High] fbnic_queue_stop() uses nv->qt[t].sub0.q_idx as the queue
identifier for every triad, but that field is 0 for two kinds of…
- [High] fbnic_alloc_qt_page_pools(): when the queue has an unreadable
memory provider, the second page_pool_create() overwrites the local…
--
Here is the review report.
eth: fbnic: use the Rx queue napi pointer to find the napi vector
The queue management ndos stop deriving the napi vector from
fbn->napi[idx % fbn->num_napi] and instead read the association that
fbnic_set_netif_napi() published in netdev_rx_queue.napi. A new helper
fbnic_rxq_nv() performs the lookup, and fbnic_queue_mem_alloc() returns
-ENETDOWN when the pointer is NULL.
> diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c
> index 81a30e2d449b2..e93174fc1239f 100644
> --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c
> +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c
[ ... ]
> @@ -2829,6 +2830,17 @@ void fbnic_napi_depletion_check(struct net_device *netdev)
> fbnic_wrfl(fbd);
> }
>
> +/* Returns the napi vector servicing an Rx queue, or NULL if the datapath
> + * is torn down. The association is published by fbnic_set_netif_napi()
> + * and cleared by fbnic_reset_netif_napi(), both under the instance lock.
> + */
> +static struct fbnic_napi_vector *fbnic_rxq_nv(struct net_device *dev, int idx)
> +{
> + struct napi_struct *napi = __netif_get_rx_queue(dev, idx)->napi;
> +
> + return napi ? container_of(napi, struct fbnic_napi_vector, napi) : NULL;
> +}
> +
[Severity: High]
The commit message says "The pointer is NULL exactly while the datapath is
down". Does that hold across the resume path?
In fbnic_pci.c, the publish and the enable are split across two calls with
the instance lock dropped in between:
__fbnic_pm_resume()
netdev_lock()
__fbnic_open()
fbnic_set_netif_queues()
fbnic_set_netif_napi()
netif_queue_set_napi() /* publishes rxq->napi */
netdev_unlock()
__fbnic_pm_attach()
if (netif_running(netdev))
fbnic_up(fbn); /* napi_enable_locked() happens here */
netif_device_attach(netdev);
In the window between those two, netif_running() is true and rxq->napi is
already non-NULL, but the NAPIs have only been added, not enabled.
Can a queue restart arriving in that window get past the new guard and
reach fbnic_queue_stop()? netdev_rx_queue_reconfig() has no
netif_device_present() gate:
err = qops->ndo_queue_mem_alloc(dev, qcfg_new, new_mem, rxq_idx);
...
if (netif_running(dev)) {
err = qops->ndo_queue_stop(dev, old_mem, rxq_idx);
One reachable caller is netdev_nl_sock_priv_destroy() on netlink socket
close, which takes netdev_lock(dev) and walks
net_devmem_unbind_dmabuf() -> netif_mp_close_rxq() ->
netdev_rx_queue_reconfig().
[ ... ]
> @@ -2841,8 +2853,16 @@ static int fbnic_queue_mem_alloc(struct net_device *dev,
> if (!netif_running(dev))
> return fbnic_alloc_qt_page_pools(fbn, qt, idx);
>
> + /* A failed PCIe recovery or resume can leave the datapath torn down
> + * while netif_running() is still true. This ndo runs before
> + * netdev_rx_queue_restart() checks netif_running(), so bail out
> + * rather than touching rings and vectors that are already freed.
> + */
> + nv = fbnic_rxq_nv(dev, idx);
> + if (!nv)
> + return -ENETDOWN;
> +
[Severity: High]
Continuing the point above: does a non-NULL rxq->napi actually imply the
datapath is up? For the resume window the pointer is published while the
vectors exist but are not enabled, so this check passes and
fbnic_queue_stop() runs napi_disable_locked() on a NAPI that was only added
by netif_napi_add_config_locked(), which leaves NAPI_STATE_SCHED and
NAPI_STATE_NPSVC set:
net/core/dev.c:napi_disable_locked() {
...
do {
while (val & (NAPIF_STATE_SCHED | NAPIF_STATE_NPSVC)) {
usleep_range(20, 200);
val = READ_ONCE(n->state);
}
...
}
napi_enable_locked() is the only thing that clears those bits, and it has
not run yet, so does this loop spin forever with the netdev instance lock
held, blocking the pending fbnic_up(), dev_close(), unregister and ethtool?
Would a "datapath enabled" state test, or moving fbnic_up() inside the
instance-locked region of the resume path, be a better fit than keying off
the published pointer?
> + real = container_of(fbn->rx[idx], struct fbnic_q_triad, cmpl);
> - nv = fbn->napi[idx % fbn->num_napi];
>
> fbnic_ring_init(&qt->sub0, real->sub0.doorbell, real->sub0.q_idx,
> real->sub0.flags);
[Severity: High]
This is a pre-existing issue, but since both branches of this ndo reach it,
could fbnic_alloc_qt_page_pools() be dereferencing an error pointer? The
second page_pool_create() overwrites the local pp, and the error label then
destroys that value rather than the pool it should be releasing:
qt->sub0.page_pool = pp;
if (netif_rxq_has_unreadable_mp(fbn->netdev, rxq_idx)) {
...
pp = page_pool_create(&pp_params);
if (IS_ERR(pp))
goto err_destroy_sub0;
...
err_destroy_sub0:
page_pool_destroy(pp);
return PTR_ERR(pp);
net/core/page_pool.c:page_pool_destroy() only filters NULL:
if (!pool)
return;
if (!page_pool_put(pool))
return;
so page_pool_put() would dereference the ERR_PTR, and qt->sub0.page_pool
leaks because netdev_rx_queue_reconfig() does not call ndo_queue_mem_free
on the mem_alloc failure path. The reachable trigger is a devmem or
io_uring bind on a queue with an unreadable memory provider where the
second page_pool_create() fails with -ENOMEM, or where the provider's
->init() returns an error. Would saving the error before destroying
qt->sub0.page_pool address this?
[ ... ]
> @@ -2915,7 +2935,7 @@ static int fbnic_queue_stop(struct net_device *dev, void *qmem, int idx)
> int err;
>
> real = container_of(fbn->rx[idx], struct fbnic_q_triad, cmpl);
> - nv = fbn->napi[idx % fbn->num_napi];
> + nv = fbnic_rxq_nv(dev, idx);
> fbnic_dbg_nv_exit(nv);
>
> napi_disable_locked(&nv->napi);
[Severity: High]
This isn't a bug introduced by this patch, but does the standalone-Tx
configuration this patch targets remain incorrect afterwards? The rest of
fbnic_queue_stop() uses sub0.q_idx as the queue identifier:
for (i = 0; i < nv->txt_count; i++)
netif_stop_subqueue(dev, nv->qt[i].sub0.q_idx);
fbnic_nv_disable(fbn, nv);
for (t = 0; t < nv->txt_count + nv->rxt_count; t++) {
err = fbnic_wait_queue_idle(fbn, t >= nv->txt_count,
nv->qt[t].sub0.q_idx);
but sub0.q_idx is 0 for two kinds of triads. For the placeholder Tx triad
of an Rx-only vector, fbnic_alloc_napi_vector() sets:
} else {
fbnic_ring_init(&qt->sub0, db, 0,
FBNIC_RING_F_DISABLED);
}
and for the Rx BDQ rings:
db = &uc_addr[FBNIC_QUEUE(rxq_idx) + FBNIC_QUEUE_BDQ_HPQ_TAIL];
fbnic_ring_init(&qt->sub0, db, 0,
FBNIC_RING_F_CTX | FBNIC_RING_F_STATS);
so the real HW block index only lives in cmpl.q_idx.
For an Rx-only vector, does netif_stop_subqueue(dev, 0) freeze netdev Tx
queue 0, a live ring owned by another vector, with __fbnic_nv_restart()
later re-waking it on both the success and err_restart paths?
for (i = 0; i < nv->txt_count; i++)
netif_wake_subqueue(fbn->netdev, nv->qt[i].sub0.q_idx);
And for Rx queue N > 0, does fbnic_wait_queue_idle(fbn, true, 0) poll Rx
queue 0's HPQ/PPQ/RCQ idle bits rather than queue N's, so quiescence of the
ring being torn down is never established before fbnic_nv_flush() cleans and
unmaps the BDQ pages and memsets the RCQ? Similarly,
fbnic_wait_queue_idle(fbn, false, 0) on the disabled placeholder polls HW Tx
queue 0, which this path never disables, so under Tx load it would time out
and take the err_restart path naming the wrong queue.
fbnic_nv_flush() already carries the guard these two loops lack:
/* Nothing else to do if Tx queue is disabled */
if (qt->sub0.flags & FBNIC_RING_F_DISABLED)
continue;
Should the stop/wake and idle-wait loops skip FBNIC_RING_F_DISABLED rings
and use the identifier appropriate to each triad? A configuration that
reaches this today is combined 2 rx 2, which fbnic_set_channels() accepts
since only rx && tx && combined is rejected; that yields num_napi 4 with
vectors 2 and 3 having tqpv == 0.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/178941996343.7700.9376081102002673062.stgit%40ahduyck-xeon-server.home.arpa
next prev parent reply other threads:[~2026-09-17 21:12 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-14 21:09 [net PATCH v2 0/6] eth: fbnic: a collection of fixes Alexander Duyck
2026-09-14 21:09 ` [net PATCH v2 1/6] net: ethtool: keep rtnl_lock for the ioctl self test Alexander Duyck
2026-09-18 16:12 ` Simon Horman
2026-09-14 21:10 ` [net PATCH v2 2/6] eth: fbnic: Handle maximum standalone channels Alexander Duyck
2026-09-18 16:12 ` Simon Horman
2026-09-14 21:10 ` [net PATCH v2 3/6] eth: fbnic: use the Rx queue napi pointer to find the napi vector Alexander Duyck
2026-09-17 21:12 ` netdev-bot+sashiko [this message]
2026-09-18 15:11 ` Alexander Duyck
2026-09-18 16:14 ` Simon Horman
2026-09-18 16:24 ` Alexander Duyck
2026-09-14 21:10 ` [net PATCH v2 4/6] eth: fbnic: reset num_napi when the napi vectors are freed Alexander Duyck
2026-09-18 16:15 ` Simon Horman
2026-09-14 21:10 ` [net PATCH v2 5/6] eth: fbnic: Set AW_FLUSH_MODE alongside AW_FLUSH when flushing the mailbox Alexander Duyck
2026-09-18 16:15 ` Simon Horman
2026-09-14 21:10 ` [net PATCH v2 6/6] eth: fbnic: Handle FW mailbox completions flagged with an error Alexander Duyck
2026-09-17 21:12 ` netdev-bot+sashiko
2026-09-18 15:17 ` Alexander Duyck
2026-09-18 16:16 ` Simon Horman
2026-09-19 0:50 ` [net PATCH v2 0/6] eth: fbnic: a collection of fixes patchwork-bot+netdevbpf
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=178967952944.22033.10329187569173798096@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