* [PATCH net] net: sparx5: don't reuse the last DCB in fdma xmit
@ 2026-10-20 8:59 Daniel Machon
2026-10-21 9:52 ` netdev-bot+sashiko
0 siblings, 1 reply; 3+ messages in thread
From: Daniel Machon @ 2026-10-20 8:59 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver,
Jens Emil Schulz Østergaard, Horatiu Vultur
Cc: netdev, linux-arm-kernel, linux-kernel
sparx5_fdma_xmit() advances dcb_index to the next tx DCB and, if the
FDMA is done with it, adds it to the end of the chain, which makes it
the new fdma->last_dcb.
Before this patch, sparx5_fdma_xmit() can pick last_dcb itself. A
failed transmit advances dcb_index without adding a DCB, so dcb_index
no longer follows last_dcb, and after a burst of failures it can land
on it. Re-adding last_dcb loses the frame, because the FDMA has already
passed the tail of the chain.
Also reject the DCB when it is the last one in the chain, using the
same !fdma_is_last() guard that lan966x and lan969x use to pick their
next tx DCB, so the tail is never re-added.
Found by an AI code review agent and confirmed by code inspection; not
reproduced. Tested on sparx5 that traffic still passes, with ping floods
in both directions.
Fixes: f4aa7e361ae2 ("net: sparx5: use FDMA library for adding DCB's in the tx path")
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
index dbe86f937b21..221b2f1932f6 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
@@ -218,7 +218,8 @@ int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb,
void *virt_addr;
fdma_dcb_advance(fdma);
- if (!fdma_db_is_done(fdma_db_get(fdma, fdma->dcb_index, 0)))
+ if (!fdma_db_is_done(fdma_db_get(fdma, fdma->dcb_index, 0)) ||
+ fdma_is_last(fdma, fdma_dcb_next_get(fdma)))
return -EINVAL;
/* Get the virtual address of the dataptr for the next DB */
---
base-commit: 07bd391eb9572e74f073c49acef327611234f9bb
change-id: 20261019-sparx5-fdma-last-dcb-e2e8491b7d86
Best regards,
--
Daniel Machon <daniel.machon@microchip.com>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net] net: sparx5: don't reuse the last DCB in fdma xmit
2026-10-20 8:59 [PATCH net] net: sparx5: don't reuse the last DCB in fdma xmit Daniel Machon
@ 2026-10-21 9:52 ` netdev-bot+sashiko
2026-10-21 11:20 ` Daniel Machon
0 siblings, 1 reply; 3+ messages in thread
From: netdev-bot+sashiko @ 2026-10-21 9:52 UTC (permalink / raw)
To: daniel.machon
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, Steen.Hegelund,
UNGLinuxDriver, jensemil.schulzostergaard, horatiu.vultur,
netdev, linux-arm-kernel, linux-kernel
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.
Critical: 0 (1) · High: 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] net: sparx5: unguarded fdma_dcb_advance() in sparx5_fdma_xmit()
breaks tx ring
Pre-existing issues:
- [Critical] This problem wasn't introduced by this patch, but the patch
adds a new way to reach it.
--
net: sparx5: don't reuse the last DCB in fdma xmit
This patch adds a check to sparx5_fdma_xmit() so it rejects the next tx
DCB when that DCB is fdma->last_dcb. This uses the same !fdma_is_last()
guard as lan966x and lan969x, so the tail of the chain is never re-added
and the frame is not lost.
> diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
> index dbe86f937b21..221b2f1932f6 100644
> --- a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
> +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
> @@ -218,7 +218,8 @@ int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb,
> void *virt_addr;
>
> fdma_dcb_advance(fdma);
> - if (!fdma_db_is_done(fdma_db_get(fdma, fdma->dcb_index, 0)))
> + if (!fdma_db_is_done(fdma_db_get(fdma, fdma->dcb_index, 0)) ||
> + fdma_is_last(fdma, fdma_dcb_next_get(fdma)))
> return -EINVAL;
[Severity: High]
Does this fully fix the drift described in the commit message? The
underlying problem looks like it is still there: fdma_dcb_advance() runs
before the done check, and fdma->dcb_index is not restored when
sparx5_fdma_xmit() returns -EINVAL. The new fdma_is_last() test only
covers the moment dcb_index lands exactly on last_dcb.
When the ring is full, a failed xmit leaves dcb_index on the oldest
in-flight DCB. The next call advances again before checking anything,
so if the FDMA has completed that DCB in the meantime it is skipped and
we test a newer DCB that is still in flight. Each failure moves
dcb_index one step further, so a burst can keep dropping frames while
completed DCBs sit unused behind dcb_index.
If dcb_index ends up just behind last_dcb and the FDMA then drains the
chain, every DCB is done and the ring is idle. The next xmit advances
onto last_dcb, hits the new fdma_is_last() check and returns -EINVAL,
which sparx5_port_xmit_impl() treats as a drop:
if (ret == -EBUSY)
goto busy;
if (ret < 0)
goto drop;
The lost frame becomes a counted drop, but it is still a drop on an
empty ring. lan966x_fdma_get_next_dcb() and lan969x_fdma_get_next_dcb()
don't hit this because they use !fdma_is_last() inside a loop over all
DCBs and pick another free one, rather than failing on a single index.
The previous tail is also still unsafe. Say the FDMA has halted on
last_dcb at index L and dcb_index is at L - 2. The first frame advances
to L - 1, which is done and not last, so it is linked from
dcbs[L].nextptr and becomes the new last_dcb. __fdma_dcb_add() does not
clear the done bit on dcbs[L], so if a second frame comes in before the
FDMA has fetched dcbs[L].nextptr after the reload, it advances onto L,
which is still done and no longer last. Re-adding it rewrites
dcbs[L].nextptr to FDMA_DCB_INVALID_DATA, so dcbs[L - 1] is cut out of
the chain. Its frame is lost, and since the FDMA never visits it again
its done bit stays clear, so later xmits that land on it fail too.
The drift itself dates back to f4aa7e361ae2, but since this patch is
meant to fix it, would it be cleaner to fix it at the source, by only
committing the dcb_index advance once the DCB has been accepted (or
rolling it back on failure)? Then dcb_index always follows last_dcb,
the next DCB is always the oldest one, and the fdma_is_last() check
should not be needed. Alternatively, sparx5 could search the ring the
way lan969x_fdma_get_next_dcb() does.
[Severity: Critical]
This is a pre-existing issue, but this patch adds a new way to reach it.
What happens to a two-step PTP skb when this returns -EINVAL?
For a two-step PTP skb, sparx5_port_xmit_impl() first calls
sparx5_ptp_txtstamp_request(). That function links the skb onto
port->tx_skbs and does not take an extra reference:
drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c:sparx5_ptp_txtstamp_request() {
...
skb_queue_tail(&port->tx_skbs, skb);
SPARX5_SKB_CB(skb)->ts_id = port->ts_id;
SPARX5_SKB_CB(skb)->jiffies = jiffies;
sparx5->ptp_skbs++;
...
}
When ops->fdma_xmit() returns -EINVAL, the caller jumps to the drop label.
Only the busy label calls sparx5_ptp_txtstamp_release():
drivers/net/ethernet/microchip/sparx5/sparx5_packet.c:sparx5_port_xmit_impl() {
...
drop:
stats->tx_dropped++;
sparx5->tx.dropped++;
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
busy:
if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
SPARX5_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP)
sparx5_ptp_txtstamp_release(port, skb);
return NETDEV_TX_BUSY;
}
That leaves the freed skb linked on port->tx_skbs. On the next two-step
request, sparx5_ptp_txtstamp_old_release() reads the freed skb's cb and
then frees it a second time:
drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c:sparx5_ptp_txtstamp_old_release() {
...
if time_after(SPARX5_SKB_CB(skb)->jiffies + SPARX5_PTP_TIMEOUT,
jiffies)
break;
__skb_unlink(skb, &port->tx_skbs);
dev_kfree_skb_any(skb);
...
}
sparx5_ptp_irq_handler() walks the same queue. It also reads
SPARX5_SKB_CB(skb)->ts_id from the freed skb. On this path the ptp_skbs
slot is never given back either.
The drop path has been like this since commit 121c6672b0191. Until now it
could only be reached through the !fdma_db_is_done() (ring full) case.
Before this patch, the case where dcb_index landed on last_dcb returned
NETDEV_TX_OK, so the PTP skb stayed queued safely. With the new
fdma_is_last() rejection, that case now goes through the same drop path.
Could the drop path call sparx5_ptp_txtstamp_release() for two-step PTP
skbs, the same way the busy path does?
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261020-sparx5-fdma-last-dcb-v1-1-fb9a956196dd%40microchip.com
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net] net: sparx5: don't reuse the last DCB in fdma xmit
2026-10-21 9:52 ` netdev-bot+sashiko
@ 2026-10-21 11:20 ` Daniel Machon
0 siblings, 0 replies; 3+ messages in thread
From: Daniel Machon @ 2026-10-21 11:20 UTC (permalink / raw)
To: netdev-bot+sashiko
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, Steen.Hegelund,
UNGLinuxDriver, jensemil.schulzostergaard, horatiu.vultur,
netdev, linux-arm-kernel, linux-kernel
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue(s) to consider.
>
> Critical: 0 (1) · High: 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] net: sparx5: unguarded fdma_dcb_advance() in sparx5_fdma_xmit()
> breaks tx ring
>
> Pre-existing issues:
> - [Critical] This problem wasn't introduced by this patch, but the patch
> adds a new way to reach it.
>
> --
>
> net: sparx5: don't reuse the last DCB in fdma xmit
>
> This patch adds a check to sparx5_fdma_xmit() so it rejects the next tx
> DCB when that DCB is fdma->last_dcb. This uses the same !fdma_is_last()
> guard as lan966x and lan969x, so the tail of the chain is never re-added
> and the frame is not lost.
>
> > diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
> > index dbe86f937b21..221b2f1932f6 100644
> > --- a/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
> > +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_fdma.c
> > @@ -218,7 +218,8 @@ int sparx5_fdma_xmit(struct sparx5 *sparx5, u32 *ifh, struct sk_buff *skb,
> > void *virt_addr;
> >
> > fdma_dcb_advance(fdma);
> > - if (!fdma_db_is_done(fdma_db_get(fdma, fdma->dcb_index, 0)))
> > + if (!fdma_db_is_done(fdma_db_get(fdma, fdma->dcb_index, 0)) ||
> > + fdma_is_last(fdma, fdma_dcb_next_get(fdma)))
> > return -EINVAL;
>
> [Severity: High]
> Does this fully fix the drift described in the commit message? The
> underlying problem looks like it is still there: fdma_dcb_advance() runs
> before the done check, and fdma->dcb_index is not restored when
> sparx5_fdma_xmit() returns -EINVAL. The new fdma_is_last() test only
> covers the moment dcb_index lands exactly on last_dcb.
>
> When the ring is full, a failed xmit leaves dcb_index on the oldest
> in-flight DCB. The next call advances again before checking anything,
> so if the FDMA has completed that DCB in the meantime it is skipped and
> we test a newer DCB that is still in flight. Each failure moves
> dcb_index one step further, so a burst can keep dropping frames while
> completed DCBs sit unused behind dcb_index.
>
> If dcb_index ends up just behind last_dcb and the FDMA then drains the
> chain, every DCB is done and the ring is idle. The next xmit advances
> onto last_dcb, hits the new fdma_is_last() check and returns -EINVAL,
> which sparx5_port_xmit_impl() treats as a drop:
>
> if (ret == -EBUSY)
> goto busy;
> if (ret < 0)
> goto drop;
>
> The lost frame becomes a counted drop, but it is still a drop on an
> empty ring. lan966x_fdma_get_next_dcb() and lan969x_fdma_get_next_dcb()
> don't hit this because they use !fdma_is_last() inside a loop over all
> DCBs and pick another free one, rather than failing on a single index.
>
> The previous tail is also still unsafe. Say the FDMA has halted on
> last_dcb at index L and dcb_index is at L - 2. The first frame advances
> to L - 1, which is done and not last, so it is linked from
> dcbs[L].nextptr and becomes the new last_dcb. __fdma_dcb_add() does not
> clear the done bit on dcbs[L], so if a second frame comes in before the
> FDMA has fetched dcbs[L].nextptr after the reload, it advances onto L,
> which is still done and no longer last. Re-adding it rewrites
> dcbs[L].nextptr to FDMA_DCB_INVALID_DATA, so dcbs[L - 1] is cut out of
> the chain. Its frame is lost, and since the FDMA never visits it again
> its done bit stays clear, so later xmits that land on it fail too.
>
> The drift itself dates back to f4aa7e361ae2, but since this patch is
> meant to fix it, would it be cleaner to fix it at the source, by only
> committing the dcb_index advance once the DCB has been accepted (or
> rolling it back on failure)? Then dcb_index always follows last_dcb,
> the next DCB is always the oldest one, and the fdma_is_last() check
> should not be needed. Alternatively, sparx5 could search the ring the
> way lan969x_fdma_get_next_dcb() does.
>
The drift is pre-existing and not what I intended to fix. That said, I think I
can fix both with a simple change where the ring index is computed from the last
index instead.
pw-bot: cr
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-21 11:20 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-20 8:59 [PATCH net] net: sparx5: don't reuse the last DCB in fdma xmit Daniel Machon
2026-10-21 9:52 ` netdev-bot+sashiko
2026-10-21 11:20 ` Daniel Machon
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®