From: Simon Horman <horms@kernel.org>
To: atharvapotdar07@gmail.com
Cc: Simon Horman <horms@kernel.org>,
hkallweit1@gmail.com, nic_swsd@realtek.com,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, romieu@fr.zoreil.com,
netdev@vger.kernel.org
Subject: Re: [PATCH v4] r8169: migrate Rx path to page_pool, prepare for XDP
Date: Thu, 9 Jul 2026 18:01:30 +0100 [thread overview]
Message-ID: <20260709170151.1577992-2-horms@kernel.org> (raw)
In-Reply-To: <20260704080653.33740-1-atharvapotdar07@gmail.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/
---
r8169: migrate Rx path to page_pool, prepare for XDP
This commit migrates the Rx path to page_pool on MACs newer than
RTL_GIGA_MAC_VER_06, reserves XDP_PACKET_HEADROOM in each buffer, and
decouples the Rx consumption loop (cur_rx) from a new refill loop
(dirty_rx) that reallocates only after napi_build_skb() takes
ownership of a page.
> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index ec4fc21fa21f..5b13880c7ed8 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c
[ ... ]
> @@ -4161,21 +4169,14 @@ static void rtl8169_mark_to_asic(struct RxDesc *desc)
> static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
> struct RxDesc *desc)
> {
> - struct device *d = tp_to_dev(tp);
> - int node = dev_to_node(d);
> dma_addr_t mapping;
> struct page *data;
>
> - data = alloc_pages_node(node, GFP_KERNEL, get_order(R8169_RX_BUF_SIZE));
> + data = page_pool_dev_alloc_pages(tp->rx_pool);
> if (!data)
> return NULL;
>
> - mapping = dma_map_page(d, data, 0, R8169_RX_BUF_SIZE, DMA_FROM_DEVICE);
> - if (unlikely(dma_mapping_error(d, mapping))) {
> - netdev_err(tp->dev, "Failed to map RX DMA!\n");
> - __free_pages(data, get_order(R8169_RX_BUF_SIZE));
> - return NULL;
> - }
> + mapping = page_pool_get_dma_addr(data) + tp->rx_headroom;
>
> desc->addr = cpu_to_le64(mapping);
> rtl8169_mark_to_asic(desc);
[Critical]
Can the NIC DMA past the end of the allocated page here? The
descriptor's addr is offset by tp->rx_headroom (XDP_PACKET_HEADROOM,
256) inside a page whose backing allocation is order-2 (16384 bytes,
since R8169_RX_BUF_SIZE is SZ_16K - 1 = 16383):
mapping = page_pool_get_dma_addr(data) + tp->rx_headroom;
but rtl8169_mark_to_asic() still programs the full R8169_RX_BUF_SIZE
into the descriptor's writable-size field:
WRITE_ONCE(desc->opts1,
cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
So the NIC's writable window becomes [page + 256, page + 16639], which
overshoots the 16384-byte allocation by up to 255 bytes. page_pool's
own accounting has already been narrowed via params.max_len =
R8169_RX_BUF_SIZE - tp->rx_headroom, but that value never propagates
to opts1.
On RTL8125/8126, rtl_jumbo_max() returns JUMBO_16K and rtl_set_rx_max_size()
programs RxMaxSize = R8169_RX_BUF_SIZE + 1, so on-wire frames close to
16 KiB are accepted, exactly the range that drives the overrun.
The same mismatch appears on the zero-copy skb path. napi_build_skb()
is called with frag_size = R8169_RX_BUF_SIZE:
skb = napi_build_skb(rx_buf, R8169_RX_BUF_SIZE);
if (likely(skb)) {
skb_reserve(skb, tp->rx_headroom);
skb_put(skb, pkt_size);
After skb_reserve(256), tailroom is around 16383 - 256 - overhead,
while pkt_size comes straight from the descriptor's 14-bit length
field (up to 16383, or 16379 after the ETH_FCS_LEN strip). A large
enough pkt_size would trip skb_over_panic() inside skb_put() → BUG().
Should the descriptor size and napi_build_skb() frag_size use
R8169_RX_BUF_SIZE - tp->rx_headroom (or params.max_len) instead of
R8169_RX_BUF_SIZE?
Also, the commit message states:
The pool is locked to order-2 (SZ_16K) allocations on all MACs,
matching the existing R8169_RX_BUF_SIZE, so this migration
introduces no change in per-descriptor allocation size or
jumbo-frame behavior relative to the current tree.
Given that rx_headroom is now consuming part of that 16 KiB on newer
MACs while the descriptor size field is unchanged, is the
"no change in ... jumbo-frame behavior" claim still accurate?
> @@ -4188,14 +4189,18 @@ static void rtl8169_rx_clear(struct rtl8169_private *tp)
> int i;
>
> for (i = 0; i < NUM_RX_DESC && tp->Rx_databuff[i]; i++) {
> - dma_unmap_page(tp_to_dev(tp),
> - le64_to_cpu(tp->RxDescArray[i].addr),
> - R8169_RX_BUF_SIZE, DMA_FROM_DEVICE);
> - __free_pages(tp->Rx_databuff[i], get_order(R8169_RX_BUF_SIZE));
> + page_pool_put_full_page(tp->rx_pool, tp->Rx_databuff[i], false);
> tp->Rx_databuff[i] = NULL;
> tp->RxDescArray[i].addr = 0;
> tp->RxDescArray[i].opts1 = 0;
> }
> +
> + if (tp->rx_pool) {
> + if (xdp_rxq_info_is_reg(&tp->xdp_rxq))
> + xdp_rxq_info_unreg(&tp->xdp_rxq);
> + page_pool_destroy(tp->rx_pool);
> + tp->rx_pool = NULL;
> + }
> }
[High]
Can this loop leak Rx_databuff pages and orphan the page_pool? The
loop condition terminates at the first NULL entry:
for (i = 0; i < NUM_RX_DESC && tp->Rx_databuff[i]; i++) {
Prior to this patch, Rx_databuff[] was uniformly non-NULL between
fill and close, so early termination was safe. With this patch, two
paths create NULL entries interspersed with live pages:
- rl_rx() sets tp->Rx_databuff[entry] = NULL when napi_build_skb()
succeeds and takes ownership of the page.
- rtl8169_rx_refill() breaks on the first
page_pool_dev_alloc_pages() failure, leaving that slot NULL while
later slots still hold live pages.
If a close happens with any NULL entry in front of a still-populated
one, the loop exits early, page_pool_put_full_page() is never called
on the remaining pages, and page_pool_destroy() then sees non-zero
in-flight pages and schedules the deferred release worker. With
tp->rx_pool set to NULL and the next rtl8169_init_ring() clearing
Rx_databuff via memset(), the surviving pointers are permanently
lost.
Should this iterate over all NUM_RX_DESC entries and null-check
inside the body instead of using the pointer as a loop condition?
[High]
A related concern that isn't in this diff, but is created by the new
Rx_databuff NULL semantics: rtl_reset_work() calls rtl8169_cleanup()
(which zeroes cur_rx and dirty_rx via rtl8169_init_ring_indexes())
and then unconditionally re-arms every descriptor:
drivers/net/ethernet/realtek/r8169_main.c:rtl_reset_work() {
...
rtl8169_cleanup(tp);
for (i = 0; i < NUM_RX_DESC; i++)
rtl8169_mark_to_asic(tp->RxDescArray + i);
napi_enable(&tp->napi);
rtl_hw_start(tp);
}
After this patch, Rx_databuff[k] can be NULL for slots whose pages
were consumed by napi_build_skb(), and the descriptor's addr for
those slots still points at a page that has been returned to the
page_pool (and may be recycled to another buffer). Once
rtl_hw_start() re-enables the NIC, it can DMA incoming frames into
those stale addresses.
On top of that, because dirty_rx == cur_rx == 0 after the reset,
rtl8169_rx_refill() has nothing to advance over, and rl_rx's
guard fires without progressing state:
if (unlikely(!tp->Rx_databuff[entry]))
break;
so the NULL slots never get repopulated.
Should rtl_reset_work() refill the NULL slots (or do a full
rx_clear/rx_fill) before marking descriptors to ASIC?
next prev parent reply other threads:[~2026-07-09 17:02 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-04 8:06 [PATCH v4] r8169: migrate Rx path to page_pool, prepare for XDP Atharva Potdar
2026-07-09 17:01 ` Simon Horman [this message]
2026-07-10 13:48 ` Atharva Potdar
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=20260709170151.1577992-2-horms@kernel.org \
--to=horms@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=atharvapotdar07@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=hkallweit1@gmail.com \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=nic_swsd@realtek.com \
--cc=pabeni@redhat.com \
--cc=romieu@fr.zoreil.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