From: netdev-bot+sashiko@kernel.org
To: vladimir.oltean@nxp.com
Cc: andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@kernel.org,
kuba@kernel.org, pabeni@redhat.com, claudiu.manoil@nxp.com,
wei.fang@nxp.com, xiaoning.wang@nxp.com, ioana.ciornei@nxp.com,
zefir.kurtisi@westermo.com, zefir.kurtisi@gmail.com,
david.laight.linux@gmail.com, netdev@vger.kernel.org,
imx@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v4 net 8/8] net: enetc: ensure enetc_xdp_xmit() calls enetc_update_tx_ring_tail()
Date: Mon, 19 Oct 2026 11:43:10 +0000 [thread overview]
Message-ID: <179241019093.3135.3031150554563643199@kernel.org> (raw)
In-Reply-To: <20261015105536.1692348-9-vladimir.oltean@nxp.com>
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.
Critical: 0 · High: 1 (1) · Medium: 0 · Low: 0
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).
- [High] In enetc_xdp_xmit()
(drivers/net/ethernet/freescale/enetc/enetc.c), the doorbell condition…
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but it comes from
the earlier commit in the same series, 'net: enetc: serialize access…
--
net: enetc: ensure enetc_xdp_xmit() calls enetc_update_tx_ring_tail()
This commit changes the doorbell condition in enetc_xdp_xmit(). The TX ring
tail is now updated only when at least one frame was sent and either
XDP_XMIT_FLUSH was requested or the frames array could not be fully
enqueued. The redundant xdp_tx_frm_cnt variable is removed in favour of k.
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
> index 218065b9a3777..3003a415418ba 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc.c
[ ... ]
> @@ -1928,22 +1928,20 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames,
> &xdp_redirect_arr[i]);
> break;
> }
> -
> - xdp_tx_frm_cnt++;
> }
>
> - if (unlikely((flags & XDP_XMIT_FLUSH) || k != xdp_tx_frm_cnt))
> + if (unlikely(k && ((flags & XDP_XMIT_FLUSH) || k < num_frames)))
> enetc_update_tx_ring_tail(tx_ring);
[Severity: High]
Does the new k && term skip the doorbell when an XDP_XMIT_FLUSH call
enqueues zero frames?
The old condition rang on every FLUSH call, because k != xdp_tx_frm_cnt was
always false. XDP_XMIT_FLUSH asks the driver to publish everything queued so
far. That includes frames from earlier ndo_xdp_xmit() calls made with
flags == 0.
devmap makes calls like that when the bulk queue fills up:
kernel/bpf/devmap.c:bq_enqueue() {
...
if (unlikely(bq->count == DEV_MAP_BULK_SIZE))
bq_xmit_all(bq, 0);
...
}
Only the later __dev_flush() passes XDP_XMIT_FLUSH:
kernel/bpf/devmap.c:__dev_flush() {
...
bq_xmit_all(bq, XDP_XMIT_FLUSH);
...
}
The flags == 0 call advances tx_ring->next_to_use without writing TPIR. If
the first frame of the next FLUSH batch fails, k is 0 and
enetc_update_tx_ring_tail() is never called.
The first frame can fail in these cases:
- enetc_xdp_tx() finds too few free BDs. This is likely right after the
previous batch used up the ring.
- The head buffer is shorter than ENETC_MIN_BUFF_SIZE.
- The frame fails the ENETC_MAX_SKB_FRAGS check.
- DMA mapping fails.
On a ring reserved for XDP, the only other TPIR writer is the XDP_TX tail
update in enetc_clean_rx_ring_xdp(). It rings only when it has enqueued
XDP_TX frames itself:
if (xdp_tx_frm_cnt) {
spin_lock(&tx_ring->xdp_lock);
enetc_update_tx_ring_tail(tx_ring);
spin_unlock(&tx_ring->xdp_lock);
}
So the stranded BDs stay unpublished until a later call both succeeds and
flushes. If traffic stops, they never go out.
If they fill the ring, every later enqueue fails on its first frame. TX
completion cannot reclaim BDs the hardware never saw. Can the XDP TX queue
then stall permanently, with the xdp_frames and pages still pinned?
Other drivers ring the tail on XDP_XMIT_FLUSH no matter how many frames
that call sent. For example, i40e_xdp_xmit():
if (unlikely(flags & XDP_XMIT_FLUSH))
i40e_xdp_ring_update_tail(xdp_ring);
Would something like this be closer to the intent?
if (unlikely((flags & XDP_XMIT_FLUSH) || (k && k < num_frames)))
The commit message says:
The correct condition on which we should ring the doorbell is when at
least one frame was sent, and either the caller required us to flush, or
we couldn't enqueue the entire passed array.
The subject says the patch ensures the tail update happens, and the patch
has a Fixes: tag. Even so, FLUSH calls with k == 0 no longer ring the
doorbell, and the commit message does not mention this change.
>
> enetc_unlock_mdio();
>
> out:
> - tx_ring->stats.xdp_tx += xdp_tx_frm_cnt;
> - tx_ring->stats.xdp_tx_drops += num_frames - xdp_tx_frm_cnt;
> + tx_ring->stats.xdp_tx += k;
> + tx_ring->stats.xdp_tx_drops += num_frames - k;
>
> spin_unlock(&tx_ring->xdp_lock);
[Severity: High]
This isn't a bug introduced by this patch. It comes from the earlier patch
in this series, "net: enetc: serialize access to the same TX ring from NAPI
XDP_TX and ndo_xdp_xmit()".
Can tx_ring->xdp_lock and enetc_mdio_lock deadlock on PREEMPT_RT when the
ERR050089 workaround (enetc_has_err050089) is enabled?
enetc_xdp_xmit() takes xdp_lock first and then the MDIO read lock:
spin_lock(&tx_ring->xdp_lock);
...
enetc_lock_mdio();
enetc_clean_rx_ring_xdp() drops xdp_lock at the out: label but keeps the
MDIO read lock. It then takes xdp_lock again, so the order is reversed:
out:
spin_unlock(&tx_ring->xdp_lock);
...
if (xdp_tx_frm_cnt) {
spin_lock(&tx_ring->xdp_lock);
enetc_update_tx_ring_tail(tx_ring);
spin_unlock(&tx_ring->xdp_lock);
}
...
enetc_unlock_mdio();
On PREEMPT_RT, rwbase_write_lock() takes the rtmutex and removes
READER_BIAS. New readers then block behind a pending writer. That would
allow this cycle:
T1 (RX NAPI, enetc_clean_rx_ring_xdp())
holds the MDIO read lock after out:
T3 (_enetc_rd_mdio_reg_wa())
write_lock_irqsave(&enetc_mdio_lock, flags);
waits for T1 to drop the read lock
T2 (enetc_xdp_xmit() on another CPU, targeting the same XDP TX ring,
e.g. after an IRQ affinity change)
spin_lock(&tx_ring->xdp_lock);
enetc_lock_mdio(); <- blocks behind T3
T1
spin_lock(&tx_ring->xdp_lock); <- blocks behind T2
Non-RT testing would not catch this. On non-RT, qrwlock readers in
interrupt context ignore a waiting writer, and lockdep treats this
read_lock as recursive.
Would it help to use one lock order in the RX tail update path? For
example, the MDIO read lock could be dropped before re-taking xdp_lock and
then taken again inside it.
>
> - return err ?: xdp_tx_frm_cnt;
> + return err ?: k;
> }
> EXPORT_SYMBOL_GPL(enetc_xdp_xmit);
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261015105536.1692348-1-vladimir.oltean%40nxp.com
prev parent reply other threads:[~2026-10-19 11:43 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 10:55 [PATCH v4 net 0/8] Fix short frame transmission in enetc vladimir.oltean
2026-10-15 10:55 ` [PATCH v4 net 1/8] net: enetc: consistently track dropped frames in enetc_xdp_xmit() vladimir.oltean
2026-10-15 10:55 ` [PATCH v4 net 2/8] net: enetc: serialize access to the same TX ring from NAPI XDP_TX and ndo_xdp_xmit() vladimir.oltean
2026-10-19 11:43 ` netdev-bot+sashiko
2026-10-15 10:55 ` [PATCH v4 net 3/8] net: enetc: fix bogus TX ring consumer index after reinitialization vladimir.oltean
2026-10-15 10:55 ` [PATCH v4 net 4/8] net: enetc: do not rely on mEMAC hardware frame padding vladimir.oltean
2026-10-19 11:43 ` netdev-bot+sashiko
2026-10-15 10:55 ` [PATCH v4 net 5/8] net: enetc: ensure head buffer has at least 16 octets vladimir.oltean
2026-10-15 10:55 ` [PATCH v4 net 6/8] net: enetc: pad short XDP frames in software vladimir.oltean
2026-10-19 11:43 ` netdev-bot+sashiko
2026-10-15 10:55 ` [PATCH v4 net 7/8] net: enetc: fix leak of pending XDP_TX frames when going down vladimir.oltean
2026-10-19 11:43 ` netdev-bot+sashiko
2026-10-15 10:55 ` [PATCH v4 net 8/8] net: enetc: ensure enetc_xdp_xmit() calls enetc_update_tx_ring_tail() vladimir.oltean
2026-10-19 11:43 ` netdev-bot+sashiko [this message]
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=179241019093.3135.3031150554563643199@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=claudiu.manoil@nxp.com \
--cc=davem@davemloft.net \
--cc=david.laight.linux@gmail.com \
--cc=edumazet@kernel.org \
--cc=imx@lists.linux.dev \
--cc=ioana.ciornei@nxp.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=vladimir.oltean@nxp.com \
--cc=wei.fang@nxp.com \
--cc=xiaoning.wang@nxp.com \
--cc=zefir.kurtisi@gmail.com \
--cc=zefir.kurtisi@westermo.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®