* [RFC net-next 1/5] r8169: recheck the TX descriptor after a vector-map interrupt
2026-11-11 22:01 [RFC net-next 0/5] r8169: RSS for the RTL8125B Vlad Banea
@ 2026-11-11 22:01 ` Vlad Banea
2026-11-11 22:01 ` [RFC net-next 2/5] r8169: describe the vector-map interrupt layout per chip Vlad Banea
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Vlad Banea @ 2026-11-11 22:01 UTC (permalink / raw)
To: netdev
Cc: hkallweit1, javen_xu, nic_swsd, andrew+netdev, davem, edumazet,
kuba, pabeni, linux-kernel
With the vector-map interrupt model the TX interrupt can arrive before
the descriptor write-back is visible. rtl_tx() then stops at a
descriptor that still looks owned, and the end of a burst stays
unreaped. Once the BQL limit drops below the stranded bytes, the queue
stops and the watchdog resets the chip.
The legacy handler reads the interrupt status register before the poll
runs; the vector-map handler only writes registers. Do as Realtek's
driver does: if the first pending descriptor still looks owned, read a
register and look again.
Fixes: 677f8f8c4c5f ("r8169: add support for new interrupt mapping")
Signed-off-by: Vlad Banea <montrealtango@gmail.com>
---
drivers/net/ethernet/realtek/r8169_main.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index cd4ebc654dd0..6bd137358d1f 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -5228,6 +5228,7 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
int budget)
{
unsigned int dirty_tx, bytes_compl = 0, pkts_compl = 0;
+ bool recheck = tp->irq_nvecs > 1;
struct sk_buff *skb;
dirty_tx = tp->dirty_tx;
@@ -5237,8 +5238,19 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
u32 status;
status = le32_to_cpu(READ_ONCE(tp->TxDescArray[entry].opts1));
- if (status & DescOwn)
- break;
+ if (status & DescOwn) {
+ /* The vector-map TX interrupt can arrive before the
+ * descriptor write-back is visible. Read a register and
+ * look again, as Realtek's driver does.
+ */
+ if (!recheck)
+ break;
+ recheck = false;
+ RTL_R8(tp, IMR_CLEAR_VEC_MAP_REG);
+ status = le32_to_cpu(READ_ONCE(tp->TxDescArray[entry].opts1));
+ if (status & DescOwn)
+ break;
+ }
skb = tp->tx_skb[entry].skb;
rtl8169_unmap_tx_skb(tp, entry);
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [RFC net-next 2/5] r8169: describe the vector-map interrupt layout per chip
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 ` Vlad Banea
2026-11-11 22:01 ` [RFC net-next 3/5] r8169: make the RSS hash types and descriptor type " Vlad Banea
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Vlad Banea @ 2026-11-11 22:01 UTC (permalink / raw)
To: netdev
Cc: hkallweit1, javen_xu, nic_swsd, andrew+netdev, davem, edumazet,
kuba, pabeni, linux-kernel
The vector-map interrupt model hard-codes the RTL8127A's MSI-X layout.
Other chips put the TX and link change vectors elsewhere, so move the
layout into a per-chip struct rtl_vec_map.
No functional change.
Signed-off-by: Vlad Banea <montrealtango@gmail.com>
---
drivers/net/ethernet/realtek/r8169_main.c | 79 ++++++++++++++---------
1 file changed, 48 insertions(+), 31 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 6bd137358d1f..a77bbedc8c02 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -81,8 +81,6 @@
#define R8169_MAX_RX_QUEUES 8
#define R8169_DEFAULT_RX_QUEUES 1
#define R8169_MAX_TX_QUEUES 1
-#define R8127_MAX_NUM_IRQVEC 32
-#define R8127_MIN_NUM_IRQVEC 30
#define R8169_IRQ_DEFAULT 1
#define RTL_RSS_KEY_SIZE 40
#define RSS_CPU_NUM_MASK GENMASK(18, 16)
@@ -489,7 +487,6 @@ enum rtl8125_registers {
IMR_SET_VEC_MAP_REG = 0x0d0c,
};
-#define MSIX_ID_VEC_MAP_LINKCHG 29
#define LEDSEL_MASK_8125 0x23f
#define RX_VLAN_INNER_8125 BIT(22)
@@ -623,9 +620,6 @@ enum rtl_register_content {
/* magic enable v2 */
MagicPacket_v2 = (1 << 16), /* Wake up when receives a Magic Packet */
-#define ISRIMR_LINKCHG BIT(29)
-#define ISRIMR_TOK_Q0 BIT(8)
-#define ISRIMR_ROK_Q0 BIT(0)
#define RTL_DESC_TYPE_CTRL 0xd8
#define RSS_KEY_REG 0x4600
#define RSS_INDIRECTION_TBL_REG 0x4700
@@ -843,6 +837,23 @@ enum rtl_sfp_mode {
RTL_SFP_8127_ATF,
};
+/* Vector-map MSI-X layout. RX queue N uses vector N; a source's bit in
+ * the vector-map IMR/ISR registers is its vector number.
+ */
+struct rtl_vec_map {
+ u8 tx; /* TX completion */
+ u8 linkchg; /* link change */
+ u8 min_nvecs; /* vectors that must be allocated */
+ u8 max_nvecs;
+};
+
+static const struct rtl_vec_map rtl_vec_map_8127 = {
+ .tx = 8,
+ .linkchg = 29,
+ .min_nvecs = 30,
+ .max_nvecs = 32,
+};
+
struct rtl8169_rss_data {
u8 rss_key[RTL_RSS_KEY_SIZE];
u8 rss_indir_tbl[RTL_MAX_INDIRECTION_TABLE_ENTRIES];
@@ -871,6 +882,7 @@ struct rtl8169_private {
unsigned int hw_supp_num_rx_queues;
struct rtl8169_rss_data *rss_data;
unsigned int irq_nvecs;
+ const struct rtl_vec_map *vec_map; /* NULL: single vector only */
enum rx_desc_type init_rx_desc_type;
struct clk *clk;
int speed;
@@ -4755,7 +4767,7 @@ static void rtl8169_napi_disable(struct rtl8169_private *tp)
if (tp->irq_nvecs > 1) {
for (int i = 0; i < tp->num_rx_rings; i++)
napi_disable(&tp->rtl8169_napi[i]);
- napi_disable(&tp->rtl8169_napi[R8169_MAX_RX_QUEUES]);
+ napi_disable(&tp->rtl8169_napi[tp->vec_map->tx]);
} else {
napi_disable(&tp->rtl8169_napi[0]);
}
@@ -4766,7 +4778,7 @@ static void rtl8169_napi_enable(struct rtl8169_private *tp)
if (tp->irq_nvecs > 1) {
for (int i = 0; i < tp->num_rx_rings; i++)
napi_enable(&tp->rtl8169_napi[i]);
- napi_enable(&tp->rtl8169_napi[R8169_MAX_RX_QUEUES]);
+ napi_enable(&tp->rtl8169_napi[tp->vec_map->tx]);
} else {
napi_enable(&tp->rtl8169_napi[0]);
}
@@ -5535,9 +5547,9 @@ static void rtl8169_free_one_irq(struct rtl8169_private *tp, int i)
napi = &tp->rtl8169_napi[i];
if (tp->irq_nvecs > 1) {
- if (i < tp->num_rx_rings || i == R8169_MAX_RX_QUEUES)
+ if (i < tp->num_rx_rings || i == tp->vec_map->tx)
free_irq(irq, napi);
- else if (i == MSIX_ID_VEC_MAP_LINKCHG)
+ else if (i == tp->vec_map->linkchg)
free_irq(irq, tp);
} else {
free_irq(irq, napi);
@@ -5591,7 +5603,7 @@ static irqreturn_t rtl8169_interrupt_other(int irq, void *dev_instance)
{
struct rtl8169_private *tp = dev_instance;
- rtl8169_clear_hw_isr(tp, MSIX_ID_VEC_MAP_LINKCHG);
+ rtl8169_clear_hw_isr(tp, tp->vec_map->linkchg);
if (tp->phydev)
phy_mac_interrupt(tp->phydev);
else if (tp->sfp_mode)
@@ -5615,11 +5627,11 @@ static int rtl8169_request_irq(struct rtl8169_private *tp)
irq = pci_irq_vector(pdev, i);
if (tp->irq_nvecs > 1) {
- /* RX: 0~7, TX: 8*/
- if (i < tp->num_rx_rings || i == R8169_MAX_RX_QUEUES)
+ /* RX queue N on vector N, TX and link at vec_map */
+ if (i < tp->num_rx_rings || i == tp->vec_map->tx)
rc = request_irq(irq, rtl8169_interrupt_msix,
irqflags, dev->name, napi);
- else if (i == MSIX_ID_VEC_MAP_LINKCHG)
+ else if (i == tp->vec_map->linkchg)
rc = request_irq(irq, rtl8169_interrupt_other,
irqflags, dev->name, tp);
else
@@ -5805,7 +5817,7 @@ static void rtl8169_netpoll(struct net_device *dev)
if (tp->irq_nvecs > 1) {
for (int i = 0; i < tp->num_rx_rings; i++)
rtl8169_interrupt_msix(0, &tp->rtl8169_napi[i]);
- rtl8169_interrupt_msix(0, &tp->rtl8169_napi[R8169_MAX_RX_QUEUES]);
+ rtl8169_interrupt_msix(0, &tp->rtl8169_napi[tp->vec_map->tx]);
} else {
rtl8169_interrupt(0, &tp->rtl8169_napi[0]);
}
@@ -6034,7 +6046,7 @@ static void r8169_free_napi(struct rtl8169_private *tp)
if (tp->irq_nvecs > 1) {
for (int i = 0; i < tp->num_rx_rings; i++)
netif_napi_del(&tp->rtl8169_napi[i]);
- netif_napi_del(&tp->rtl8169_napi[R8169_MAX_RX_QUEUES]);
+ netif_napi_del(&tp->rtl8169_napi[tp->vec_map->tx]);
} else {
netif_napi_del(&tp->rtl8169_napi[0]);
}
@@ -6091,9 +6103,9 @@ static const struct net_device_ops rtl_netdev_ops = {
static void rtl_set_irq_mask(struct rtl8169_private *tp)
{
if (tp->irq_nvecs > 1) {
- tp->irq_mask = ISRIMR_LINKCHG | ISRIMR_TOK_Q0;
+ tp->irq_mask = BIT(tp->vec_map->linkchg) | BIT(tp->vec_map->tx);
for (int i = 0; i < tp->num_rx_rings; i++)
- tp->irq_mask |= ISRIMR_ROK_Q0 << i;
+ tp->irq_mask |= BIT(i);
} else {
tp->irq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;
@@ -6102,18 +6114,24 @@ static void rtl_set_irq_mask(struct rtl8169_private *tp)
}
}
+static const struct rtl_vec_map *rtl_get_vec_map(struct rtl8169_private *tp)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_80:
+ return &rtl_vec_map_8127;
+ default:
+ return NULL;
+ }
+}
+
static int get_max_irq_nvecs(struct rtl8169_private *tp)
{
- if (tp->mac_version == RTL_GIGA_MAC_VER_80)
- return R8127_MAX_NUM_IRQVEC;
- return R8169_IRQ_DEFAULT;
+ return tp->vec_map ? tp->vec_map->max_nvecs : R8169_IRQ_DEFAULT;
}
static int get_min_irq_nvecs(struct rtl8169_private *tp)
{
- if (tp->mac_version == RTL_GIGA_MAC_VER_80)
- return R8127_MIN_NUM_IRQVEC;
- return R8169_IRQ_DEFAULT;
+ return tp->vec_map ? tp->vec_map->min_nvecs : R8169_IRQ_DEFAULT;
}
static void rtl8169_set_rx_ring_num(struct rtl8169_private *tp)
@@ -6727,10 +6745,8 @@ static int rtl8169_poll_msix_tx(struct napi_struct *napi, int budget)
return 0;
}
-/* RTL8127 MSI-X vector layout:
- * Vectors 0 .. (MAX_RXQ - 1) : Rx Queues
- * Vectors MAX_RXQ .. (MAX_RXQ + MAX_TXQ - 1) : Tx Queues
- * NAPI is only allocated for data path
+/* Vector-map MSI-X: RX queue N on vector N, TX and link change on the
+ * chip's vec_map vectors. NAPI is only allocated for the data path.
*/
static void r8169_init_napi(struct rtl8169_private *tp)
{
@@ -6744,10 +6760,10 @@ static void r8169_init_napi(struct rtl8169_private *tp)
pci_irq_vector(tp->pci_dev, i));
}
netif_napi_add_tx(tp->dev,
- &tp->rtl8169_napi[R8169_MAX_RX_QUEUES],
+ &tp->rtl8169_napi[tp->vec_map->tx],
rtl8169_poll_msix_tx);
- netif_napi_set_irq(&tp->rtl8169_napi[R8169_MAX_RX_QUEUES],
- pci_irq_vector(tp->pci_dev, R8169_MAX_RX_QUEUES));
+ netif_napi_set_irq(&tp->rtl8169_napi[tp->vec_map->tx],
+ pci_irq_vector(tp->pci_dev, tp->vec_map->tx));
} else {
netif_napi_add(tp->dev, &tp->rtl8169_napi[0], rtl8169_poll);
netif_napi_set_irq(&tp->rtl8169_napi[0],
@@ -6913,6 +6929,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
rtl_hw_reset(tp);
rtl_setup_rx_params(tp);
+ tp->vec_map = rtl_get_vec_map(tp);
rc = rtl_alloc_irq(tp);
if (rc < 0)
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [RFC net-next 3/5] r8169: make the RSS hash types and descriptor type per chip
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 ` Vlad Banea
2026-11-11 22:01 ` [RFC net-next 4/5] r8169: add the type 3 RX descriptor Vlad Banea
2026-11-11 22:01 ` [RFC net-next 5/5] r8169: enable RSS on the RTL8125B Vlad Banea
4 siblings, 0 replies; 6+ messages in thread
From: Vlad Banea @ 2026-11-11 22:01 UTC (permalink / raw)
To: netdev
Cc: hkallweit1, javen_xu, nic_swsd, andrew+netdev, davem, edumazet,
kuba, pabeni, linux-kernel
The RSS hash types and the RX descriptor type used with RSS are those of
the RTL8127A. Make both per chip, set in rtl_setup_rx_params().
No functional change.
Signed-off-by: Vlad Banea <montrealtango@gmail.com>
---
drivers/net/ethernet/realtek/r8169_main.c | 29 +++++++++++++----------
1 file changed, 17 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index a77bbedc8c02..cc253728109c 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -858,6 +858,7 @@ struct rtl8169_rss_data {
u8 rss_key[RTL_RSS_KEY_SIZE];
u8 rss_indir_tbl[RTL_MAX_INDIRECTION_TABLE_ENTRIES];
unsigned int hw_supp_indir_tbl_entries;
+ u32 hash_types; /* RSS_CTRL_*_SUPP bits to hash on */
};
struct rtl8169_private {
@@ -884,6 +885,7 @@ struct rtl8169_private {
unsigned int irq_nvecs;
const struct rtl_vec_map *vec_map; /* NULL: single vector only */
enum rx_desc_type init_rx_desc_type;
+ enum rx_desc_type rss_desc_type; /* layout used with RSS on */
struct clk *clk;
int speed;
@@ -2906,6 +2908,15 @@ static void rtl_setup_rx_params(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_80:
tp->hw_supp_num_rx_queues = R8169_MAX_RX_QUEUES;
tp->rss_data->hw_supp_indir_tbl_entries = RTL_MAX_INDIRECTION_TABLE_ENTRIES;
+ tp->rss_data->hash_types = RSS_CTRL_TCP_IPV4_SUPP |
+ RSS_CTRL_IPV4_SUPP |
+ RSS_CTRL_IPV6_SUPP |
+ RSS_CTRL_IPV6_EXT_SUPP |
+ RSS_CTRL_TCP_IPV6_SUPP |
+ RSS_CTRL_TCP_IPV6_EXT_SUPP |
+ RSS_CTRL_UDP_IPV4_SUPP |
+ RSS_CTRL_UDP_IPV6_SUPP;
+ tp->rss_desc_type = RX_DESC_TYPE_RSS;
break;
default:
tp->hw_supp_num_rx_queues = R8169_DEFAULT_RX_QUEUES;
@@ -3066,14 +3077,7 @@ static void rtl8169_set_rss_hash_opt(struct rtl8169_private *tp)
rss_ctrl = FIELD_PREP(RSS_CPU_NUM_MASK, ilog2(tp->num_rx_rings));
/* Perform hash on these packet types */
- rss_ctrl |= RSS_CTRL_TCP_IPV4_SUPP |
- RSS_CTRL_IPV4_SUPP |
- RSS_CTRL_IPV6_SUPP |
- RSS_CTRL_IPV6_EXT_SUPP |
- RSS_CTRL_TCP_IPV6_SUPP |
- RSS_CTRL_TCP_IPV6_EXT_SUPP |
- RSS_CTRL_UDP_IPV4_SUPP |
- RSS_CTRL_UDP_IPV6_SUPP;
+ rss_ctrl |= tp->rss_data->hash_types;
rss_ctrl |= FIELD_PREP(RSS_HASH_MASK,
ilog2(tp->rss_data->hw_supp_indir_tbl_entries));
@@ -4272,11 +4276,12 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp)
tp->mac_version == RTL_GIGA_MAC_VER_80)
RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02);
- /* enable rx descriptor type v4 and set queue num for rss */
+ /* set queue num for rss and enable its rx descriptor type */
if (tp->num_rx_rings > 1) {
rtl8125_set_rx_q_num(tp);
- RTL_W8(tp, RTL_DESC_TYPE_CTRL,
- RTL_R8(tp, RTL_DESC_TYPE_CTRL) | RTL_DESC_TYPE_RSS);
+ if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+ RTL_W8(tp, RTL_DESC_TYPE_CTRL,
+ RTL_R8(tp, RTL_DESC_TYPE_CTRL) | RTL_DESC_TYPE_RSS);
}
if (tp->mac_version == RTL_GIGA_MAC_VER_80)
@@ -6142,7 +6147,7 @@ static void rtl8169_set_rx_ring_num(struct rtl8169_private *tp)
tp->num_rx_rings = rounddown_pow_of_two(min(rss_queue_num,
tp->hw_supp_num_rx_queues));
if (tp->num_rx_rings >= 2)
- tp->init_rx_desc_type = RX_DESC_TYPE_RSS;
+ tp->init_rx_desc_type = tp->rss_desc_type;
}
}
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [RFC net-next 4/5] r8169: add the type 3 RX descriptor
2026-11-11 22:01 [RFC net-next 0/5] r8169: RSS for the RTL8125B Vlad Banea
` (2 preceding siblings ...)
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
2026-11-11 22:01 ` [RFC net-next 5/5] r8169: enable RSS on the RTL8125B Vlad Banea
4 siblings, 0 replies; 6+ messages in thread
From: Vlad Banea @ 2026-11-11 22:01 UTC (permalink / raw)
To: netdev
Cc: hkallweit1, javen_xu, nic_swsd, andrew+netdev, davem, edumazet,
kuba, pabeni, linux-kernel
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
^ permalink raw reply [flat|nested] 6+ messages in thread* [RFC net-next 5/5] r8169: enable RSS on the RTL8125B
2026-11-11 22:01 [RFC net-next 0/5] r8169: RSS for the RTL8125B Vlad Banea
` (3 preceding siblings ...)
2026-11-11 22:01 ` [RFC net-next 4/5] r8169: add the type 3 RX descriptor Vlad Banea
@ 2026-11-11 22:01 ` Vlad Banea
4 siblings, 0 replies; 6+ messages in thread
From: Vlad Banea @ 2026-11-11 22:01 UTC (permalink / raw)
To: netdev
Cc: hkallweit1, javen_xu, nic_swsd, andrew+netdev, davem, edumazet,
kuba, pabeni, linux-kernel
The RTL8125B has four RSS queues with the vector-map interrupt model:
RX queue N on vector N, TX on 16, link change on 21, and at least 22
vectors. RSS requires the type 3 descriptor. The RTL8125A has neither,
so enable this for RTL_GIGA_MAC_VER_63 only.
Hash on TCP and IP only: the RTL8125B does not spread UDP flows by port.
Signed-off-by: Vlad Banea <montrealtango@gmail.com>
---
drivers/net/ethernet/realtek/r8169_main.c | 27 ++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 85e2c03fa61d..a313216998d2 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -78,6 +78,7 @@
#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
+#define R8125B_MAX_RX_QUEUES 4
#define R8169_DEFAULT_RX_QUEUES 1
#define R8169_MAX_TX_QUEUES 1
#define R8169_IRQ_DEFAULT 1
@@ -880,6 +881,13 @@ struct rtl_vec_map {
u8 max_nvecs;
};
+static const struct rtl_vec_map rtl_vec_map_8125b = {
+ .tx = 16,
+ .linkchg = 21,
+ .min_nvecs = 22,
+ .max_nvecs = 32,
+};
+
static const struct rtl_vec_map rtl_vec_map_8127 = {
.tx = 8,
.linkchg = 29,
@@ -1835,7 +1843,8 @@ static enum rtl_sfp_mode rtl_get_sfp_mode(struct rtl8169_private *tp)
static bool rtl_hw_support_rss(struct rtl8169_private *tp)
{
- return tp->mac_version == RTL_GIGA_MAC_VER_80;
+ return tp->mac_version == RTL_GIGA_MAC_VER_63 ||
+ tp->mac_version == RTL_GIGA_MAC_VER_80;
}
static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp)
@@ -2965,6 +2974,20 @@ static void rtl_setup_rx_params(struct rtl8169_private *tp)
{
tp->num_rx_rings = 1;
switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_63:
+ /* The UDP hash types do not spread UDP flows by port on the
+ * RTL8125B, so hash TCP and IP only.
+ */
+ tp->hw_supp_num_rx_queues = R8125B_MAX_RX_QUEUES;
+ tp->rss_data->hw_supp_indir_tbl_entries = RTL_MAX_INDIRECTION_TABLE_ENTRIES;
+ tp->rss_data->hash_types = RSS_CTRL_TCP_IPV4_SUPP |
+ RSS_CTRL_IPV4_SUPP |
+ RSS_CTRL_IPV6_SUPP |
+ RSS_CTRL_IPV6_EXT_SUPP |
+ RSS_CTRL_TCP_IPV6_SUPP |
+ RSS_CTRL_TCP_IPV6_EXT_SUPP;
+ tp->rss_desc_type = RX_DESC_TYPE_V3;
+ break;
case RTL_GIGA_MAC_VER_80:
tp->hw_supp_num_rx_queues = R8169_MAX_RX_QUEUES;
tp->rss_data->hw_supp_indir_tbl_entries = RTL_MAX_INDIRECTION_TABLE_ENTRIES;
@@ -6236,6 +6259,8 @@ static void rtl_set_irq_mask(struct rtl8169_private *tp)
static const struct rtl_vec_map *rtl_get_vec_map(struct rtl8169_private *tp)
{
switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_63:
+ return &rtl_vec_map_8125b;
case RTL_GIGA_MAC_VER_80:
return &rtl_vec_map_8127;
default:
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread