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 on link-up 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.