From: Vlad Banea <montrealtango@gmail.com>
To: netdev@vger.kernel.org
Cc: hkallweit1@gmail.com, javen_xu@realsil.com.cn,
nic_swsd@realtek.com, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
linux-kernel@vger.kernel.org
Subject: [RFC net-next 4/5] r8169: add the type 3 RX descriptor
Date: Wed, 11 Nov 2026 17:01:13 -0500 [thread overview]
Message-ID: <20261111220114.3863505-5-montrealtango@gmail.com> (raw)
In-Reply-To: <20261111220114.3863505-1-montrealtango@gmail.com>
The RTL8125A/B family has a 32-byte RX descriptor ("type 3" in Realtek's
driver), which the RTL8125B requires with RSS. Its second half is laid
out like RX_DESC_TYPE_RSS, so add RX_DESC_TYPE_V3 as a 16-byte header
followed by a struct RxDesc and reuse the RSS accessors.
Differences from RX_DESC_TYPE_RSS:
- FirstFrag/LastFrag at bits 25/24 and RES/RUNT/RWT/CRC at 20-17 of
opts1;
- one checksum flag per protocol in opts2, and only the matching
protocol's failure flag is valid;
- the RSS hash in rss_result, its type in hdr_info;
- selected by RxConfig bit 24, set before the receiver is enabled.
No chip uses it yet.
Signed-off-by: Vlad Banea <montrealtango@gmail.com>
---
drivers/net/ethernet/realtek/r8169_main.c | 158 +++++++++++++++++++---
1 file changed, 136 insertions(+), 22 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index cc253728109c..85e2c03fa61d 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -75,7 +75,6 @@
#define NUM_TX_DESC 256 /* Number of Tx descriptor registers */
#define NUM_RX_DESC 256 /* Number of Rx descriptor registers */
#define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc))
-#define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc))
#define R8169_TX_STOP_THRS (MAX_SKB_FRAGS + 1)
#define R8169_TX_START_THRS (2 * R8169_TX_STOP_THRS)
#define R8169_MAX_RX_QUEUES 8
@@ -492,6 +491,26 @@ enum rtl8125_registers {
#define RX_VLAN_INNER_8125 BIT(22)
#define RX_VLAN_OUTER_8125 BIT(23)
#define RX_VLAN_8125 (RX_VLAN_INNER_8125 | RX_VLAN_OUTER_8125)
+#define RX_DESC_V3_8125 BIT(24) /* type 3 (32-byte) RX descriptors */
+
+/* RX_DESC_TYPE_V3 status bits, opts1 unless noted */
+#define RX_FIRST_FRAG_V3 BIT(25)
+#define RX_LAST_FRAG_V3 BIT(24)
+#define RX_RES_V3 BIT(20)
+#define RX_RUNT_V3 BIT(19)
+#define RX_RWT_V3 BIT(18)
+#define RX_CRC_V3 BIT(17)
+#define RX_UDPT_V3 BIT(29) /* opts2: UDP packet */
+#define RX_TCPT_V3 BIT(28) /* opts2: TCP packet */
+#define RX_IPF_V3 BIT(26) /* opts2: IP checksum failed */
+#define RX_UDPF_V3 BIT(25) /* opts2: UDP checksum failed */
+#define RX_TCPF_V3 BIT(24) /* opts2: TCP checksum failed */
+#define RXS_RSS_UDP_V3 BIT(9) /* hdr_info */
+#define RXS_RSS_IPV4_V3 BIT(10)
+#define RXS_RSS_IPV6_V3 BIT(12)
+#define RXS_RSS_TCP_V3 BIT(13)
+#define RXS_RSS_L3_V3 (RXS_RSS_IPV4_V3 | RXS_RSS_IPV6_V3)
+#define RXS_RSS_L4_V3 (RXS_RSS_TCP_V3 | RXS_RSS_UDP_V3)
#define RX_FETCH_DFLT_8125 (8 << 27)
@@ -740,6 +759,19 @@ struct RxDesc {
};
};
+/* 32-byte RX descriptor of the RTL8125A/B family ("type 3") */
+struct RxDescV3 {
+ __le32 rsv1;
+ __le32 rsv2;
+ __le32 rss_result;
+ __le16 hdr_buf_len;
+ __le16 hdr_info;
+ struct RxDesc desc;
+};
+
+static_assert(sizeof(struct RxDesc) == 16);
+static_assert(sizeof(struct RxDescV3) == 32);
+
struct ring_info {
struct sk_buff *skb;
u32 len;
@@ -812,6 +844,7 @@ enum rtl_dash_type {
enum rx_desc_type {
RX_DESC_TYPE_DEFAULT,
RX_DESC_TYPE_RSS,
+ RX_DESC_TYPE_V3, /* 32-byte struct RxDescV3 */
};
struct rtl8169_rx_ring {
@@ -2112,6 +2145,7 @@ static void rtl8169_rx_vlan_tag(struct rtl8169_private *tp,
switch (tp->init_rx_desc_type) {
case RX_DESC_TYPE_RSS:
+ case RX_DESC_TYPE_V3:
opts2 = le32_to_cpu(desc->rss_opts2);
break;
default:
@@ -2817,7 +2851,9 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
break;
case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_LAST:
RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST |
- RX_PAUSE_SLOT_ON);
+ RX_PAUSE_SLOT_ON |
+ (tp->init_rx_desc_type == RX_DESC_TYPE_V3 ?
+ RX_DESC_V3_8125 : 0));
break;
default:
RTL_W32(tp, RxConfig, RX128_INT_EN | RX_DMA_BURST);
@@ -2825,12 +2861,36 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
}
}
+/* RX_DESC_TYPE_RSS and RX_DESC_TYPE_V3 share the address/opts2/opts1 order */
+static bool rtl8169_rx_desc_rss_layout(struct rtl8169_private *tp)
+{
+ return tp->init_rx_desc_type != RX_DESC_TYPE_DEFAULT;
+}
+
+static size_t rtl8169_rx_ring_bytes(struct rtl8169_private *tp)
+{
+ if (tp->init_rx_desc_type == RX_DESC_TYPE_V3)
+ return NUM_RX_DESC * sizeof(struct RxDescV3);
+
+ return NUM_RX_DESC * sizeof(struct RxDesc);
+}
+
+static struct RxDesc *rtl8169_rx_desc(struct rtl8169_private *tp,
+ struct rtl8169_rx_ring *ring,
+ unsigned int index)
+{
+ if (tp->init_rx_desc_type == RX_DESC_TYPE_V3)
+ return &((struct RxDescV3 *)ring->rx_desc_array)[index].desc;
+
+ return &ring->rx_desc_array[index];
+}
+
static void rtl8169_rx_desc_init(struct rtl8169_private *tp)
{
for (int i = 0; i < tp->num_rx_rings; i++) {
struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
- memset(ring->rx_desc_array, 0x0, R8169_RX_RING_BYTES);
+ memset(ring->rx_desc_array, 0x0, rtl8169_rx_ring_bytes(tp));
}
}
@@ -4533,6 +4593,9 @@ static void rtl_hw_start(struct rtl8169_private *tp)
/* Initially a 10 us delay. Turned it into a PCI commit. - FR */
rtl_pci_commit(tp);
+ /* Select the 32-byte descriptor before the receiver is enabled */
+ if (tp->init_rx_desc_type == RX_DESC_TYPE_V3)
+ rtl_init_rxcfg(tp);
RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb);
rtl_init_rxcfg(tp);
rtl_set_tx_config_registers(tp);
@@ -4570,6 +4633,7 @@ static void rtl8169_mark_to_asic(struct rtl8169_private *tp, struct RxDesc *desc
switch (tp->init_rx_desc_type) {
case RX_DESC_TYPE_RSS:
+ case RX_DESC_TYPE_V3:
eor = le32_to_cpu(desc->rss_opts1) & RingEnd;
desc->rss_opts2 = cpu_to_le32(0);
/* Force memory writes to complete before releasing descriptor */
@@ -4589,7 +4653,7 @@ static void rtl8169_mark_to_asic(struct rtl8169_private *tp, struct RxDesc *desc
static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
struct rtl8169_rx_ring *ring, unsigned int index)
{
- struct RxDesc *desc = ring->rx_desc_array + index;
+ struct RxDesc *desc = rtl8169_rx_desc(tp, ring, index);
struct device *d = tp_to_dev(tp);
int node = dev_to_node(d);
dma_addr_t mapping;
@@ -4607,7 +4671,7 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
}
ring->rx_buf_dma_addr[index] = mapping;
- if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+ if (rtl8169_rx_desc_rss_layout(tp))
desc->rss_addr = cpu_to_le64(mapping);
else
desc->addr = cpu_to_le64(mapping);
@@ -4619,6 +4683,7 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
static void rtl8169_rx_clear(struct rtl8169_private *tp,
struct rtl8169_rx_ring *ring)
{
+ struct RxDesc *desc;
int i;
for (i = 0; i < NUM_RX_DESC && ring->rx_databuff[i]; i++) {
@@ -4628,12 +4693,13 @@ static void rtl8169_rx_clear(struct rtl8169_private *tp,
__free_pages(ring->rx_databuff[i], get_order(R8169_RX_BUF_SIZE));
ring->rx_databuff[i] = NULL;
ring->rx_buf_dma_addr[i] = 0;
- if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS) {
- ring->rx_desc_array[i].rss_addr = 0;
- ring->rx_desc_array[i].rss_opts1 = 0;
+ desc = rtl8169_rx_desc(tp, ring, i);
+ if (rtl8169_rx_desc_rss_layout(tp)) {
+ desc->rss_addr = 0;
+ desc->rss_opts1 = 0;
} else {
- ring->rx_desc_array[i].addr = 0;
- ring->rx_desc_array[i].opts1 = 0;
+ desc->addr = 0;
+ desc->opts1 = 0;
}
}
}
@@ -4642,6 +4708,7 @@ static void rtl8169_mark_as_last_descriptor(struct rtl8169_private *tp, struct R
{
switch (tp->init_rx_desc_type) {
case RX_DESC_TYPE_RSS:
+ case RX_DESC_TYPE_V3:
desc->rss_opts1 |= cpu_to_le32(RingEnd);
break;
default:
@@ -4666,7 +4733,7 @@ static int rtl8169_rx_fill(struct rtl8169_private *tp, struct rtl8169_rx_ring *r
}
/* mark as last descriptor in the ring */
- rtl8169_mark_as_last_descriptor(tp, &ring->rx_desc_array[NUM_RX_DESC - 1]);
+ rtl8169_mark_as_last_descriptor(tp, rtl8169_rx_desc(tp, ring, NUM_RX_DESC - 1));
return 0;
}
@@ -4679,7 +4746,7 @@ static int rtl8169_alloc_rx_desc(struct rtl8169_private *tp)
for (int i = 0; i < tp->num_rx_rings; i++) {
ring = &tp->rx_ring[i];
ring->rx_desc_array = dma_alloc_coherent(&pdev->dev,
- R8169_RX_RING_BYTES,
+ rtl8169_rx_ring_bytes(tp),
&ring->rx_phy_addr,
GFP_KERNEL);
if (!ring->rx_desc_array)
@@ -4697,7 +4764,7 @@ static void rtl8169_free_rx_desc(struct rtl8169_private *tp)
ring = &tp->rx_ring[i];
if (ring->rx_desc_array) {
dma_free_coherent(&pdev->dev,
- R8169_RX_RING_BYTES,
+ rtl8169_rx_ring_bytes(tp),
ring->rx_desc_array,
ring->rx_phy_addr);
ring->rx_desc_array = NULL;
@@ -4832,11 +4899,12 @@ static void rtl8169_rx_desc_reset(struct rtl8169_private *tp)
struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
for (int j = 0; j < NUM_RX_DESC; j++) {
+ struct RxDesc *desc = rtl8169_rx_desc(tp, ring, j);
dma_addr_t phy_addr = ring->rx_buf_dma_addr[j];
- if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
- ring->rx_desc_array[j].rss_addr = cpu_to_le64(phy_addr);
- rtl8169_mark_to_asic(tp, ring->rx_desc_array + j);
+ if (rtl8169_rx_desc_rss_layout(tp))
+ desc->rss_addr = cpu_to_le64(phy_addr);
+ rtl8169_mark_to_asic(tp, desc);
}
}
}
@@ -5300,9 +5368,13 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
}
}
-static inline int rtl8169_fragmented_frame(u32 status)
+static inline int rtl8169_fragmented_frame(struct rtl8169_private *tp,
+ u32 status)
{
- return (status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag);
+ u32 frags = tp->init_rx_desc_type == RX_DESC_TYPE_V3 ?
+ RX_FIRST_FRAG_V3 | RX_LAST_FRAG_V3 : FirstFrag | LastFrag;
+
+ return (status & frags) != frags;
}
static void rtl8169_rx_hash(struct rtl8169_private *tp,
@@ -5315,6 +5387,20 @@ static void rtl8169_rx_hash(struct rtl8169_private *tp,
if (!(tp->dev->features & NETIF_F_RXHASH))
return;
+ if (tp->init_rx_desc_type == RX_DESC_TYPE_V3) {
+ const struct RxDescV3 *v3 = container_of(desc, struct RxDescV3,
+ desc);
+
+ rss_header_info = le16_to_cpu(v3->hdr_info);
+ if (!(rss_header_info & RXS_RSS_L3_V3))
+ return;
+
+ skb_set_hash(skb, le32_to_cpu(v3->rss_result),
+ (rss_header_info & RXS_RSS_L4_V3) ?
+ PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3);
+ return;
+ }
+
rss_header_info = le32_to_cpu(desc->rss_dword.rss_info);
if (!(rss_header_info & RXS_RSS_L3_TYPE_MASK))
@@ -5329,11 +5415,23 @@ static void rtl8169_rx_hash(struct rtl8169_private *tp,
static void rtl8169_rx_csum(struct rtl8169_private *tp,
struct sk_buff *skb,
+ struct RxDesc *desc,
u32 opts1)
{
bool csum_ok = false;
+ u32 opts2;
switch (tp->init_rx_desc_type) {
+ case RX_DESC_TYPE_V3:
+ /* The chip also sets the failure flag of the other
+ * protocol (a TCP frame carries UDPF); check only the
+ * matching one.
+ */
+ opts2 = le32_to_cpu(desc->rss_opts2);
+ if (((opts2 & RX_TCPT_V3) && !(opts2 & (RX_TCPF_V3 | RX_IPF_V3))) ||
+ ((opts2 & RX_UDPT_V3) && !(opts2 & (RX_UDPF_V3 | RX_IPF_V3))))
+ csum_ok = true;
+ break;
case RX_DESC_TYPE_RSS:
if (((opts1 & RX_TCPT_DESC_RSS) && !(opts1 & RX_TCPF_DESC_RSS)) ||
((opts1 & RX_UDPT_DESC_RSS) && !(opts1 & RX_UDPF_DESC_RSS)))
@@ -5358,6 +5456,7 @@ static __le32 rtl8169_rx_desc_opts1(struct rtl8169_private *tp, struct RxDesc *d
{
switch (tp->init_rx_desc_type) {
case RX_DESC_TYPE_RSS:
+ case RX_DESC_TYPE_V3:
return READ_ONCE(desc->rss_opts1);
default:
return READ_ONCE(desc->opts1);
@@ -5369,6 +5468,17 @@ static bool rtl8169_check_rx_desc_error(struct rtl8169_rx_ring *ring,
u32 status)
{
switch (tp->init_rx_desc_type) {
+ case RX_DESC_TYPE_V3:
+ if (unlikely(status & RX_RES_V3)) {
+ u64_stats_update_begin(&ring->stats.syncp);
+ if (status & (RX_RWT_V3 | RX_RUNT_V3))
+ ring->stats.rx_length_errors++;
+ if (status & RX_CRC_V3)
+ ring->stats.rx_crc_errors++;
+ u64_stats_update_end(&ring->stats.syncp);
+ return true;
+ }
+ break;
case RX_DESC_TYPE_RSS:
if (unlikely(status & RX_RES_RSS)) {
u64_stats_update_begin(&ring->stats.syncp);
@@ -5407,7 +5517,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
for (count = 0; count < budget; count++, ring->cur_rx++) {
unsigned int pkt_size, entry = ring->cur_rx % NUM_RX_DESC;
- struct RxDesc *desc = ring->rx_desc_array + entry;
+ struct RxDesc *desc = rtl8169_rx_desc(tp, ring, entry);
struct sk_buff *skb;
const void *rx_buf;
dma_addr_t addr;
@@ -5437,6 +5547,10 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
if (tp->init_rx_desc_type == RX_DESC_TYPE_DEFAULT) {
if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
goto release_descriptor;
+ } else if (tp->init_rx_desc_type == RX_DESC_TYPE_V3) {
+ if (status & RX_RWT_V3 ||
+ !(status & (RX_RUNT_V3 | RX_CRC_V3)))
+ goto release_descriptor;
} else {
if (!(status & (RXRUNT_RSS | RXCRC_RSS)))
goto release_descriptor;
@@ -5454,7 +5568,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
/* The driver does not support incoming fragmented frames.
* They are seen as a symptom of over-mtu sized frames.
*/
- if (unlikely(rtl8169_fragmented_frame(status))) {
+ if (unlikely(rtl8169_fragmented_frame(tp, status))) {
u64_stats_update_begin(&ring->stats.syncp);
ring->stats.rx_dropped++;
ring->stats.rx_length_errors++;
@@ -5484,7 +5598,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
rtl8169_rx_hash(tp, desc, skb);
skb_record_rx_queue(skb, ring - tp->rx_ring);
}
- rtl8169_rx_csum(tp, skb, status);
+ rtl8169_rx_csum(tp, skb, desc, status);
skb->protocol = eth_type_trans(skb, dev);
rtl8169_rx_vlan_tag(tp, desc, skb);
@@ -5499,7 +5613,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
dev_sw_netstats_rx_add(dev, pkt_size);
release_descriptor:
- if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+ if (rtl8169_rx_desc_rss_layout(tp))
desc->rss_addr = cpu_to_le64(ring->rx_buf_dma_addr[entry]);
rtl8169_mark_to_asic(tp, desc);
}
--
2.53.0
next prev parent reply other threads:[~2026-11-11 22:01 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 22:01 [RFC net-next 0/5] r8169: RSS for the RTL8125B Vlad Banea
2026-11-11 22:01 ` [RFC net-next 1/5] r8169: recheck the TX descriptor after a vector-map interrupt Vlad Banea
2026-11-11 22:01 ` [RFC net-next 2/5] r8169: describe the vector-map interrupt layout per chip Vlad Banea
2026-11-11 22:01 ` [RFC net-next 3/5] r8169: make the RSS hash types and descriptor type " Vlad Banea
2026-11-11 22:01 ` Vlad Banea [this message]
2026-11-11 22:01 ` [RFC net-next 5/5] r8169: enable RSS on the RTL8125B Vlad Banea
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=20261111220114.3863505-5-montrealtango@gmail.com \
--to=montrealtango@gmail.com \
--cc=andrew+netdev@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=hkallweit1@gmail.com \
--cc=javen_xu@realsil.com.cn \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=nic_swsd@realtek.com \
--cc=pabeni@redhat.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®