From: sashiko-bot@kernel.org
To: "Steffen Dirkwinkel" <lists@steffen.cc>
Cc: linux-rt-devel@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: Re: [PATCH RFC net 5/6] net: macb: kick the transmitter on link-up
Date: Fri, 23 Oct 2026 09:39:18 +0000 [thread overview]
Message-ID: <sashiko-outbox-182615@kernel.org> (raw)
In-Reply-To: <20261022-std-macb-v14-send-v1-5-9187bc545bf6@beckhoff.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] net: macb: DMA of freed skb in macb_mac_link_up() when empty ring resets TBQP
- [High] net: macb: unbased TBQP index in macb_tx_restart() breaks link-up kick
--
commit 19c83c729b5e6d7a587a6b6ecc879a9a247907de
Author: Steffen Dirkwinkel <s.dirkwinkel@beckhoff.com>
net: macb: kick the transmitter on link-up
This commit calls macb_tx_restart() for each queue in macb_mac_link_up()
after enabling transmit so that pending frames in the ring are transmitted
even if byte queue limits previously stopped the queue.
> diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
> index 6e7ead23d0b74..3dfc7ffdd5985 100644
> --- a/drivers/net/ethernet/cadence/macb_main.c
> +++ b/drivers/net/ethernet/cadence/macb_main.c
[ ... ]
> @@ -969,6 +971,10 @@ static void macb_mac_link_up(struct phylink_config *config,
> spin_unlock_irqrestore(&bp->lock, flags);
>
> if (!(bp->caps & MACB_CAPS_MACB_IS_EMAC)) {
> + /* With BQL holding a queue stopped no xmit would issue TSTART */
> + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue)
> + macb_tx_restart(queue);
[Severity: Critical]
Can kicking the transmitter here in macb_mac_link_up() cause the controller
to transmit an already unmapped and freed skb buffer on an empty queue?
When a multi-descriptor frame wraps across the end of the ring,
macb_tx_map() clears the used bit on all descriptors of the frame and
places the sentinel used bit at the new queue->tx_head. The controller only
sets the used bit on the first descriptor of a transmitted frame, and
macb_tx_complete() unmaps the buffers via macb_tx_unmap() and frees the skb
without setting the used bit on the remaining descriptors. As a result,
descriptor 0 can retain a cleared used bit and a stale buffer address while
the sentinel sits at start % ring_size != 0.
When that queue is empty (count == 0) with start % ring_size != 0 during
link-up, macb_mac_link_up() -> gem_shuffle_tx_rings() ->
gem_shuffle_tx_one_ring() skips the ring rotation and reprograms hardware
to descriptor 0:
drivers/net/ethernet/cadence/macb_main.c:gem_shuffle_tx_one_ring() {
if (!(start % ring_size))
goto reset_hw_ptr;
if (!count) {
queue->tx_head = 0;
queue->tx_tail = 0;
goto reset_hw_ptr;
}
...
reset_hw_ptr:
queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma));
}
Unlike macb_tx_error_task(), which explicitly marks descriptor 0 as used
before resetting the hardware pointer:
drivers/net/ethernet/cadence/macb_main.c:macb_tx_error_task() {
/* Set end of TX queue */
desc = macb_tx_desc(queue, 0);
macb_set_addr(bp, desc, 0);
desc->ctrl = MACB_BIT(TX_USED);
}
gem_shuffle_tx_one_ring() leaves descriptor 0 with its stale control word.
When macb_tx_restart() then sets MACB_BIT(TSTART) in the global NCR
register because another queue has pending frames, won't the controller
start reading from descriptor 0 on the empty queue, transmit the freed
buffer onto the network, and advance the hardware pointer past index 0
while queue->tx_head and queue->tx_tail remain at 0?
> +
> /* Reap the frames completed while link-down had TCOMP masked */
> local_bh_disable();
> for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue)
[Severity: High]
Does macb_tx_restart() compute the descriptor index from queue_readl(queue,
TBQP) without subtracting the ring base address, causing missed kicks or
spurious restarts on link-up?
Earlier in macb_mac_link_up(), gem_shuffle_tx_one_ring() rotates the ring
so that done completed frames sit at [ring_size - done, ring_size), unsent
frames sit at [0, count - done), and the hardware pointer is reset to the
ring base:
drivers/net/ethernet/cadence/macb_main.c:gem_shuffle_tx_one_ring() {
queue->tx_tail = done ? ring_size - done : 0;
queue->tx_head = queue->tx_tail + count;
/* Make descriptor updates visible to hardware */
wmb();
reset_hw_ptr:
queue_writel(queue, TBQP, lower_32_bits(queue->tx_ring_dma));
}
Then macb_mac_link_up() calls macb_tx_restart(queue) before scheduling
queue->napi_tx to reap those completed frames:
drivers/net/ethernet/cadence/macb_main.c:macb_tx_restart() {
if (queue->tx_head == queue->tx_tail)
goto out_tx_ptr_unlock;
tbqp = queue_readl(queue, TBQP) / macb_dma_desc_get_size(bp);
tbqp = macb_adj_dma_desc_idx(bp, macb_tx_ring_wrap(bp, tbqp));
head_idx = macb_adj_dma_desc_idx(bp, macb_tx_ring_wrap(bp, queue->tx_head));
if (tbqp == head_idx)
goto out_tx_ptr_unlock;
}
Because queue_readl(queue, TBQP) returns the raw 32-bit address
lower_32_bits(queue->tx_ring_dma) rather than a byte offset from the start
of the ring, dividing it directly by macb_dma_desc_get_size(bp) only wraps
to index 0 if lower_32_bits(queue->tx_ring_dma) is an exact multiple of
bp->tx_ring_size * macb_dma_desc_get_size(bp).
In macb_alloc(), queue->tx_ring_dma is not aligned to that product in
several configurations:
drivers/net/ethernet/cadence/macb_main.c:macb_alloc() {
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
queue->tx_ring = tx + macb_tx_ring_size_per_queue(bp) * q;
queue->tx_ring_dma = tx_dma + macb_tx_ring_size_per_queue(bp) * q;
}
Specifically:
- On multi-queue controllers with descriptor prefetch,
macb_tx_ring_size_per_queue(bp) adds bp->tx_bd_rd_prefetch bytes per
queue, shifting queue->tx_ring_dma for queues >= 1.
- When 64-bit addressing and ptp timestamping are both enabled,
macb_dma_desc_get_size(bp) is 24 bytes, which does not evenly divide a
page-aligned base address even on queue 0.
- When bp->tx_ring_size * macb_dma_desc_get_size(bp) exceeds page size and
dma_alloc_coherent() returns an address aligned only to page size.
When tbqp at the ring base wraps to a non-zero base_idx instead of 0:
- If count - done == base_idx unsent frames are waiting at [0, base_idx),
head_idx equals tbqp, so macb_tx_restart() skips writing MACB_BIT(TSTART)
and leaves the stopped queue stuck.
- If all queued frames completed while link-down masked completion
interrupts (done == count > 0), queue->tx_head != queue->tx_tail is still
true before napi_tx runs, and head_idx is 0 while tbqp is non-zero, so
macb_tx_restart() writes MACB_BIT(TSTART) onto a used descriptor at index
0 and triggers a spurious used-bit-read interrupt.
Should macb_tx_restart() subtract lower_32_bits(queue->tx_ring_dma) from
queue_readl(queue, TBQP) before dividing by macb_dma_desc_get_size(bp)?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261022-std-macb-v14-send-v1-0-9187bc545bf6@beckhoff.com?part=5
next prev parent reply other threads:[~2026-10-23 9:39 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-22 9:38 [PATCH RFC net 0/6] net: macb: fix TX stalls, IRQ storms and a lost NCR update " Steffen Dirkwinkel
2026-10-22 9:38 ` [PATCH RFC net 1/6] net: macb: take bp->lock around NCR read-modify-writes Steffen Dirkwinkel
2026-10-22 9:38 ` [PATCH RFC net 2/6] net: macb: reprogram TBQP after shuffling the TX ring on link-up Steffen Dirkwinkel
2026-10-23 9:39 ` sashiko-bot
2026-10-22 9:38 ` [PATCH RFC net 3/6] net: macb: mask TXUBR during TX NAPI poll to prevent IRQ storms Steffen Dirkwinkel
2026-10-23 9:39 ` sashiko-bot
2026-10-22 9:38 ` [PATCH RFC net 4/6] net: macb: rotate unreaped TX frames behind the ring base on link-up Steffen Dirkwinkel
2026-10-22 9:38 ` [PATCH RFC net 5/6] net: macb: kick the transmitter " Steffen Dirkwinkel
2026-10-23 9:39 ` sashiko-bot [this message]
2026-10-22 9:38 ` [PATCH RFC net 6/6] net: macb: keep best-effort traffic on TX queue 0 Steffen Dirkwinkel
2026-10-22 9:55 ` [PATCH RFC net 0/6] net: macb: fix TX stalls, IRQ storms and a lost NCR update on link-up Nicolai Buchwitz
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=sashiko-outbox-182615@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rt-devel@lists.linux.dev \
--cc=lists@steffen.cc \
--cc=sashiko-reviews@lists.linux.dev \
/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®