* [RFC Patch net-next v1 1/9] r8169: add some register definitions
2026-04-20 2:19 [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 javen
@ 2026-04-20 2:19 ` javen
2026-04-20 2:19 ` [RFC Patch net-next v1 2/9] r8169: add napi and irq support javen
` (8 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: javen @ 2026-04-20 2:19 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
To support rss, this patch adds some macro definitions and register
definitions.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
drivers/net/ethernet/realtek/r8169_main.c | 75 +++++++++++++++++++++++
1 file changed, 75 insertions(+)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 791277e750ba..0fbec27e4a0d 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -77,6 +77,23 @@
#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
+#define R8169_MAX_TX_QUEUES 1
+#define R8169_MAX_MSIX_VEC 32
+#define R8127_MAX_TX_QUEUES 1
+#define R8127_MAX_RX_QUEUES 8
+#define R8127_MAX_IRQ 32
+#define R8127_MIN_IRQ 30
+#define RTL8127_RSS_KEY_SIZE 40
+#define RSS_CPU_NUM_OFFSET 16
+#define RSS_MASK_BITS_OFFSET 8
+#define RTL8127_MAX_INDIRECTION_TABLE_ENTRIES 128
+#define RXS_8125B_RSS_UDP_V4 BIT(27)
+#define RXS_8125_RSS_IPV4_V4 BIT(28)
+#define RXS_8125_RSS_IPV6_V4 BIT(29)
+#define RXS_8125_RSS_TCP_V4 BIT(30)
+#define RTL8127_RXS_RSS_L3_TYPE_MASK_V4 (RXS_8125_RSS_IPV4_V4 | RXS_8125_RSS_IPV6_V4)
+#define RTL8127_RXS_RSS_L4_TYPE_MASK_V4 (RXS_8125_RSS_TCP_V4 | RXS_8125B_RSS_UDP_V4)
#define OCP_STD_PHY_BASE 0xa400
@@ -435,6 +452,8 @@ enum rtl8125_registers {
#define INT_CFG0_CLKREQEN BIT(3)
IntrMask_8125 = 0x38,
IntrStatus_8125 = 0x3c,
+ IntrMask1_8125 = 0x800,
+ IntrStatus1_8125 = 0x802,
INT_CFG1_8125 = 0x7a,
LEDSEL2 = 0x84,
LEDSEL1 = 0x86,
@@ -444,6 +463,36 @@ enum rtl8125_registers {
RSS_CTRL_8125 = 0x4500,
Q_NUM_CTRL_8125 = 0x4800,
EEE_TXIDLE_TIMER_8125 = 0x6048,
+ TNPDS_Q1_LOW = 0x2100,
+ RDSAR_Q1_LOW = 0x4000,
+ IMR_V2_SET_REG_8125 = 0x0d0c,
+ IMR_V2_CLEAR_REG_8125 = 0x0d00,
+ IMR_V4_L2_CLEAR_REG_8125 = 0x0d10,
+ ISR_V2_8125 = 0x0d04,
+ ISR_V4_L2_8125 = 0x0d14,
+};
+
+enum rtl8127_msix_id {
+ MSIX_ID_V4_LINKCHG = 29,
+};
+
+enum rtl8127_rss_register_content {
+ RSS_CTRL_TCP_IPV4_SUPP = (1 << 0),
+ RSS_CTRL_IPV4_SUPP = (1 << 1),
+ RSS_CTRL_TCP_IPV6_SUPP = (1 << 2),
+ RSS_CTRL_IPV6_SUPP = (1 << 3),
+ RSS_CTRL_IPV6_EXT_SUPP = (1 << 4),
+ RSS_CTRL_TCP_IPV6_EXT_SUPP = (1 << 5),
+ RSS_CTRL_UDP_IPV4_SUPP = (1 << 11),
+ RSS_CTRL_UDP_IPV6_SUPP = (1 << 12),
+ RSS_CTRL_UDP_IPV6_EXT_SUPP = (1 << 13),
+ RSS_INDIRECTION_TBL_8125_V2 = 0x4700,
+ RSS_KEY_8125 = 0x4600,
+};
+
+enum rtl8127_rss_flag {
+ RTL_8125_RSS_FLAG_HASH_UDP_IPV4 = (1 << 0),
+ RTL_8125_RSS_FLAG_HASH_UDP_IPV6 = (1 << 1),
};
#define LEDSEL_MASK_8125 0x23f
@@ -474,6 +523,10 @@ enum rtl_register_content {
RxRUNT = (1 << 20),
RxCRC = (1 << 19),
+ RxRES_RSS = (1 << 22),
+ RxRUNT_RSS = (1 << 21),
+ RxCRC_RSS = (1 << 20),
+
/* ChipCmdBits */
StopReq = 0x80,
CmdReset = 0x10,
@@ -576,6 +629,9 @@ enum rtl_register_content {
/* magic enable v2 */
MagicPacket_v2 = (1 << 16), /* Wake up when receives a Magic Packet */
+ ISRIMR_V6_LINKCHG = (1 << 29),
+ ISRIMR_V6_TOK_Q0 = (1 << 8),
+ ISRIMR_V6_ROK_Q0 = (1 << 0),
};
enum rtl_desc_bit {
@@ -633,6 +689,11 @@ enum rtl_rx_desc_bit {
#define RxProtoIP (PID1 | PID0)
#define RxProtoMask RxProtoIP
+ RxUDPT_v4 = (1 << 19),
+ RxTCPT_v4 = (1 << 18),
+ RxUDPF_v4 = (1 << 16), /* UDP/IP checksum failed */
+ RxTCPF_v4 = (1 << 15), /* TCP/IP checksum failed */
+
IPFail = (1 << 16), /* IP checksum failed */
UDPFail = (1 << 15), /* UDP/IP checksum failed */
TCPFail = (1 << 14), /* TCP/IP checksum failed */
@@ -659,6 +720,11 @@ struct RxDesc {
__le64 addr;
};
+enum features {
+ RTL_FEATURE_MSI = (1 << 1),
+ RTL_FEATURE_MSIX = (1 << 2),
+};
+
struct ring_info {
struct sk_buff *skb;
u32 len;
@@ -728,6 +794,13 @@ enum rtl_dash_type {
RTL_DASH_25_BP,
};
+enum rx_desc_ring_type {
+ RX_DESC_RING_TYPE_UNKNOWN = 0,
+ RX_DESC_RING_TYPE_DEAFULT,
+ RX_DESC_RING_TYPE_RSS,
+ RX_DESC_RING_TYPE_MAX
+};
+
struct rtl8169_private {
void __iomem *mmio_addr; /* memory map physical address */
struct pci_dev *pci_dev;
@@ -763,6 +836,8 @@ struct rtl8169_private {
unsigned aspm_manageable:1;
unsigned dash_enabled:1;
bool sfp_mode:1;
+ bool rss_support:1;
+ bool rss_enable:1;
dma_addr_t counters_phys_addr;
struct rtl8169_counters *counters;
struct rtl8169_tc_offsets tc_offset;
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [RFC Patch net-next v1 2/9] r8169: add napi and irq support
2026-04-20 2:19 [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 javen
2026-04-20 2:19 ` [RFC Patch net-next v1 1/9] r8169: add some register definitions javen
@ 2026-04-20 2:19 ` javen
2026-04-20 2:19 ` [RFC Patch net-next v1 3/9] r8169: add support for multi tx queues javen
` (7 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: javen @ 2026-04-20 2:19 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
Multi tx queues and rx queues require multi irq support. Each irq pairs
with one napi.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
drivers/net/ethernet/realtek/r8169_main.c | 205 ++++++++++++++++++++--
1 file changed, 187 insertions(+), 18 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 0fbec27e4a0d..d1f3a208bd1b 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -794,6 +794,19 @@ enum rtl_dash_type {
RTL_DASH_25_BP,
};
+struct rtl8169_napi {
+ struct napi_struct napi;
+ void *priv;
+ int index;
+};
+
+struct rtl8169_irq {
+ irq_handler_t handler;
+ unsigned int vector;
+ u8 requested;
+ char name[IFNAMSIZ + 10];
+};
+
enum rx_desc_ring_type {
RX_DESC_RING_TYPE_UNKNOWN = 0,
RX_DESC_RING_TYPE_DEAFULT,
@@ -818,9 +831,20 @@ struct rtl8169_private {
dma_addr_t RxPhyAddr;
struct page *Rx_databuff[NUM_RX_DESC]; /* Rx data buffers */
struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */
+ struct rtl8169_irq irq_tbl[R8169_MAX_MSIX_VEC];
+ struct rtl8169_napi r8169napi[R8169_MAX_MSIX_VEC];
+ u16 isr_reg[R8169_MAX_MSIX_VEC];
+ u16 imr_reg[R8169_MAX_MSIX_VEC];
+ unsigned int num_tx_rings;
+ unsigned int num_rx_rings;
u16 cp_cmd;
u16 tx_lpi_timer;
u32 irq_mask;
+ u8 min_irq_nvecs;
+ u8 max_irq_nvecs;
+ u8 HwSuppIsrVer;
+ u8 HwCurrIsrVer;
+ u8 irq_nvecs;
int irq;
struct clk *clk;
@@ -2755,6 +2779,45 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
}
+static void rtl_setup_mqs_reg(struct rtl8169_private *tp)
+{
+ if (tp->mac_version <= RTL_GIGA_MAC_VER_52) {
+ tp->isr_reg[0] = IntrStatus;
+ tp->imr_reg[0] = IntrMask;
+ } else {
+ tp->isr_reg[0] = IntrStatus_8125;
+ tp->imr_reg[0] = IntrMask_8125;
+ }
+
+ for (int i = 1; i < tp->max_irq_nvecs; i++)
+ tp->isr_reg[i] = (u16)(IntrStatus1_8125 + (i - 1) * 4);
+
+ for (int i = 1; i < tp->max_irq_nvecs; i++)
+ tp->imr_reg[i] = (u16)(IntrMask1_8125 + (i - 1) * 4);
+}
+
+static void rtl_software_parameter_initialize(struct rtl8169_private *tp)
+{
+ tp->num_rx_rings = 1;
+ tp->num_tx_rings = 1;
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_80:
+ tp->min_irq_nvecs = R8127_MIN_IRQ;
+ tp->max_irq_nvecs = R8127_MAX_IRQ;
+ tp->HwSuppIsrVer = 6;
+ break;
+ default:
+ tp->min_irq_nvecs = 1;
+ tp->max_irq_nvecs = 1;
+ tp->HwSuppIsrVer = 1;
+ break;
+ }
+ tp->HwCurrIsrVer = tp->HwSuppIsrVer;
+
+ rtl_setup_mqs_reg(tp);
+}
+
static void rtl_request_firmware(struct rtl8169_private *tp)
{
struct rtl_fw *rtl_fw;
@@ -4294,6 +4357,7 @@ static int rtl8169_rx_fill(struct rtl8169_private *tp)
return 0;
}
+
static int rtl8169_init_ring(struct rtl8169_private *tp)
{
rtl8169_init_ring_indexes(tp);
@@ -4313,6 +4377,7 @@ static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp, unsigned int entry)
DMA_TO_DEVICE);
memset(desc, 0, sizeof(*desc));
memset(tx_skb, 0, sizeof(*tx_skb));
+
}
static void rtl8169_tx_clear_range(struct rtl8169_private *tp, u32 start,
@@ -4341,9 +4406,21 @@ static void rtl8169_tx_clear(struct rtl8169_private *tp)
netdev_reset_queue(tp->dev);
}
+static void rtl8169_napi_disable(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->irq_nvecs; i++)
+ napi_disable(&tp->r8169napi[i].napi);
+}
+
+static void rtl8169_napi_enable(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->irq_nvecs; i++)
+ napi_enable(&tp->r8169napi[i].napi);
+}
+
static void rtl8169_cleanup(struct rtl8169_private *tp)
{
- napi_disable(&tp->napi);
+ rtl8169_napi_disable(tp);
/* Give a racing hard_start_xmit a few cycles to complete. */
synchronize_net();
@@ -4389,7 +4466,8 @@ static void rtl_reset_work(struct rtl8169_private *tp)
for (i = 0; i < NUM_RX_DESC; i++)
rtl8169_mark_to_asic(tp->RxDescArray + i);
- napi_enable(&tp->napi);
+ rtl8169_napi_enable(tp);
+
rtl_hw_start(tp);
}
@@ -4895,7 +4973,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
goto release_descriptor;
}
- skb = napi_alloc_skb(&tp->napi, pkt_size);
+ skb = napi_alloc_skb(&tp->r8169napi[0].napi, pkt_size);
if (unlikely(!skb)) {
dev->stats.rx_dropped++;
goto release_descriptor;
@@ -4919,7 +4997,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
if (skb->pkt_type == PACKET_MULTICAST)
dev->stats.multicast++;
- napi_gro_receive(&tp->napi, skb);
+ napi_gro_receive(&tp->r8169napi[0].napi, skb);
dev_sw_netstats_rx_add(dev, pkt_size);
release_descriptor:
@@ -4931,7 +5009,8 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
{
- struct rtl8169_private *tp = dev_instance;
+ struct rtl8169_napi *napi = dev_instance;
+ struct rtl8169_private *tp = napi->priv;
u32 status = rtl_get_events(tp);
if ((status & 0xffff) == 0xffff || !(status & tp->irq_mask))
@@ -4948,13 +5027,53 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
phy_mac_interrupt(tp->phydev);
rtl_irq_disable(tp);
- napi_schedule(&tp->napi);
+ napi_schedule(&napi->napi);
out:
rtl_ack_events(tp, status);
return IRQ_HANDLED;
}
+static void rtl8169_free_irq(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->irq_nvecs; i++) {
+ struct rtl8169_irq *irq = &tp->irq_tbl[i];
+ struct rtl8169_napi *napi = &tp->r8169napi[i];
+
+ if (irq->requested) {
+ irq->requested = 0;
+ pci_free_irq(tp->pci_dev, i, napi);
+ }
+ }
+}
+
+static int rtl8169_request_irq(struct rtl8169_private *tp)
+{
+ struct net_device *dev = tp->dev;
+ int rc = 0;
+ struct rtl8169_irq *irq;
+ struct rtl8169_napi *napi;
+ const int len = sizeof(tp->irq_tbl[0].name);
+
+ for (int i = 0; i < tp->irq_nvecs; i++) {
+ irq = &tp->irq_tbl[i];
+
+ napi = &tp->r8169napi[i];
+ snprintf(irq->name, len, "%s-%d", dev->name, i);
+ rc = pci_request_irq(tp->pci_dev, i, rtl8169_interrupt, NULL, napi, irq->name);
+
+ if (rc)
+ break;
+
+ irq->vector = pci_irq_vector(tp->pci_dev, i);
+ irq->requested = 1;
+ }
+
+ if (rc)
+ rtl8169_free_irq(tp);
+ return rc;
+}
+
static void rtl_task(struct work_struct *work)
{
struct rtl8169_private *tp =
@@ -4989,9 +5108,10 @@ static void rtl_task(struct work_struct *work)
static int rtl8169_poll(struct napi_struct *napi, int budget)
{
- struct rtl8169_private *tp = container_of(napi, struct rtl8169_private, napi);
+ struct rtl8169_napi *r8169_napi = container_of(napi, struct rtl8169_napi, napi);
+ struct rtl8169_private *tp = r8169_napi->priv;
struct net_device *dev = tp->dev;
- int work_done;
+ int work_done = 0;
rtl_tx(dev, tp, budget);
@@ -5110,7 +5230,7 @@ static void rtl8169_up(struct rtl8169_private *tp)
phy_init_hw(tp->phydev);
phy_resume(tp->phydev);
rtl8169_init_phy(tp);
- napi_enable(&tp->napi);
+ rtl8169_napi_enable(tp);
enable_work(&tp->wk.work);
rtl_reset_work(tp);
@@ -5125,10 +5245,12 @@ static int rtl8169_close(struct net_device *dev)
pm_runtime_get_sync(&pdev->dev);
netif_stop_queue(dev);
+
rtl8169_down(tp);
rtl8169_rx_clear(tp);
- free_irq(tp->irq, tp);
+
+ rtl8169_free_irq(tp);
phy_disconnect(tp->phydev);
@@ -5183,14 +5305,14 @@ static int rtl_open(struct net_device *dev)
rtl_request_firmware(tp);
irqflags = pci_dev_msi_enabled(pdev) ? IRQF_NO_THREAD : IRQF_SHARED;
- retval = request_irq(tp->irq, rtl8169_interrupt, irqflags, dev->name, tp);
+
+ retval = rtl8169_request_irq(tp);
if (retval < 0)
goto err_release_fw_2;
retval = r8169_phy_connect(tp);
if (retval)
goto err_free_irq;
-
rtl8169_up(tp);
rtl8169_init_counter_offsets(tp);
netif_start_queue(dev);
@@ -5200,7 +5322,7 @@ static int rtl_open(struct net_device *dev)
return retval;
err_free_irq:
- free_irq(tp->irq, tp);
+ rtl8169_free_irq(tp);
err_release_fw_2:
rtl_release_firmware(tp);
rtl8169_rx_clear(tp);
@@ -5265,7 +5387,7 @@ static int rtl8169_runtime_resume(struct device *dev)
rtl_rar_set(tp, tp->dev->dev_addr);
__rtl8169_set_wol(tp, tp->saved_wolopts);
- if (tp->TxDescArray)
+ if (netif_running(tp->dev))
rtl8169_up(tp);
netif_device_attach(tp->dev);
@@ -5303,7 +5425,7 @@ static int rtl8169_runtime_suspend(struct device *device)
{
struct rtl8169_private *tp = dev_get_drvdata(device);
- if (!tp->TxDescArray) {
+ if (!netif_running(tp->dev)) {
netif_device_detach(tp->dev);
return 0;
}
@@ -5403,7 +5525,9 @@ static void rtl_set_irq_mask(struct rtl8169_private *tp)
static int rtl_alloc_irq(struct rtl8169_private *tp)
{
+ struct pci_dev *pdev = tp->pci_dev;
unsigned int flags;
+ int nvecs = 1;
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06:
@@ -5419,7 +5543,15 @@ static int rtl_alloc_irq(struct rtl8169_private *tp)
break;
}
- return pci_alloc_irq_vectors(tp->pci_dev, 1, 1, flags);
+ nvecs = pci_alloc_irq_vectors(pdev, tp->min_irq_nvecs, tp->max_irq_nvecs, flags);
+
+ if (nvecs < 0)
+ nvecs = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+
+ tp->irq = pdev->irq;
+ tp->irq_nvecs = 1;
+
+ return nvecs;
}
static void rtl_read_mac_address(struct rtl8169_private *tp,
@@ -5614,6 +5746,18 @@ static void rtl_hw_initialize(struct rtl8169_private *tp)
}
}
+static int rtl8169_set_real_num_queue(struct rtl8169_private *tp)
+{
+ int retval;
+
+ retval = netif_set_real_num_tx_queues(tp->dev, tp->num_tx_rings);
+ if (retval < 0)
+ return retval;
+
+ retval = netif_set_real_num_rx_queues(tp->dev, tp->num_rx_rings);
+ return retval;
+}
+
static int rtl_jumbo_max(struct rtl8169_private *tp)
{
/* Non-GBit versions don't support jumbo frames */
@@ -5674,6 +5818,20 @@ static bool rtl_aspm_is_safe(struct rtl8169_private *tp)
return false;
}
+
+static void r8169_init_napi(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->irq_nvecs; i++) {
+ struct rtl8169_napi *r8169napi = &tp->r8169napi[i];
+ int (*poll)(struct napi_struct *napi, int budget);
+
+ poll = rtl8169_poll;
+ netif_napi_add(tp->dev, &r8169napi->napi, poll);
+ r8169napi->priv = tp;
+ r8169napi->index = i;
+ }
+}
+
static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
const struct rtl_chip_info *chip;
@@ -5778,11 +5936,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
rtl_hw_reset(tp);
+ rtl_software_parameter_initialize(tp);
+
rc = rtl_alloc_irq(tp);
if (rc < 0)
return dev_err_probe(&pdev->dev, rc, "Can't allocate interrupt\n");
- tp->irq = pci_irq_vector(pdev, 0);
INIT_WORK(&tp->wk.work, rtl_task);
disable_work(&tp->wk.work);
@@ -5791,7 +5950,13 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->ethtool_ops = &rtl8169_ethtool_ops;
- netif_napi_add(dev, &tp->napi, rtl8169_poll);
+ if (!tp->rss_support) {
+ netif_napi_add(dev, &tp->r8169napi[0].napi, rtl8169_poll);
+ tp->r8169napi[0].priv = tp;
+ tp->r8169napi[0].index = 0;
+ } else {
+ r8169_init_napi(tp);
+ }
dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
@@ -5853,6 +6018,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (jumbo_max)
dev->max_mtu = jumbo_max;
+ rc = rtl8169_set_real_num_queue(tp);
+ if (rc < 0)
+ return dev_err_probe(&pdev->dev, rc, "set tx/rx num failure\n");
+
rtl_set_irq_mask(tp);
tp->counters = dmam_alloc_coherent (&pdev->dev, sizeof(*tp->counters),
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [RFC Patch net-next v1 3/9] r8169: add support for multi tx queues
2026-04-20 2:19 [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 javen
2026-04-20 2:19 ` [RFC Patch net-next v1 1/9] r8169: add some register definitions javen
2026-04-20 2:19 ` [RFC Patch net-next v1 2/9] r8169: add napi and irq support javen
@ 2026-04-20 2:19 ` javen
2026-04-20 2:19 ` [RFC Patch net-next v1 4/9] r8169: add support for multi rx queues javen
` (6 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: javen @ 2026-04-20 2:19 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This patch support for multi tx queues. But we still use one tx queue
here. More support will be available in rss support patch.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
drivers/net/ethernet/realtek/r8169_main.c | 278 ++++++++++++++++------
1 file changed, 199 insertions(+), 79 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index d1f3a208bd1b..05f0cb532a31 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -73,7 +73,6 @@
#define R8169_RX_BUF_SIZE (SZ_16K - 1)
#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)
@@ -794,6 +793,18 @@ enum rtl_dash_type {
RTL_DASH_25_BP,
};
+struct rtl8169_tx_ring {
+ u32 index; /* Tx queue index */
+ u32 cur_tx; /* Index into the Tx descriptor buffer of next Tx pkt. */
+ u32 dirty_tx; /* Index into the Tx descriptor buffer for recycling. */
+ u32 num_tx_desc; /* num of Tx desc */
+ struct TxDesc *TxDescArray; /* array of Rx Desc*/
+ u32 TxDescAllocSize; /* memory size per descs of ring */
+ dma_addr_t TxPhyAddr; /* Tx desc physical address */
+ struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */
+ u16 tdsar_reg; /* Transmit Descriptor Start Address */
+};
+
struct rtl8169_napi {
struct napi_struct napi;
void *priv;
@@ -823,16 +834,12 @@ struct rtl8169_private {
enum mac_version mac_version;
enum rtl_dash_type dash_type;
u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
- u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
- u32 dirty_tx;
- struct TxDesc *TxDescArray; /* 256-aligned Tx descriptor ring */
struct RxDesc *RxDescArray; /* 256-aligned Rx descriptor ring */
- dma_addr_t TxPhyAddr;
dma_addr_t RxPhyAddr;
struct page *Rx_databuff[NUM_RX_DESC]; /* Rx data buffers */
- struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */
struct rtl8169_irq irq_tbl[R8169_MAX_MSIX_VEC];
struct rtl8169_napi r8169napi[R8169_MAX_MSIX_VEC];
+ struct rtl8169_tx_ring tx_ring[R8169_MAX_TX_QUEUES];
u16 isr_reg[R8169_MAX_MSIX_VEC];
u16 imr_reg[R8169_MAX_MSIX_VEC];
unsigned int num_tx_rings;
@@ -840,11 +847,14 @@ struct rtl8169_private {
u16 cp_cmd;
u16 tx_lpi_timer;
u32 irq_mask;
+ u16 HwSuppNumTxQueues;
u8 min_irq_nvecs;
u8 max_irq_nvecs;
u8 HwSuppIsrVer;
u8 HwCurrIsrVer;
u8 irq_nvecs;
+ u8 recheck_desc_ownbit;
+ unsigned int features;
int irq;
struct clk *clk;
@@ -2718,9 +2728,28 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
}
}
+static void rtl8169_tx_desc_init(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->num_tx_rings; i++) {
+ struct rtl8169_tx_ring *ring = &tp->tx_ring[i];
+
+ memset(ring->TxDescArray, 0x0, ring->TxDescAllocSize);
+ ring->TxDescArray[ring->num_tx_desc - 1].opts1 = cpu_to_le32(RingEnd);
+ }
+}
+
static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
{
- tp->dirty_tx = tp->cur_tx = tp->cur_rx = 0;
+ struct net_device *dev = tp->dev;
+
+ for (int i = 0; i < tp->HwSuppNumTxQueues; i++) {
+ struct rtl8169_tx_ring *ring = &tp->tx_ring[i];
+
+ ring->dirty_tx = 0;
+ ring->cur_tx = 0;
+ ring->index = i;
+ netdev_tx_reset_queue(netdev_get_tx_queue(dev, i));
+ }
}
static void rtl_jumbo_config(struct rtl8169_private *tp)
@@ -2779,8 +2808,18 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
}
+static void rtl_set_ring_size(struct rtl8169_private *tp, u32 rx_num, u32 tx_num)
+{
+ for (int i = 0; i < tp->HwSuppNumTxQueues; i++)
+ tp->tx_ring[i].num_tx_desc = tx_num;
+}
+
static void rtl_setup_mqs_reg(struct rtl8169_private *tp)
{
+ tp->tx_ring[0].tdsar_reg = TxDescStartAddrLow;
+ for (int i = 1; i < tp->HwSuppNumTxQueues; i++)
+ tp->tx_ring[i].tdsar_reg = (u16)(TNPDS_Q1_LOW + (i - 1) * 8);
+
if (tp->mac_version <= RTL_GIGA_MAC_VER_52) {
tp->isr_reg[0] = IntrStatus;
tp->imr_reg[0] = IntrMask;
@@ -2805,17 +2844,20 @@ static void rtl_software_parameter_initialize(struct rtl8169_private *tp)
case RTL_GIGA_MAC_VER_80:
tp->min_irq_nvecs = R8127_MIN_IRQ;
tp->max_irq_nvecs = R8127_MAX_IRQ;
+ tp->HwSuppNumTxQueues = R8127_MAX_TX_QUEUES;
tp->HwSuppIsrVer = 6;
break;
default:
tp->min_irq_nvecs = 1;
tp->max_irq_nvecs = 1;
+ tp->HwSuppNumTxQueues = 1;
tp->HwSuppIsrVer = 1;
break;
}
tp->HwCurrIsrVer = tp->HwSuppIsrVer;
rtl_setup_mqs_reg(tp);
+ rtl_set_ring_size(tp, NUM_RX_DESC, NUM_TX_DESC);
}
static void rtl_request_firmware(struct rtl8169_private *tp)
@@ -2947,10 +2989,14 @@ static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
* register to be written before TxDescAddrLow to work.
* Switching from MMIO to I/O access fixes the issue as well.
*/
- RTL_W32(tp, TxDescStartAddrHigh, ((u64) tp->TxPhyAddr) >> 32);
- RTL_W32(tp, TxDescStartAddrLow, ((u64) tp->TxPhyAddr) & DMA_BIT_MASK(32));
RTL_W32(tp, RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32);
RTL_W32(tp, RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_BIT_MASK(32));
+ for (int i = 0; i < tp->num_tx_rings; i++) {
+ struct rtl8169_tx_ring *ring = &tp->tx_ring[i];
+
+ RTL_W32(tp, ring->tdsar_reg, ((u64)ring->TxPhyAddr & DMA_BIT_MASK(32)));
+ RTL_W32(tp, ring->tdsar_reg + 4, ((u64)ring->TxPhyAddr >> 32));
+ }
}
static void rtl8169_set_magic_reg(struct rtl8169_private *tp)
@@ -4357,43 +4403,85 @@ static int rtl8169_rx_fill(struct rtl8169_private *tp)
return 0;
}
+static int rtl8169_alloc_tx_desc(struct rtl8169_private *tp)
+{
+ struct rtl8169_tx_ring *ring;
+ struct pci_dev *pdev = tp->pci_dev;
+
+ for (int i = 0; i < tp->num_tx_rings; i++) {
+ ring = &tp->tx_ring[i];
+ ring->TxDescAllocSize = (ring->num_tx_desc + 1) * sizeof(struct TxDesc);
+ ring->TxDescArray = dma_alloc_coherent(&pdev->dev,
+ ring->TxDescAllocSize,
+ &ring->TxPhyAddr,
+ GFP_KERNEL);
+ if (!ring->TxDescArray)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void rtl8169_free_tx_desc(struct rtl8169_private *tp)
+{
+ struct rtl8169_tx_ring *ring;
+ struct pci_dev *pdev = tp->pci_dev;
+
+ for (int i = 0; i < tp->num_tx_rings; i++) {
+ ring = &tp->tx_ring[i];
+ if (ring->TxDescArray) {
+ dma_free_coherent(&pdev->dev,
+ ring->TxDescAllocSize,
+ ring->TxDescArray,
+ ring->TxPhyAddr);
+ ring->TxDescArray = NULL;
+ }
+ }
+}
+
static int rtl8169_init_ring(struct rtl8169_private *tp)
{
+ int retval = 0;
+
rtl8169_init_ring_indexes(tp);
+ rtl8169_tx_desc_init(tp);
+
+ for (int i = 0; i < tp->num_tx_rings; i++) {
+ struct rtl8169_tx_ring *ring = &tp->tx_ring[i];
+
+ memset(ring->tx_skb, 0x0, sizeof(ring->tx_skb));
+ }
+
- memset(tp->tx_skb, 0, sizeof(tp->tx_skb));
- memset(tp->Rx_databuff, 0, sizeof(tp->Rx_databuff));
return rtl8169_rx_fill(tp);
}
-static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp, unsigned int entry)
+static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp,
+ struct ring_info *tx_skb,
+ struct TxDesc *desc)
{
- struct ring_info *tx_skb = tp->tx_skb + entry;
- struct TxDesc *desc = tp->TxDescArray + entry;
+ dma_unmap_single(tp_to_dev(tp), le64_to_cpu(desc->addr), tx_skb->len, DMA_TO_DEVICE);
- dma_unmap_single(tp_to_dev(tp), le64_to_cpu(desc->addr), tx_skb->len,
- DMA_TO_DEVICE);
memset(desc, 0, sizeof(*desc));
memset(tx_skb, 0, sizeof(*tx_skb));
}
-static void rtl8169_tx_clear_range(struct rtl8169_private *tp, u32 start,
- unsigned int n)
+static void rtl8169_tx_clear_range(struct rtl8169_private *tp, struct rtl8169_tx_ring *ring,
+ u32 start, unsigned int n)
{
unsigned int i;
for (i = 0; i < n; i++) {
- unsigned int entry = (start + i) % NUM_TX_DESC;
- struct ring_info *tx_skb = tp->tx_skb + entry;
+ unsigned int entry = (start + i) % ring->num_tx_desc;
+ struct ring_info *tx_skb = ring->tx_skb + entry;
unsigned int len = tx_skb->len;
if (len) {
struct sk_buff *skb = tx_skb->skb;
- rtl8169_unmap_tx_skb(tp, entry);
+ rtl8169_unmap_tx_skb(tp, tx_skb, ring->TxDescArray + entry);
if (skb)
dev_consume_skb_any(skb);
}
@@ -4402,7 +4490,13 @@ static void rtl8169_tx_clear_range(struct rtl8169_private *tp, u32 start,
static void rtl8169_tx_clear(struct rtl8169_private *tp)
{
- rtl8169_tx_clear_range(tp, tp->dirty_tx, NUM_TX_DESC);
+ for (int i = 0; i < tp->num_tx_rings; i++) {
+ struct rtl8169_tx_ring *ring = &tp->tx_ring[i];
+
+ rtl8169_tx_clear_range(tp, ring, ring->dirty_tx, ring->num_tx_desc);
+ ring->cur_tx = 0;
+ ring->dirty_tx = 0;
+ }
netdev_reset_queue(tp->dev);
}
@@ -4478,10 +4572,15 @@ static void rtl8169_tx_timeout(struct net_device *dev, unsigned int txqueue)
rtl_schedule_task(tp, RTL_FLAG_TASK_TX_TIMEOUT);
}
-static int rtl8169_tx_map(struct rtl8169_private *tp, const u32 *opts, u32 len,
- void *addr, unsigned int entry, bool desc_own)
+static int rtl8169_tx_map(struct rtl8169_private *tp,
+ struct rtl8169_tx_ring *ring,
+ const u32 *opts,
+ u32 len,
+ void *addr,
+ unsigned int entry,
+ bool desc_own)
{
- struct TxDesc *txd = tp->TxDescArray + entry;
+ struct TxDesc *txd = ring->TxDescArray + entry;
struct device *d = tp_to_dev(tp);
dma_addr_t mapping;
u32 opts1;
@@ -4505,13 +4604,16 @@ static int rtl8169_tx_map(struct rtl8169_private *tp, const u32 *opts, u32 len,
opts1 |= DescOwn;
txd->opts1 = cpu_to_le32(opts1);
- tp->tx_skb[entry].len = len;
+ ring->tx_skb[entry].len = len;
return 0;
}
-static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
- const u32 *opts, unsigned int entry)
+static int rtl8169_xmit_frags(struct rtl8169_private *tp,
+ struct rtl8169_tx_ring *ring,
+ struct sk_buff *skb,
+ const u32 *opts,
+ unsigned int entry)
{
struct skb_shared_info *info = skb_shinfo(skb);
unsigned int cur_frag;
@@ -4523,14 +4625,14 @@ static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
entry = (entry + 1) % NUM_TX_DESC;
- if (unlikely(rtl8169_tx_map(tp, opts, len, addr, entry, true)))
+ if (unlikely(rtl8169_tx_map(tp, ring, opts, len, addr, entry, true)))
goto err_out;
}
return 0;
err_out:
- rtl8169_tx_clear_range(tp, tp->cur_tx + 1, cur_frag);
+ rtl8169_tx_clear_range(tp, ring, ring->cur_tx + 1, cur_frag);
return -EIO;
}
@@ -4684,9 +4786,9 @@ static bool rtl8169_tso_csum_v2(struct rtl8169_private *tp,
return true;
}
-static unsigned int rtl_tx_slots_avail(struct rtl8169_private *tp)
+static unsigned int rtl_tx_slots_avail(struct rtl8169_tx_ring *ring)
{
- return READ_ONCE(tp->dirty_tx) + NUM_TX_DESC - READ_ONCE(tp->cur_tx);
+ return READ_ONCE(ring->dirty_tx) + NUM_TX_DESC - READ_ONCE(ring->cur_tx);
}
/* Versions RTL8102e and from RTL8168c onwards support csum_v2 */
@@ -4701,10 +4803,10 @@ static bool rtl_chip_supports_csum_v2(struct rtl8169_private *tp)
}
}
-static void rtl8169_doorbell(struct rtl8169_private *tp)
+static void rtl8169_doorbell(struct rtl8169_private *tp, struct rtl8169_tx_ring *ring)
{
if (rtl_is_8125(tp))
- RTL_W16(tp, TxPoll_8125, BIT(0));
+ RTL_W16(tp, TxPoll_8125, BIT(ring->index));
else
RTL_W8(tp, TxPoll, NPQ);
}
@@ -4713,16 +4815,18 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct rtl8169_private *tp = netdev_priv(dev);
- unsigned int entry = tp->cur_tx % NUM_TX_DESC;
struct TxDesc *txd_first, *txd_last;
bool stop_queue, door_bell;
unsigned int frags;
u32 opts[2];
+ const u16 queue_mapping = skb_get_queue_mapping(skb);
+ struct rtl8169_tx_ring *ring = &tp->tx_ring[queue_mapping];
+ unsigned int entry = ring->cur_tx % ring->num_tx_desc;
- if (unlikely(!rtl_tx_slots_avail(tp))) {
+ if (unlikely(!rtl_tx_slots_avail(ring))) {
if (net_ratelimit())
netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
- netif_stop_queue(dev);
+ netif_stop_subqueue(dev, queue_mapping);
return NETDEV_TX_BUSY;
}
@@ -4734,47 +4838,49 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
else if (!rtl8169_tso_csum_v2(tp, skb, opts))
goto err_dma_0;
- if (unlikely(rtl8169_tx_map(tp, opts, skb_headlen(skb), skb->data,
+ if (unlikely(rtl8169_tx_map(tp, ring, opts, skb_headlen(skb), skb->data,
entry, false)))
goto err_dma_0;
- txd_first = tp->TxDescArray + entry;
+ txd_first = ring->TxDescArray + entry;
frags = skb_shinfo(skb)->nr_frags;
if (frags) {
- if (rtl8169_xmit_frags(tp, skb, opts, entry))
+ if (rtl8169_xmit_frags(tp, ring, skb, opts, entry))
goto err_dma_1;
entry = (entry + frags) % NUM_TX_DESC;
}
- txd_last = tp->TxDescArray + entry;
+ txd_last = ring->TxDescArray + entry;
txd_last->opts1 |= cpu_to_le32(LastFrag);
- tp->tx_skb[entry].skb = skb;
+ ring->tx_skb[entry].skb = skb;
skb_tx_timestamp(skb);
/* Force memory writes to complete before releasing descriptor */
dma_wmb();
- door_bell = __netdev_sent_queue(dev, skb->len, netdev_xmit_more());
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_mapping);
+
+ door_bell = __netdev_tx_sent_queue(txq, skb->len, netdev_xmit_more());
txd_first->opts1 |= cpu_to_le32(DescOwn | FirstFrag);
/* rtl_tx needs to see descriptor changes before updated tp->cur_tx */
smp_wmb();
- WRITE_ONCE(tp->cur_tx, tp->cur_tx + frags + 1);
+ WRITE_ONCE(ring->cur_tx, ring->cur_tx + frags + 1);
- stop_queue = !netif_subqueue_maybe_stop(dev, 0, rtl_tx_slots_avail(tp),
+ stop_queue = !netif_subqueue_maybe_stop(dev, 0, rtl_tx_slots_avail(ring),
R8169_TX_STOP_THRS,
R8169_TX_START_THRS);
if (door_bell || stop_queue)
- rtl8169_doorbell(tp);
+ rtl8169_doorbell(tp, ring);
return NETDEV_TX_OK;
err_dma_1:
- rtl8169_unmap_tx_skb(tp, entry);
+ rtl8169_unmap_tx_skb(tp, ring->tx_skb + entry, txd_first);
err_dma_0:
dev_kfree_skb_any(skb);
dev->stats.tx_dropped++;
@@ -4859,24 +4965,38 @@ static void rtl8169_pcierr_interrupt(struct net_device *dev)
rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
}
-static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
- int budget)
+static void rtl8169_desc_quirk(struct rtl8169_private *tp)
+{
+ RTL_R8(tp, tp->imr_reg[0]);
+}
+
+static int rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
+ struct rtl8169_tx_ring *ring, int budget)
{
unsigned int dirty_tx, bytes_compl = 0, pkts_compl = 0;
struct sk_buff *skb;
+ u32 status;
+ unsigned int count = 0;
+
+ dirty_tx = ring->dirty_tx;
+
+ while (READ_ONCE(ring->cur_tx) != dirty_tx) {
+ unsigned int entry = dirty_tx % ring->num_tx_desc;
+ struct ring_info *tx_skb = ring->tx_skb + entry;
+
+ status = le32_to_cpu(READ_ONCE(ring->TxDescArray[entry].opts1));
+ if (status & DescOwn) {
+ if (!tp->recheck_desc_ownbit)
+ break;
+ tp->recheck_desc_ownbit = false;
+ rtl8169_desc_quirk(tp);
+ status = le32_to_cpu(READ_ONCE(ring->TxDescArray[entry].opts1));
+ if (status & DescOwn)
+ break;
+ }
- dirty_tx = tp->dirty_tx;
-
- while (READ_ONCE(tp->cur_tx) != dirty_tx) {
- unsigned int entry = dirty_tx % NUM_TX_DESC;
- u32 status;
-
- 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);
+ skb = tx_skb->skb;
+ rtl8169_unmap_tx_skb(tp, tx_skb, ring->TxDescArray + entry);
if (skb) {
pkts_compl++;
@@ -4886,12 +5006,13 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
dirty_tx++;
}
- if (tp->dirty_tx != dirty_tx) {
+ if (ring->dirty_tx != dirty_tx) {
+ count = dirty_tx - ring->dirty_tx;
dev_sw_netstats_tx_add(dev, pkts_compl, bytes_compl);
- WRITE_ONCE(tp->dirty_tx, dirty_tx);
+ WRITE_ONCE(ring->dirty_tx, dirty_tx);
netif_subqueue_completed_wake(dev, 0, pkts_compl, bytes_compl,
- rtl_tx_slots_avail(tp),
+ rtl_tx_slots_avail(ring),
R8169_TX_START_THRS);
/*
* 8168 hack: TxPoll requests are lost when the Tx packets are
@@ -4901,9 +5022,10 @@ static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
* If skb is NULL then we come here again once a tx irq is
* triggered after the last fragment is marked transmitted.
*/
- if (READ_ONCE(tp->cur_tx) != dirty_tx && skb)
- rtl8169_doorbell(tp);
+ if (READ_ONCE(ring->cur_tx) != dirty_tx && skb)
+ rtl8169_doorbell(tp, ring);
}
+ return count;
}
static inline int rtl8169_fragmented_frame(u32 status)
@@ -5027,6 +5149,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
phy_mac_interrupt(tp->phydev);
rtl_irq_disable(tp);
+ tp->recheck_desc_ownbit = true;
napi_schedule(&napi->napi);
out:
rtl_ack_events(tp, status);
@@ -5113,7 +5236,8 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
struct net_device *dev = tp->dev;
int work_done = 0;
- rtl_tx(dev, tp, budget);
+ for (int i = 0; i < tp->num_tx_rings; i++)
+ rtl_tx(dev, tp, &tp->tx_ring[i], budget);
work_done = rtl_rx(dev, tp, budget);
@@ -5256,10 +5380,8 @@ static int rtl8169_close(struct net_device *dev)
dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray,
tp->RxPhyAddr);
- dma_free_coherent(&pdev->dev, R8169_TX_RING_BYTES, tp->TxDescArray,
- tp->TxPhyAddr);
- tp->TxDescArray = NULL;
tp->RxDescArray = NULL;
+ rtl8169_free_tx_desc(tp);
pm_runtime_put_sync(&pdev->dev);
@@ -5288,20 +5410,17 @@ static int rtl_open(struct net_device *dev)
* Rx and Tx descriptors needs 256 bytes alignment.
* dma_alloc_coherent provides more.
*/
- tp->TxDescArray = dma_alloc_coherent(&pdev->dev, R8169_TX_RING_BYTES,
- &tp->TxPhyAddr, GFP_KERNEL);
- if (!tp->TxDescArray)
- goto out;
tp->RxDescArray = dma_alloc_coherent(&pdev->dev, R8169_RX_RING_BYTES,
&tp->RxPhyAddr, GFP_KERNEL);
- if (!tp->RxDescArray)
+ if (rtl8169_alloc_tx_desc(tp) < 0)
goto err_free_tx_0;
retval = rtl8169_init_ring(tp);
if (retval < 0)
goto err_free_rx_1;
+
rtl_request_firmware(tp);
irqflags = pci_dev_msi_enabled(pdev) ? IRQF_NO_THREAD : IRQF_SHARED;
@@ -5331,9 +5450,7 @@ static int rtl_open(struct net_device *dev)
tp->RxPhyAddr);
tp->RxDescArray = NULL;
err_free_tx_0:
- dma_free_coherent(&pdev->dev, R8169_TX_RING_BYTES, tp->TxDescArray,
- tp->TxPhyAddr);
- tp->TxDescArray = NULL;
+ rtl8169_free_tx_desc(tp);
goto out;
}
@@ -5842,7 +5959,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
u32 txconfig;
u32 xid;
- dev = devm_alloc_etherdev(&pdev->dev, sizeof (*tp));
+ dev = devm_alloc_etherdev_mqs(&pdev->dev, sizeof(*tp),
+ R8169_MAX_TX_QUEUES,
+ R8169_MAX_RX_QUEUES);
+
if (!dev)
return -ENOMEM;
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [RFC Patch net-next v1 4/9] r8169: add support for multi rx queues
2026-04-20 2:19 [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 javen
` (2 preceding siblings ...)
2026-04-20 2:19 ` [RFC Patch net-next v1 3/9] r8169: add support for multi tx queues javen
@ 2026-04-20 2:19 ` javen
2026-04-20 2:19 ` [RFC Patch net-next v1 5/9] r8169: add support for msix javen
` (5 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: javen @ 2026-04-20 2:19 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This patch supports for multi rx queues. But we set rx queue num 1 here.
We will add support for rx queue num 8 in rss patch.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
drivers/net/ethernet/realtek/r8169_main.c | 272 +++++++++++++++++-----
1 file changed, 212 insertions(+), 60 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 05f0cb532a31..52e690eba644 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -73,7 +73,6 @@
#define R8169_RX_BUF_SIZE (SZ_16K - 1)
#define NUM_TX_DESC 256 /* Number of Tx descriptor registers */
#define NUM_RX_DESC 256 /* Number of Rx descriptor registers */
-#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
@@ -793,6 +792,19 @@ enum rtl_dash_type {
RTL_DASH_25_BP,
};
+struct rtl8169_rx_ring {
+ u32 index; /* Rx queue index */
+ u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
+ u32 dirty_rx; /* Index into the Rx descriptor buffer for recycling. */
+ u32 num_rx_desc; /* num of Rx desc */
+ struct RxDesc *RxDescArray; /* array of Rx Desc*/
+ u32 RxDescAllocSize; /* memory size per descs of ring */
+ dma_addr_t RxDescPhyAddr[NUM_RX_DESC]; /* Rx data buffer physical dma address */
+ dma_addr_t RxPhyAddr; /* Rx desc physical address */
+ struct page *Rx_databuff[NUM_RX_DESC]; /* Rx data buffers */
+ u16 rdsar_reg; /* Receive Descriptor Start Address */
+};
+
struct rtl8169_tx_ring {
u32 index; /* Tx queue index */
u32 cur_tx; /* Index into the Tx descriptor buffer of next Tx pkt. */
@@ -833,12 +845,9 @@ struct rtl8169_private {
struct napi_struct napi;
enum mac_version mac_version;
enum rtl_dash_type dash_type;
- u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
- struct RxDesc *RxDescArray; /* 256-aligned Rx descriptor ring */
- dma_addr_t RxPhyAddr;
- struct page *Rx_databuff[NUM_RX_DESC]; /* Rx data buffers */
struct rtl8169_irq irq_tbl[R8169_MAX_MSIX_VEC];
struct rtl8169_napi r8169napi[R8169_MAX_MSIX_VEC];
+ struct rtl8169_rx_ring rx_ring[R8169_MAX_RX_QUEUES];
struct rtl8169_tx_ring tx_ring[R8169_MAX_TX_QUEUES];
u16 isr_reg[R8169_MAX_MSIX_VEC];
u16 imr_reg[R8169_MAX_MSIX_VEC];
@@ -848,11 +857,13 @@ struct rtl8169_private {
u16 tx_lpi_timer;
u32 irq_mask;
u16 HwSuppNumTxQueues;
+ u16 HwSuppNumRxQueues;
u8 min_irq_nvecs;
u8 max_irq_nvecs;
u8 HwSuppIsrVer;
u8 HwCurrIsrVer;
u8 irq_nvecs;
+ u8 InitRxDescType;
u8 recheck_desc_ownbit;
unsigned int features;
int irq;
@@ -2728,6 +2739,15 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)
}
}
+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->RxDescArray, 0x0, ring->RxDescAllocSize);
+ }
+}
+
static void rtl8169_tx_desc_init(struct rtl8169_private *tp)
{
for (int i = 0; i < tp->num_tx_rings; i++) {
@@ -2750,6 +2770,14 @@ static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
ring->index = i;
netdev_tx_reset_queue(netdev_get_tx_queue(dev, i));
}
+
+ for (int i = 0; i < tp->HwSuppNumRxQueues; i++) {
+ struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
+
+ ring->dirty_rx = 0;
+ ring->cur_rx = 0;
+ ring->index = i;
+ }
}
static void rtl_jumbo_config(struct rtl8169_private *tp)
@@ -2810,6 +2838,9 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
static void rtl_set_ring_size(struct rtl8169_private *tp, u32 rx_num, u32 tx_num)
{
+ for (int i = 0; i < tp->HwSuppNumRxQueues; i++)
+ tp->rx_ring[i].num_rx_desc = rx_num;
+
for (int i = 0; i < tp->HwSuppNumTxQueues; i++)
tp->tx_ring[i].num_tx_desc = tx_num;
}
@@ -2820,6 +2851,10 @@ static void rtl_setup_mqs_reg(struct rtl8169_private *tp)
for (int i = 1; i < tp->HwSuppNumTxQueues; i++)
tp->tx_ring[i].tdsar_reg = (u16)(TNPDS_Q1_LOW + (i - 1) * 8);
+ tp->rx_ring[0].rdsar_reg = RxDescAddrLow;
+ for (int i = 1; i < tp->HwSuppNumRxQueues; i++)
+ tp->rx_ring[i].rdsar_reg = (u16)(RDSAR_Q1_LOW + (i - 1) * 8);
+
if (tp->mac_version <= RTL_GIGA_MAC_VER_52) {
tp->isr_reg[0] = IntrStatus;
tp->imr_reg[0] = IntrMask;
@@ -2845,15 +2880,18 @@ static void rtl_software_parameter_initialize(struct rtl8169_private *tp)
tp->min_irq_nvecs = R8127_MIN_IRQ;
tp->max_irq_nvecs = R8127_MAX_IRQ;
tp->HwSuppNumTxQueues = R8127_MAX_TX_QUEUES;
+ tp->HwSuppNumRxQueues = R8127_MAX_RX_QUEUES;
tp->HwSuppIsrVer = 6;
break;
default:
tp->min_irq_nvecs = 1;
tp->max_irq_nvecs = 1;
tp->HwSuppNumTxQueues = 1;
+ tp->HwSuppNumRxQueues = 1;
tp->HwSuppIsrVer = 1;
break;
}
+ tp->InitRxDescType = RX_DESC_RING_TYPE_DEAFULT;
tp->HwCurrIsrVer = tp->HwSuppIsrVer;
rtl_setup_mqs_reg(tp);
@@ -2989,14 +3027,18 @@ static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
* register to be written before TxDescAddrLow to work.
* Switching from MMIO to I/O access fixes the issue as well.
*/
- RTL_W32(tp, RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32);
- RTL_W32(tp, RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_BIT_MASK(32));
for (int i = 0; i < tp->num_tx_rings; i++) {
struct rtl8169_tx_ring *ring = &tp->tx_ring[i];
RTL_W32(tp, ring->tdsar_reg, ((u64)ring->TxPhyAddr & DMA_BIT_MASK(32)));
RTL_W32(tp, ring->tdsar_reg + 4, ((u64)ring->TxPhyAddr >> 32));
}
+ for (int i = 0; i < tp->num_rx_rings; i++) {
+ struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
+
+ RTL_W32(tp, ring->rdsar_reg, ((u64)ring->RxPhyAddr) & DMA_BIT_MASK(32));
+ RTL_W32(tp, ring->rdsar_reg + 4, ((u64)ring->RxPhyAddr >> 32));
+ }
}
static void rtl8169_set_magic_reg(struct rtl8169_private *tp)
@@ -4332,7 +4374,7 @@ static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
-static void rtl8169_mark_to_asic(struct RxDesc *desc)
+static void rtl8169_mark_to_asic_default(struct RxDesc *desc)
{
u32 eor = le32_to_cpu(desc->opts1) & RingEnd;
@@ -4342,13 +4384,19 @@ static void rtl8169_mark_to_asic(struct RxDesc *desc)
WRITE_ONCE(desc->opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
}
+static void rtl8169_mark_to_asic(struct rtl8169_private *tp, struct RxDesc *desc)
+{
+ rtl8169_mark_to_asic_default(desc);
+}
+
static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
- struct RxDesc *desc)
+ struct rtl8169_rx_ring *ring, unsigned int index)
{
struct device *d = tp_to_dev(tp);
int node = dev_to_node(d);
dma_addr_t mapping;
struct page *data;
+ struct RxDesc *desc = ring->RxDescArray + index;
data = alloc_pages_node(node, GFP_KERNEL, get_order(R8169_RX_BUF_SIZE));
if (!data)
@@ -4361,44 +4409,56 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
return NULL;
}
+ ring->RxDescPhyAddr[index] = mapping;
desc->addr = cpu_to_le64(mapping);
- rtl8169_mark_to_asic(desc);
+ rtl8169_mark_to_asic(tp, desc);
return data;
}
-static void rtl8169_rx_clear(struct rtl8169_private *tp)
+static void rtl8169_rx_clear(struct rtl8169_private *tp, struct rtl8169_rx_ring *ring)
{
int i;
- for (i = 0; i < NUM_RX_DESC && tp->Rx_databuff[i]; i++) {
+ for (i = 0; i < NUM_RX_DESC && ring->Rx_databuff[i]; i++) {
dma_unmap_page(tp_to_dev(tp),
- le64_to_cpu(tp->RxDescArray[i].addr),
+ ring->RxDescPhyAddr[i],
R8169_RX_BUF_SIZE, DMA_FROM_DEVICE);
- __free_pages(tp->Rx_databuff[i], get_order(R8169_RX_BUF_SIZE));
- tp->Rx_databuff[i] = NULL;
- tp->RxDescArray[i].addr = 0;
- tp->RxDescArray[i].opts1 = 0;
+ __free_pages(ring->Rx_databuff[i], get_order(R8169_RX_BUF_SIZE));
+ ring->Rx_databuff[i] = NULL;
+ ring->RxDescPhyAddr[i] = 0;
+ ring->RxDescArray[i].addr = 0;
+ ring->RxDescArray[i].opts1 = 0;
}
}
-static int rtl8169_rx_fill(struct rtl8169_private *tp)
+static void rtl8169_mark_as_last_descriptor_default(struct RxDesc *desc)
+{
+ desc->opts1 |= cpu_to_le32(RingEnd);
+}
+
+static void rtl8169_mark_as_last_descriptor(struct rtl8169_private *tp, struct RxDesc *desc)
+{
+ rtl8169_mark_as_last_descriptor_default(desc);
+}
+
+static int rtl8169_rx_fill(struct rtl8169_private *tp, struct rtl8169_rx_ring *ring)
{
int i;
for (i = 0; i < NUM_RX_DESC; i++) {
struct page *data;
- data = rtl8169_alloc_rx_data(tp, tp->RxDescArray + i);
+ data = rtl8169_alloc_rx_data(tp, ring, i);
if (!data) {
- rtl8169_rx_clear(tp);
+ rtl8169_rx_clear(tp, ring);
return -ENOMEM;
}
- tp->Rx_databuff[i] = data;
+ ring->Rx_databuff[i] = data;
}
/* mark as last descriptor in the ring */
- tp->RxDescArray[NUM_RX_DESC - 1].opts1 |= cpu_to_le32(RingEnd);
+ rtl8169_mark_as_last_descriptor(tp, &ring->RxDescArray[NUM_RX_DESC - 1]);
return 0;
}
@@ -4438,6 +4498,40 @@ static void rtl8169_free_tx_desc(struct rtl8169_private *tp)
}
}
+static int rtl8169_alloc_rx_desc(struct rtl8169_private *tp)
+{
+ struct rtl8169_rx_ring *ring;
+ struct pci_dev *pdev = tp->pci_dev;
+
+ for (int i = 0; i < tp->num_rx_rings; i++) {
+ ring = &tp->rx_ring[i];
+ ring->RxDescAllocSize = (ring->num_rx_desc + 1) * sizeof(struct RxDesc);
+ ring->RxDescArray = dma_alloc_coherent(&pdev->dev,
+ ring->RxDescAllocSize,
+ &ring->RxPhyAddr,
+ GFP_KERNEL);
+ if (!ring->RxDescArray)
+ return -1;
+ }
+ return 0;
+}
+
+static void rtl8169_free_rx_desc(struct rtl8169_private *tp)
+{
+ struct rtl8169_rx_ring *ring;
+ struct pci_dev *pdev = tp->pci_dev;
+
+ for (int i = 0; i < tp->num_rx_rings; i++) {
+ ring = &tp->rx_ring[i];
+ if (ring->RxDescArray) {
+ dma_free_coherent(&pdev->dev,
+ ring->RxDescAllocSize,
+ ring->RxDescArray,
+ ring->RxPhyAddr);
+ ring->RxDescArray = NULL;
+ }
+ }
+}
static int rtl8169_init_ring(struct rtl8169_private *tp)
{
@@ -4445,6 +4539,7 @@ static int rtl8169_init_ring(struct rtl8169_private *tp)
rtl8169_init_ring_indexes(tp);
rtl8169_tx_desc_init(tp);
+ rtl8169_rx_desc_init(tp);
for (int i = 0; i < tp->num_tx_rings; i++) {
struct rtl8169_tx_ring *ring = &tp->tx_ring[i];
@@ -4452,9 +4547,14 @@ static int rtl8169_init_ring(struct rtl8169_private *tp)
memset(ring->tx_skb, 0x0, sizeof(ring->tx_skb));
}
+ for (int i = 0; i < tp->num_rx_rings; i++) {
+ struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
+ memset(ring->Rx_databuff, 0, sizeof(ring->Rx_databuff));
+ retval = rtl8169_rx_fill(tp, ring);
+ }
- return rtl8169_rx_fill(tp);
+ return retval;
}
static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp,
@@ -4549,16 +4649,23 @@ static void rtl8169_cleanup(struct rtl8169_private *tp)
rtl8169_init_ring_indexes(tp);
}
-static void rtl_reset_work(struct rtl8169_private *tp)
+static void rtl8169_rx_desc_reset(struct rtl8169_private *tp)
{
- int i;
+ for (int i = 0; i < tp->num_rx_rings; i++) {
+ struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
+
+ for (int j = 0; j < ring->num_rx_desc; j++)
+ rtl8169_mark_to_asic(tp, ring->RxDescArray + j);
+ }
+}
+static void rtl_reset_work(struct rtl8169_private *tp)
+{
netif_stop_queue(tp->dev);
rtl8169_cleanup(tp);
- for (i = 0; i < NUM_RX_DESC; i++)
- rtl8169_mark_to_asic(tp->RxDescArray + i);
+ rtl8169_rx_desc_reset(tp);
rtl8169_napi_enable(tp);
@@ -5033,9 +5140,11 @@ static inline int rtl8169_fragmented_frame(u32 status)
return (status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag);
}
-static inline void rtl8169_rx_csum(struct sk_buff *skb, u32 opts1)
+static inline void rtl8169_rx_csum_default(struct rtl8169_private *tp,
+ struct sk_buff *skb,
+ struct RxDesc *desc)
{
- u32 status = opts1 & (RxProtoMask | RxCSFailMask);
+ u32 status = le32_to_cpu(desc->opts1) & (RxProtoMask | RxCSFailMask);
if (status == RxProtoTCP || status == RxProtoUDP)
skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -5043,22 +5152,67 @@ static inline void rtl8169_rx_csum(struct sk_buff *skb, u32 opts1)
skb_checksum_none_assert(skb);
}
-static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget)
+static inline void rtl8169_rx_csum(struct rtl8169_private *tp,
+ struct sk_buff *skb,
+ struct RxDesc *desc)
+{
+ rtl8169_rx_csum_default(tp, skb, desc);
+}
+
+static u32 rtl8169_rx_desc_opts1(struct rtl8169_private *tp, struct RxDesc *desc)
+{
+ return READ_ONCE(desc->opts1);
+}
+
+static int rtl8169_check_rx_desc_error(struct net_device *dev,
+ struct rtl8169_private *tp,
+ u32 status)
+{
+ int ret = 0;
+
+ if (unlikely(status & RxRES)) {
+ if (status & (RxRWT | RxRUNT))
+ dev->stats.rx_length_errors++;
+ if (status & RxCRC)
+ dev->stats.rx_crc_errors++;
+ ret = -1;
+ }
+ return ret;
+}
+
+static inline void rtl8169_set_desc_dma_addr(struct rtl8169_private *tp,
+ struct RxDesc *desc,
+ dma_addr_t mapping)
+{
+ desc->addr = cpu_to_le64(mapping);
+}
+
+static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
+ struct rtl8169_rx_ring *ring, int budget)
{
struct device *d = tp_to_dev(tp);
int count;
- for (count = 0; count < budget; count++, tp->cur_rx++) {
- unsigned int pkt_size, entry = tp->cur_rx % NUM_RX_DESC;
- struct RxDesc *desc = tp->RxDescArray + entry;
+ for (count = 0; count < budget; count++, ring->cur_rx++) {
+ unsigned int pkt_size, entry = ring->cur_rx % ring->num_rx_desc;
+ struct RxDesc *desc = ring->RxDescArray + entry;
struct sk_buff *skb;
const void *rx_buf;
dma_addr_t addr;
u32 status;
- status = le32_to_cpu(READ_ONCE(desc->opts1));
- if (status & DescOwn)
- break;
+ status = le32_to_cpu(rtl8169_rx_desc_opts1(tp, desc));
+
+ if (status & DescOwn) {
+ if (!tp->recheck_desc_ownbit)
+ break;
+
+ tp->recheck_desc_ownbit = false;
+ rtl8169_desc_quirk(tp);
+ status = le32_to_cpu(rtl8169_rx_desc_opts1(tp, desc));
+ if (status & DescOwn)
+ break;
+ }
/* This barrier is needed to keep us from reading
* any other fields out of the Rx descriptor until
@@ -5066,20 +5220,15 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
*/
dma_rmb();
- if (unlikely(status & RxRES)) {
+ if (rtl8169_check_rx_desc_error(dev, tp, status) < 0) {
if (net_ratelimit())
netdev_warn(dev, "Rx ERROR. status = %08x\n",
status);
+
dev->stats.rx_errors++;
- if (status & (RxRWT | RxRUNT))
- dev->stats.rx_length_errors++;
- if (status & RxCRC)
- dev->stats.rx_crc_errors++;
if (!(dev->features & NETIF_F_RXALL))
goto release_descriptor;
- else if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
- goto release_descriptor;
}
pkt_size = status & GENMASK(13, 0);
@@ -5095,14 +5244,14 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
goto release_descriptor;
}
- skb = napi_alloc_skb(&tp->r8169napi[0].napi, pkt_size);
+ skb = napi_alloc_skb(&tp->r8169napi[ring->index].napi, pkt_size);
if (unlikely(!skb)) {
dev->stats.rx_dropped++;
goto release_descriptor;
}
- addr = le64_to_cpu(desc->addr);
- rx_buf = page_address(tp->Rx_databuff[entry]);
+ addr = ring->RxDescPhyAddr[entry];
+ rx_buf = page_address(ring->Rx_databuff[entry]);
dma_sync_single_for_cpu(d, addr, pkt_size, DMA_FROM_DEVICE);
prefetch(rx_buf);
@@ -5111,7 +5260,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
skb->len = pkt_size;
dma_sync_single_for_device(d, addr, pkt_size, DMA_FROM_DEVICE);
- rtl8169_rx_csum(skb, status);
+ rtl8169_rx_csum(tp, skb, desc);
skb->protocol = eth_type_trans(skb, dev);
rtl8169_rx_vlan_tag(desc, skb);
@@ -5119,11 +5268,13 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
if (skb->pkt_type == PACKET_MULTICAST)
dev->stats.multicast++;
- napi_gro_receive(&tp->r8169napi[0].napi, skb);
+ napi_gro_receive(&tp->r8169napi[ring->index].napi, skb);
dev_sw_netstats_rx_add(dev, pkt_size);
release_descriptor:
- rtl8169_mark_to_asic(desc);
+ rtl8169_set_desc_dma_addr(tp, desc, ring->RxDescPhyAddr[entry]);
+ dma_wmb();
+ rtl8169_mark_to_asic(tp, desc);
}
return count;
@@ -5239,7 +5390,8 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
for (int i = 0; i < tp->num_tx_rings; i++)
rtl_tx(dev, tp, &tp->tx_ring[i], budget);
- work_done = rtl_rx(dev, tp, budget);
+ for (int i = 0; i < tp->num_rx_rings; i++)
+ work_done += rtl_rx(dev, tp, &tp->rx_ring[i], budget);
if (work_done < budget && napi_complete_done(napi, work_done))
rtl_irq_enable(tp);
@@ -5371,16 +5523,16 @@ static int rtl8169_close(struct net_device *dev)
netif_stop_queue(dev);
rtl8169_down(tp);
- rtl8169_rx_clear(tp);
+ for (int i = 0; i < tp->num_rx_rings; i++)
+ rtl8169_rx_clear(tp, &tp->rx_ring[i]);
rtl8169_free_irq(tp);
phy_disconnect(tp->phydev);
- dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray,
- tp->RxPhyAddr);
- tp->RxDescArray = NULL;
+ rtl8169_free_rx_desc(tp);
+
rtl8169_free_tx_desc(tp);
pm_runtime_put_sync(&pdev->dev);
@@ -5411,11 +5563,12 @@ static int rtl_open(struct net_device *dev)
* dma_alloc_coherent provides more.
*/
- tp->RxDescArray = dma_alloc_coherent(&pdev->dev, R8169_RX_RING_BYTES,
- &tp->RxPhyAddr, GFP_KERNEL);
if (rtl8169_alloc_tx_desc(tp) < 0)
goto err_free_tx_0;
+ if (rtl8169_alloc_rx_desc(tp) < 0)
+ goto err_free_rx_1;
+
retval = rtl8169_init_ring(tp);
if (retval < 0)
goto err_free_rx_1;
@@ -5444,11 +5597,10 @@ static int rtl_open(struct net_device *dev)
rtl8169_free_irq(tp);
err_release_fw_2:
rtl_release_firmware(tp);
- rtl8169_rx_clear(tp);
+ for (int i = 0; i < tp->num_rx_rings; i++)
+ rtl8169_rx_clear(tp, &tp->rx_ring[i]);
err_free_rx_1:
- dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray,
- tp->RxPhyAddr);
- tp->RxDescArray = NULL;
+ rtl8169_free_rx_desc(tp);
err_free_tx_0:
rtl8169_free_tx_desc(tp);
goto out;
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [RFC Patch net-next v1 5/9] r8169: add support for msix
2026-04-20 2:19 [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 javen
` (3 preceding siblings ...)
2026-04-20 2:19 ` [RFC Patch net-next v1 4/9] r8169: add support for multi rx queues javen
@ 2026-04-20 2:19 ` javen
2026-04-20 2:19 ` [RFC Patch net-next v1 6/9] r8169: enable msix for RTL8127 javen
` (4 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: javen @ 2026-04-20 2:19 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This patch add support for msix. But we still use MSI here. And we force
nvecs to 1. We will modify it in rss patch.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
drivers/net/ethernet/realtek/r8169_main.c | 162 ++++++++++++++++++++--
1 file changed, 151 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 52e690eba644..7d493342ab4b 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -1764,26 +1764,40 @@ static u32 rtl_get_events(struct rtl8169_private *tp)
static void rtl_ack_events(struct rtl8169_private *tp, u32 bits)
{
- if (rtl_is_8125(tp))
+ if (rtl_is_8125(tp)) {
RTL_W32(tp, IntrStatus_8125, bits);
- else
+ if (tp->features & RTL_FEATURE_MSIX) {
+ RTL_W32(tp, ISR_V2_8125, 0xffffffff);
+ RTL_W32(tp, ISR_V4_L2_8125, 0xffffffff);
+ }
+ } else {
RTL_W16(tp, IntrStatus, bits);
+ }
}
static void rtl_irq_disable(struct rtl8169_private *tp)
{
- if (rtl_is_8125(tp))
+ if (rtl_is_8125(tp)) {
RTL_W32(tp, IntrMask_8125, 0);
- else
+ if (tp->features & RTL_FEATURE_MSIX) {
+ RTL_W32(tp, IMR_V2_CLEAR_REG_8125, 0xffffffff);
+ RTL_W32(tp, IMR_V4_L2_CLEAR_REG_8125, 0xffffffff);
+ }
+ } else {
RTL_W16(tp, IntrMask, 0);
+ }
}
static void rtl_irq_enable(struct rtl8169_private *tp)
{
- if (rtl_is_8125(tp))
- RTL_W32(tp, IntrMask_8125, tp->irq_mask);
- else
+ if (rtl_is_8125(tp)) {
+ if (tp->features & RTL_FEATURE_MSIX)
+ RTL_W32(tp, IMR_V2_SET_REG_8125, tp->irq_mask);
+ else
+ RTL_W32(tp, IntrMask_8125, tp->irq_mask);
+ } else {
RTL_W16(tp, IntrMask, tp->irq_mask);
+ }
}
static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
@@ -2894,6 +2908,10 @@ static void rtl_software_parameter_initialize(struct rtl8169_private *tp)
tp->InitRxDescType = RX_DESC_RING_TYPE_DEAFULT;
tp->HwCurrIsrVer = tp->HwSuppIsrVer;
+ /* This just force nvecs, and will be remove in the following patch*/
+ tp->min_irq_nvecs = 1;
+ tp->max_irq_nvecs = 1;
+
rtl_setup_mqs_reg(tp);
rtl_set_ring_size(tp, NUM_RX_DESC, NUM_TX_DESC);
}
@@ -5321,6 +5339,44 @@ static void rtl8169_free_irq(struct rtl8169_private *tp)
}
}
+static void rtl8169_disable_hw_interrupt_msix(struct rtl8169_private *tp, int message_id)
+{
+ RTL_W32(tp, IMR_V2_CLEAR_REG_8125, BIT(message_id));
+}
+
+static void rtl8169_clear_hw_isr(struct rtl8169_private *tp, int message_id)
+{
+ RTL_W32(tp, ISR_V2_8125, BIT(message_id));
+}
+
+static void rtl8169_enable_hw_interrupt_msix(struct rtl8169_private *tp, int message_id)
+{
+ RTL_W32(tp, IMR_V2_SET_REG_8125, BIT(message_id));
+}
+
+static irqreturn_t rtl8169_interrupt_msix(int irq, void *dev_instance)
+{
+ struct rtl8169_napi *napi = dev_instance;
+ struct rtl8169_private *tp = napi->priv;
+ int message_id = napi->index;
+
+ rtl8169_disable_hw_interrupt_msix(tp, message_id);
+
+ rtl8169_clear_hw_isr(tp, message_id);
+
+ if (message_id == MSIX_ID_V4_LINKCHG) {
+ phy_mac_interrupt(tp->phydev);
+ rtl8169_enable_hw_interrupt_msix(tp, message_id);
+ return IRQ_HANDLED;
+ }
+
+ tp->recheck_desc_ownbit = true;
+
+ napi_schedule(&napi->napi);
+
+ return IRQ_HANDLED;
+}
+
static int rtl8169_request_irq(struct rtl8169_private *tp)
{
struct net_device *dev = tp->dev;
@@ -5331,10 +5387,14 @@ static int rtl8169_request_irq(struct rtl8169_private *tp)
for (int i = 0; i < tp->irq_nvecs; i++) {
irq = &tp->irq_tbl[i];
+ if (tp->features & RTL_FEATURE_MSIX && tp->HwCurrIsrVer > 1)
+ irq->handler = rtl8169_interrupt_msix;
+ else
+ irq->handler = rtl8169_interrupt;
napi = &tp->r8169napi[i];
snprintf(irq->name, len, "%s-%d", dev->name, i);
- rc = pci_request_irq(tp->pci_dev, i, rtl8169_interrupt, NULL, napi, irq->name);
+ rc = pci_request_irq(tp->pci_dev, i, irq->handler, NULL, napi, irq->name);
if (rc)
break;
@@ -5786,10 +5846,18 @@ static const struct net_device_ops rtl_netdev_ops = {
static void rtl_set_irq_mask(struct rtl8169_private *tp)
{
- tp->irq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;
+ if (tp->features & RTL_FEATURE_MSIX) {
+ tp->irq_mask = ISRIMR_V6_LINKCHG;
+ for (int i = 0; i < tp->num_tx_rings; i++)
+ tp->irq_mask |= ISRIMR_V6_TOK_Q0 << i;
+ for (int i = 0; i < tp->num_rx_rings; i++)
+ tp->irq_mask |= ISRIMR_V6_ROK_Q0 << i;
+ } else {
+ tp->irq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;
- if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
- tp->irq_mask |= SYSErr | RxFIFOOver;
+ if (tp->mac_version <= RTL_GIGA_MAC_VER_06)
+ tp->irq_mask |= SYSErr | RxFIFOOver;
+ }
}
static int rtl_alloc_irq(struct rtl8169_private *tp)
@@ -5817,6 +5885,18 @@ static int rtl_alloc_irq(struct rtl8169_private *tp)
if (nvecs < 0)
nvecs = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+ tp->features &= ~(RTL_FEATURE_MSIX | RTL_FEATURE_MSI);
+
+ if (nvecs > 0) {
+ tp->irq_nvecs = nvecs;
+ tp->irq = pci_irq_vector(pdev, 0);
+ if (nvecs > 1)
+ tp->features |= RTL_FEATURE_MSIX;
+ else if (pci_dev_msi_enabled(pdev))
+ tp->features |= RTL_FEATURE_MSI;
+ return 0;
+ }
+
tp->irq = pdev->irq;
tp->irq_nvecs = 1;
@@ -6087,6 +6167,52 @@ static bool rtl_aspm_is_safe(struct rtl8169_private *tp)
return false;
}
+static int rtl8169_poll_msix_rx(struct napi_struct *napi, int budget)
+{
+ struct rtl8169_napi *r8169_napi = container_of(napi, struct rtl8169_napi, napi);
+ struct rtl8169_private *tp = r8169_napi->priv;
+ struct net_device *dev = tp->dev;
+ const int message_id = r8169_napi->index;
+ int work_done = 0;
+
+ if (message_id < tp->num_rx_rings)
+ work_done += rtl_rx(dev, tp, &tp->rx_ring[message_id], budget);
+
+ if (work_done < budget && napi_complete_done(napi, work_done))
+ rtl8169_enable_hw_interrupt_msix(tp, message_id);
+
+ return work_done;
+}
+
+static int rtl8169_poll_msix_tx(struct napi_struct *napi, int budget)
+{
+ struct rtl8169_napi *r8169_napi = container_of(napi, struct rtl8169_napi, napi);
+ struct rtl8169_private *tp = r8169_napi->priv;
+ struct net_device *dev = tp->dev;
+ unsigned int work_done = 0;
+ const int message_id = r8169_napi->index;
+ int tx_ring_idx = message_id - 8;
+
+ if (tx_ring_idx >= 0 && tx_ring_idx < tp->num_tx_rings)
+ work_done += rtl_tx(dev, tp, &tp->tx_ring[tx_ring_idx], budget);
+
+ if (work_done < budget && napi_complete_done(napi, work_done))
+ rtl8169_enable_hw_interrupt_msix(tp, message_id);
+
+ return work_done;
+}
+
+static int rtl8169_poll_msix_other(struct napi_struct *napi, int budget)
+{
+ struct rtl8169_napi *r8169_napi = container_of(napi, struct rtl8169_napi, napi);
+ struct rtl8169_private *tp = r8169_napi->priv;
+ const int message_id = r8169_napi->index;
+
+ napi_complete_done(napi, budget);
+ rtl8169_enable_hw_interrupt_msix(tp, message_id);
+
+ return 1;
+}
static void r8169_init_napi(struct rtl8169_private *tp)
{
@@ -6095,6 +6221,20 @@ static void r8169_init_napi(struct rtl8169_private *tp)
int (*poll)(struct napi_struct *napi, int budget);
poll = rtl8169_poll;
+ if (tp->features & RTL_FEATURE_MSIX) {
+ switch (tp->HwCurrIsrVer) {
+ case 6:
+ if (i < R8127_MAX_RX_QUEUES)
+ poll = rtl8169_poll_msix_rx;
+ else if (i > 7 && i < 16)
+ poll = rtl8169_poll_msix_tx;
+ else
+ poll = rtl8169_poll_msix_other;
+ break;
+ default:
+ break;
+ }
+ }
netif_napi_add(tp->dev, &r8169napi->napi, poll);
r8169napi->priv = tp;
r8169napi->index = i;
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [RFC Patch net-next v1 6/9] r8169: enable msix for RTL8127
2026-04-20 2:19 [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 javen
` (4 preceding siblings ...)
2026-04-20 2:19 ` [RFC Patch net-next v1 5/9] r8169: add support for msix javen
@ 2026-04-20 2:19 ` javen
2026-04-20 2:19 ` [RFC Patch net-next v1 7/9] r8169: add support and enable rss javen
` (3 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: javen @ 2026-04-20 2:19 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This patch enables msix for RTL8127.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
drivers/net/ethernet/realtek/r8169_main.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 7d493342ab4b..622ee8905a05 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -4102,6 +4102,16 @@ DECLARE_RTL_COND(rtl_mac_ocp_e00e_cond)
return r8168_mac_ocp_read(tp, 0xe00e) & BIT(13);
}
+
+static void rtl8125_hw_set_interrupt_type(struct rtl8169_private *tp)
+{
+ u8 tmp;
+
+ tmp = RTL_R8(tp, INT_CFG0_8125);
+ tmp |= INT_CFG0_ENABLE_8125;
+ RTL_W8(tp, INT_CFG0_8125, tmp);
+}
+
static void rtl_hw_start_8125_common(struct rtl8169_private *tp)
{
rtl_pcie_state_l2l3_disable(tp);
@@ -4110,6 +4120,9 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp)
RTL_W32(tp, RSS_CTRL_8125, 0);
RTL_W16(tp, Q_NUM_CTRL_8125, 0);
+ if (tp->features & RTL_FEATURE_MSIX)
+ rtl8125_hw_set_interrupt_type(tp);
+
/* disable UPS */
r8168_mac_ocp_modify(tp, 0xd40a, 0x0010, 0x0000);
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [RFC Patch net-next v1 7/9] r8169: add support and enable rss
2026-04-20 2:19 [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 javen
` (5 preceding siblings ...)
2026-04-20 2:19 ` [RFC Patch net-next v1 6/9] r8169: enable msix for RTL8127 javen
@ 2026-04-20 2:19 ` javen
2026-04-20 2:19 ` [RFC Patch net-next v1 8/9] r8169: move struct ethtool_ops javen
` (2 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: javen @ 2026-04-20 2:19 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This patch adds support and enable rss for RTL8127. We remove the
setting nvecs 1 to support multi queue and rss.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
drivers/net/ethernet/realtek/r8169_main.c | 340 ++++++++++++++++++++--
1 file changed, 321 insertions(+), 19 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 622ee8905a05..b3f15e6fd5e9 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -718,6 +718,21 @@ struct RxDesc {
__le64 addr;
};
+struct RxDescRss {
+ union {
+ __le64 addr;
+ struct {
+ __le32 RSSInfo;
+ __le32 RSSResult;
+ } RxDescRSSDWord;
+ };
+
+ struct {
+ __le32 opts2;
+ __le32 opts1;
+ } RxDescOpts;
+};
+
enum features {
RTL_FEATURE_MSI = (1 << 1),
RTL_FEATURE_MSIX = (1 << 2),
@@ -853,9 +868,13 @@ struct rtl8169_private {
u16 imr_reg[R8169_MAX_MSIX_VEC];
unsigned int num_tx_rings;
unsigned int num_rx_rings;
+ u32 rss_flags;
u16 cp_cmd;
u16 tx_lpi_timer;
u32 irq_mask;
+ u8 rss_key[RTL8127_RSS_KEY_SIZE];
+ u8 rss_indir_tbl[RTL8127_MAX_INDIRECTION_TABLE_ENTRIES];
+ u8 HwSuppIndirTblEntries;
u16 HwSuppNumTxQueues;
u16 HwSuppNumRxQueues;
u8 min_irq_nvecs;
@@ -1698,6 +1717,13 @@ static bool rtl_dash_is_enabled(struct rtl8169_private *tp)
}
}
+static bool rtl_check_rss_support(struct rtl8169_private *tp)
+{
+ if (tp->mac_version == RTL_GIGA_MAC_VER_80)
+ return true;
+ return false;
+}
+
static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp)
{
switch (tp->mac_version) {
@@ -2001,9 +2027,20 @@ static inline u32 rtl8169_tx_vlan_tag(struct sk_buff *skb)
TxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00;
}
-static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb)
+static void rtl8169_rx_vlan_tag(struct rtl8169_private *tp,
+ struct RxDesc *desc,
+ struct sk_buff *skb)
{
- u32 opts2 = le32_to_cpu(desc->opts2);
+ u32 opts2;
+
+ switch (tp->InitRxDescType) {
+ case RX_DESC_RING_TYPE_RSS:
+ opts2 = le32_to_cpu(((struct RxDescRss *)desc)->RxDescOpts.opts2);
+ break;
+ default:
+ opts2 = le32_to_cpu(desc->opts2);
+ break;
+ }
if (opts2 & RxVlanTag)
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff));
@@ -2884,6 +2921,14 @@ static void rtl_setup_mqs_reg(struct rtl8169_private *tp)
tp->imr_reg[i] = (u16)(IntrMask1_8125 + (i - 1) * 4);
}
+static void rtl8169_init_rss(struct rtl8169_private *tp)
+{
+ for (int i = 0; i < tp->HwSuppIndirTblEntries; i++)
+ tp->rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, tp->num_rx_rings);
+
+ netdev_rss_key_fill(tp->rss_key, RTL8127_RSS_KEY_SIZE);
+}
+
static void rtl_software_parameter_initialize(struct rtl8169_private *tp)
{
tp->num_rx_rings = 1;
@@ -2895,6 +2940,7 @@ static void rtl_software_parameter_initialize(struct rtl8169_private *tp)
tp->max_irq_nvecs = R8127_MAX_IRQ;
tp->HwSuppNumTxQueues = R8127_MAX_TX_QUEUES;
tp->HwSuppNumRxQueues = R8127_MAX_RX_QUEUES;
+ tp->HwSuppIndirTblEntries = RTL8127_MAX_INDIRECTION_TABLE_ENTRIES;
tp->HwSuppIsrVer = 6;
break;
default:
@@ -2908,10 +2954,6 @@ static void rtl_software_parameter_initialize(struct rtl8169_private *tp)
tp->InitRxDescType = RX_DESC_RING_TYPE_DEAFULT;
tp->HwCurrIsrVer = tp->HwSuppIsrVer;
- /* This just force nvecs, and will be remove in the following patch*/
- tp->min_irq_nvecs = 1;
- tp->max_irq_nvecs = 1;
-
rtl_setup_mqs_reg(tp);
rtl_set_ring_size(tp, NUM_RX_DESC, NUM_TX_DESC);
}
@@ -3038,6 +3080,76 @@ static void rtl_set_rx_max_size(struct rtl8169_private *tp)
RTL_W16(tp, RxMaxSize, R8169_RX_BUF_SIZE + 1);
}
+static void rtl8169_store_rss_key(struct rtl8169_private *tp)
+{
+ const u16 rss_key_reg = RSS_KEY_8125;
+ u32 i, rss_key_size = sizeof(tp->rss_key);
+ u32 *rss_key = (u32 *)tp->rss_key;
+
+ /* Write redirection table to HW */
+ for (i = 0; i < rss_key_size; i += 4)
+ RTL_W32(tp, rss_key_reg + i, *rss_key++);
+}
+
+static void rtl8169_store_reta(struct rtl8169_private *tp)
+{
+ u16 indir_tbl_reg = RSS_INDIRECTION_TBL_8125_V2;
+ u32 i, reta_entries = tp->HwSuppIndirTblEntries;
+ u32 reta = 0;
+ u8 *indir_tbl = tp->rss_indir_tbl;
+
+ /* Write redirection table to HW */
+ for (i = 0; i < reta_entries; i++) {
+ reta |= indir_tbl[i] << (i & 0x3) * 8;
+ if ((i & 3) == 3) {
+ RTL_W32(tp, indir_tbl_reg, reta);
+ indir_tbl_reg += 4;
+ reta = 0;
+ }
+ }
+}
+
+static int rtl8169_set_rss_hash_opt(struct rtl8169_private *tp)
+{
+ u32 rss_flags = tp->rss_flags;
+ u32 hash_mask_len;
+ u32 rss_ctrl;
+
+ rss_ctrl = ilog2(tp->num_rx_rings);
+ rss_ctrl &= (BIT(0) | BIT(1) | BIT(2));
+ rss_ctrl <<= RSS_CPU_NUM_OFFSET;
+
+ /* 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;
+
+ if (rss_flags & RTL_8125_RSS_FLAG_HASH_UDP_IPV4)
+ rss_ctrl |= RSS_CTRL_UDP_IPV4_SUPP;
+
+ if (rss_flags & RTL_8125_RSS_FLAG_HASH_UDP_IPV6)
+ rss_ctrl |= RSS_CTRL_UDP_IPV6_SUPP |
+ RSS_CTRL_UDP_IPV6_EXT_SUPP;
+
+ hash_mask_len = ilog2(tp->HwSuppIndirTblEntries);
+ hash_mask_len &= (BIT(0) | BIT(1) | BIT(2));
+ rss_ctrl |= hash_mask_len << RSS_MASK_BITS_OFFSET;
+
+ RTL_W32(tp, RSS_CTRL_8125, rss_ctrl);
+
+ return 0;
+}
+
+static void rtl_set_rss_config(struct rtl8169_private *tp)
+{
+ rtl8169_set_rss_hash_opt(tp);
+ rtl8169_store_reta(tp);
+ rtl8169_store_rss_key(tp);
+}
+
static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
{
/*
@@ -4102,6 +4214,29 @@ DECLARE_RTL_COND(rtl_mac_ocp_e00e_cond)
return r8168_mac_ocp_read(tp, 0xe00e) & BIT(13);
}
+static void rtl8125_set_tx_q_num(struct rtl8169_private *tp)
+{
+ u16 mac_ocp_data;
+
+ mac_ocp_data = r8168_mac_ocp_read(tp, 0xe63e);
+ mac_ocp_data &= ~(BIT(11) | BIT(10));
+ mac_ocp_data |= ((ilog2(tp->num_tx_rings) & 0x03) << 10);
+ r8168_mac_ocp_write(tp, 0xe63e, mac_ocp_data);
+}
+
+static void rtl8125_set_rx_q_num(struct rtl8169_private *tp)
+{
+ u16 q_ctrl;
+ u16 rx_q_num;
+
+ rx_q_num = (u16)ilog2(tp->num_rx_rings);
+ rx_q_num &= (BIT(0) | BIT(1) | BIT(2));
+ rx_q_num <<= 2;
+ q_ctrl = RTL_R16(tp, Q_NUM_CTRL_8125);
+ q_ctrl &= ~(BIT(2) | BIT(3) | BIT(4));
+ q_ctrl |= rx_q_num;
+ RTL_W16(tp, Q_NUM_CTRL_8125, q_ctrl);
+}
static void rtl8125_hw_set_interrupt_type(struct rtl8169_private *tp)
{
@@ -4142,6 +4277,13 @@ 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*/
+ if (tp->rss_enable) {
+ rtl8125_set_rx_q_num(tp);
+ rtl8125_set_tx_q_num(tp);
+ RTL_W8(tp, 0xd8, RTL_R8(tp, 0xd8) | 0x02);
+ }
+
if (tp->mac_version == RTL_GIGA_MAC_VER_80)
r8168_mac_ocp_modify(tp, 0xe614, 0x0f00, 0x0f00);
else if (tp->mac_version == RTL_GIGA_MAC_VER_70)
@@ -4378,6 +4520,12 @@ static void rtl_hw_start(struct rtl8169_private *tp)
rtl_hw_aspm_clkreq_enable(tp, true);
rtl_set_rx_max_size(tp);
rtl_set_rx_tx_desc_registers(tp);
+ if (rtl_is_8125(tp)) {
+ if (tp->rss_enable)
+ rtl_set_rss_config(tp);
+ else
+ RTL_W32(tp, RSS_CTRL_8125, 0x00);
+ }
rtl_lock_config_regs(tp);
rtl_jumbo_config(tp);
@@ -4405,6 +4553,16 @@ static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
+static void rtl8169_mark_to_asic_rss(struct RxDescRss *descrss)
+{
+ u32 eor = le32_to_cpu(descrss->RxDescOpts.opts1) & RingEnd;
+
+ descrss->RxDescOpts.opts2 = 0;
+ /* Force memory writes to complete before releasing descriptor */
+ dma_wmb();
+ WRITE_ONCE(descrss->RxDescOpts.opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
+}
+
static void rtl8169_mark_to_asic_default(struct RxDesc *desc)
{
u32 eor = le32_to_cpu(desc->opts1) & RingEnd;
@@ -4417,7 +4575,14 @@ static void rtl8169_mark_to_asic_default(struct RxDesc *desc)
static void rtl8169_mark_to_asic(struct rtl8169_private *tp, struct RxDesc *desc)
{
- rtl8169_mark_to_asic_default(desc);
+ switch (tp->InitRxDescType) {
+ case RX_DESC_RING_TYPE_RSS:
+ rtl8169_mark_to_asic_rss((struct RxDescRss *)desc);
+ break;
+ default:
+ rtl8169_mark_to_asic_default(desc);
+ break;
+ }
}
static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
@@ -4441,7 +4606,13 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
}
ring->RxDescPhyAddr[index] = mapping;
- desc->addr = cpu_to_le64(mapping);
+ if (tp->InitRxDescType == RX_DESC_RING_TYPE_RSS) {
+ struct RxDescRss *descrss = (struct RxDescRss *)(ring->RxDescArray) + index;
+
+ descrss->addr = cpu_to_le64(mapping);
+ } else {
+ desc->addr = cpu_to_le64(mapping);
+ }
rtl8169_mark_to_asic(tp, desc);
return data;
@@ -4468,9 +4639,21 @@ static void rtl8169_mark_as_last_descriptor_default(struct RxDesc *desc)
desc->opts1 |= cpu_to_le32(RingEnd);
}
+static void rtl8169_mark_as_last_descriptor_rss(struct RxDescRss *descrss)
+{
+ descrss->RxDescOpts.opts1 |= cpu_to_le32(RingEnd);
+}
+
static void rtl8169_mark_as_last_descriptor(struct rtl8169_private *tp, struct RxDesc *desc)
{
- rtl8169_mark_as_last_descriptor_default(desc);
+ switch (tp->InitRxDescType) {
+ case RX_DESC_RING_TYPE_RSS:
+ rtl8169_mark_as_last_descriptor_rss((struct RxDescRss *)desc);
+ break;
+ default:
+ rtl8169_mark_as_last_descriptor_default(desc);
+ break;
+ }
}
static int rtl8169_rx_fill(struct rtl8169_private *tp, struct rtl8169_rx_ring *ring)
@@ -5171,6 +5354,28 @@ static inline int rtl8169_fragmented_frame(u32 status)
return (status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag);
}
+static inline void rtl8169_rx_hash(struct rtl8169_private *tp,
+ struct RxDescRss *desc,
+ struct sk_buff *skb)
+{
+ u32 rss_header_info;
+ u32 hash_val;
+
+ if (!(tp->dev->features & NETIF_F_RXHASH))
+ return;
+
+ rss_header_info = le32_to_cpu(desc->RxDescRSSDWord.RSSInfo);
+
+ if (!(rss_header_info & RTL8127_RXS_RSS_L3_TYPE_MASK_V4))
+ return;
+
+ hash_val = le32_to_cpu(desc->RxDescRSSDWord.RSSResult);
+
+ skb_set_hash(skb, hash_val,
+ (RTL8127_RXS_RSS_L4_TYPE_MASK_V4 & rss_header_info) ?
+ PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3);
+}
+
static inline void rtl8169_rx_csum_default(struct rtl8169_private *tp,
struct sk_buff *skb,
struct RxDesc *desc)
@@ -5183,16 +5388,41 @@ static inline void rtl8169_rx_csum_default(struct rtl8169_private *tp,
skb_checksum_none_assert(skb);
}
+static inline void rtl8169_rx_csum_rss(struct rtl8169_private *tp,
+ struct sk_buff *skb,
+ struct RxDescRss *descrss)
+{
+ u32 opts1 = le32_to_cpu(descrss->RxDescOpts.opts1);
+
+ if (((opts1 & RxTCPT_v4) && !(opts1 & RxTCPF_v4)) ||
+ ((opts1 & RxUDPT_v4) && !(opts1 & RxUDPF_v4)))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb_checksum_none_assert(skb);
+}
+
static inline void rtl8169_rx_csum(struct rtl8169_private *tp,
struct sk_buff *skb,
struct RxDesc *desc)
{
- rtl8169_rx_csum_default(tp, skb, desc);
+ switch (tp->InitRxDescType) {
+ case RX_DESC_RING_TYPE_RSS:
+ rtl8169_rx_csum_rss(tp, skb, (struct RxDescRss *)desc);
+ break;
+ default:
+ rtl8169_rx_csum_default(tp, skb, desc);
+ break;
+ }
}
static u32 rtl8169_rx_desc_opts1(struct rtl8169_private *tp, struct RxDesc *desc)
{
- return READ_ONCE(desc->opts1);
+ switch (tp->InitRxDescType) {
+ case RX_DESC_RING_TYPE_RSS:
+ return READ_ONCE(((struct RxDescRss *)desc)->RxDescOpts.opts1);
+ default:
+ return READ_ONCE(desc->opts1);
+ }
}
static int rtl8169_check_rx_desc_error(struct net_device *dev,
@@ -5201,12 +5431,25 @@ static int rtl8169_check_rx_desc_error(struct net_device *dev,
{
int ret = 0;
- if (unlikely(status & RxRES)) {
- if (status & (RxRWT | RxRUNT))
- dev->stats.rx_length_errors++;
- if (status & RxCRC)
- dev->stats.rx_crc_errors++;
- ret = -1;
+ switch (tp->InitRxDescType) {
+ case RX_DESC_RING_TYPE_RSS:
+ if (unlikely(status & RxRES_RSS)) {
+ if (status & RxRUNT_RSS)
+ dev->stats.rx_length_errors++;
+ if (status & RxCRC_RSS)
+ dev->stats.rx_crc_errors++;
+ ret = -1;
+ }
+ break;
+ default:
+ if (unlikely(status & RxRES)) {
+ if (status & (RxRWT | RxRUNT))
+ dev->stats.rx_length_errors++;
+ if (status & RxCRC)
+ dev->stats.rx_crc_errors++;
+ ret = -1;
+ }
+ break;
}
return ret;
}
@@ -5215,7 +5458,14 @@ static inline void rtl8169_set_desc_dma_addr(struct rtl8169_private *tp,
struct RxDesc *desc,
dma_addr_t mapping)
{
- desc->addr = cpu_to_le64(mapping);
+ switch (tp->InitRxDescType) {
+ case RX_DESC_RING_TYPE_RSS:
+ ((struct RxDescRss *)desc)->addr = cpu_to_le64(mapping);
+ break;
+ default:
+ desc->addr = cpu_to_le64(mapping);
+ break;
+ }
}
static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
@@ -5291,10 +5541,13 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
skb->len = pkt_size;
dma_sync_single_for_device(d, addr, pkt_size, DMA_FROM_DEVICE);
+ if (tp->rss_enable)
+ rtl8169_rx_hash(tp, (struct RxDescRss *)desc, skb);
+
rtl8169_rx_csum(tp, skb, desc);
skb->protocol = eth_type_trans(skb, dev);
- rtl8169_rx_vlan_tag(desc, skb);
+ rtl8169_rx_vlan_tag(tp, desc, skb);
if (skb->pkt_type == PACKET_MULTICAST)
dev->stats.multicast++;
@@ -5873,6 +6126,45 @@ static void rtl_set_irq_mask(struct rtl8169_private *tp)
}
}
+static void rtl8169_double_check_rss_support(struct rtl8169_private *tp)
+{
+ if (tp->HwCurrIsrVer > 1) {
+ if (!(tp->features & RTL_FEATURE_MSIX) || tp->irq_nvecs < tp->min_irq_nvecs)
+ tp->HwCurrIsrVer = 1;
+ }
+
+ if (tp->rss_support && tp->HwCurrIsrVer > 1) {
+ u8 rss_queue_num = netif_get_num_default_rss_queues();
+
+ tp->num_rx_rings = min(rss_queue_num, tp->HwSuppNumRxQueues);
+ if (!(tp->num_rx_rings >= 2 && tp->irq_nvecs >= tp->min_irq_nvecs))
+ tp->num_rx_rings = 1;
+ }
+
+ if (tp->num_rx_rings >= 2) {
+ tp->rss_enable = 1;
+ tp->InitRxDescType = RX_DESC_RING_TYPE_RSS;
+ } else {
+ tp->rss_enable = 0;
+ if (tp->irq_nvecs > 1) {
+ pci_free_irq_vectors(tp->pci_dev);
+
+ tp->irq_nvecs = pci_alloc_irq_vectors(tp->pci_dev, 1, 1, PCI_IRQ_ALL_TYPES);
+
+ if (tp->irq_nvecs > 0) {
+ tp->irq = pci_irq_vector(tp->pci_dev, 0);
+ } else {
+ tp->irq = tp->pci_dev->irq;
+ tp->irq_nvecs = 1;
+ }
+
+ tp->features &= ~RTL_FEATURE_MSIX;
+ if (pci_dev_msi_enabled(tp->pci_dev))
+ tp->features |= RTL_FEATURE_MSI;
+ }
+ }
+}
+
static int rtl_alloc_irq(struct rtl8169_private *tp)
{
struct pci_dev *pdev = tp->pci_dev;
@@ -6346,6 +6638,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
tp->dash_type = rtl_get_dash_type(tp);
tp->dash_enabled = rtl_dash_is_enabled(tp);
+ tp->rss_support = rtl_check_rss_support(tp);
tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK;
@@ -6367,6 +6660,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc < 0)
return dev_err_probe(&pdev->dev, rc, "Can't allocate interrupt\n");
+ rtl8169_double_check_rss_support(tp);
+
+ if (tp->rss_support)
+ rtl8169_init_rss(tp);
INIT_WORK(&tp->wk.work, rtl_task);
disable_work(&tp->wk.work);
@@ -6388,6 +6685,11 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+ if (tp->rss_support) {
+ dev->hw_features |= NETIF_F_RXHASH;
+ dev->features |= NETIF_F_RXHASH;
+ }
+
/*
* Pretend we are using VLANs; This bypasses a nasty bug where
* Interrupts stop flowing on high load on 8110SCd controllers.
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [RFC Patch net-next v1 8/9] r8169: move struct ethtool_ops
2026-04-20 2:19 [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 javen
` (6 preceding siblings ...)
2026-04-20 2:19 ` [RFC Patch net-next v1 7/9] r8169: add support and enable rss javen
@ 2026-04-20 2:19 ` javen
2026-04-20 14:33 ` Andrew Lunn
2026-04-20 2:19 ` [RFC Patch net-next v1 9/9] r8169: add support for ethtool javen
2026-04-20 11:06 ` [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 FUKAUMI Naoki
9 siblings, 1 reply; 13+ messages in thread
From: javen @ 2026-04-20 2:19 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This patch move struct ethtool_ops, no changes. Prepare for next patch.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
drivers/net/ethernet/realtek/r8169_main.c | 56 +++++++++++------------
1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index b3f15e6fd5e9..6b574fc336d6 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -2587,34 +2587,6 @@ static int rtl8169_set_link_ksettings(struct net_device *ndev,
return 0;
}
-static const struct ethtool_ops rtl8169_ethtool_ops = {
- .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
- ETHTOOL_COALESCE_MAX_FRAMES,
- .get_drvinfo = rtl8169_get_drvinfo,
- .get_regs_len = rtl8169_get_regs_len,
- .get_link = ethtool_op_get_link,
- .get_coalesce = rtl_get_coalesce,
- .set_coalesce = rtl_set_coalesce,
- .get_regs = rtl8169_get_regs,
- .get_wol = rtl8169_get_wol,
- .set_wol = rtl8169_set_wol,
- .get_strings = rtl8169_get_strings,
- .get_sset_count = rtl8169_get_sset_count,
- .get_ethtool_stats = rtl8169_get_ethtool_stats,
- .get_ts_info = ethtool_op_get_ts_info,
- .nway_reset = phy_ethtool_nway_reset,
- .get_eee = rtl8169_get_eee,
- .set_eee = rtl8169_set_eee,
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
- .set_link_ksettings = rtl8169_set_link_ksettings,
- .get_ringparam = rtl8169_get_ringparam,
- .get_pause_stats = rtl8169_get_pause_stats,
- .get_pauseparam = rtl8169_get_pauseparam,
- .set_pauseparam = rtl8169_set_pauseparam,
- .get_eth_mac_stats = rtl8169_get_eth_mac_stats,
- .get_eth_ctrl_stats = rtl8169_get_eth_ctrl_stats,
-};
-
static const struct rtl_chip_info *rtl8169_get_chip_version(u32 xid, bool gmii)
{
/* Chips combining a 1Gbps MAC with a 100Mbps PHY */
@@ -6546,6 +6518,34 @@ static void r8169_init_napi(struct rtl8169_private *tp)
}
}
+static const struct ethtool_ops rtl8169_ethtool_ops = {
+ .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
+ ETHTOOL_COALESCE_MAX_FRAMES,
+ .get_drvinfo = rtl8169_get_drvinfo,
+ .get_regs_len = rtl8169_get_regs_len,
+ .get_link = ethtool_op_get_link,
+ .get_coalesce = rtl_get_coalesce,
+ .set_coalesce = rtl_set_coalesce,
+ .get_regs = rtl8169_get_regs,
+ .get_wol = rtl8169_get_wol,
+ .set_wol = rtl8169_set_wol,
+ .get_strings = rtl8169_get_strings,
+ .get_sset_count = rtl8169_get_sset_count,
+ .get_ethtool_stats = rtl8169_get_ethtool_stats,
+ .get_ts_info = ethtool_op_get_ts_info,
+ .nway_reset = phy_ethtool_nway_reset,
+ .get_eee = rtl8169_get_eee,
+ .set_eee = rtl8169_set_eee,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = rtl8169_set_link_ksettings,
+ .get_ringparam = rtl8169_get_ringparam,
+ .get_pause_stats = rtl8169_get_pause_stats,
+ .get_pauseparam = rtl8169_get_pauseparam,
+ .set_pauseparam = rtl8169_set_pauseparam,
+ .get_eth_mac_stats = rtl8169_get_eth_mac_stats,
+ .get_eth_ctrl_stats = rtl8169_get_eth_ctrl_stats,
+};
+
static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
const struct rtl_chip_info *chip;
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [RFC Patch net-next v1 8/9] r8169: move struct ethtool_ops
2026-04-20 2:19 ` [RFC Patch net-next v1 8/9] r8169: move struct ethtool_ops javen
@ 2026-04-20 14:33 ` Andrew Lunn
0 siblings, 0 replies; 13+ messages in thread
From: Andrew Lunn @ 2026-04-20 14:33 UTC (permalink / raw)
To: javen
Cc: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms, netdev, linux-kernel
On Mon, Apr 20, 2026 at 10:19:56AM +0800, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> This patch move struct ethtool_ops, no changes. Prepare for next patch.
Commit messages are all about "Why?". You normally move code because a
local function is after where it will be called from. I assume your
next patch is going to add a function which will be listed in the
ethtool ops, and it calls a function which is currently after the
ethtool ops. Please name names.
Andrew
---
pw-bot: cr
^ permalink raw reply [flat|nested] 13+ messages in thread
* [RFC Patch net-next v1 9/9] r8169: add support for ethtool
2026-04-20 2:19 [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 javen
` (7 preceding siblings ...)
2026-04-20 2:19 ` [RFC Patch net-next v1 8/9] r8169: move struct ethtool_ops javen
@ 2026-04-20 2:19 ` javen
2026-04-20 13:10 ` Andrew Lunn
2026-04-20 11:06 ` [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 FUKAUMI Naoki
9 siblings, 1 reply; 13+ messages in thread
From: javen @ 2026-04-20 2:19 UTC (permalink / raw)
To: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel, Javen Xu
From: Javen Xu <javen_xu@realsil.com.cn>
This patch add support for changing rx queues by ethtool. We can set rx
1, 2, 4, 8 by ethtool -L eth1 rx num.
Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
---
drivers/net/ethernet/realtek/r8169_main.c | 68 +++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index 6b574fc336d6..57087abe7d88 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -6518,6 +6518,72 @@ static void r8169_init_napi(struct rtl8169_private *tp)
}
}
+static void rtl8169_get_channels(struct net_device *dev,
+ struct ethtool_channels *ch)
+{
+ struct rtl8169_private *tp = netdev_priv(dev);
+
+ ch->max_rx = tp->HwSuppNumRxQueues ? tp->HwSuppNumRxQueues : 1;
+ ch->max_tx = tp->HwSuppNumTxQueues ? tp->HwSuppNumTxQueues : 1;
+ ch->max_other = 0;
+ ch->max_combined = 0;
+
+ ch->rx_count = tp->num_rx_rings;
+ ch->tx_count = tp->num_tx_rings;
+ ch->other_count = 0;
+ ch->combined_count = 0;
+}
+
+static int rtl8169_set_channels(struct net_device *dev,
+ struct ethtool_channels *ch)
+{
+ struct rtl8169_private *tp = netdev_priv(dev);
+ bool if_running = netif_running(dev);
+ int i;
+
+ if (!tp->rss_support && (ch->rx_count > 1 || ch->tx_count > 1)) {
+ netdev_warn(dev, "This chip does not support multiple channels/RSS.\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (ch->rx_count == 0 || ch->tx_count == 0)
+ return -EINVAL;
+ if (ch->rx_count > tp->HwSuppNumRxQueues ||
+ ch->tx_count > tp->HwSuppNumTxQueues)
+ return -EINVAL;
+ if (ch->other_count || ch->combined_count)
+ return -EINVAL;
+
+ if (ch->rx_count == tp->num_rx_rings &&
+ ch->tx_count == tp->num_tx_rings)
+ return 0;
+
+ if (if_running)
+ rtl8169_close(dev);
+
+ tp->num_rx_rings = ch->rx_count;
+ tp->num_tx_rings = ch->tx_count;
+
+ tp->rss_enable = (tp->num_rx_rings > 1 && tp->rss_support);
+
+ for (i = 0; i < tp->HwSuppIndirTblEntries; i++) {
+ if (tp->rss_enable)
+ tp->rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, tp->num_rx_rings);
+ else
+ tp->rss_indir_tbl[i] = 0;
+ }
+
+ if (tp->rss_enable)
+ tp->InitRxDescType = RX_DESC_RING_TYPE_RSS;
+ else
+ tp->InitRxDescType = RX_DESC_RING_TYPE_DEAFULT;
+
+ if (if_running)
+ return rtl_open(dev);
+
+ return 0;
+}
+
static const struct ethtool_ops rtl8169_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES,
@@ -6536,6 +6602,8 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
.nway_reset = phy_ethtool_nway_reset,
.get_eee = rtl8169_get_eee,
.set_eee = rtl8169_set_eee,
+ .get_channels = rtl8169_get_channels,
+ .set_channels = rtl8169_set_channels,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = rtl8169_set_link_ksettings,
.get_ringparam = rtl8169_get_ringparam,
--
2.43.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [RFC Patch net-next v1 9/9] r8169: add support for ethtool
2026-04-20 2:19 ` [RFC Patch net-next v1 9/9] r8169: add support for ethtool javen
@ 2026-04-20 13:10 ` Andrew Lunn
0 siblings, 0 replies; 13+ messages in thread
From: Andrew Lunn @ 2026-04-20 13:10 UTC (permalink / raw)
To: javen
Cc: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms, netdev, linux-kernel
> +static int rtl8169_set_channels(struct net_device *dev,
> + struct ethtool_channels *ch)
> +{
> + struct rtl8169_private *tp = netdev_priv(dev);
> + bool if_running = netif_running(dev);
> + int i;
> +
> + if (!tp->rss_support && (ch->rx_count > 1 || ch->tx_count > 1)) {
> + netdev_warn(dev, "This chip does not support multiple channels/RSS.\n");
> + return -EOPNOTSUPP;
> + }
> +
> + if (ch->rx_count == 0 || ch->tx_count == 0)
> + return -EINVAL;
> + if (ch->rx_count > tp->HwSuppNumRxQueues ||
> + ch->tx_count > tp->HwSuppNumTxQueues)
> + return -EINVAL;
> + if (ch->other_count || ch->combined_count)
> + return -EINVAL;
> +
> + if (ch->rx_count == tp->num_rx_rings &&
> + ch->tx_count == tp->num_tx_rings)
> + return 0;
> +
> + if (if_running)
> + rtl8169_close(dev);
I assume this releases all the memory from the rings?
> +
> + tp->num_rx_rings = ch->rx_count;
> + tp->num_tx_rings = ch->tx_count;
> +
> + tp->rss_enable = (tp->num_rx_rings > 1 && tp->rss_support);
> +
> + for (i = 0; i < tp->HwSuppIndirTblEntries; i++) {
> + if (tp->rss_enable)
> + tp->rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, tp->num_rx_rings);
> + else
> + tp->rss_indir_tbl[i] = 0;
> + }
> +
> + if (tp->rss_enable)
> + tp->InitRxDescType = RX_DESC_RING_TYPE_RSS;
> + else
> + tp->InitRxDescType = RX_DESC_RING_TYPE_DEAFULT;
> +
> + if (if_running)
> + return rtl_open(dev);
And this tries to allocate the memory needed for the rings? And if the
system is under memory pressure, it fails and your network is dead?
Please modify the code so that is first allocated the new rings and
then frees the old rings, so you can fail gracefully.
Andrew
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127
2026-04-20 2:19 [RFC Patch net-next v1 0/9] r8169: add RSS support for RTL8127 javen
` (8 preceding siblings ...)
2026-04-20 2:19 ` [RFC Patch net-next v1 9/9] r8169: add support for ethtool javen
@ 2026-04-20 11:06 ` FUKAUMI Naoki
9 siblings, 0 replies; 13+ messages in thread
From: FUKAUMI Naoki @ 2026-04-20 11:06 UTC (permalink / raw)
To: javen, hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms
Cc: netdev, linux-kernel
Hi Javen,
Thank you very much for your nice work!
On 4/20/26 11:19, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> This series patch adds RSS support for RTL8127 in the r8169 driver.
>
> Currently, without RSS support, a single CPU core handles all incoming
> traffic. Under heavy loads, this single core becomes a bottleneck, causing
> high softirq usage and leading to unstable and degraded network throughput.
>
> As a result, we add rss support for RTL8127. This RFC patch is just for
> discussing. And we do some experiments on AMD platform. Below is the
> result.
>
> Platform: AMD Ryzen Embedded R2514 with Radeon Graphics(4 Cores/8 Threads)
> Arch: x86_64
> Test command:
> Server: iperf3 -s
> Client: iperf3 -c 192.168.2.1 -P 20 -t 3600
> Monitor: mpstat -P ALL 1
>
> Before this patch (Without RSS):
> Throughput: Unstable, fluctuating between 3.76 Gbits/sec and
> 8.2 Gbits/sec.
> CPU Usage: A single CPU core is fully occupied with softirq reaching
> up to 96%.
>
> After this patch (With RSS enabled):
> Throughput: Stable at 9.42 Gbits/sec.
> CPU Usage: The traffic load is evenly distributed across multiple CPU
> cores. The maximum softirq on a single core dropped to 63%.
Platform: Radxa ROCK 5T (RK3588: 4x Cortex-A76, 4x Cortex-A55)
Arch: aarch64
Configuration: smp_affinity is set to use only the big cores.
Vanilla Linux v7.0:
Throughput: 5.5 Gbps (4.3 Gbps with -P 20)
CPU Usage: ~100% on a single A76 core.
Linux v7.0 + this patch series:
Throughput: 9.4 Gbps with -P 20
CPU Usage: distributed across all four A76 cores.
Looks good to me!
Feel free to use:
Tested-by: FUKAUMI Naoki <naoki@radxa.com>
Best regards,
--
FUKAUMI Naoki
Radxa Computer (Shenzhen) Co., Ltd.
> Patch summary:
> Patch 1: Adds necessary macro and register definitions for RSS.
> Patch 2-4: Support NAPI and multi RX/TX queues.
> Patch 5-6: Support MSI-X and enables it specifically for RTL8127.
> Patch 7: Enables RSS for RTL8127.
> Patch 8-9: Adds ethtool support to configure the number of RX queues.
>
> Javen Xu (9):
> r8169: add some register definitions
> r8169: add napi and irq support
> r8169: add support for multi tx queues
> r8169: add support for multi rx queues
> r8169: add support for msix
> r8169: enable msix for RTL8127
> r8169: add support and enable rss
> r8169: move struct ethtool_ops
> r8169: add support for ethtool
>
> drivers/net/ethernet/realtek/r8169_main.c | 1437 ++++++++++++++++++---
> 1 file changed, 1238 insertions(+), 199 deletions(-)
^ permalink raw reply [flat|nested] 13+ messages in thread