AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/14 02:26 flow patch-triage
4m Args:
null

Results:
{
  "EnableConfigs": [
    "R8169",
    "NET_VENDOR_REALTEK",
    "PCI"
  ],
  "FocusSymbols": [
    "rtl8169_interrupt_msix",
    "rtl8169_interrupt_other",
    "rtl8169_poll_msix_rx",
    "rtl8169_poll_msix_tx",
    "rtl8169_get_channels"
  ],
  "KMSANReasoning": "The patch series refactors the r8169 driver to support multiple RX queues, RSS, and MSI-X. It introduces new structures for RX rings, RSS data, and NAPI, which are all allocated using zeroing allocation functions (`kcalloc`, `devm_kzalloc`, `devm_alloc_etherdev_mqs`). The RX descriptors are allocated via `dma_alloc_coherent` and explicitly zeroed with `memset`. The ethtool callback `rtl8169_get_channels` only writes to the `ethtool_channels` struct, which is pre-zeroed by the ethtool core before the callback is invoked. Similarly, `rtl8169_get_stats64` updates the `rtnl_link_stats64` struct, which is also pre-zeroed by the networking core. All local variables and stack structures introduced in the patch are properly initialized before use. There are no new IOCTLs, netlink attributes, or other userspace interfaces introduced that could leak uninitialized memory. Any potential bugs introduced by these changes, such as out-of-bounds accesses or use-after-free, will be effectively caught by standard KASAN and other bug detectors. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch adds Receive Side Scaling (RSS) and multiple RX queues support to the Realtek r8169 network driver. It introduces new MSI-X interrupt handlers and NAPI polling functions for both RX and TX paths. The driver is reachable via PCI fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/08/14 02:26 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit b16e1b25c7d702b5dbd0fd5deb3cc7a1653131cf\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Aug 14 02:26:25 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c\nindex ec4fc21fa21fb..13372fc55eeef 100644\n--- a/drivers/net/ethernet/realtek/r8169_main.c\n+++ b/drivers/net/ethernet/realtek/r8169_main.c\n@@ -29,6 +29,7 @@\n #include \u003clinux/prefetch.h\u003e\n #include \u003clinux/ipv6.h\u003e\n #include \u003clinux/unaligned.h\u003e\n+#include \u003clinux/u64_stats_sync.h\u003e\n #include \u003cnet/ip6_checksum.h\u003e\n #include \u003cnet/netdev_queues.h\u003e\n #include \u003cnet/phy/realtek_phy.h\u003e\n@@ -74,9 +75,33 @@\n #define NUM_TX_DESC\t256\t/* Number of Tx descriptor registers */\n #define NUM_RX_DESC\t256\t/* Number of Rx descriptor registers */\n #define R8169_TX_RING_BYTES\t(NUM_TX_DESC * sizeof(struct TxDesc))\n-#define R8169_RX_RING_BYTES\t(NUM_RX_DESC * sizeof(struct RxDesc))\n+\n+/*\n+ * Workaround for the hardware DMA prefetcher. The H/W might aggressively\n+ * fetch one more descriptor even after hitting the RingEnd mark. We\n+ * allocate this extra dummy space as padding to prevent out-of-bounds\n+ * access and potential IOMMU faults.\n+ */\n+#define R8169_RX_RING_BYTES\t((NUM_RX_DESC + 1) * sizeof(struct RxDesc))\n #define R8169_TX_STOP_THRS\t(MAX_SKB_FRAGS + 1)\n #define R8169_TX_START_THRS\t(2 * R8169_TX_STOP_THRS)\n+#define R8169_MAX_QUEUES\t16\n+#define R8169_MAX_RX_QUEUES\t8\n+#define R8169_DEFAULT_RX_QUEUES\t1\n+#define R8169_MAX_TX_QUEUES\t1\n+#define R8127_MAX_NUM_IRQVEC\t32\n+#define R8127_MIN_NUM_IRQVEC\t30\n+#define R8169_IRQ_DEFAULT\t1\n+#define RTL_RSS_KEY_SIZE\t40\n+#define RSS_CPU_NUM_MASK\tGENMASK(18, 16)\n+#define RSS_HASH_MASK\t\tGENMASK(10, 8)\n+#define RTL_MAX_INDIRECTION_TABLE_ENTRIES 128\n+#define RXS_RSS_UDP\t\tBIT(27)\n+#define RXS_RSS_IPV4\t\tBIT(28)\n+#define RXS_RSS_IPV6\t\tBIT(29)\n+#define RXS_RSS_TCP\t\tBIT(30)\n+#define RXS_RSS_L3_TYPE_MASK\t(RXS_RSS_IPV4 | RXS_RSS_IPV6)\n+#define RXS_RSS_L4_TYPE_MASK\t(RXS_RSS_TCP | RXS_RSS_UDP)\n \n #define OCP_STD_PHY_BASE\t0xa400\n \n@@ -441,11 +466,16 @@ enum rtl8125_registers {\n \tTxPoll_8125\t\t= 0x90,\n \tLEDSEL3\t\t\t= 0x96,\n \tMAC0_BKP\t\t= 0x19e0,\n+\tRDSAR_Q1_LOW\t\t= 0x4000,\n \tRSS_CTRL_8125\t\t= 0x4500,\n \tQ_NUM_CTRL_8125\t\t= 0x4800,\n \tEEE_TXIDLE_TIMER_8125\t= 0x6048,\n+\tIMR_CLEAR_VEC_MAP_REG\t= 0x0d00,\n+\tISR_VEC_MAP_REG\t\t= 0x0d04,\n+\tIMR_SET_VEC_MAP_REG\t= 0x0d0c,\n };\n \n+#define MSIX_ID_VEC_MAP_LINKCHG\t29\n #define LEDSEL_MASK_8125\t0x23f\n \n #define RX_VLAN_INNER_8125\tBIT(22)\n@@ -474,6 +504,9 @@ enum rtl_register_content {\n \tRxRUNT\t= (1 \u003c\u003c 20),\n \tRxCRC\t= (1 \u003c\u003c 19),\n \n+\tRXRUNT_RSS\t= (1 \u003c\u003c 21),\n+\tRXCRC_RSS\t= (1 \u003c\u003c 20),\n+\n \t/* ChipCmdBits */\n \tStopReq\t\t= 0x80,\n \tCmdReset\t= 0x10,\n@@ -576,6 +609,23 @@ enum rtl_register_content {\n \n \t/* magic enable v2 */\n \tMagicPacket_v2\t= (1 \u003c\u003c 16),\t/* Wake up when receives a Magic Packet */\n+#define\tISRIMR_LINKCHG\tBIT(29)\n+#define\tISRIMR_TOK_Q0\tBIT(8)\n+#define\tISRIMR_ROK_Q0\tBIT(0)\n+#define RTL_DESC_TYPE_CTRL\t\t0xd8\n+#define RSS_KEY_REG\t\t\t0x4600\n+#define RSS_INDIRECTION_TBL_REG\t\t0x4700\n+#define RSS_CTRL_TCP_IPV4_SUPP\t\tBIT(0)\n+#define RTL_DESC_TYPE_RSS\t\tBIT(1)\n+#define RSS_CTRL_IPV4_SUPP\t\tBIT(1)\n+#define RSS_CTRL_TCP_IPV6_SUPP\t\tBIT(2)\n+#define RSS_CTRL_IPV6_SUPP\t\tBIT(3)\n+#define RSS_CTRL_IPV6_EXT_SUPP\t\tBIT(4)\n+#define RSS_CTRL_TCP_IPV6_EXT_SUPP\tBIT(5)\n+#define\tRX_RES_RSS\t\t\tBIT(22)\n+#define\tRX_RUNT_RSS\t\t\tBIT(21)\n+#define\tRX_CRC_RSS\t\t\tBIT(20)\n+#define RTL_RX_Q_NUM_MASK\t\tGENMASK(4, 2)\n };\n \n enum rtl_desc_bit {\n@@ -633,6 +683,11 @@ enum rtl_rx_desc_bit {\n #define RxProtoIP\t(PID1 | PID0)\n #define RxProtoMask\tRxProtoIP\n \n+#define\tRX_UDPT_DESC_RSS\tBIT(19)\n+#define\tRX_TCPT_DESC_RSS\tBIT(18)\n+#define\tRX_UDPF_DESC_RSS\tBIT(16) /* UDP/IP checksum failed */\n+#define\tRX_TCPF_DESC_RSS\tBIT(15) /* TCP/IP checksum failed */\n+\n \tIPFail\t\t= (1 \u003c\u003c 16), /* IP checksum failed */\n \tUDPFail\t\t= (1 \u003c\u003c 15), /* UDP/IP checksum failed */\n \tTCPFail\t\t= (1 \u003c\u003c 14), /* TCP/IP checksum failed */\n@@ -654,9 +709,27 @@ struct TxDesc {\n };\n \n struct RxDesc {\n-\t__le32 opts1;\n-\t__le32 opts2;\n-\t__le64 addr;\n+\tunion {\n+\t\t/* RX_DESC_TYPE_DEFAULT */\n+\t\tstruct {\n+\t\t\t__le32 opts1;\n+\t\t\t__le32 opts2;\n+\t\t\t__le64 addr;\n+\t\t};\n+\n+\t\t/* RX_DESC_TYPE_RSS */\n+\t\tstruct {\n+\t\t\tunion {\n+\t\t\t\t__le64 rss_addr;\n+\t\t\t\tstruct {\n+\t\t\t\t\t__le32 rss_info;\n+\t\t\t\t\t__le32 rss_result;\n+\t\t\t\t} rss_dword;\n+\t\t\t};\n+\t\t\t__le32 rss_opts2;\n+\t\t\t__le32 rss_opts1;\n+\t\t};\n+\t};\n };\n \n struct ring_info {\n@@ -728,27 +801,57 @@ enum rtl_dash_type {\n \tRTL_DASH_25_BP,\n };\n \n+enum rx_desc_type {\n+\tRX_DESC_TYPE_DEFAULT,\n+\tRX_DESC_TYPE_RSS,\n+};\n+\n+struct rtl8169_rx_ring {\n+\tu32 cur_rx;\n+\tu32 dirty_rx;\n+\tstruct RxDesc *rx_desc_array;\n+\tdma_addr_t rx_desc_phy_addr[NUM_RX_DESC];\n+\tdma_addr_t rx_phy_addr;\n+\tstruct page *rx_databuff[NUM_RX_DESC];\n+\n+\tstruct {\n+\t\tu64 rx_errors;\n+\t\tu64 rx_dropped;\n+\t\tu64 rx_length_errors;\n+\t\tu64 rx_crc_errors;\n+\t\tu64 multicast;\n+\t\tstruct u64_stats_sync syncp;\n+\t} stats;\n+};\n+\n+struct rtl8169_rss_data {\n+\tu8 rss_key[RTL_RSS_KEY_SIZE];\n+\tu8 rss_indir_tbl[RTL_MAX_INDIRECTION_TABLE_ENTRIES];\n+\tunsigned int hw_supp_indir_tbl_entries;\n+};\n+\n struct rtl8169_private {\n \tvoid __iomem *mmio_addr;\t/* memory map physical address */\n \tstruct pci_dev *pci_dev;\n \tstruct net_device *dev;\n \tstruct phy_device *phydev;\n-\tstruct napi_struct napi;\n \tenum mac_version mac_version;\n \tenum rtl_dash_type dash_type;\n-\tu32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */\n \tu32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */\n \tu32 dirty_tx;\n \tstruct TxDesc *TxDescArray;\t/* 256-aligned Tx descriptor ring */\n-\tstruct RxDesc *RxDescArray;\t/* 256-aligned Rx descriptor ring */\n \tdma_addr_t TxPhyAddr;\n-\tdma_addr_t RxPhyAddr;\n-\tstruct page *Rx_databuff[NUM_RX_DESC];\t/* Rx data buffers */\n \tstruct ring_info tx_skb[NUM_TX_DESC];\t/* Tx data buffers */\n+\tstruct napi_struct *rtl8169_napi;\n+\tstruct rtl8169_rx_ring *rx_ring;\n+\tunsigned int num_rx_rings;\n \tu16 cp_cmd;\n \tu16 tx_lpi_timer;\n \tu32 irq_mask;\n-\tint irq;\n+\tunsigned int hw_supp_num_rx_queues;\n+\tstruct rtl8169_rss_data *rss_data;\n+\tunsigned int irq_nvecs;\n+\tenum rx_desc_type init_rx_desc_type;\n \tstruct clk *clk;\n \n \tstruct {\n@@ -1578,6 +1681,11 @@ static bool rtl_dash_is_enabled(struct rtl8169_private *tp)\n \t}\n }\n \n+static bool rtl_hw_support_rss(struct rtl8169_private *tp)\n+{\n+\treturn tp-\u003emac_version == RTL_GIGA_MAC_VER_80;\n+}\n+\n static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp)\n {\n \tswitch (tp-\u003emac_version) {\n@@ -1644,26 +1752,38 @@ static u32 rtl_get_events(struct rtl8169_private *tp)\n \n static void rtl_ack_events(struct rtl8169_private *tp, u32 bits)\n {\n-\tif (rtl_is_8125(tp))\n-\t\tRTL_W32(tp, IntrStatus_8125, bits);\n-\telse\n+\tif (rtl_is_8125(tp)) {\n+\t\tif (tp-\u003eirq_nvecs \u003e 1)\n+\t\t\tRTL_W32(tp, ISR_VEC_MAP_REG, bits);\n+\t\telse\n+\t\t\tRTL_W32(tp, IntrStatus_8125, bits);\n+\t} else {\n \t\tRTL_W16(tp, IntrStatus, bits);\n+\t}\n }\n \n static void rtl_irq_disable(struct rtl8169_private *tp)\n {\n-\tif (rtl_is_8125(tp))\n-\t\tRTL_W32(tp, IntrMask_8125, 0);\n-\telse\n+\tif (rtl_is_8125(tp)) {\n+\t\tif (tp-\u003eirq_nvecs \u003e 1)\n+\t\t\tRTL_W32(tp, IMR_CLEAR_VEC_MAP_REG, 0xffffffff);\n+\t\telse\n+\t\t\tRTL_W32(tp, IntrMask_8125, 0);\n+\t} else {\n \t\tRTL_W16(tp, IntrMask, 0);\n+\t}\n }\n \n static void rtl_irq_enable(struct rtl8169_private *tp)\n {\n-\tif (rtl_is_8125(tp))\n-\t\tRTL_W32(tp, IntrMask_8125, tp-\u003eirq_mask);\n-\telse\n+\tif (rtl_is_8125(tp)) {\n+\t\tif (tp-\u003eirq_nvecs \u003e 1)\n+\t\t\tRTL_W32(tp, IMR_SET_VEC_MAP_REG, tp-\u003eirq_mask);\n+\t\telse\n+\t\t\tRTL_W32(tp, IntrMask_8125, tp-\u003eirq_mask);\n+\t} else {\n \t\tRTL_W16(tp, IntrMask, tp-\u003eirq_mask);\n+\t}\n }\n \n static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)\n@@ -1867,9 +1987,20 @@ static inline u32 rtl8169_tx_vlan_tag(struct sk_buff *skb)\n \t\tTxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00;\n }\n \n-static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb)\n+static void rtl8169_rx_vlan_tag(struct rtl8169_private *tp,\n+\t\t\t\tstruct RxDesc *desc,\n+\t\t\t\tstruct sk_buff *skb)\n {\n-\tu32 opts2 = le32_to_cpu(desc-\u003eopts2);\n+\tu32 opts2;\n+\n+\tswitch (tp-\u003einit_rx_desc_type) {\n+\tcase RX_DESC_TYPE_RSS:\n+\t\topts2 = le32_to_cpu(desc-\u003erss_opts2);\n+\t\tbreak;\n+\tdefault:\n+\t\topts2 = le32_to_cpu(desc-\u003eopts2);\n+\t\tbreak;\n+\t}\n \n \tif (opts2 \u0026 RxVlanTag)\n \t\t__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 \u0026 0xffff));\n@@ -2416,34 +2547,6 @@ static int rtl8169_set_link_ksettings(struct net_device *ndev,\n \treturn 0;\n }\n \n-static const struct ethtool_ops rtl8169_ethtool_ops = {\n-\t.supported_coalesce_params = ETHTOOL_COALESCE_USECS |\n-\t\t\t\t     ETHTOOL_COALESCE_MAX_FRAMES,\n-\t.get_drvinfo\t\t= rtl8169_get_drvinfo,\n-\t.get_regs_len\t\t= rtl8169_get_regs_len,\n-\t.get_link\t\t= ethtool_op_get_link,\n-\t.get_coalesce\t\t= rtl_get_coalesce,\n-\t.set_coalesce\t\t= rtl_set_coalesce,\n-\t.get_regs\t\t= rtl8169_get_regs,\n-\t.get_wol\t\t= rtl8169_get_wol,\n-\t.set_wol\t\t= rtl8169_set_wol,\n-\t.get_strings\t\t= rtl8169_get_strings,\n-\t.get_sset_count\t\t= rtl8169_get_sset_count,\n-\t.get_ethtool_stats\t= rtl8169_get_ethtool_stats,\n-\t.get_ts_info\t\t= ethtool_op_get_ts_info,\n-\t.nway_reset\t\t= phy_ethtool_nway_reset,\n-\t.get_eee\t\t= rtl8169_get_eee,\n-\t.set_eee\t\t= rtl8169_set_eee,\n-\t.get_link_ksettings\t= phy_ethtool_get_link_ksettings,\n-\t.set_link_ksettings\t= rtl8169_set_link_ksettings,\n-\t.get_ringparam\t\t= rtl8169_get_ringparam,\n-\t.get_pause_stats\t= rtl8169_get_pause_stats,\n-\t.get_pauseparam\t\t= rtl8169_get_pauseparam,\n-\t.set_pauseparam\t\t= rtl8169_set_pauseparam,\n-\t.get_eth_mac_stats\t= rtl8169_get_eth_mac_stats,\n-\t.get_eth_ctrl_stats\t= rtl8169_get_eth_ctrl_stats,\n-};\n-\n static const struct rtl_chip_info *rtl8169_get_chip_version(u32 xid, bool gmii)\n {\n \t/* Chips combining a 1Gbps MAC with a 100Mbps PHY */\n@@ -2619,9 +2722,26 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp)\n \t}\n }\n \n+static void rtl8169_rx_desc_init(struct rtl8169_private *tp)\n+{\n+\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++) {\n+\t\tstruct rtl8169_rx_ring *ring = \u0026tp-\u003erx_ring[i];\n+\n+\t\tmemset(ring-\u003erx_desc_array, 0x0, R8169_RX_RING_BYTES);\n+\t}\n+}\n+\n static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)\n {\n-\ttp-\u003edirty_tx = tp-\u003ecur_tx = tp-\u003ecur_rx = 0;\n+\ttp-\u003edirty_tx = 0;\n+\ttp-\u003ecur_tx = 0;\n+\n+\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++) {\n+\t\tstruct rtl8169_rx_ring *ring = \u0026tp-\u003erx_ring[i];\n+\n+\t\tring-\u003edirty_rx = 0;\n+\t\tring-\u003ecur_rx = 0;\n+\t}\n }\n \n static void rtl_jumbo_config(struct rtl8169_private *tp)\n@@ -2680,6 +2800,29 @@ static void rtl_hw_reset(struct rtl8169_private *tp)\n \trtl_loop_wait_low(tp, \u0026rtl_chipcmd_cond, 100, 100);\n }\n \n+static void rtl8169_init_rss(struct rtl8169_private *tp)\n+{\n+\tfor (int i = 0; i \u003c tp-\u003erss_data-\u003ehw_supp_indir_tbl_entries; i++)\n+\t\ttp-\u003erss_data-\u003erss_indir_tbl[i] = ethtool_rxfh_indir_default(i, tp-\u003enum_rx_rings);\n+\n+\tnetdev_rss_key_fill(tp-\u003erss_data-\u003erss_key, RTL_RSS_KEY_SIZE);\n+}\n+\n+static void rtl_setup_rx_params(struct rtl8169_private *tp)\n+{\n+\ttp-\u003enum_rx_rings = 1;\n+\tswitch (tp-\u003emac_version) {\n+\tcase RTL_GIGA_MAC_VER_80:\n+\t\ttp-\u003ehw_supp_num_rx_queues = R8169_MAX_RX_QUEUES;\n+\t\ttp-\u003erss_data-\u003ehw_supp_indir_tbl_entries = RTL_MAX_INDIRECTION_TABLE_ENTRIES;\n+\t\tbreak;\n+\tdefault:\n+\t\ttp-\u003ehw_supp_num_rx_queues = R8169_DEFAULT_RX_QUEUES;\n+\t\tbreak;\n+\t}\n+\ttp-\u003einit_rx_desc_type = RX_DESC_TYPE_DEFAULT;\n+}\n+\n static void rtl_request_firmware(struct rtl8169_private *tp)\n {\n \tstruct rtl_fw *rtl_fw;\n@@ -2802,8 +2945,60 @@ static void rtl_set_rx_max_size(struct rtl8169_private *tp)\n \tRTL_W16(tp, RxMaxSize, R8169_RX_BUF_SIZE + 1);\n }\n \n+static void rtl8169_store_rss_key(struct rtl8169_private *tp)\n+{\n+\tu8 *rss_key = tp-\u003erss_data-\u003erss_key;\n+\tconst u16 rss_key_reg = RSS_KEY_REG;\n+\n+\t/* Write redirection table to HW */\n+\tfor (int i = 0; i \u003c RTL_RSS_KEY_SIZE; i += sizeof(u32))\n+\t\tRTL_W32(tp, rss_key_reg + i, get_unaligned_le32(rss_key + i));\n+}\n+\n+static void rtl8169_store_reta(struct rtl8169_private *tp)\n+{\n+\tu8 *indir_tbl = tp-\u003erss_data-\u003erss_indir_tbl;\n+\tunsigned int i;\n+\n+\t/* Write redirection table to HW */\n+\tfor (i = 0; i \u003c tp-\u003erss_data-\u003ehw_supp_indir_tbl_entries; i += 4) {\n+\t\tu32 reta = get_unaligned_le32(\u0026indir_tbl[i]);\n+\n+\t\tRTL_W32(tp, RSS_INDIRECTION_TBL_REG + i, reta);\n+\t}\n+}\n+\n+static void rtl8169_set_rss_hash_opt(struct rtl8169_private *tp)\n+{\n+\tu32 rss_ctrl;\n+\n+\trss_ctrl = FIELD_PREP(RSS_CPU_NUM_MASK, ilog2(tp-\u003enum_rx_rings));\n+\n+\t/* Perform hash on these packet types */\n+\trss_ctrl |= RSS_CTRL_TCP_IPV4_SUPP |\n+\t\t    RSS_CTRL_IPV4_SUPP |\n+\t\t    RSS_CTRL_IPV6_SUPP |\n+\t\t    RSS_CTRL_IPV6_EXT_SUPP |\n+\t\t    RSS_CTRL_TCP_IPV6_SUPP |\n+\t\t    RSS_CTRL_TCP_IPV6_EXT_SUPP;\n+\n+\trss_ctrl |= FIELD_PREP(RSS_HASH_MASK,\n+\t\t\t       ilog2(tp-\u003erss_data-\u003ehw_supp_indir_tbl_entries));\n+\n+\tRTL_W32(tp, RSS_CTRL_8125, rss_ctrl);\n+}\n+\n+static void rtl_set_rss_config(struct rtl8169_private *tp)\n+{\n+\trtl8169_set_rss_hash_opt(tp);\n+\trtl8169_store_reta(tp);\n+\trtl8169_store_rss_key(tp);\n+}\n+\n static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)\n {\n+\tstruct rtl8169_rx_ring *ring = \u0026tp-\u003erx_ring[0];\n+\n \t/*\n \t * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh\n \t * register to be written before TxDescAddrLow to work.\n@@ -2811,8 +3006,18 @@ static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)\n \t */\n \tRTL_W32(tp, TxDescStartAddrHigh, ((u64) tp-\u003eTxPhyAddr) \u003e\u003e 32);\n \tRTL_W32(tp, TxDescStartAddrLow, ((u64) tp-\u003eTxPhyAddr) \u0026 DMA_BIT_MASK(32));\n-\tRTL_W32(tp, RxDescAddrHigh, ((u64) tp-\u003eRxPhyAddr) \u003e\u003e 32);\n-\tRTL_W32(tp, RxDescAddrLow, ((u64) tp-\u003eRxPhyAddr) \u0026 DMA_BIT_MASK(32));\n+\tRTL_W32(tp, RxDescAddrHigh, ((u64)ring-\u003erx_phy_addr) \u003e\u003e 32);\n+\tRTL_W32(tp, RxDescAddrLow,\n+\t\t((u64)ring-\u003erx_phy_addr) \u0026 DMA_BIT_MASK(32));\n+\n+\tfor (int i = 1; i \u003c tp-\u003enum_rx_rings; i++) {\n+\t\tunsigned int rdsar_reg = RDSAR_Q1_LOW + (i - 1) * 8;\n+\t\tstruct rtl8169_rx_ring *ring = \u0026tp-\u003erx_ring[i];\n+\n+\t\tRTL_W32(tp, rdsar_reg + 4, ((u64)ring-\u003erx_phy_addr \u003e\u003e 32));\n+\t\tRTL_W32(tp, rdsar_reg,\n+\t\t\t((u64)ring-\u003erx_phy_addr) \u0026 DMA_BIT_MASK(32));\n+\t}\n }\n \n static void rtl8169_set_magic_reg(struct rtl8169_private *tp)\n@@ -3858,6 +4063,27 @@ DECLARE_RTL_COND(rtl_mac_ocp_e00e_cond)\n \treturn r8168_mac_ocp_read(tp, 0xe00e) \u0026 BIT(13);\n }\n \n+static void rtl8125_set_rx_q_num(struct rtl8169_private *tp)\n+{\n+\tu16 rx_q_num;\n+\tu16 q_ctrl;\n+\n+\trx_q_num = ilog2(tp-\u003enum_rx_rings);\n+\tq_ctrl = RTL_R16(tp, Q_NUM_CTRL_8125);\n+\tq_ctrl \u0026= ~RTL_RX_Q_NUM_MASK;\n+\tq_ctrl |= FIELD_PREP(RTL_RX_Q_NUM_MASK, rx_q_num);\n+\tRTL_W16(tp, Q_NUM_CTRL_8125, q_ctrl);\n+}\n+\n+static void rtl8169_hw_enable_vec_mapping(struct rtl8169_private *tp)\n+{\n+\tu8 tmp;\n+\n+\ttmp = RTL_R8(tp, INT_CFG0_8125);\n+\ttmp |= INT_CFG0_ENABLE_8125;\n+\tRTL_W8(tp, INT_CFG0_8125, tmp);\n+}\n+\n static void rtl_hw_start_8125_common(struct rtl8169_private *tp)\n {\n \trtl_pcie_state_l2l3_disable(tp);\n@@ -3866,6 +4092,9 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp)\n \tRTL_W32(tp, RSS_CTRL_8125, 0);\n \tRTL_W16(tp, Q_NUM_CTRL_8125, 0);\n \n+\tif (tp-\u003eirq_nvecs \u003e 1)\n+\t\trtl8169_hw_enable_vec_mapping(tp);\n+\n \t/* disable UPS */\n \tr8168_mac_ocp_modify(tp, 0xd40a, 0x0010, 0x0000);\n \n@@ -3885,6 +4114,13 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp)\n \t    tp-\u003emac_version == RTL_GIGA_MAC_VER_80)\n \t\tRTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) \u0026 ~0x02);\n \n+\t/* enable rx descriptor type v4 and set queue num for rss */\n+\tif (tp-\u003enum_rx_rings \u003e 1) {\n+\t\trtl8125_set_rx_q_num(tp);\n+\t\tRTL_W8(tp, RTL_DESC_TYPE_CTRL,\n+\t\t       RTL_R8(tp, RTL_DESC_TYPE_CTRL) | RTL_DESC_TYPE_RSS);\n+\t}\n+\n \tif (tp-\u003emac_version == RTL_GIGA_MAC_VER_80)\n \t\tr8168_mac_ocp_modify(tp, 0xe614, 0x0f00, 0x0f00);\n \telse if (tp-\u003emac_version == RTL_GIGA_MAC_VER_70)\n@@ -4121,6 +4357,12 @@ static void rtl_hw_start(struct  rtl8169_private *tp)\n \trtl_hw_aspm_clkreq_enable(tp, true);\n \trtl_set_rx_max_size(tp);\n \trtl_set_rx_tx_desc_registers(tp);\n+\tif (rtl_is_8125(tp)) {\n+\t\tif (tp-\u003enum_rx_rings \u003e 1)\n+\t\t\trtl_set_rss_config(tp);\n+\t\telse\n+\t\t\tRTL_W32(tp, RSS_CTRL_8125, 0x00);\n+\t}\n \trtl_lock_config_regs(tp);\n \n \trtl_jumbo_config(tp);\n@@ -4148,19 +4390,32 @@ static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)\n \treturn 0;\n }\n \n-static void rtl8169_mark_to_asic(struct RxDesc *desc)\n+static void rtl8169_mark_to_asic(struct rtl8169_private *tp, struct RxDesc *desc)\n {\n-\tu32 eor = le32_to_cpu(desc-\u003eopts1) \u0026 RingEnd;\n+\tu32 eor;\n \n-\tdesc-\u003eopts2 = 0;\n-\t/* Force memory writes to complete before releasing descriptor */\n-\tdma_wmb();\n-\tWRITE_ONCE(desc-\u003eopts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));\n+\tswitch (tp-\u003einit_rx_desc_type) {\n+\tcase RX_DESC_TYPE_RSS:\n+\t\teor = le32_to_cpu(desc-\u003erss_opts1) \u0026 RingEnd;\n+\t\tdesc-\u003erss_opts2 = cpu_to_le32(0);\n+\t\t/* Force memory writes to complete before releasing descriptor */\n+\t\tdma_wmb();\n+\t\tWRITE_ONCE(desc-\u003erss_opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));\n+\t\tbreak;\n+\tdefault:\n+\t\teor = le32_to_cpu(desc-\u003eopts1) \u0026 RingEnd;\n+\t\tdesc-\u003eopts2 = cpu_to_le32(0);\n+\t\t/* Force memory writes to complete before releasing descriptor */\n+\t\tdma_wmb();\n+\t\tWRITE_ONCE(desc-\u003eopts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));\n+\t\tbreak;\n+\t}\n }\n \n static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,\n-\t\t\t\t\t  struct RxDesc *desc)\n+\t\t\t\t\t  struct rtl8169_rx_ring *ring, unsigned int index)\n {\n+\tstruct RxDesc *desc = ring-\u003erx_desc_array + index;\n \tstruct device *d = tp_to_dev(tp);\n \tint node = dev_to_node(d);\n \tdma_addr_t mapping;\n@@ -4177,56 +4432,128 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,\n \t\treturn NULL;\n \t}\n \n-\tdesc-\u003eaddr = cpu_to_le64(mapping);\n-\trtl8169_mark_to_asic(desc);\n+\tring-\u003erx_desc_phy_addr[index] = mapping;\n+\tif (tp-\u003einit_rx_desc_type == RX_DESC_TYPE_RSS)\n+\t\tdesc-\u003erss_addr = cpu_to_le64(mapping);\n+\telse\n+\t\tdesc-\u003eaddr = cpu_to_le64(mapping);\n+\trtl8169_mark_to_asic(tp, desc);\n \n \treturn data;\n }\n \n-static void rtl8169_rx_clear(struct rtl8169_private *tp)\n+static void rtl8169_rx_clear(struct rtl8169_private *tp,\n+\t\t\t     struct rtl8169_rx_ring *ring)\n {\n \tint i;\n \n-\tfor (i = 0; i \u003c NUM_RX_DESC \u0026\u0026 tp-\u003eRx_databuff[i]; i++) {\n+\tfor (i = 0; i \u003c NUM_RX_DESC \u0026\u0026 ring-\u003erx_databuff[i]; i++) {\n \t\tdma_unmap_page(tp_to_dev(tp),\n-\t\t\t       le64_to_cpu(tp-\u003eRxDescArray[i].addr),\n+\t\t\t       ring-\u003erx_desc_phy_addr[i],\n \t\t\t       R8169_RX_BUF_SIZE, DMA_FROM_DEVICE);\n-\t\t__free_pages(tp-\u003eRx_databuff[i], get_order(R8169_RX_BUF_SIZE));\n-\t\ttp-\u003eRx_databuff[i] = NULL;\n-\t\ttp-\u003eRxDescArray[i].addr = 0;\n-\t\ttp-\u003eRxDescArray[i].opts1 = 0;\n+\t\t__free_pages(ring-\u003erx_databuff[i], get_order(R8169_RX_BUF_SIZE));\n+\t\tring-\u003erx_databuff[i] = NULL;\n+\t\tring-\u003erx_desc_phy_addr[i] = 0;\n+\t\tif (tp-\u003einit_rx_desc_type == RX_DESC_TYPE_RSS) {\n+\t\t\tring-\u003erx_desc_array[i].rss_addr = 0;\n+\t\t\tring-\u003erx_desc_array[i].rss_opts1 = 0;\n+\t\t} else {\n+\t\t\tring-\u003erx_desc_array[i].addr = 0;\n+\t\t\tring-\u003erx_desc_array[i].opts1 = 0;\n+\t\t}\n+\t}\n+}\n+\n+static void rtl8169_mark_as_last_descriptor(struct rtl8169_private *tp, struct RxDesc *desc)\n+{\n+\tswitch (tp-\u003einit_rx_desc_type) {\n+\tcase RX_DESC_TYPE_RSS:\n+\t\tdesc-\u003erss_opts1 |= cpu_to_le32(RingEnd);\n+\t\tbreak;\n+\tdefault:\n+\t\tdesc-\u003eopts1 |= cpu_to_le32(RingEnd);\n+\t\tbreak;\n \t}\n }\n \n-static int rtl8169_rx_fill(struct rtl8169_private *tp)\n+static int rtl8169_rx_fill(struct rtl8169_private *tp, struct rtl8169_rx_ring *ring)\n {\n \tint i;\n \n \tfor (i = 0; i \u003c NUM_RX_DESC; i++) {\n \t\tstruct page *data;\n \n-\t\tdata = rtl8169_alloc_rx_data(tp, tp-\u003eRxDescArray + i);\n+\t\tdata = rtl8169_alloc_rx_data(tp, ring, i);\n \t\tif (!data) {\n-\t\t\trtl8169_rx_clear(tp);\n+\t\t\trtl8169_rx_clear(tp, ring);\n \t\t\treturn -ENOMEM;\n \t\t}\n-\t\ttp-\u003eRx_databuff[i] = data;\n+\t\tring-\u003erx_databuff[i] = data;\n \t}\n \n \t/* mark as last descriptor in the ring */\n-\ttp-\u003eRxDescArray[NUM_RX_DESC - 1].opts1 |= cpu_to_le32(RingEnd);\n+\trtl8169_mark_as_last_descriptor(tp, \u0026ring-\u003erx_desc_array[NUM_RX_DESC - 1]);\n \n \treturn 0;\n }\n \n+static int rtl8169_alloc_rx_desc(struct rtl8169_private *tp)\n+{\n+\tstruct pci_dev *pdev = tp-\u003epci_dev;\n+\tstruct rtl8169_rx_ring *ring;\n+\n+\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++) {\n+\t\tring = \u0026tp-\u003erx_ring[i];\n+\t\tring-\u003erx_desc_array = dma_alloc_coherent(\u0026pdev-\u003edev,\n+\t\t\t\t\t\t\t R8169_RX_RING_BYTES,\n+\t\t\t\t\t\t\t \u0026ring-\u003erx_phy_addr,\n+\t\t\t\t\t\t\t GFP_KERNEL);\n+\t\tif (!ring-\u003erx_desc_array)\n+\t\t\treturn -ENOMEM;\n+\t}\n+\treturn 0;\n+}\n+\n+static void rtl8169_free_rx_desc(struct rtl8169_private *tp)\n+{\n+\tstruct pci_dev *pdev = tp-\u003epci_dev;\n+\tstruct rtl8169_rx_ring *ring;\n+\n+\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++) {\n+\t\tring = \u0026tp-\u003erx_ring[i];\n+\t\tif (ring-\u003erx_desc_array) {\n+\t\t\tdma_free_coherent(\u0026pdev-\u003edev,\n+\t\t\t\t\t  R8169_RX_RING_BYTES,\n+\t\t\t\t\t  ring-\u003erx_desc_array,\n+\t\t\t\t\t  ring-\u003erx_phy_addr);\n+\t\t\tring-\u003erx_desc_array = NULL;\n+\t\t}\n+\t}\n+}\n+\n static int rtl8169_init_ring(struct rtl8169_private *tp)\n {\n+\tint i, ret;\n+\n \trtl8169_init_ring_indexes(tp);\n+\trtl8169_rx_desc_init(tp);\n \n \tmemset(tp-\u003etx_skb, 0, sizeof(tp-\u003etx_skb));\n-\tmemset(tp-\u003eRx_databuff, 0, sizeof(tp-\u003eRx_databuff));\n \n-\treturn rtl8169_rx_fill(tp);\n+\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++) {\n+\t\tstruct rtl8169_rx_ring *ring = \u0026tp-\u003erx_ring[i];\n+\n+\t\tmemset(ring-\u003erx_databuff, 0, sizeof(ring-\u003erx_databuff));\n+\t\tret = rtl8169_rx_fill(tp, ring);\n+\t\tif (ret \u003c 0)\n+\t\t\tgoto err_clear;\n+\t}\n+\treturn 0;\n+\n+err_clear:\n+\twhile (--i \u003e= 0)\n+\t\trtl8169_rx_clear(tp, \u0026tp-\u003erx_ring[i]);\n+\treturn ret;\n }\n \n static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp, unsigned int entry)\n@@ -4266,9 +4593,25 @@ static void rtl8169_tx_clear(struct rtl8169_private *tp)\n \tnetdev_reset_queue(tp-\u003edev);\n }\n \n+static void rtl8169_napi_disable(struct rtl8169_private *tp)\n+{\n+\tint napi_num = min(tp-\u003eirq_nvecs, R8169_MAX_QUEUES);\n+\n+\tfor (int i = 0; i \u003c napi_num; i++)\n+\t\tnapi_disable(\u0026tp-\u003ertl8169_napi[i]);\n+}\n+\n+static void rtl8169_napi_enable(struct rtl8169_private *tp)\n+{\n+\tint napi_num = min(tp-\u003eirq_nvecs, R8169_MAX_QUEUES);\n+\n+\tfor (int i = 0; i \u003c napi_num; i++)\n+\t\tnapi_enable(\u0026tp-\u003ertl8169_napi[i]);\n+}\n+\n static void rtl8169_cleanup(struct rtl8169_private *tp)\n {\n-\tnapi_disable(\u0026tp-\u003enapi);\n+\trtl8169_napi_disable(tp);\n \n \t/* Give a racing hard_start_xmit a few cycles to complete. */\n \tsynchronize_net();\n@@ -4303,18 +4646,30 @@ static void rtl8169_cleanup(struct rtl8169_private *tp)\n \trtl8169_init_ring_indexes(tp);\n }\n \n-static void rtl_reset_work(struct rtl8169_private *tp)\n+static void rtl8169_rx_desc_reset(struct rtl8169_private *tp)\n {\n-\tint i;\n+\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++) {\n+\t\tstruct rtl8169_rx_ring *ring = \u0026tp-\u003erx_ring[i];\n+\n+\t\tfor (int j = 0; j \u003c NUM_RX_DESC; j++) {\n+\t\t\tdma_addr_t phy_addr = ring-\u003erx_desc_phy_addr[j];\n+\n+\t\t\tif (tp-\u003einit_rx_desc_type == RX_DESC_TYPE_RSS)\n+\t\t\t\tring-\u003erx_desc_array[j].rss_addr = cpu_to_le64(phy_addr);\n+\t\t\trtl8169_mark_to_asic(tp, ring-\u003erx_desc_array + j);\n+\t\t}\n+\t}\n+}\n \n+static void rtl_reset_work(struct rtl8169_private *tp)\n+{\n \tnetif_stop_queue(tp-\u003edev);\n \n \trtl8169_cleanup(tp);\n \n-\tfor (i = 0; i \u003c NUM_RX_DESC; i++)\n-\t\trtl8169_mark_to_asic(tp-\u003eRxDescArray + i);\n+\trtl8169_rx_desc_reset(tp);\n \n-\tnapi_enable(\u0026tp-\u003enapi);\n+\trtl8169_napi_enable(tp);\n \trtl_hw_start(tp);\n }\n \n@@ -4758,30 +5113,111 @@ static inline int rtl8169_fragmented_frame(u32 status)\n \treturn (status \u0026 (FirstFrag | LastFrag)) != (FirstFrag | LastFrag);\n }\n \n-static inline void rtl8169_rx_csum(struct sk_buff *skb, u32 opts1)\n+static inline void rtl8169_rx_hash(struct rtl8169_private *tp,\n+\t\t\t\t   struct RxDesc *desc,\n+\t\t\t\t   struct sk_buff *skb)\n+{\n+\tu32 rss_header_info;\n+\tu32 hash_val;\n+\n+\tif (!(tp-\u003edev-\u003efeatures \u0026 NETIF_F_RXHASH))\n+\t\treturn;\n+\n+\trss_header_info = le32_to_cpu(desc-\u003erss_dword.rss_info);\n+\n+\tif (!(rss_header_info \u0026 RXS_RSS_L3_TYPE_MASK))\n+\t\treturn;\n+\n+\thash_val = le32_to_cpu(desc-\u003erss_dword.rss_result);\n+\n+\tskb_set_hash(skb, hash_val,\n+\t\t     (RXS_RSS_L4_TYPE_MASK \u0026 rss_header_info) ?\n+\t\t     PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3);\n+}\n+\n+static inline void rtl8169_rx_csum(struct rtl8169_private *tp,\n+\t\t\t\t   struct sk_buff *skb,\n+\t\t\t\t   u32 opts1)\n {\n-\tu32 status = opts1 \u0026 (RxProtoMask | RxCSFailMask);\n+\tbool csum_ok = false;\n \n-\tif (status == RxProtoTCP || status == RxProtoUDP)\n+\tswitch (tp-\u003einit_rx_desc_type) {\n+\tcase RX_DESC_TYPE_RSS:\n+\t\tif (((opts1 \u0026 RX_TCPT_DESC_RSS) \u0026\u0026 !(opts1 \u0026 RX_TCPF_DESC_RSS)) ||\n+\t\t    ((opts1 \u0026 RX_UDPT_DESC_RSS) \u0026\u0026 !(opts1 \u0026 RX_UDPF_DESC_RSS)))\n+\t\t\tcsum_ok = true;\n+\t\tbreak;\n+\tdefault: {\n+\t\tu32 status = opts1 \u0026 (RxProtoMask | RxCSFailMask);\n+\n+\t\tif (status == RxProtoTCP || status == RxProtoUDP)\n+\t\t\tcsum_ok = true;\n+\t\tbreak;\n+\t}\n+\t}\n+\n+\tif (csum_ok)\n \t\tskb-\u003eip_summed = CHECKSUM_UNNECESSARY;\n \telse\n \t\tskb_checksum_none_assert(skb);\n }\n \n-static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget)\n+static __le32 rtl8169_rx_desc_opts1(struct rtl8169_private *tp, struct RxDesc *desc)\n+{\n+\tswitch (tp-\u003einit_rx_desc_type) {\n+\tcase RX_DESC_TYPE_RSS:\n+\t\treturn READ_ONCE(desc-\u003erss_opts1);\n+\tdefault:\n+\t\treturn READ_ONCE(desc-\u003eopts1);\n+\t}\n+}\n+\n+static bool rtl8169_check_rx_desc_error(struct rtl8169_rx_ring *ring,\n+\t\t\t\t\tstruct rtl8169_private *tp,\n+\t\t\t\t\tu32 status)\n+{\n+\tswitch (tp-\u003einit_rx_desc_type) {\n+\tcase RX_DESC_TYPE_RSS:\n+\t\tif (unlikely(status \u0026 RX_RES_RSS)) {\n+\t\t\tu64_stats_update_begin(\u0026ring-\u003estats.syncp);\n+\t\t\tif (status \u0026 RX_RUNT_RSS)\n+\t\t\t\tring-\u003estats.rx_length_errors++;\n+\t\t\tif (status \u0026 RX_CRC_RSS)\n+\t\t\t\tring-\u003estats.rx_crc_errors++;\n+\t\t\tu64_stats_update_end(\u0026ring-\u003estats.syncp);\n+\t\t\treturn true;\n+\t\t}\n+\t\tbreak;\n+\tdefault:\n+\t\tif (unlikely(status \u0026 RxRES)) {\n+\t\t\tu64_stats_update_begin(\u0026ring-\u003estats.syncp);\n+\t\t\tif (status \u0026 (RxRWT | RxRUNT))\n+\t\t\t\tring-\u003estats.rx_length_errors++;\n+\t\t\tif (status \u0026 RxCRC)\n+\t\t\t\tring-\u003estats.rx_crc_errors++;\n+\t\t\tu64_stats_update_end(\u0026ring-\u003estats.syncp);\n+\t\t\treturn true;\n+\t\t}\n+\t}\n+\treturn false;\n+}\n+\n+static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,\n+\t\t  struct rtl8169_rx_ring *ring, int budget,\n+\t\t  struct napi_struct *napi)\n {\n \tstruct device *d = tp_to_dev(tp);\n \tint count;\n \n-\tfor (count = 0; count \u003c budget; count++, tp-\u003ecur_rx++) {\n-\t\tunsigned int pkt_size, entry = tp-\u003ecur_rx % NUM_RX_DESC;\n-\t\tstruct RxDesc *desc = tp-\u003eRxDescArray + entry;\n+\tfor (count = 0; count \u003c budget; count++, ring-\u003ecur_rx++) {\n+\t\tunsigned int pkt_size, entry = ring-\u003ecur_rx % NUM_RX_DESC;\n+\t\tstruct RxDesc *desc = ring-\u003erx_desc_array + entry;\n \t\tstruct sk_buff *skb;\n \t\tconst void *rx_buf;\n \t\tdma_addr_t addr;\n \t\tu32 status;\n \n-\t\tstatus = le32_to_cpu(READ_ONCE(desc-\u003eopts1));\n+\t\tstatus = le32_to_cpu(rtl8169_rx_desc_opts1(tp, desc));\n \t\tif (status \u0026 DescOwn)\n \t\t\tbreak;\n \n@@ -4791,20 +5227,24 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget\n \t\t */\n \t\tdma_rmb();\n \n-\t\tif (unlikely(status \u0026 RxRES)) {\n+\t\tif (rtl8169_check_rx_desc_error(ring, tp, status)) {\n \t\t\tif (net_ratelimit())\n \t\t\t\tnetdev_warn(dev, \"Rx ERROR. status = %08x\\n\",\n \t\t\t\t\t    status);\n-\t\t\tdev-\u003estats.rx_errors++;\n-\t\t\tif (status \u0026 (RxRWT | RxRUNT))\n-\t\t\t\tdev-\u003estats.rx_length_errors++;\n-\t\t\tif (status \u0026 RxCRC)\n-\t\t\t\tdev-\u003estats.rx_crc_errors++;\n+\t\t\tu64_stats_update_begin(\u0026ring-\u003estats.syncp);\n+\t\t\tring-\u003estats.rx_errors++;\n+\t\t\tu64_stats_update_end(\u0026ring-\u003estats.syncp);\n \n \t\t\tif (!(dev-\u003efeatures \u0026 NETIF_F_RXALL))\n \t\t\t\tgoto release_descriptor;\n-\t\t\telse if (status \u0026 RxRWT || !(status \u0026 (RxRUNT | RxCRC)))\n-\t\t\t\tgoto release_descriptor;\n+\n+\t\t\tif (tp-\u003einit_rx_desc_type == RX_DESC_TYPE_DEFAULT) {\n+\t\t\t\tif (status \u0026 RxRWT || !(status \u0026 (RxRUNT | RxCRC)))\n+\t\t\t\t\tgoto release_descriptor;\n+\t\t\t} else {\n+\t\t\t\tif (!(status \u0026 (RXRUNT_RSS | RXCRC_RSS)))\n+\t\t\t\t\tgoto release_descriptor;\n+\t\t\t}\n \t\t}\n \n \t\tpkt_size = status \u0026 GENMASK(13, 0);\n@@ -4815,19 +5255,23 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget\n \t\t * They are seen as a symptom of over-mtu sized frames.\n \t\t */\n \t\tif (unlikely(rtl8169_fragmented_frame(status))) {\n-\t\t\tdev-\u003estats.rx_dropped++;\n-\t\t\tdev-\u003estats.rx_length_errors++;\n+\t\t\tu64_stats_update_begin(\u0026ring-\u003estats.syncp);\n+\t\t\tring-\u003estats.rx_dropped++;\n+\t\t\tring-\u003estats.rx_length_errors++;\n+\t\t\tu64_stats_update_end(\u0026ring-\u003estats.syncp);\n \t\t\tgoto release_descriptor;\n \t\t}\n \n-\t\tskb = napi_alloc_skb(\u0026tp-\u003enapi, pkt_size);\n+\t\tskb = napi_alloc_skb(napi, pkt_size);\n \t\tif (unlikely(!skb)) {\n-\t\t\tdev-\u003estats.rx_dropped++;\n+\t\t\tu64_stats_update_begin(\u0026ring-\u003estats.syncp);\n+\t\t\tring-\u003estats.rx_dropped++;\n+\t\t\tu64_stats_update_end(\u0026ring-\u003estats.syncp);\n \t\t\tgoto release_descriptor;\n \t\t}\n \n-\t\taddr = le64_to_cpu(desc-\u003eaddr);\n-\t\trx_buf = page_address(tp-\u003eRx_databuff[entry]);\n+\t\taddr = ring-\u003erx_desc_phy_addr[entry];\n+\t\trx_buf = page_address(ring-\u003erx_databuff[entry]);\n \n \t\tdma_sync_single_for_cpu(d, addr, pkt_size, DMA_FROM_DEVICE);\n \t\tprefetch(rx_buf);\n@@ -4836,19 +5280,26 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget\n \t\tskb-\u003elen = pkt_size;\n \t\tdma_sync_single_for_device(d, addr, pkt_size, DMA_FROM_DEVICE);\n \n-\t\trtl8169_rx_csum(skb, status);\n+\t\tif (tp-\u003enum_rx_rings \u003e 1)\n+\t\t\trtl8169_rx_hash(tp, desc, skb);\n+\t\trtl8169_rx_csum(tp, skb, status);\n \t\tskb-\u003eprotocol = eth_type_trans(skb, dev);\n \n-\t\trtl8169_rx_vlan_tag(desc, skb);\n+\t\trtl8169_rx_vlan_tag(tp, desc, skb);\n \n-\t\tif (skb-\u003epkt_type == PACKET_MULTICAST)\n-\t\t\tdev-\u003estats.multicast++;\n+\t\tif (skb-\u003epkt_type == PACKET_MULTICAST) {\n+\t\t\tu64_stats_update_begin(\u0026ring-\u003estats.syncp);\n+\t\t\tring-\u003estats.multicast++;\n+\t\t\tu64_stats_update_end(\u0026ring-\u003estats.syncp);\n+\t\t}\n \n-\t\tnapi_gro_receive(\u0026tp-\u003enapi, skb);\n+\t\tnapi_gro_receive(napi, skb);\n \n \t\tdev_sw_netstats_rx_add(dev, pkt_size);\n release_descriptor:\n-\t\trtl8169_mark_to_asic(desc);\n+\t\tif (tp-\u003einit_rx_desc_type == RX_DESC_TYPE_RSS)\n+\t\t\tdesc-\u003erss_addr = cpu_to_le64(ring-\u003erx_desc_phy_addr[entry]);\n+\t\trtl8169_mark_to_asic(tp, desc);\n \t}\n \n \treturn count;\n@@ -4856,8 +5307,12 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget\n \n static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)\n {\n-\tstruct rtl8169_private *tp = dev_instance;\n-\tu32 status = rtl_get_events(tp);\n+\tstruct napi_struct *napi = dev_instance;\n+\tstruct rtl8169_private *tp;\n+\tu32 status;\n+\n+\ttp = netdev_priv(napi-\u003edev);\n+\tstatus = rtl_get_events(tp);\n \n \tif ((status \u0026 0xffff) == 0xffff || !(status \u0026 tp-\u003eirq_mask))\n \t\treturn IRQ_NONE;\n@@ -4873,13 +5328,114 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)\n \t\tphy_mac_interrupt(tp-\u003ephydev);\n \n \trtl_irq_disable(tp);\n-\tnapi_schedule(\u0026tp-\u003enapi);\n+\tnapi_schedule(napi);\n out:\n \trtl_ack_events(tp, status);\n \n \treturn IRQ_HANDLED;\n }\n \n+static void rtl8169_free_one_irq(struct rtl8169_private *tp, int i)\n+{\n+\tif (tp-\u003eirq_nvecs \u003e 1) {\n+\t\tif (i \u003c R8169_MAX_QUEUES)\n+\t\t\tpci_free_irq(tp-\u003epci_dev, i, \u0026tp-\u003ertl8169_napi[i]);\n+\t\telse if (i == MSIX_ID_VEC_MAP_LINKCHG)\n+\t\t\tpci_free_irq(tp-\u003epci_dev, i, tp);\n+\t} else {\n+\t\tpci_free_irq(tp-\u003epci_dev, i, \u0026tp-\u003ertl8169_napi[i]);\n+\t}\n+}\n+\n+static void rtl8169_free_irq(struct rtl8169_private *tp)\n+{\n+\tfor (int i = 0; i \u003c tp-\u003eirq_nvecs; i++)\n+\t\trtl8169_free_one_irq(tp, i);\n+}\n+\n+static void rtl8169_disable_hw_interrupt_msix(struct rtl8169_private *tp,\n+\t\t\t\t\t      int message_id)\n+{\n+\tRTL_W32(tp, IMR_CLEAR_VEC_MAP_REG, BIT(message_id));\n+}\n+\n+static void rtl8169_clear_hw_isr(struct rtl8169_private *tp, int message_id)\n+{\n+\tRTL_W32(tp, ISR_VEC_MAP_REG, BIT(message_id));\n+}\n+\n+static void rtl8169_enable_hw_interrupt_msix(struct rtl8169_private *tp,\n+\t\t\t\t\t     int message_id)\n+{\n+\tRTL_W32(tp, IMR_SET_VEC_MAP_REG, BIT(message_id));\n+}\n+\n+static irqreturn_t rtl8169_interrupt_msix(int irq, void *dev_instance)\n+{\n+\tstruct napi_struct *napi = dev_instance;\n+\tstruct net_device *dev = napi-\u003edev;\n+\tstruct rtl8169_private *tp;\n+\tint message_id;\n+\n+\ttp = netdev_priv(dev);\n+\tmessage_id = napi - tp-\u003ertl8169_napi;\n+\n+\trtl8169_disable_hw_interrupt_msix(tp, message_id);\n+\trtl8169_clear_hw_isr(tp, message_id);\n+\n+\tnapi_schedule(napi);\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+static irqreturn_t rtl8169_interrupt_other(int irq, void *dev_instance)\n+{\n+\tstruct rtl8169_private *tp = dev_instance;\n+\n+\trtl8169_clear_hw_isr(tp, MSIX_ID_VEC_MAP_LINKCHG);\n+\tphy_mac_interrupt(tp-\u003ephydev);\n+\treturn IRQ_HANDLED;\n+}\n+\n+static int rtl8169_request_irq(struct rtl8169_private *tp)\n+{\n+\tstruct net_device *dev = tp-\u003edev;\n+\tstruct napi_struct *napi;\n+\tint i, rc;\n+\n+\tfor (i = 0; i \u003c tp-\u003eirq_nvecs; i++) {\n+\t\tnapi = \u0026tp-\u003ertl8169_napi[i];\n+\t\tif (tp-\u003eirq_nvecs \u003e 1) {\n+\t\t\tif (i \u003c R8169_MAX_QUEUES)\n+\t\t\t\trc = pci_request_irq(tp-\u003epci_dev, i,\n+\t\t\t\t\t\t     rtl8169_interrupt_msix,\n+\t\t\t\t\t\t     NULL, napi, \"%s-%d\",\n+\t\t\t\t\t\t     dev-\u003ename, i);\n+\t\t\telse if (i == MSIX_ID_VEC_MAP_LINKCHG)\n+\t\t\t\trc = pci_request_irq(tp-\u003epci_dev, i,\n+\t\t\t\t\t\t     rtl8169_interrupt_other,\n+\t\t\t\t\t\t     NULL, tp, \"%s-%d\",\n+\t\t\t\t\t\t     dev-\u003ename, i);\n+\t\t\telse\n+\t\t\t\tcontinue;\n+\t\t} else {\n+\t\t\trc = pci_request_irq(tp-\u003epci_dev, i,\n+\t\t\t\t\t     rtl8169_interrupt,\n+\t\t\t\t\t     NULL, napi, \"%s-%d\",\n+\t\t\t\t\t     dev-\u003ename, i);\n+\t\t}\n+\n+\t\tif (rc)\n+\t\t\tgoto free_irq;\n+\t}\n+\treturn 0;\n+\n+free_irq:\n+\twhile (--i \u003e= 0)\n+\t\trtl8169_free_one_irq(tp, i);\n+\treturn rc;\n+}\n+\n static void rtl_task(struct work_struct *work)\n {\n \tstruct rtl8169_private *tp =\n@@ -4914,13 +5470,14 @@ static void rtl_task(struct work_struct *work)\n \n static int rtl8169_poll(struct napi_struct *napi, int budget)\n {\n-\tstruct rtl8169_private *tp = container_of(napi, struct rtl8169_private, napi);\n-\tstruct net_device *dev = tp-\u003edev;\n-\tint work_done;\n+\tstruct rtl8169_private *tp = netdev_priv(napi-\u003edev);\n+\tstruct net_device *dev = napi-\u003edev;\n+\tint work_done = 0;\n \n \trtl_tx(dev, tp, budget);\n \n-\twork_done = rtl_rx(dev, tp, budget);\n+\t/* rtl8169_poll() is used only when there is a single RX ring. */\n+\twork_done = rtl_rx(dev, tp, \u0026tp-\u003erx_ring[0], budget, napi);\n \n \tif (work_done \u003c budget \u0026\u0026 napi_complete_done(napi, work_done))\n \t\trtl_irq_enable(tp);\n@@ -5035,7 +5592,7 @@ static void rtl8169_up(struct rtl8169_private *tp)\n \tphy_init_hw(tp-\u003ephydev);\n \tphy_resume(tp-\u003ephydev);\n \trtl8169_init_phy(tp);\n-\tnapi_enable(\u0026tp-\u003enapi);\n+\trtl8169_napi_enable(tp);\n \tenable_work(\u0026tp-\u003ewk.work);\n \trtl_reset_work(tp);\n \n@@ -5051,18 +5608,17 @@ static int rtl8169_close(struct net_device *dev)\n \n \tnetif_stop_queue(dev);\n \trtl8169_down(tp);\n-\trtl8169_rx_clear(tp);\n+\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++)\n+\t\trtl8169_rx_clear(tp, \u0026tp-\u003erx_ring[i]);\n \n-\tfree_irq(tp-\u003eirq, tp);\n+\trtl8169_free_irq(tp);\n \n \tphy_disconnect(tp-\u003ephydev);\n \n-\tdma_free_coherent(\u0026pdev-\u003edev, R8169_RX_RING_BYTES, tp-\u003eRxDescArray,\n-\t\t\t  tp-\u003eRxPhyAddr);\n \tdma_free_coherent(\u0026pdev-\u003edev, R8169_TX_RING_BYTES, tp-\u003eTxDescArray,\n \t\t\t  tp-\u003eTxPhyAddr);\n \ttp-\u003eTxDescArray = NULL;\n-\ttp-\u003eRxDescArray = NULL;\n+\trtl8169_free_rx_desc(tp);\n \n \tpm_runtime_put_sync(\u0026pdev-\u003edev);\n \n@@ -5074,7 +5630,14 @@ static void rtl8169_netpoll(struct net_device *dev)\n {\n \tstruct rtl8169_private *tp = netdev_priv(dev);\n \n-\trtl8169_interrupt(tp-\u003eirq, tp);\n+\tfor (int i = 0; i \u003c tp-\u003eirq_nvecs; i++) {\n+\t\tif (tp-\u003eirq_nvecs \u003e 1)\n+\t\t\trtl8169_interrupt_msix(pci_irq_vector(tp-\u003epci_dev, i),\n+\t\t\t\t\t       \u0026tp-\u003ertl8169_napi[i]);\n+\t\telse\n+\t\t\trtl8169_interrupt(pci_irq_vector(tp-\u003epci_dev, i),\n+\t\t\t\t\t  \u0026tp-\u003ertl8169_napi[i]);\n+\t}\n }\n #endif\n \n@@ -5082,7 +5645,6 @@ static int rtl_open(struct net_device *dev)\n {\n \tstruct rtl8169_private *tp = netdev_priv(dev);\n \tstruct pci_dev *pdev = tp-\u003epci_dev;\n-\tunsigned long irqflags;\n \tint retval = -ENOMEM;\n \n \tpm_runtime_get_sync(\u0026pdev-\u003edev);\n@@ -5096,10 +5658,8 @@ static int rtl_open(struct net_device *dev)\n \tif (!tp-\u003eTxDescArray)\n \t\tgoto out;\n \n-\ttp-\u003eRxDescArray = dma_alloc_coherent(\u0026pdev-\u003edev, R8169_RX_RING_BYTES,\n-\t\t\t\t\t     \u0026tp-\u003eRxPhyAddr, GFP_KERNEL);\n-\tif (!tp-\u003eRxDescArray)\n-\t\tgoto err_free_tx_0;\n+\tif (rtl8169_alloc_rx_desc(tp) \u003c 0)\n+\t\tgoto err_free_rx_1;\n \n \tretval = rtl8169_init_ring(tp);\n \tif (retval \u003c 0)\n@@ -5107,8 +5667,7 @@ static int rtl_open(struct net_device *dev)\n \n \trtl_request_firmware(tp);\n \n-\tirqflags = pci_dev_msi_enabled(pdev) ? IRQF_NO_THREAD : IRQF_SHARED;\n-\tretval = request_irq(tp-\u003eirq, rtl8169_interrupt, irqflags, dev-\u003ename, tp);\n+\tretval = rtl8169_request_irq(tp);\n \tif (retval \u003c 0)\n \t\tgoto err_release_fw_2;\n \n@@ -5125,15 +5684,13 @@ static int rtl_open(struct net_device *dev)\n \treturn retval;\n \n err_free_irq:\n-\tfree_irq(tp-\u003eirq, tp);\n+\trtl8169_free_irq(tp);\n err_release_fw_2:\n \trtl_release_firmware(tp);\n-\trtl8169_rx_clear(tp);\n+\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++)\n+\t\trtl8169_rx_clear(tp, \u0026tp-\u003erx_ring[i]);\n err_free_rx_1:\n-\tdma_free_coherent(\u0026pdev-\u003edev, R8169_RX_RING_BYTES, tp-\u003eRxDescArray,\n-\t\t\t  tp-\u003eRxPhyAddr);\n-\ttp-\u003eRxDescArray = NULL;\n-err_free_tx_0:\n+\trtl8169_free_rx_desc(tp);\n \tdma_free_coherent(\u0026pdev-\u003edev, R8169_TX_RING_BYTES, tp-\u003eTxDescArray,\n \t\t\t  tp-\u003eTxPhyAddr);\n \ttp-\u003eTxDescArray = NULL;\n@@ -5152,6 +5709,27 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)\n \tnetdev_stats_to_stats64(stats, \u0026dev-\u003estats);\n \tdev_fetch_sw_netstats(stats, dev-\u003etstats);\n \n+\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++) {\n+\t\tu64 errors, dropped, length_errors, crc_errors, multicast;\n+\t\tstruct rtl8169_rx_ring *ring = \u0026tp-\u003erx_ring[i];\n+\t\tunsigned int start;\n+\n+\t\tdo {\n+\t\t\tstart = u64_stats_fetch_begin(\u0026ring-\u003estats.syncp);\n+\t\t\terrors = ring-\u003estats.rx_errors;\n+\t\t\tdropped = ring-\u003estats.rx_dropped;\n+\t\t\tlength_errors = ring-\u003estats.rx_length_errors;\n+\t\t\tcrc_errors = ring-\u003estats.rx_crc_errors;\n+\t\t\tmulticast = ring-\u003estats.multicast;\n+\t\t} while (u64_stats_fetch_retry(\u0026ring-\u003estats.syncp, start));\n+\n+\t\tstats-\u003erx_errors += errors;\n+\t\tstats-\u003erx_dropped += dropped;\n+\t\tstats-\u003erx_length_errors += length_errors;\n+\t\tstats-\u003erx_crc_errors += crc_errors;\n+\t\tstats-\u003emulticast += multicast;\n+\t}\n+\n \t/*\n \t * Fetch additional counter values missing in stats collected by driver\n \t * from tally counters.\n@@ -5275,6 +5853,16 @@ static void rtl_shutdown(struct pci_dev *pdev)\n \t\tpci_prepare_to_sleep(pdev);\n }\n \n+static void r8169_free_napi(struct rtl8169_private *tp)\n+{\n+\tint napi_num = min(tp-\u003eirq_nvecs, R8169_MAX_QUEUES);\n+\n+\tfor (int i = 0; i \u003c napi_num; i++)\n+\t\tnetif_napi_del(\u0026tp-\u003ertl8169_napi[i]);\n+\n+\tkfree(tp-\u003ertl8169_napi);\n+}\n+\n static void rtl_remove_one(struct pci_dev *pdev)\n {\n \tstruct rtl8169_private *tp = pci_get_drvdata(pdev);\n@@ -5289,6 +5877,9 @@ static void rtl_remove_one(struct pci_dev *pdev)\n \n \tunregister_netdev(tp-\u003edev);\n \n+\tr8169_free_napi(tp);\n+\tkfree(tp-\u003erx_ring);\n+\n \tif (tp-\u003edash_type != RTL_DASH_NONE)\n \t\trtl8168_driver_stop(tp);\n \n@@ -5320,15 +5911,49 @@ static const struct net_device_ops rtl_netdev_ops = {\n \n static void rtl_set_irq_mask(struct rtl8169_private *tp)\n {\n-\ttp-\u003eirq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;\n+\tif (tp-\u003eirq_nvecs \u003e 1) {\n+\t\ttp-\u003eirq_mask = ISRIMR_LINKCHG | ISRIMR_TOK_Q0;\n+\t\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++)\n+\t\t\ttp-\u003eirq_mask |= ISRIMR_ROK_Q0 \u003c\u003c i;\n+\t} else {\n+\t\ttp-\u003eirq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg;\n \n-\tif (tp-\u003emac_version \u003c= RTL_GIGA_MAC_VER_06)\n-\t\ttp-\u003eirq_mask |= SYSErr | RxFIFOOver;\n+\t\tif (tp-\u003emac_version \u003c= RTL_GIGA_MAC_VER_06)\n+\t\t\ttp-\u003eirq_mask |= SYSErr | RxFIFOOver;\n+\t}\n+}\n+\n+static int get_max_irq_nvecs(struct rtl8169_private *tp)\n+{\n+\tif (tp-\u003emac_version == RTL_GIGA_MAC_VER_80)\n+\t\treturn R8127_MAX_NUM_IRQVEC;\n+\treturn R8169_IRQ_DEFAULT;\n+}\n+\n+static int get_min_irq_nvecs(struct rtl8169_private *tp)\n+{\n+\tif (tp-\u003emac_version == RTL_GIGA_MAC_VER_80)\n+\t\treturn R8127_MIN_NUM_IRQVEC;\n+\treturn R8169_IRQ_DEFAULT;\n+}\n+\n+static void rtl8169_set_rx_ring_num(struct rtl8169_private *tp)\n+{\n+\tif (tp-\u003eirq_nvecs \u003e= get_min_irq_nvecs(tp)) {\n+\t\tunsigned int rss_queue_num = netif_get_num_default_rss_queues();\n+\n+\t\ttp-\u003enum_rx_rings = rounddown_pow_of_two(min(rss_queue_num,\n+\t\t\t\t\t\t\t    tp-\u003ehw_supp_num_rx_queues));\n+\t\tif (tp-\u003enum_rx_rings \u003e= 2)\n+\t\t\ttp-\u003einit_rx_desc_type = RX_DESC_TYPE_RSS;\n+\t}\n }\n \n static int rtl_alloc_irq(struct rtl8169_private *tp)\n {\n+\tstruct pci_dev *pdev = tp-\u003epci_dev;\n \tunsigned int flags;\n+\tint nvecs;\n \n \tswitch (tp-\u003emac_version) {\n \tcase RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06:\n@@ -5344,7 +5969,18 @@ static int rtl_alloc_irq(struct rtl8169_private *tp)\n \t\tbreak;\n \t}\n \n-\treturn pci_alloc_irq_vectors(tp-\u003epci_dev, 1, 1, flags);\n+\tnvecs = pci_alloc_irq_vectors(pdev, get_min_irq_nvecs(tp),\n+\t\t\t\t      get_max_irq_nvecs(tp), flags);\n+\n+\tif (nvecs \u003c 0)\n+\t\tnvecs = pci_alloc_irq_vectors(pdev, 1, 1, flags);\n+\n+\tif (nvecs \u003c 0)\n+\t\treturn nvecs;\n+\n+\ttp-\u003eirq_nvecs = nvecs;\n+\n+\treturn 0;\n }\n \n static void rtl_read_mac_address(struct rtl8169_private *tp,\n@@ -5599,6 +6235,107 @@ static bool rtl_aspm_is_safe(struct rtl8169_private *tp)\n \treturn false;\n }\n \n+static int rtl8169_poll_msix_rx(struct napi_struct *napi, int budget)\n+{\n+\tstruct net_device *dev = napi-\u003edev;\n+\tstruct rtl8169_private *tp;\n+\tint work_done = 0;\n+\tint message_id;\n+\n+\ttp = netdev_priv(dev);\n+\tmessage_id = napi - tp-\u003ertl8169_napi;\n+\n+\tif (message_id \u003c tp-\u003enum_rx_rings)\n+\t\twork_done += rtl_rx(dev, tp, \u0026tp-\u003erx_ring[message_id],\n+\t\t\t\t    budget, napi);\n+\n+\tif (work_done \u003c budget \u0026\u0026 napi_complete_done(napi, work_done))\n+\t\trtl8169_enable_hw_interrupt_msix(tp, message_id);\n+\n+\treturn work_done;\n+}\n+\n+static int rtl8169_poll_msix_tx(struct napi_struct *napi, int budget)\n+{\n+\tstruct net_device *dev = napi-\u003edev;\n+\tstruct rtl8169_private *tp;\n+\n+\ttp = netdev_priv(dev);\n+\n+\t/* Currently r8169 only supports a single Tx ring.\n+\t * Therefore, we don't need a per-ring Tx processing loop here.\n+\t */\n+\trtl_tx(dev, tp, budget);\n+\n+\tif (napi_complete_done(napi, 0))\n+\t\trtl8169_enable_hw_interrupt_msix(tp, (int)(napi - tp-\u003ertl8169_napi));\n+\n+\treturn 0;\n+}\n+\n+/* RTL8127 MSI-X vector layout:\n+ * Vectors 0 .. (RxQs - 1)\t\t: Rx Queues\n+ * Vectors RxQs .. (RxQs + TxQs - 1)\t: Tx Queues\n+ * NAPI is only allocated for data path\n+ */\n+static void r8169_init_napi(struct rtl8169_private *tp)\n+{\n+\tint napi_num = min(tp-\u003eirq_nvecs, R8169_MAX_QUEUES);\n+\n+\tfor (int i = 0; i \u003c napi_num; i++) {\n+\t\tint (*poll_fn)(struct napi_struct *, int) = rtl8169_poll;\n+\n+\t\tif (tp-\u003eirq_nvecs \u003e 1) {\n+\t\t\tif (i \u003c R8169_MAX_RX_QUEUES)\n+\t\t\t\tpoll_fn = rtl8169_poll_msix_rx;\n+\t\t\telse\n+\t\t\t\tpoll_fn = rtl8169_poll_msix_tx;\n+\t\t}\n+\t\tnetif_napi_add(tp-\u003edev, \u0026tp-\u003ertl8169_napi[i], poll_fn);\n+\t}\n+}\n+\n+static void rtl8169_get_channels(struct net_device *dev,\n+\t\t\t\t struct ethtool_channels *ch)\n+{\n+\tstruct rtl8169_private *tp = netdev_priv(dev);\n+\n+\tch-\u003emax_rx = tp-\u003ehw_supp_num_rx_queues;\n+\tch-\u003emax_tx = 1;\n+\n+\tch-\u003erx_count = tp-\u003enum_rx_rings;\n+\tch-\u003etx_count = 1;\n+}\n+\n+static const struct ethtool_ops rtl8169_ethtool_ops = {\n+\t.supported_coalesce_params = ETHTOOL_COALESCE_USECS |\n+\t\t\t\t     ETHTOOL_COALESCE_MAX_FRAMES,\n+\t.get_drvinfo\t\t= rtl8169_get_drvinfo,\n+\t.get_regs_len\t\t= rtl8169_get_regs_len,\n+\t.get_link\t\t= ethtool_op_get_link,\n+\t.get_coalesce\t\t= rtl_get_coalesce,\n+\t.set_coalesce\t\t= rtl_set_coalesce,\n+\t.get_regs\t\t= rtl8169_get_regs,\n+\t.get_wol\t\t= rtl8169_get_wol,\n+\t.set_wol\t\t= rtl8169_set_wol,\n+\t.get_strings\t\t= rtl8169_get_strings,\n+\t.get_sset_count\t\t= rtl8169_get_sset_count,\n+\t.get_ethtool_stats\t= rtl8169_get_ethtool_stats,\n+\t.get_ts_info\t\t= ethtool_op_get_ts_info,\n+\t.nway_reset\t\t= phy_ethtool_nway_reset,\n+\t.get_eee\t\t= rtl8169_get_eee,\n+\t.set_eee\t\t= rtl8169_set_eee,\n+\t.get_channels\t\t= rtl8169_get_channels,\n+\t.get_link_ksettings\t= phy_ethtool_get_link_ksettings,\n+\t.set_link_ksettings\t= rtl8169_set_link_ksettings,\n+\t.get_ringparam\t\t= rtl8169_get_ringparam,\n+\t.get_pause_stats\t= rtl8169_get_pause_stats,\n+\t.get_pauseparam\t\t= rtl8169_get_pauseparam,\n+\t.set_pauseparam\t\t= rtl8169_set_pauseparam,\n+\t.get_eth_mac_stats\t= rtl8169_get_eth_mac_stats,\n+\t.get_eth_ctrl_stats\t= rtl8169_get_eth_ctrl_stats,\n+};\n+\n static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n {\n \tconst struct rtl_chip_info *chip;\n@@ -5609,7 +6346,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n \tu32 txconfig;\n \tu32 xid;\n \n-\tdev = devm_alloc_etherdev(\u0026pdev-\u003edev, sizeof (*tp));\n+\tdev = devm_alloc_etherdev_mqs(\u0026pdev-\u003edev, sizeof(*tp),\n+\t\t\t\t      R8169_MAX_TX_QUEUES,\n+\t\t\t\t      R8169_MAX_RX_QUEUES);\n+\n \tif (!dev)\n \t\treturn -ENOMEM;\n \n@@ -5689,6 +6429,13 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n \ttp-\u003edash_type = rtl_get_dash_type(tp);\n \ttp-\u003edash_enabled = rtl_dash_is_enabled(tp);\n \n+\tif (rtl_hw_support_rss(tp)) {\n+\t\ttp-\u003erss_data = devm_kzalloc(\u0026pdev-\u003edev, sizeof(*tp-\u003erss_data),\n+\t\t\t\t\t    GFP_KERNEL);\n+\t\tif (!tp-\u003erss_data)\n+\t\t\treturn -ENOMEM;\n+\t}\n+\n \ttp-\u003ecp_cmd = RTL_R16(tp, CPlusCmd) \u0026 CPCMD_MASK;\n \n \tif (sizeof(dma_addr_t) \u003e 4 \u0026\u0026 tp-\u003emac_version \u003e= RTL_GIGA_MAC_VER_18 \u0026\u0026\n@@ -5703,11 +6450,16 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n \n \trtl_hw_reset(tp);\n \n+\trtl_setup_rx_params(tp);\n+\n \trc = rtl_alloc_irq(tp);\n \tif (rc \u003c 0)\n \t\treturn dev_err_probe(\u0026pdev-\u003edev, rc, \"Can't allocate interrupt\\n\");\n \n-\ttp-\u003eirq = pci_irq_vector(pdev, 0);\n+\trtl8169_set_rx_ring_num(tp);\n+\n+\tif (rtl_hw_support_rss(tp))\n+\t\trtl8169_init_rss(tp);\n \n \tINIT_WORK(\u0026tp-\u003ewk.work, rtl_task);\n \tdisable_work(\u0026tp-\u003ewk.work);\n@@ -5716,13 +6468,16 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n \n \tdev-\u003eethtool_ops = \u0026rtl8169_ethtool_ops;\n \n-\tnetif_napi_add(dev, \u0026tp-\u003enapi, rtl8169_poll);\n-\n \tdev-\u003ehw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |\n \t\t\t   NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;\n \tdev-\u003evlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;\n \tdev-\u003epriv_flags |= IFF_LIVE_ADDR_CHANGE;\n \n+\tif (rtl_hw_support_rss(tp) \u0026\u0026 tp-\u003enum_rx_rings \u003e 1) {\n+\t\tdev-\u003ehw_features |= NETIF_F_RXHASH;\n+\t\tdev-\u003efeatures |= NETIF_F_RXHASH;\n+\t}\n+\n \t/*\n \t * Pretend we are using VLANs; This bypasses a nasty bug where\n \t * Interrupts stop flowing on high load on 8110SCd controllers.\n@@ -5778,6 +6533,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n \tif (jumbo_max)\n \t\tdev-\u003emax_mtu = jumbo_max;\n \n+\trc = netif_set_real_num_queues(tp-\u003edev, 1, tp-\u003enum_rx_rings);\n+\tif (rc \u003c 0)\n+\t\treturn dev_err_probe(\u0026pdev-\u003edev, rc, \"set tx/rx num failure\\n\");\n+\n \trtl_set_irq_mask(tp);\n \n \ttp-\u003ecounters = dmam_alloc_coherent (\u0026pdev-\u003edev, sizeof(*tp-\u003ecounters),\n@@ -5792,9 +6551,26 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n \tif (rc)\n \t\treturn rc;\n \n+\ttp-\u003erx_ring = kcalloc(tp-\u003enum_rx_rings, sizeof(struct rtl8169_rx_ring),\n+\t\t\t      GFP_KERNEL);\n+\tif (!tp-\u003erx_ring)\n+\t\treturn -ENOMEM;\n+\n+\tfor (int i = 0; i \u003c tp-\u003enum_rx_rings; i++)\n+\t\tu64_stats_init(\u0026tp-\u003erx_ring[i].stats.syncp);\n+\n+\ttp-\u003ertl8169_napi = kcalloc(tp-\u003eirq_nvecs, sizeof(struct napi_struct),\n+\t\t\t\t   GFP_KERNEL);\n+\tif (!tp-\u003ertl8169_napi) {\n+\t\tkfree(tp-\u003erx_ring);\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\tr8169_init_napi(tp);\n+\n \trc = register_netdev(dev);\n \tif (rc)\n-\t\treturn rc;\n+\t\tgoto err_free_napi;\n \n \tif (IS_ENABLED(CONFIG_R8169_LEDS)) {\n \t\tif (rtl_is_8125(tp))\n@@ -5803,8 +6579,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n \t\t\ttp-\u003eleds = rtl8168_init_leds(dev);\n \t}\n \n-\tnetdev_info(dev, \"%s, %pM, %sXID %x, IRQ %d\\n\",\n-\t\t    chip-\u003ename, dev-\u003edev_addr, ext_xid_str, xid, tp-\u003eirq);\n+\tnetdev_info(dev, \"%s, %pM, %sXID %x, IRQ %d (%d total)\\n\",\n+\t\t    chip-\u003ename, dev-\u003edev_addr, ext_xid_str, xid,\n+\t\t    pci_irq_vector(pdev, 0), tp-\u003eirq_nvecs);\n \n \tif (jumbo_max)\n \t\tnetdev_info(dev, \"jumbo features [frames: %d bytes, tx checksumming: %s]\\n\",\n@@ -5821,6 +6598,11 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n \t\tpm_runtime_put_sync(\u0026pdev-\u003edev);\n \n \treturn 0;\n+\n+err_free_napi:\n+\tr8169_free_napi(tp);\n+\tkfree(tp-\u003erx_ring);\n+\treturn rc;\n }\n \n static struct pci_driver rtl8169_pci_driver = {\n"
}

2/1 2026/08/14 02:26 agent patch-evaluator
3m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "R8169",
    "NET_VENDOR_REALTEK",
    "PCI"
  ],
  "FocusSymbols": [
    "rtl8169_interrupt_msix",
    "rtl8169_interrupt_other",
    "rtl8169_poll_msix_rx",
    "rtl8169_poll_msix_tx",
    "rtl8169_get_channels"
  ],
  "Reasoning": "The patch adds Receive Side Scaling (RSS) and multiple RX queues support to the Realtek r8169 network driver. It introduces new MSI-X interrupt handlers and NAPI polling functions for both RX and TX paths. The driver is reachable via PCI fuzzing.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit b16e1b25c7d702b5dbd0fd5deb3cc7a1653131cf
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 14 02:26:25 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index ec4fc21fa21fb..13372fc55eeef 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -29,6 +29,7 @@
 #include <linux/prefetch.h>
 #include <linux/ipv6.h>
 #include <linux/unaligned.h>
+#include <linux/u64_stats_sync.h>
 #include <net/ip6_checksum.h>
 #include <net/netdev_queues.h>
 #include <net/phy/realtek_phy.h>
@@ -74,9 +75,33 @@
 #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))
+
+/*
+ * Workaround for the hardware DMA prefetcher. The H/W might aggressively
+ * fetch one more descriptor even after hitting the RingEnd mark. We
+ * allocate this extra dummy space as padding to prevent out-of-bounds
+ * access and potential IOMMU faults.
+ */
+#define R8169_RX_RING_BYTES	((NUM_RX_DESC + 1) * 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_QUEUES	16
+#define R8169_MAX_RX_QUEUES	8
+#define R8169_DEFAULT_RX_QUEUES	1
+#define R8169_MAX_TX_QUEUES	1
+#define R8127_MAX_NUM_IRQVEC	32
+#define R8127_MIN_NUM_IRQVEC	30
+#define R8169_IRQ_DEFAULT	1
+#define RTL_RSS_KEY_SIZE	40
+#define RSS_CPU_NUM_MASK	GENMASK(18, 16)
+#define RSS_HASH_MASK		GENMASK(10, 8)
+#define RTL_MAX_INDIRECTION_TABLE_ENTRIES 128
+#define RXS_RSS_UDP		BIT(27)
+#define RXS_RSS_IPV4		BIT(28)
+#define RXS_RSS_IPV6		BIT(29)
+#define RXS_RSS_TCP		BIT(30)
+#define RXS_RSS_L3_TYPE_MASK	(RXS_RSS_IPV4 | RXS_RSS_IPV6)
+#define RXS_RSS_L4_TYPE_MASK	(RXS_RSS_TCP | RXS_RSS_UDP)
 
 #define OCP_STD_PHY_BASE	0xa400
 
@@ -441,11 +466,16 @@ enum rtl8125_registers {
 	TxPoll_8125		= 0x90,
 	LEDSEL3			= 0x96,
 	MAC0_BKP		= 0x19e0,
+	RDSAR_Q1_LOW		= 0x4000,
 	RSS_CTRL_8125		= 0x4500,
 	Q_NUM_CTRL_8125		= 0x4800,
 	EEE_TXIDLE_TIMER_8125	= 0x6048,
+	IMR_CLEAR_VEC_MAP_REG	= 0x0d00,
+	ISR_VEC_MAP_REG		= 0x0d04,
+	IMR_SET_VEC_MAP_REG	= 0x0d0c,
 };
 
+#define MSIX_ID_VEC_MAP_LINKCHG	29
 #define LEDSEL_MASK_8125	0x23f
 
 #define RX_VLAN_INNER_8125	BIT(22)
@@ -474,6 +504,9 @@ enum rtl_register_content {
 	RxRUNT	= (1 << 20),
 	RxCRC	= (1 << 19),
 
+	RXRUNT_RSS	= (1 << 21),
+	RXCRC_RSS	= (1 << 20),
+
 	/* ChipCmdBits */
 	StopReq		= 0x80,
 	CmdReset	= 0x10,
@@ -576,6 +609,23 @@ enum rtl_register_content {
 
 	/* magic enable v2 */
 	MagicPacket_v2	= (1 << 16),	/* Wake up when receives a Magic Packet */
+#define	ISRIMR_LINKCHG	BIT(29)
+#define	ISRIMR_TOK_Q0	BIT(8)
+#define	ISRIMR_ROK_Q0	BIT(0)
+#define RTL_DESC_TYPE_CTRL		0xd8
+#define RSS_KEY_REG			0x4600
+#define RSS_INDIRECTION_TBL_REG		0x4700
+#define RSS_CTRL_TCP_IPV4_SUPP		BIT(0)
+#define RTL_DESC_TYPE_RSS		BIT(1)
+#define RSS_CTRL_IPV4_SUPP		BIT(1)
+#define RSS_CTRL_TCP_IPV6_SUPP		BIT(2)
+#define RSS_CTRL_IPV6_SUPP		BIT(3)
+#define RSS_CTRL_IPV6_EXT_SUPP		BIT(4)
+#define RSS_CTRL_TCP_IPV6_EXT_SUPP	BIT(5)
+#define	RX_RES_RSS			BIT(22)
+#define	RX_RUNT_RSS			BIT(21)
+#define	RX_CRC_RSS			BIT(20)
+#define RTL_RX_Q_NUM_MASK		GENMASK(4, 2)
 };
 
 enum rtl_desc_bit {
@@ -633,6 +683,11 @@ enum rtl_rx_desc_bit {
 #define RxProtoIP	(PID1 | PID0)
 #define RxProtoMask	RxProtoIP
 
+#define	RX_UDPT_DESC_RSS	BIT(19)
+#define	RX_TCPT_DESC_RSS	BIT(18)
+#define	RX_UDPF_DESC_RSS	BIT(16) /* UDP/IP checksum failed */
+#define	RX_TCPF_DESC_RSS	BIT(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 */
@@ -654,9 +709,27 @@ struct TxDesc {
 };
 
 struct RxDesc {
-	__le32 opts1;
-	__le32 opts2;
-	__le64 addr;
+	union {
+		/* RX_DESC_TYPE_DEFAULT */
+		struct {
+			__le32 opts1;
+			__le32 opts2;
+			__le64 addr;
+		};
+
+		/* RX_DESC_TYPE_RSS */
+		struct {
+			union {
+				__le64 rss_addr;
+				struct {
+					__le32 rss_info;
+					__le32 rss_result;
+				} rss_dword;
+			};
+			__le32 rss_opts2;
+			__le32 rss_opts1;
+		};
+	};
 };
 
 struct ring_info {
@@ -728,27 +801,57 @@ enum rtl_dash_type {
 	RTL_DASH_25_BP,
 };
 
+enum rx_desc_type {
+	RX_DESC_TYPE_DEFAULT,
+	RX_DESC_TYPE_RSS,
+};
+
+struct rtl8169_rx_ring {
+	u32 cur_rx;
+	u32 dirty_rx;
+	struct RxDesc *rx_desc_array;
+	dma_addr_t rx_desc_phy_addr[NUM_RX_DESC];
+	dma_addr_t rx_phy_addr;
+	struct page *rx_databuff[NUM_RX_DESC];
+
+	struct {
+		u64 rx_errors;
+		u64 rx_dropped;
+		u64 rx_length_errors;
+		u64 rx_crc_errors;
+		u64 multicast;
+		struct u64_stats_sync syncp;
+	} stats;
+};
+
+struct rtl8169_rss_data {
+	u8 rss_key[RTL_RSS_KEY_SIZE];
+	u8 rss_indir_tbl[RTL_MAX_INDIRECTION_TABLE_ENTRIES];
+	unsigned int hw_supp_indir_tbl_entries;
+};
+
 struct rtl8169_private {
 	void __iomem *mmio_addr;	/* memory map physical address */
 	struct pci_dev *pci_dev;
 	struct net_device *dev;
 	struct phy_device *phydev;
-	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. */
 	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 napi_struct *rtl8169_napi;
+	struct rtl8169_rx_ring *rx_ring;
+	unsigned int num_rx_rings;
 	u16 cp_cmd;
 	u16 tx_lpi_timer;
 	u32 irq_mask;
-	int irq;
+	unsigned int hw_supp_num_rx_queues;
+	struct rtl8169_rss_data *rss_data;
+	unsigned int irq_nvecs;
+	enum rx_desc_type init_rx_desc_type;
 	struct clk *clk;
 
 	struct {
@@ -1578,6 +1681,11 @@ static bool rtl_dash_is_enabled(struct rtl8169_private *tp)
 	}
 }
 
+static bool rtl_hw_support_rss(struct rtl8169_private *tp)
+{
+	return tp->mac_version == RTL_GIGA_MAC_VER_80;
+}
+
 static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp)
 {
 	switch (tp->mac_version) {
@@ -1644,26 +1752,38 @@ 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))
-		RTL_W32(tp, IntrStatus_8125, bits);
-	else
+	if (rtl_is_8125(tp)) {
+		if (tp->irq_nvecs > 1)
+			RTL_W32(tp, ISR_VEC_MAP_REG, bits);
+		else
+			RTL_W32(tp, IntrStatus_8125, bits);
+	} else {
 		RTL_W16(tp, IntrStatus, bits);
+	}
 }
 
 static void rtl_irq_disable(struct rtl8169_private *tp)
 {
-	if (rtl_is_8125(tp))
-		RTL_W32(tp, IntrMask_8125, 0);
-	else
+	if (rtl_is_8125(tp)) {
+		if (tp->irq_nvecs > 1)
+			RTL_W32(tp, IMR_CLEAR_VEC_MAP_REG, 0xffffffff);
+		else
+			RTL_W32(tp, IntrMask_8125, 0);
+	} 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->irq_nvecs > 1)
+			RTL_W32(tp, IMR_SET_VEC_MAP_REG, 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)
@@ -1867,9 +1987,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->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		opts2 = le32_to_cpu(desc->rss_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));
@@ -2416,34 +2547,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 */
@@ -2619,9 +2722,26 @@ 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->rx_desc_array, 0x0, R8169_RX_RING_BYTES);
+	}
+}
+
 static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
 {
-	tp->dirty_tx = tp->cur_tx = tp->cur_rx = 0;
+	tp->dirty_tx = 0;
+	tp->cur_tx = 0;
+
+	for (int i = 0; i < tp->num_rx_rings; i++) {
+		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
+
+		ring->dirty_rx = 0;
+		ring->cur_rx = 0;
+	}
 }
 
 static void rtl_jumbo_config(struct rtl8169_private *tp)
@@ -2680,6 +2800,29 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
 	rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
 }
 
+static void rtl8169_init_rss(struct rtl8169_private *tp)
+{
+	for (int i = 0; i < tp->rss_data->hw_supp_indir_tbl_entries; i++)
+		tp->rss_data->rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, tp->num_rx_rings);
+
+	netdev_rss_key_fill(tp->rss_data->rss_key, RTL_RSS_KEY_SIZE);
+}
+
+static void rtl_setup_rx_params(struct rtl8169_private *tp)
+{
+	tp->num_rx_rings = 1;
+	switch (tp->mac_version) {
+	case RTL_GIGA_MAC_VER_80:
+		tp->hw_supp_num_rx_queues = R8169_MAX_RX_QUEUES;
+		tp->rss_data->hw_supp_indir_tbl_entries = RTL_MAX_INDIRECTION_TABLE_ENTRIES;
+		break;
+	default:
+		tp->hw_supp_num_rx_queues = R8169_DEFAULT_RX_QUEUES;
+		break;
+	}
+	tp->init_rx_desc_type = RX_DESC_TYPE_DEFAULT;
+}
+
 static void rtl_request_firmware(struct rtl8169_private *tp)
 {
 	struct rtl_fw *rtl_fw;
@@ -2802,8 +2945,60 @@ 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)
+{
+	u8 *rss_key = tp->rss_data->rss_key;
+	const u16 rss_key_reg = RSS_KEY_REG;
+
+	/* Write redirection table to HW */
+	for (int i = 0; i < RTL_RSS_KEY_SIZE; i += sizeof(u32))
+		RTL_W32(tp, rss_key_reg + i, get_unaligned_le32(rss_key + i));
+}
+
+static void rtl8169_store_reta(struct rtl8169_private *tp)
+{
+	u8 *indir_tbl = tp->rss_data->rss_indir_tbl;
+	unsigned int i;
+
+	/* Write redirection table to HW */
+	for (i = 0; i < tp->rss_data->hw_supp_indir_tbl_entries; i += 4) {
+		u32 reta = get_unaligned_le32(&indir_tbl[i]);
+
+		RTL_W32(tp, RSS_INDIRECTION_TBL_REG + i, reta);
+	}
+}
+
+static void rtl8169_set_rss_hash_opt(struct rtl8169_private *tp)
+{
+	u32 rss_ctrl;
+
+	rss_ctrl = FIELD_PREP(RSS_CPU_NUM_MASK, ilog2(tp->num_rx_rings));
+
+	/* Perform hash on these packet types */
+	rss_ctrl |= RSS_CTRL_TCP_IPV4_SUPP |
+		    RSS_CTRL_IPV4_SUPP |
+		    RSS_CTRL_IPV6_SUPP |
+		    RSS_CTRL_IPV6_EXT_SUPP |
+		    RSS_CTRL_TCP_IPV6_SUPP |
+		    RSS_CTRL_TCP_IPV6_EXT_SUPP;
+
+	rss_ctrl |= FIELD_PREP(RSS_HASH_MASK,
+			       ilog2(tp->rss_data->hw_supp_indir_tbl_entries));
+
+	RTL_W32(tp, RSS_CTRL_8125, rss_ctrl);
+}
+
+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)
 {
+	struct rtl8169_rx_ring *ring = &tp->rx_ring[0];
+
 	/*
 	 * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh
 	 * register to be written before TxDescAddrLow to work.
@@ -2811,8 +3006,18 @@ static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
 	 */
 	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));
+	RTL_W32(tp, RxDescAddrHigh, ((u64)ring->rx_phy_addr) >> 32);
+	RTL_W32(tp, RxDescAddrLow,
+		((u64)ring->rx_phy_addr) & DMA_BIT_MASK(32));
+
+	for (int i = 1; i < tp->num_rx_rings; i++) {
+		unsigned int rdsar_reg = RDSAR_Q1_LOW + (i - 1) * 8;
+		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
+
+		RTL_W32(tp, rdsar_reg + 4, ((u64)ring->rx_phy_addr >> 32));
+		RTL_W32(tp, rdsar_reg,
+			((u64)ring->rx_phy_addr) & DMA_BIT_MASK(32));
+	}
 }
 
 static void rtl8169_set_magic_reg(struct rtl8169_private *tp)
@@ -3858,6 +4063,27 @@ DECLARE_RTL_COND(rtl_mac_ocp_e00e_cond)
 	return r8168_mac_ocp_read(tp, 0xe00e) & BIT(13);
 }
 
+static void rtl8125_set_rx_q_num(struct rtl8169_private *tp)
+{
+	u16 rx_q_num;
+	u16 q_ctrl;
+
+	rx_q_num = ilog2(tp->num_rx_rings);
+	q_ctrl = RTL_R16(tp, Q_NUM_CTRL_8125);
+	q_ctrl &= ~RTL_RX_Q_NUM_MASK;
+	q_ctrl |= FIELD_PREP(RTL_RX_Q_NUM_MASK, rx_q_num);
+	RTL_W16(tp, Q_NUM_CTRL_8125, q_ctrl);
+}
+
+static void rtl8169_hw_enable_vec_mapping(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);
@@ -3866,6 +4092,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->irq_nvecs > 1)
+		rtl8169_hw_enable_vec_mapping(tp);
+
 	/* disable UPS */
 	r8168_mac_ocp_modify(tp, 0xd40a, 0x0010, 0x0000);
 
@@ -3885,6 +4114,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->num_rx_rings > 1) {
+		rtl8125_set_rx_q_num(tp);
+		RTL_W8(tp, RTL_DESC_TYPE_CTRL,
+		       RTL_R8(tp, RTL_DESC_TYPE_CTRL) | RTL_DESC_TYPE_RSS);
+	}
+
 	if (tp->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)
@@ -4121,6 +4357,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->num_rx_rings > 1)
+			rtl_set_rss_config(tp);
+		else
+			RTL_W32(tp, RSS_CTRL_8125, 0x00);
+	}
 	rtl_lock_config_regs(tp);
 
 	rtl_jumbo_config(tp);
@@ -4148,19 +4390,32 @@ 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(struct rtl8169_private *tp, struct RxDesc *desc)
 {
-	u32 eor = le32_to_cpu(desc->opts1) & RingEnd;
+	u32 eor;
 
-	desc->opts2 = 0;
-	/* Force memory writes to complete before releasing descriptor */
-	dma_wmb();
-	WRITE_ONCE(desc->opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		eor = le32_to_cpu(desc->rss_opts1) & RingEnd;
+		desc->rss_opts2 = cpu_to_le32(0);
+		/* Force memory writes to complete before releasing descriptor */
+		dma_wmb();
+		WRITE_ONCE(desc->rss_opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
+		break;
+	default:
+		eor = le32_to_cpu(desc->opts1) & RingEnd;
+		desc->opts2 = cpu_to_le32(0);
+		/* Force memory writes to complete before releasing descriptor */
+		dma_wmb();
+		WRITE_ONCE(desc->opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
+		break;
+	}
 }
 
 static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
-					  struct RxDesc *desc)
+					  struct rtl8169_rx_ring *ring, unsigned int index)
 {
+	struct RxDesc *desc = ring->rx_desc_array + index;
 	struct device *d = tp_to_dev(tp);
 	int node = dev_to_node(d);
 	dma_addr_t mapping;
@@ -4177,56 +4432,128 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
 		return NULL;
 	}
 
-	desc->addr = cpu_to_le64(mapping);
-	rtl8169_mark_to_asic(desc);
+	ring->rx_desc_phy_addr[index] = mapping;
+	if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+		desc->rss_addr = cpu_to_le64(mapping);
+	else
+		desc->addr = cpu_to_le64(mapping);
+	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->rx_desc_phy_addr[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->rx_desc_phy_addr[i] = 0;
+		if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS) {
+			ring->rx_desc_array[i].rss_addr = 0;
+			ring->rx_desc_array[i].rss_opts1 = 0;
+		} else {
+			ring->rx_desc_array[i].addr = 0;
+			ring->rx_desc_array[i].opts1 = 0;
+		}
+	}
+}
+
+static void rtl8169_mark_as_last_descriptor(struct rtl8169_private *tp, struct RxDesc *desc)
+{
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		desc->rss_opts1 |= cpu_to_le32(RingEnd);
+		break;
+	default:
+		desc->opts1 |= cpu_to_le32(RingEnd);
+		break;
 	}
 }
 
-static int rtl8169_rx_fill(struct rtl8169_private *tp)
+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->rx_desc_array[NUM_RX_DESC - 1]);
 
 	return 0;
 }
 
+static int rtl8169_alloc_rx_desc(struct rtl8169_private *tp)
+{
+	struct pci_dev *pdev = tp->pci_dev;
+	struct rtl8169_rx_ring *ring;
+
+	for (int i = 0; i < tp->num_rx_rings; i++) {
+		ring = &tp->rx_ring[i];
+		ring->rx_desc_array = dma_alloc_coherent(&pdev->dev,
+							 R8169_RX_RING_BYTES,
+							 &ring->rx_phy_addr,
+							 GFP_KERNEL);
+		if (!ring->rx_desc_array)
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+static void rtl8169_free_rx_desc(struct rtl8169_private *tp)
+{
+	struct pci_dev *pdev = tp->pci_dev;
+	struct rtl8169_rx_ring *ring;
+
+	for (int i = 0; i < tp->num_rx_rings; i++) {
+		ring = &tp->rx_ring[i];
+		if (ring->rx_desc_array) {
+			dma_free_coherent(&pdev->dev,
+					  R8169_RX_RING_BYTES,
+					  ring->rx_desc_array,
+					  ring->rx_phy_addr);
+			ring->rx_desc_array = NULL;
+		}
+	}
+}
+
 static int rtl8169_init_ring(struct rtl8169_private *tp)
 {
+	int i, ret;
+
 	rtl8169_init_ring_indexes(tp);
+	rtl8169_rx_desc_init(tp);
 
 	memset(tp->tx_skb, 0, sizeof(tp->tx_skb));
-	memset(tp->Rx_databuff, 0, sizeof(tp->Rx_databuff));
 
-	return rtl8169_rx_fill(tp);
+	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));
+		ret = rtl8169_rx_fill(tp, ring);
+		if (ret < 0)
+			goto err_clear;
+	}
+	return 0;
+
+err_clear:
+	while (--i >= 0)
+		rtl8169_rx_clear(tp, &tp->rx_ring[i]);
+	return ret;
 }
 
 static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp, unsigned int entry)
@@ -4266,9 +4593,25 @@ static void rtl8169_tx_clear(struct rtl8169_private *tp)
 	netdev_reset_queue(tp->dev);
 }
 
+static void rtl8169_napi_disable(struct rtl8169_private *tp)
+{
+	int napi_num = min(tp->irq_nvecs, R8169_MAX_QUEUES);
+
+	for (int i = 0; i < napi_num; i++)
+		napi_disable(&tp->rtl8169_napi[i]);
+}
+
+static void rtl8169_napi_enable(struct rtl8169_private *tp)
+{
+	int napi_num = min(tp->irq_nvecs, R8169_MAX_QUEUES);
+
+	for (int i = 0; i < napi_num; i++)
+		napi_enable(&tp->rtl8169_napi[i]);
+}
+
 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();
@@ -4303,18 +4646,30 @@ 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 < NUM_RX_DESC; j++) {
+			dma_addr_t phy_addr = ring->rx_desc_phy_addr[j];
+
+			if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+				ring->rx_desc_array[j].rss_addr = cpu_to_le64(phy_addr);
+			rtl8169_mark_to_asic(tp, ring->rx_desc_array + j);
+		}
+	}
+}
 
+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);
 
-	napi_enable(&tp->napi);
+	rtl8169_napi_enable(tp);
 	rtl_hw_start(tp);
 }
 
@@ -4758,30 +5113,111 @@ 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_hash(struct rtl8169_private *tp,
+				   struct RxDesc *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->rss_dword.rss_info);
+
+	if (!(rss_header_info & RXS_RSS_L3_TYPE_MASK))
+		return;
+
+	hash_val = le32_to_cpu(desc->rss_dword.rss_result);
+
+	skb_set_hash(skb, hash_val,
+		     (RXS_RSS_L4_TYPE_MASK & rss_header_info) ?
+		     PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3);
+}
+
+static inline void rtl8169_rx_csum(struct rtl8169_private *tp,
+				   struct sk_buff *skb,
+				   u32 opts1)
 {
-	u32 status = opts1 & (RxProtoMask | RxCSFailMask);
+	bool csum_ok = false;
 
-	if (status == RxProtoTCP || status == RxProtoUDP)
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		if (((opts1 & RX_TCPT_DESC_RSS) && !(opts1 & RX_TCPF_DESC_RSS)) ||
+		    ((opts1 & RX_UDPT_DESC_RSS) && !(opts1 & RX_UDPF_DESC_RSS)))
+			csum_ok = true;
+		break;
+	default: {
+		u32 status = opts1 & (RxProtoMask | RxCSFailMask);
+
+		if (status == RxProtoTCP || status == RxProtoUDP)
+			csum_ok = true;
+		break;
+	}
+	}
+
+	if (csum_ok)
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	else
 		skb_checksum_none_assert(skb);
 }
 
-static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget)
+static __le32 rtl8169_rx_desc_opts1(struct rtl8169_private *tp, struct RxDesc *desc)
+{
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		return READ_ONCE(desc->rss_opts1);
+	default:
+		return READ_ONCE(desc->opts1);
+	}
+}
+
+static bool rtl8169_check_rx_desc_error(struct rtl8169_rx_ring *ring,
+					struct rtl8169_private *tp,
+					u32 status)
+{
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		if (unlikely(status & RX_RES_RSS)) {
+			u64_stats_update_begin(&ring->stats.syncp);
+			if (status & RX_RUNT_RSS)
+				ring->stats.rx_length_errors++;
+			if (status & RX_CRC_RSS)
+				ring->stats.rx_crc_errors++;
+			u64_stats_update_end(&ring->stats.syncp);
+			return true;
+		}
+		break;
+	default:
+		if (unlikely(status & RxRES)) {
+			u64_stats_update_begin(&ring->stats.syncp);
+			if (status & (RxRWT | RxRUNT))
+				ring->stats.rx_length_errors++;
+			if (status & RxCRC)
+				ring->stats.rx_crc_errors++;
+			u64_stats_update_end(&ring->stats.syncp);
+			return true;
+		}
+	}
+	return false;
+}
+
+static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
+		  struct rtl8169_rx_ring *ring, int budget,
+		  struct napi_struct *napi)
 {
 	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 % NUM_RX_DESC;
+		struct RxDesc *desc = ring->rx_desc_array + entry;
 		struct sk_buff *skb;
 		const void *rx_buf;
 		dma_addr_t addr;
 		u32 status;
 
-		status = le32_to_cpu(READ_ONCE(desc->opts1));
+		status = le32_to_cpu(rtl8169_rx_desc_opts1(tp, desc));
 		if (status & DescOwn)
 			break;
 
@@ -4791,20 +5227,24 @@ 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(ring, tp, status)) {
 			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++;
+			u64_stats_update_begin(&ring->stats.syncp);
+			ring->stats.rx_errors++;
+			u64_stats_update_end(&ring->stats.syncp);
 
 			if (!(dev->features & NETIF_F_RXALL))
 				goto release_descriptor;
-			else if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
-				goto release_descriptor;
+
+			if (tp->init_rx_desc_type == RX_DESC_TYPE_DEFAULT) {
+				if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
+					goto release_descriptor;
+			} else {
+				if (!(status & (RXRUNT_RSS | RXCRC_RSS)))
+					goto release_descriptor;
+			}
 		}
 
 		pkt_size = status & GENMASK(13, 0);
@@ -4815,19 +5255,23 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
 		 * They are seen as a symptom of over-mtu sized frames.
 		 */
 		if (unlikely(rtl8169_fragmented_frame(status))) {
-			dev->stats.rx_dropped++;
-			dev->stats.rx_length_errors++;
+			u64_stats_update_begin(&ring->stats.syncp);
+			ring->stats.rx_dropped++;
+			ring->stats.rx_length_errors++;
+			u64_stats_update_end(&ring->stats.syncp);
 			goto release_descriptor;
 		}
 
-		skb = napi_alloc_skb(&tp->napi, pkt_size);
+		skb = napi_alloc_skb(napi, pkt_size);
 		if (unlikely(!skb)) {
-			dev->stats.rx_dropped++;
+			u64_stats_update_begin(&ring->stats.syncp);
+			ring->stats.rx_dropped++;
+			u64_stats_update_end(&ring->stats.syncp);
 			goto release_descriptor;
 		}
 
-		addr = le64_to_cpu(desc->addr);
-		rx_buf = page_address(tp->Rx_databuff[entry]);
+		addr = ring->rx_desc_phy_addr[entry];
+		rx_buf = page_address(ring->rx_databuff[entry]);
 
 		dma_sync_single_for_cpu(d, addr, pkt_size, DMA_FROM_DEVICE);
 		prefetch(rx_buf);
@@ -4836,19 +5280,26 @@ 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);
+		if (tp->num_rx_rings > 1)
+			rtl8169_rx_hash(tp, desc, skb);
+		rtl8169_rx_csum(tp, skb, status);
 		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++;
+		if (skb->pkt_type == PACKET_MULTICAST) {
+			u64_stats_update_begin(&ring->stats.syncp);
+			ring->stats.multicast++;
+			u64_stats_update_end(&ring->stats.syncp);
+		}
 
-		napi_gro_receive(&tp->napi, skb);
+		napi_gro_receive(napi, skb);
 
 		dev_sw_netstats_rx_add(dev, pkt_size);
 release_descriptor:
-		rtl8169_mark_to_asic(desc);
+		if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+			desc->rss_addr = cpu_to_le64(ring->rx_desc_phy_addr[entry]);
+		rtl8169_mark_to_asic(tp, desc);
 	}
 
 	return count;
@@ -4856,8 +5307,12 @@ 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;
-	u32 status = rtl_get_events(tp);
+	struct napi_struct *napi = dev_instance;
+	struct rtl8169_private *tp;
+	u32 status;
+
+	tp = netdev_priv(napi->dev);
+	status = rtl_get_events(tp);
 
 	if ((status & 0xffff) == 0xffff || !(status & tp->irq_mask))
 		return IRQ_NONE;
@@ -4873,13 +5328,114 @@ 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);
 out:
 	rtl_ack_events(tp, status);
 
 	return IRQ_HANDLED;
 }
 
+static void rtl8169_free_one_irq(struct rtl8169_private *tp, int i)
+{
+	if (tp->irq_nvecs > 1) {
+		if (i < R8169_MAX_QUEUES)
+			pci_free_irq(tp->pci_dev, i, &tp->rtl8169_napi[i]);
+		else if (i == MSIX_ID_VEC_MAP_LINKCHG)
+			pci_free_irq(tp->pci_dev, i, tp);
+	} else {
+		pci_free_irq(tp->pci_dev, i, &tp->rtl8169_napi[i]);
+	}
+}
+
+static void rtl8169_free_irq(struct rtl8169_private *tp)
+{
+	for (int i = 0; i < tp->irq_nvecs; i++)
+		rtl8169_free_one_irq(tp, i);
+}
+
+static void rtl8169_disable_hw_interrupt_msix(struct rtl8169_private *tp,
+					      int message_id)
+{
+	RTL_W32(tp, IMR_CLEAR_VEC_MAP_REG, BIT(message_id));
+}
+
+static void rtl8169_clear_hw_isr(struct rtl8169_private *tp, int message_id)
+{
+	RTL_W32(tp, ISR_VEC_MAP_REG, BIT(message_id));
+}
+
+static void rtl8169_enable_hw_interrupt_msix(struct rtl8169_private *tp,
+					     int message_id)
+{
+	RTL_W32(tp, IMR_SET_VEC_MAP_REG, BIT(message_id));
+}
+
+static irqreturn_t rtl8169_interrupt_msix(int irq, void *dev_instance)
+{
+	struct napi_struct *napi = dev_instance;
+	struct net_device *dev = napi->dev;
+	struct rtl8169_private *tp;
+	int message_id;
+
+	tp = netdev_priv(dev);
+	message_id = napi - tp->rtl8169_napi;
+
+	rtl8169_disable_hw_interrupt_msix(tp, message_id);
+	rtl8169_clear_hw_isr(tp, message_id);
+
+	napi_schedule(napi);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t rtl8169_interrupt_other(int irq, void *dev_instance)
+{
+	struct rtl8169_private *tp = dev_instance;
+
+	rtl8169_clear_hw_isr(tp, MSIX_ID_VEC_MAP_LINKCHG);
+	phy_mac_interrupt(tp->phydev);
+	return IRQ_HANDLED;
+}
+
+static int rtl8169_request_irq(struct rtl8169_private *tp)
+{
+	struct net_device *dev = tp->dev;
+	struct napi_struct *napi;
+	int i, rc;
+
+	for (i = 0; i < tp->irq_nvecs; i++) {
+		napi = &tp->rtl8169_napi[i];
+		if (tp->irq_nvecs > 1) {
+			if (i < R8169_MAX_QUEUES)
+				rc = pci_request_irq(tp->pci_dev, i,
+						     rtl8169_interrupt_msix,
+						     NULL, napi, "%s-%d",
+						     dev->name, i);
+			else if (i == MSIX_ID_VEC_MAP_LINKCHG)
+				rc = pci_request_irq(tp->pci_dev, i,
+						     rtl8169_interrupt_other,
+						     NULL, tp, "%s-%d",
+						     dev->name, i);
+			else
+				continue;
+		} else {
+			rc = pci_request_irq(tp->pci_dev, i,
+					     rtl8169_interrupt,
+					     NULL, napi, "%s-%d",
+					     dev->name, i);
+		}
+
+		if (rc)
+			goto free_irq;
+	}
+	return 0;
+
+free_irq:
+	while (--i >= 0)
+		rtl8169_free_one_irq(tp, i);
+	return rc;
+}
+
 static void rtl_task(struct work_struct *work)
 {
 	struct rtl8169_private *tp =
@@ -4914,13 +5470,14 @@ 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 net_device *dev = tp->dev;
-	int work_done;
+	struct rtl8169_private *tp = netdev_priv(napi->dev);
+	struct net_device *dev = napi->dev;
+	int work_done = 0;
 
 	rtl_tx(dev, tp, budget);
 
-	work_done = rtl_rx(dev, tp, budget);
+	/* rtl8169_poll() is used only when there is a single RX ring. */
+	work_done = rtl_rx(dev, tp, &tp->rx_ring[0], budget, napi);
 
 	if (work_done < budget && napi_complete_done(napi, work_done))
 		rtl_irq_enable(tp);
@@ -5035,7 +5592,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);
 
@@ -5051,18 +5608,17 @@ 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]);
 
-	free_irq(tp->irq, tp);
+	rtl8169_free_irq(tp);
 
 	phy_disconnect(tp->phydev);
 
-	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_rx_desc(tp);
 
 	pm_runtime_put_sync(&pdev->dev);
 
@@ -5074,7 +5630,14 @@ static void rtl8169_netpoll(struct net_device *dev)
 {
 	struct rtl8169_private *tp = netdev_priv(dev);
 
-	rtl8169_interrupt(tp->irq, tp);
+	for (int i = 0; i < tp->irq_nvecs; i++) {
+		if (tp->irq_nvecs > 1)
+			rtl8169_interrupt_msix(pci_irq_vector(tp->pci_dev, i),
+					       &tp->rtl8169_napi[i]);
+		else
+			rtl8169_interrupt(pci_irq_vector(tp->pci_dev, i),
+					  &tp->rtl8169_napi[i]);
+	}
 }
 #endif
 
@@ -5082,7 +5645,6 @@ static int rtl_open(struct net_device *dev)
 {
 	struct rtl8169_private *tp = netdev_priv(dev);
 	struct pci_dev *pdev = tp->pci_dev;
-	unsigned long irqflags;
 	int retval = -ENOMEM;
 
 	pm_runtime_get_sync(&pdev->dev);
@@ -5096,10 +5658,8 @@ static int rtl_open(struct net_device *dev)
 	if (!tp->TxDescArray)
 		goto out;
 
-	tp->RxDescArray = dma_alloc_coherent(&pdev->dev, R8169_RX_RING_BYTES,
-					     &tp->RxPhyAddr, GFP_KERNEL);
-	if (!tp->RxDescArray)
-		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)
@@ -5107,8 +5667,7 @@ 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;
 
@@ -5125,15 +5684,13 @@ 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);
+	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;
-err_free_tx_0:
+	rtl8169_free_rx_desc(tp);
 	dma_free_coherent(&pdev->dev, R8169_TX_RING_BYTES, tp->TxDescArray,
 			  tp->TxPhyAddr);
 	tp->TxDescArray = NULL;
@@ -5152,6 +5709,27 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 	netdev_stats_to_stats64(stats, &dev->stats);
 	dev_fetch_sw_netstats(stats, dev->tstats);
 
+	for (int i = 0; i < tp->num_rx_rings; i++) {
+		u64 errors, dropped, length_errors, crc_errors, multicast;
+		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
+		unsigned int start;
+
+		do {
+			start = u64_stats_fetch_begin(&ring->stats.syncp);
+			errors = ring->stats.rx_errors;
+			dropped = ring->stats.rx_dropped;
+			length_errors = ring->stats.rx_length_errors;
+			crc_errors = ring->stats.rx_crc_errors;
+			multicast = ring->stats.multicast;
+		} while (u64_stats_fetch_retry(&ring->stats.syncp, start));
+
+		stats->rx_errors += errors;
+		stats->rx_dropped += dropped;
+		stats->rx_length_errors += length_errors;
+		stats->rx_crc_errors += crc_errors;
+		stats->multicast += multicast;
+	}
+
 	/*
 	 * Fetch additional counter values missing in stats collected by driver
 	 * from tally counters.
@@ -5275,6 +5853,16 @@ static void rtl_shutdown(struct pci_dev *pdev)
 		pci_prepare_to_sleep(pdev);
 }
 
+static void r8169_free_napi(struct rtl8169_private *tp)
+{
+	int napi_num = min(tp->irq_nvecs, R8169_MAX_QUEUES);
+
+	for (int i = 0; i < napi_num; i++)
+		netif_napi_del(&tp->rtl8169_napi[i]);
+
+	kfree(tp->rtl8169_napi);
+}
+
 static void rtl_remove_one(struct pci_dev *pdev)
 {
 	struct rtl8169_private *tp = pci_get_drvdata(pdev);
@@ -5289,6 +5877,9 @@ static void rtl_remove_one(struct pci_dev *pdev)
 
 	unregister_netdev(tp->dev);
 
+	r8169_free_napi(tp);
+	kfree(tp->rx_ring);
+
 	if (tp->dash_type != RTL_DASH_NONE)
 		rtl8168_driver_stop(tp);
 
@@ -5320,15 +5911,49 @@ 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->irq_nvecs > 1) {
+		tp->irq_mask = ISRIMR_LINKCHG | ISRIMR_TOK_Q0;
+		for (int i = 0; i < tp->num_rx_rings; i++)
+			tp->irq_mask |= ISRIMR_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 get_max_irq_nvecs(struct rtl8169_private *tp)
+{
+	if (tp->mac_version == RTL_GIGA_MAC_VER_80)
+		return R8127_MAX_NUM_IRQVEC;
+	return R8169_IRQ_DEFAULT;
+}
+
+static int get_min_irq_nvecs(struct rtl8169_private *tp)
+{
+	if (tp->mac_version == RTL_GIGA_MAC_VER_80)
+		return R8127_MIN_NUM_IRQVEC;
+	return R8169_IRQ_DEFAULT;
+}
+
+static void rtl8169_set_rx_ring_num(struct rtl8169_private *tp)
+{
+	if (tp->irq_nvecs >= get_min_irq_nvecs(tp)) {
+		unsigned int rss_queue_num = netif_get_num_default_rss_queues();
+
+		tp->num_rx_rings = rounddown_pow_of_two(min(rss_queue_num,
+							    tp->hw_supp_num_rx_queues));
+		if (tp->num_rx_rings >= 2)
+			tp->init_rx_desc_type = RX_DESC_TYPE_RSS;
+	}
 }
 
 static int rtl_alloc_irq(struct rtl8169_private *tp)
 {
+	struct pci_dev *pdev = tp->pci_dev;
 	unsigned int flags;
+	int nvecs;
 
 	switch (tp->mac_version) {
 	case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06:
@@ -5344,7 +5969,18 @@ 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, get_min_irq_nvecs(tp),
+				      get_max_irq_nvecs(tp), flags);
+
+	if (nvecs < 0)
+		nvecs = pci_alloc_irq_vectors(pdev, 1, 1, flags);
+
+	if (nvecs < 0)
+		return nvecs;
+
+	tp->irq_nvecs = nvecs;
+
+	return 0;
 }
 
 static void rtl_read_mac_address(struct rtl8169_private *tp,
@@ -5599,6 +6235,107 @@ 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 net_device *dev = napi->dev;
+	struct rtl8169_private *tp;
+	int work_done = 0;
+	int message_id;
+
+	tp = netdev_priv(dev);
+	message_id = napi - tp->rtl8169_napi;
+
+	if (message_id < tp->num_rx_rings)
+		work_done += rtl_rx(dev, tp, &tp->rx_ring[message_id],
+				    budget, napi);
+
+	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 net_device *dev = napi->dev;
+	struct rtl8169_private *tp;
+
+	tp = netdev_priv(dev);
+
+	/* Currently r8169 only supports a single Tx ring.
+	 * Therefore, we don't need a per-ring Tx processing loop here.
+	 */
+	rtl_tx(dev, tp, budget);
+
+	if (napi_complete_done(napi, 0))
+		rtl8169_enable_hw_interrupt_msix(tp, (int)(napi - tp->rtl8169_napi));
+
+	return 0;
+}
+
+/* RTL8127 MSI-X vector layout:
+ * Vectors 0 .. (RxQs - 1)		: Rx Queues
+ * Vectors RxQs .. (RxQs + TxQs - 1)	: Tx Queues
+ * NAPI is only allocated for data path
+ */
+static void r8169_init_napi(struct rtl8169_private *tp)
+{
+	int napi_num = min(tp->irq_nvecs, R8169_MAX_QUEUES);
+
+	for (int i = 0; i < napi_num; i++) {
+		int (*poll_fn)(struct napi_struct *, int) = rtl8169_poll;
+
+		if (tp->irq_nvecs > 1) {
+			if (i < R8169_MAX_RX_QUEUES)
+				poll_fn = rtl8169_poll_msix_rx;
+			else
+				poll_fn = rtl8169_poll_msix_tx;
+		}
+		netif_napi_add(tp->dev, &tp->rtl8169_napi[i], poll_fn);
+	}
+}
+
+static void rtl8169_get_channels(struct net_device *dev,
+				 struct ethtool_channels *ch)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	ch->max_rx = tp->hw_supp_num_rx_queues;
+	ch->max_tx = 1;
+
+	ch->rx_count = tp->num_rx_rings;
+	ch->tx_count = 1;
+}
+
+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_channels		= rtl8169_get_channels,
+	.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;
@@ -5609,7 +6346,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;
 
@@ -5689,6 +6429,13 @@ 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);
 
+	if (rtl_hw_support_rss(tp)) {
+		tp->rss_data = devm_kzalloc(&pdev->dev, sizeof(*tp->rss_data),
+					    GFP_KERNEL);
+		if (!tp->rss_data)
+			return -ENOMEM;
+	}
+
 	tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK;
 
 	if (sizeof(dma_addr_t) > 4 && tp->mac_version >= RTL_GIGA_MAC_VER_18 &&
@@ -5703,11 +6450,16 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	rtl_hw_reset(tp);
 
+	rtl_setup_rx_params(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);
+	rtl8169_set_rx_ring_num(tp);
+
+	if (rtl_hw_support_rss(tp))
+		rtl8169_init_rss(tp);
 
 	INIT_WORK(&tp->wk.work, rtl_task);
 	disable_work(&tp->wk.work);
@@ -5716,13 +6468,16 @@ 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);
-
 	dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
 			   NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
 	dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
 
+	if (rtl_hw_support_rss(tp) && tp->num_rx_rings > 1) {
+		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.
@@ -5778,6 +6533,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 = netif_set_real_num_queues(tp->dev, 1, tp->num_rx_rings);
+	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),
@@ -5792,9 +6551,26 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 	if (rc)
 		return rc;
 
+	tp->rx_ring = kcalloc(tp->num_rx_rings, sizeof(struct rtl8169_rx_ring),
+			      GFP_KERNEL);
+	if (!tp->rx_ring)
+		return -ENOMEM;
+
+	for (int i = 0; i < tp->num_rx_rings; i++)
+		u64_stats_init(&tp->rx_ring[i].stats.syncp);
+
+	tp->rtl8169_napi = kcalloc(tp->irq_nvecs, sizeof(struct napi_struct),
+				   GFP_KERNEL);
+	if (!tp->rtl8169_napi) {
+		kfree(tp->rx_ring);
+		return -ENOMEM;
+	}
+
+	r8169_init_napi(tp);
+
 	rc = register_netdev(dev);
 	if (rc)
-		return rc;
+		goto err_free_napi;
 
 	if (IS_ENABLED(CONFIG_R8169_LEDS)) {
 		if (rtl_is_8125(tp))
@@ -5803,8 +6579,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 			tp->leds = rtl8168_init_leds(dev);
 	}
 
-	netdev_info(dev, "%s, %pM, %sXID %x, IRQ %d\n",
-		    chip->name, dev->dev_addr, ext_xid_str, xid, tp->irq);
+	netdev_info(dev, "%s, %pM, %sXID %x, IRQ %d (%d total)\n",
+		    chip->name, dev->dev_addr, ext_xid_str, xid,
+		    pci_irq_vector(pdev, 0), tp->irq_nvecs);
 
 	if (jumbo_max)
 		netdev_info(dev, "jumbo features [frames: %d bytes, tx checksumming: %s]\n",
@@ -5821,6 +6598,11 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 		pm_runtime_put_sync(&pdev->dev);
 
 	return 0;
+
+err_free_napi:
+	r8169_free_napi(tp);
+	kfree(tp->rx_ring);
+	return rc;
 }
 
 static struct pci_driver rtl8169_pci_driver = {

3/2 2026/08/14 02:26 llm patch-evaluator
1m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 24215
							output: 38
							thoughts: 1468
						

Thoughts:
**Reaching the r8169 Driver in a Fuzzing Context**

Okay, so we have this patch that introduces RSS and multiple RX queue support to the `r8169` driver. That’s a significant change; MSI-X, new NAPI polling functions... pretty core stuff. My immediate thought is, can we actually *hit* this code in a virtualized environment? QEMU is the obvious candidate, but does QEMU even *emulate* the RTL8169/8110/8168/8111/8125 chipset this driver handles? QEMU *does* have an RTL8139 emulation... but no dice on `r8169` out of the box.

Now, wait a minute, the prompt says the concern is code unreachable in QEMU *unless* you're talking about specific vendor hardware. Is `r8169` in that bucket, like a SmartNIC or GPU driver? No, it's a standard consumer Gigabit Ethernet driver. That means QEMU's lack of native emulation isn’t the end of the story. Syzkaller, that’s where things get interesting. Syzkaller has a PCI fuzzer, which is the key. Syzkaller can simulate PCI devices and feed them with PCI config space and MMIO. The question then becomes, does syzkaller *actually* fuzz `r8169`? Does it even *try* to reach it?

My thinking goes through several branches: checking if `r8169` is enabled in syzkaller's config, if it's considered reachable, and if the PCI fuzzer is active. I recall syzkaller using `vhci`, `e1000e`, or `virtio` for PCI devices in other network drivers, but that's a distraction. What's important is the fact that syzkaller has a PCI subsystem specifically designed to fuzz any PCI driver *by simulating PCI devices*.

Yes, it has a PCI fuzzer which can fuzz PCI drivers. `r8169` is a PCI driver!

So, even if QEMU doesn't emulate the hardware, Syzkaller's fuzzer can simulate it. The patch adds functionality to core functions so it makes it an important fuzzing target:

*   `rtl8169_interrupt_msix`
*   `rtl8169_poll_msix_rx`
*   `rtl8169_poll_msix_tx`
*   `rtl8169_init_rss`
*   `rtl_set_rss_config`
*   `rtl8169_alloc_rx_desc`

These are all now good candidates for focus, and the fact that they are used in interrupt handlers and NAPI polling functions means they will get called during network activity. This driver is indeed reachable.



4/2 2026/08/14 02:27 tool grepper
0m Args:
{
  "Expression": "config R8169",
  "PathPrefix": "drivers/net/ethernet/realtek/Kconfig"
}

Results:
{
  "Output": "drivers/net/ethernet/realtek/Kconfig=74=config 8139_OLD_RX_RESET\n--\ndrivers/net/ethernet/realtek/Kconfig-83-\ndrivers/net/ethernet/realtek/Kconfig:84:config R8169\ndrivers/net/ethernet/realtek/Kconfig-85-\ttristate \"Realtek 8169/8168/8101/8125 ethernet support\"\n--\ndrivers/net/ethernet/realtek/Kconfig-101-\ndrivers/net/ethernet/realtek/Kconfig:102:config R8169_LEDS\ndrivers/net/ethernet/realtek/Kconfig-103-\tbool \"Support for controlling the NIC LEDs\"\n"
}

5/2 2026/08/14 02:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26563
							output: 34
							thoughts: 14
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Analyzing the R8169 Dependencies**

Okay, let's dive into this `R8169` situation. My first step is to definitively establish its dependencies. I need to be absolutely certain what other kernel modules, libraries, or system components this driver relies upon. I'm thinking about the standard kernel module dependencies, of course, but also anything that might be specific to this particular NIC controller or its implementation.

I'll be looking for things like: are there any specific PHY drivers this might be calling? Are there any helper functions or data structures defined in other parts of the kernel that `R8169` leverages? Any device tree bindings or ACPI tables I need to understand? I'll probably start with `modinfo` and then move onto analyzing the source code itself, paying close attention to include statements and function calls.

I'll keep a sharp eye out for potential versioning issues – are there particular kernel versions it's known to work best with? Are there known conflicts with other drivers or hardware configurations? Knowing the dependencies is crucial; this informs my ability to effectively debug, troubleshoot, or potentially modify this driver if needed. The goal is a complete, reliable, and precise understanding of the `R8169` driver's interconnectedness with the system.



6/2 2026/08/14 02:27 tool grepper
0m Args:
{
  "Expression": "depends on",
  "PathPrefix": "drivers/net/ethernet/realtek/Kconfig"
}

Results:
{
  "Output": "drivers/net/ethernet/realtek/Kconfig=6=config NET_VENDOR_REALTEK\n--\ndrivers/net/ethernet/realtek/Kconfig-8-\tdefault y\ndrivers/net/ethernet/realtek/Kconfig:9:\tdepends on PCI\ndrivers/net/ethernet/realtek/Kconfig-10-\thelp\n--\ndrivers/net/ethernet/realtek/Kconfig=20=config 8139CP\ndrivers/net/ethernet/realtek/Kconfig-21-\ttristate \"RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support\"\ndrivers/net/ethernet/realtek/Kconfig:22:\tdepends on PCI\ndrivers/net/ethernet/realtek/Kconfig-23-\tselect CRC32\n--\ndrivers/net/ethernet/realtek/Kconfig=32=config 8139TOO\ndrivers/net/ethernet/realtek/Kconfig-33-\ttristate \"RealTek RTL-8129/8130/8139 PCI Fast Ethernet Adapter support\"\ndrivers/net/ethernet/realtek/Kconfig:34:\tdepends on PCI\ndrivers/net/ethernet/realtek/Kconfig-35-\tselect CRC32\n--\ndrivers/net/ethernet/realtek/Kconfig=44=config 8139TOO_PIO\n--\ndrivers/net/ethernet/realtek/Kconfig-46-\tdefault y\ndrivers/net/ethernet/realtek/Kconfig:47:\tdepends on 8139TOO \u0026\u0026 !NO_IOPORT_MAP\ndrivers/net/ethernet/realtek/Kconfig-48-\thelp\n--\ndrivers/net/ethernet/realtek/Kconfig=54=config 8139TOO_TUNE_TWISTER\ndrivers/net/ethernet/realtek/Kconfig-55-\tbool \"Support for uncommon RTL-8139 rev. K (automatic channel equalization)\"\ndrivers/net/ethernet/realtek/Kconfig:56:\tdepends on 8139TOO\ndrivers/net/ethernet/realtek/Kconfig-57-\thelp\n--\ndrivers/net/ethernet/realtek/Kconfig=65=config 8139TOO_8129\ndrivers/net/ethernet/realtek/Kconfig-66-\tbool \"Support for older RTL-8129/8130 boards\"\ndrivers/net/ethernet/realtek/Kconfig:67:\tdepends on 8139TOO\ndrivers/net/ethernet/realtek/Kconfig-68-\thelp\n--\ndrivers/net/ethernet/realtek/Kconfig=74=config 8139_OLD_RX_RESET\ndrivers/net/ethernet/realtek/Kconfig-75-\tbool \"Use older RX-reset method\"\ndrivers/net/ethernet/realtek/Kconfig:76:\tdepends on 8139TOO\ndrivers/net/ethernet/realtek/Kconfig-77-\thelp\n--\ndrivers/net/ethernet/realtek/Kconfig=84=config R8169\ndrivers/net/ethernet/realtek/Kconfig-85-\ttristate \"Realtek 8169/8168/8101/8125 ethernet support\"\ndrivers/net/ethernet/realtek/Kconfig:86:\tdepends on PCI\ndrivers/net/ethernet/realtek/Kconfig-87-\tselect FW_LOADER\n--\ndrivers/net/ethernet/realtek/Kconfig=102=config R8169_LEDS\ndrivers/net/ethernet/realtek/Kconfig-103-\tbool \"Support for controlling the NIC LEDs\"\ndrivers/net/ethernet/realtek/Kconfig:104:\tdepends on R8169 \u0026\u0026 LEDS_TRIGGER_NETDEV\ndrivers/net/ethernet/realtek/Kconfig:105:\tdepends on !(R8169=y \u0026\u0026 LEDS_CLASS=m)\ndrivers/net/ethernet/realtek/Kconfig-106-\thelp\n--\ndrivers/net/ethernet/realtek/Kconfig=110=config RTASE\ndrivers/net/ethernet/realtek/Kconfig-111-\ttristate \"Realtek Automotive Switch 9054/9068/9072/9075/9068/9071 PCIe Interface support\"\ndrivers/net/ethernet/realtek/Kconfig:112:\tdepends on PCI\ndrivers/net/ethernet/realtek/Kconfig-113-\tselect CRC32\n"
}

7/2 2026/08/14 02:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28046
							output: 49
							thoughts: 60
						

Thoughts:
**My Kernel Module Inclusion Decisions**

Okay, so I'm looking at this module dependency tree, and it's pretty straightforward. `R8169`, that Realtek gigabit Ethernet driver, it relies on the `PCI` subsystem. Makes sense. And, as expected, the wider `NET_VENDOR_REALTEK` option, which likely encompasses more Realtek network drivers, also has a dependency on `PCI`. So, to be safe and ensure everything's properly enabled, I'll definitely include both `R8169` and `NET_VENDOR_REALTEK`. Now, I need to pinpoint the key functions within these modules that I need to pay close attention to, so I'm shifting focus toward those.



8/2 2026/08/14 02:27 tool grepper
0m Args:
{
  "Expression": "rtl8169_interrupt_msix",
  "PathPrefix": "drivers/net/ethernet/realtek/r8169_main.c"
}

Results:
{
  "Output": "drivers/net/ethernet/realtek/r8169_main.c=5367=static void rtl8169_enable_hw_interrupt_msix(struct rtl8169_private *tp,\n--\ndrivers/net/ethernet/realtek/r8169_main.c-5372-\ndrivers/net/ethernet/realtek/r8169_main.c:5373:static irqreturn_t rtl8169_interrupt_msix(int irq, void *dev_instance)\ndrivers/net/ethernet/realtek/r8169_main.c-5374-{\n--\ndrivers/net/ethernet/realtek/r8169_main.c=5400=static int rtl8169_request_irq(struct rtl8169_private *tp)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-5410-\t\t\t\trc = pci_request_irq(tp-\u003epci_dev, i,\ndrivers/net/ethernet/realtek/r8169_main.c:5411:\t\t\t\t\t\t     rtl8169_interrupt_msix,\ndrivers/net/ethernet/realtek/r8169_main.c-5412-\t\t\t\t\t\t     NULL, napi, \"%s-%d\",\n--\ndrivers/net/ethernet/realtek/r8169_main.c=5629=static void rtl8169_netpoll(struct net_device *dev)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-5634-\t\tif (tp-\u003eirq_nvecs \u003e 1)\ndrivers/net/ethernet/realtek/r8169_main.c:5635:\t\t\trtl8169_interrupt_msix(pci_irq_vector(tp-\u003epci_dev, i),\ndrivers/net/ethernet/realtek/r8169_main.c-5636-\t\t\t\t\t       \u0026tp-\u003ertl8169_napi[i]);\n"
}

9/2 2026/08/14 02:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28847
							output: 51
							thoughts: 303
						

Thoughts:
**Analyzing Potential Function Inlining in the RTL8169 Driver**

Okay, so I'm looking at these functions within the RTL8169 driver and trying to figure out if the compiler is likely to inline any of them. Here's what I've got:

First, I see `rtl8169_interrupt_msix`, `rtl8169_poll_msix_rx`, and `rtl8169_poll_msix_tx` are all static, which *could* mean they're good candidates for inlining, but their addresses are being taken. Specifically, `rtl8169_interrupt_msix` gets passed to `pci_request_irq`, and the other two go to `netif_napi_add`.  Since those functions require direct pointers to these interrupt handlers, the compiler *won't* inline them. That's a definite no.

Now, `rtl8169_init_rss`, `rtl_set_rss_config`, and `rtl8169_alloc_rx_desc` are a different story. They're also static, but their calling contexts look a bit less rigid. They seem to be called from a couple of places - `rtl_init_one` and `rtl_hw_start`, and from `rtl_open`, respectively. The call sites seem less numerous. Therefore, there's a good chance the compiler *might* inline these to optimize the driver code. I'll need to check the assembly or disassembly to be sure, but my gut feeling is that they're the prime candidates for inlining. I'll take a closer look at the compiled output to confirm that assumption.



10/2 2026/08/14 02:28 tool grepper
0m Args:
{
  "Expression": "rtl8169_poll_msix_rx",
  "PathPrefix": "drivers/net/ethernet/realtek/r8169_main.c"
}

Results:
{
  "Output": "drivers/net/ethernet/realtek/r8169_main.c=6229=static bool rtl_aspm_is_safe(struct rtl8169_private *tp)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-6237-\ndrivers/net/ethernet/realtek/r8169_main.c:6238:static int rtl8169_poll_msix_rx(struct napi_struct *napi, int budget)\ndrivers/net/ethernet/realtek/r8169_main.c-6239-{\n--\ndrivers/net/ethernet/realtek/r8169_main.c=6281=static void r8169_init_napi(struct rtl8169_private *tp)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-6289-\t\t\tif (i \u003c R8169_MAX_RX_QUEUES)\ndrivers/net/ethernet/realtek/r8169_main.c:6290:\t\t\t\tpoll_fn = rtl8169_poll_msix_rx;\ndrivers/net/ethernet/realtek/r8169_main.c-6291-\t\t\telse\n"
}

11/2 2026/08/14 02:28 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29905
							output: 48
							thoughts: 304
						

Thoughts:
**My Initial Assessment of Potential Bottlenecks**

Okay, so I'm looking at these Realtek network driver functions and trying to pinpoint the most likely performance bottlenecks. Based on what I've seen so far, `rtl8169_poll_msix_rx` and `rtl8169_poll_msix_tx` definitely stand out as good initial candidates. The MSIX interrupt handler, `rtl8169_interrupt_msix`, is also worth a close look. 

I'm also considering the potential impact of static inline functions on performance. I'm thinking about whether or not the compiler is inlining functions and what the implication of that is. The `rtl8169_rx_hash` and `rtl8169_rx_csum` functions could be potential hotspots if they're inlined frequently. Similarly, `rtl8169_check_rx_desc_error`, `rtl8169_rx_fill`, `rtl8169_rx_clear`, and `rtl8169_alloc_rx_desc`, could be problematic if they are inlined. It is important to remember that `rtl8169_alloc_rx_desc` is called from `rtl_open` so, it may not be inlined.

Given all of that, I think the most profitable path forward is to hone in on:

*   `rtl8169_interrupt_msix`
*   `rtl8169_poll_msix_rx`
*   `rtl8169_poll_msix_tx`
*   `rtl8169_interrupt_other`

I'm going to start by taking a closer look at `rtl8169_interrupt_other` to ensure that I have covered all the bases.



12/2 2026/08/14 02:28 tool grepper
0m Args:
{
  "Expression": "rtl8169_interrupt_other",
  "PathPrefix": "drivers/net/ethernet/realtek/r8169_main.c"
}

Results:
{
  "Output": "drivers/net/ethernet/realtek/r8169_main.c=5373=static irqreturn_t rtl8169_interrupt_msix(int irq, void *dev_instance)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-5390-\ndrivers/net/ethernet/realtek/r8169_main.c:5391:static irqreturn_t rtl8169_interrupt_other(int irq, void *dev_instance)\ndrivers/net/ethernet/realtek/r8169_main.c-5392-{\n--\ndrivers/net/ethernet/realtek/r8169_main.c=5400=static int rtl8169_request_irq(struct rtl8169_private *tp)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-5415-\t\t\t\trc = pci_request_irq(tp-\u003epci_dev, i,\ndrivers/net/ethernet/realtek/r8169_main.c:5416:\t\t\t\t\t\t     rtl8169_interrupt_other,\ndrivers/net/ethernet/realtek/r8169_main.c-5417-\t\t\t\t\t\t     NULL, tp, \"%s-%d\",\n"
}

13/2 2026/08/14 02:28 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 31002
							output: 50
							thoughts: 39
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Checking Interrupt Handlers and RX Descriptor Allocation**

Alright, so the architecture I'm working with is passing `rtl8169_interrupt_other` as a function pointer, just like I suspected. Good. That confirms a specific interrupt handling strategy within this network interface controller (NIC) driver.

Now, I need to dig deeper into the details of the receive path. Let's see... the next logical step to understand the flow is to examine how receive descriptors are allocated. Let's take a look at the `rtl8169_alloc_rx_desc` function. I want to see how the driver is setting up the ring buffer, how it's managing memory, and how it's preparing to receive incoming packets. Understanding this allocation process is key to grasping the overall receive data path efficiency and how interrupts and descriptors are interacting.



14/2 2026/08/14 02:28 tool grepper
0m Args:
{
  "Expression": "rtl8169_alloc_rx_desc",
  "PathPrefix": "drivers/net/ethernet/realtek/r8169_main.c"
}

Results:
{
  "Output": "drivers/net/ethernet/realtek/r8169_main.c=4479=static int rtl8169_rx_fill(struct rtl8169_private *tp, struct rtl8169_rx_ring *ring)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-4499-\ndrivers/net/ethernet/realtek/r8169_main.c:4500:static int rtl8169_alloc_rx_desc(struct rtl8169_private *tp)\ndrivers/net/ethernet/realtek/r8169_main.c-4501-{\n--\ndrivers/net/ethernet/realtek/r8169_main.c=5644=static int rtl_open(struct net_device *dev)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-5660-\ndrivers/net/ethernet/realtek/r8169_main.c:5661:\tif (rtl8169_alloc_rx_desc(tp) \u003c 0)\ndrivers/net/ethernet/realtek/r8169_main.c-5662-\t\tgoto err_free_rx_1;\n"
}

15/2 2026/08/14 02:28 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 31622
							output: 48
							thoughts: 30
						

Thoughts:
Okay, here's my interpretation of those thoughts, presented as a first-person expert summary:

**Analyzing Code Execution Context**

Right, so I see this function is only called once. That's a strong indicator that the compiler might be aggressively inlining it. Inlining is a common optimization, especially for smaller functions, which is something I need to keep in mind for my analysis, especially performance aspects. The calling context is critical, and a single call simplifies the control flow in this case.

Okay, let's dive deeper and verify my assumptions. I'm going to quickly check what's inside `rtl8169_init_rss`. Understanding that function and its interactions will give me the information I need.



16/2 2026/08/14 02:28 tool grepper
0m Args:
{
  "Expression": "rtl8169_init_rss",
  "PathPrefix": "drivers/net/ethernet/realtek/r8169_main.c"
}

Results:
{
  "Output": "drivers/net/ethernet/realtek/r8169_main.c=2796=static void rtl_hw_reset(struct rtl8169_private *tp)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-2802-\ndrivers/net/ethernet/realtek/r8169_main.c:2803:static void rtl8169_init_rss(struct rtl8169_private *tp)\ndrivers/net/ethernet/realtek/r8169_main.c-2804-{\n--\ndrivers/net/ethernet/realtek/r8169_main.c=6339=static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-6461-\tif (rtl_hw_support_rss(tp))\ndrivers/net/ethernet/realtek/r8169_main.c:6462:\t\trtl8169_init_rss(tp);\ndrivers/net/ethernet/realtek/r8169_main.c-6463-\n"
}

17/2 2026/08/14 02:28 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 32175
							output: 46
							thoughts: 23
						

Thoughts:
Here's my summary, as though I'm thinking through this myself:

**Quick Scan & Verification**

Okay, first things first, this function was only called once, so there's a good chance it got inlined. That's a key detail, it simplifies things *considerably* if it did. But I should confirm that before I dig in any deeper. Let's start by looking specifically at `rtl_set_rss_config`. I need to ensure the calling context doesn't reveal any surprising interactions or dependencies. I need to understand what it does and how.



18/2 2026/08/14 02:29 tool grepper
0m Args:
{
  "Expression": "rtl_set_rss_config",
  "PathPrefix": "drivers/net/ethernet/realtek/r8169_main.c"
}

Results:
{
  "Output": "drivers/net/ethernet/realtek/r8169_main.c=2971=static void rtl8169_set_rss_hash_opt(struct rtl8169_private *tp)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-2990-\ndrivers/net/ethernet/realtek/r8169_main.c:2991:static void rtl_set_rss_config(struct rtl8169_private *tp)\ndrivers/net/ethernet/realtek/r8169_main.c-2992-{\n--\ndrivers/net/ethernet/realtek/r8169_main.c=4340=static void rtl_hw_start(struct  rtl8169_private *tp)\n--\ndrivers/net/ethernet/realtek/r8169_main.c-4361-\t\tif (tp-\u003enum_rx_rings \u003e 1)\ndrivers/net/ethernet/realtek/r8169_main.c:4362:\t\t\trtl_set_rss_config(tp);\ndrivers/net/ethernet/realtek/r8169_main.c-4363-\t\telse\n"
}

19/2 2026/08/14 02:29 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 32689
							output: 173
							thoughts: 705
						

Thoughts:
**Analyzing Potential Optimization Targets**

Okay, so I'm looking at these functions, and it seems like they're only called once, which suggests they *could* be inlined by the compiler. That's good to keep in mind, because that potentially means less function call overhead and better performance. With that in mind, I want to zero in on the functions that *aren't* likely to be inlined, the ones called in a more dynamic way.

Right off the bat, I figure I should start with: `rtl8169_interrupt_msix`, `rtl8169_interrupt_other`, `rtl8169_poll_msix_rx`, and `rtl8169_poll_msix_tx`.

Hold on... `rtl8169_poll_msix_tx` is just a wrapper around `rtl_tx`, and `rtl8169_poll_msix_rx` is just a wrapper around `rtl_rx`. Both of those could be key, but the wrappers themselves are still important.

Let's check real quick if `rtl8169_rx_hash` is inlined... yup, it's `static inline`. Okay, good to know. And the same for `rtl8169_rx_csum` - also `static inline`. These aren't my primary targets.

Now, let's see what else isn't inlined or passed as a function pointer:
`rtl8169_set_rx_ring_num`, `rtl8169_get_channels`, `r8169_init_napi`, `r8169_free_napi`, `rtl8169_free_rx_desc`, `rtl8169_rx_desc_reset`, `rtl8169_napi_disable`, `rtl8169_napi_enable`, `rtl8169_rx_desc_init`, `rtl8169_store_rss_key`, `rtl8169_store_reta`, `rtl8169_set_rss_hash_opt`, `rtl8125_set_rx_q_num`, `rtl8169_hw_enable_vec_mapping`, `rtl8169_mark_to_asic`, `rtl8169_rx_clear`, `rtl8169_mark_as_last_descriptor`, `rtl8169_rx_desc_opts1`, `rtl8169_check_rx_desc_error`, `rtl8169_free_one_irq`, `rtl8169_free_irq`, `rtl8169_disable_hw_interrupt_msix`, `rtl8169_clear_hw_isr`, `rtl8169_enable_hw_interrupt_msix`, `rtl8169_request_irq`.

Most of these are static, which still leaves the possibility that they're inlined.

The ones I can guarantee *aren't* inlined are: `rtl8169_interrupt_msix`, `rtl8169_interrupt_other`, `rtl8169_poll_msix_rx`, `rtl8169_poll_msix_tx`, and `rtl8169_get_channels`.

These are my key targets. That's where I need to focus my efforts.

Finally, I'm just quickly confirming the configuration options: `R8169`, `NET_VENDOR_REALTEK`, and `PCI`. Let's just make sure those are all set and verified.



20/2 2026/08/14 02:29 tool set-results
0m Args:
{
  "EnableConfigs": [
    "R8169",
    "NET_VENDOR_REALTEK",
    "PCI"
  ],
  "FocusSymbols": [
    "rtl8169_interrupt_msix",
    "rtl8169_interrupt_other",
    "rtl8169_poll_msix_rx",
    "rtl8169_poll_msix_tx",
    "rtl8169_get_channels"
  ],
  "Reasoning": "The patch adds Receive Side Scaling (RSS) and multiple RX queues support to the Realtek r8169 network driver. It introduces new MSI-X interrupt handlers and NAPI polling functions for both RX and TX paths. The driver is reachable via PCI fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "R8169",
    "NET_VENDOR_REALTEK",
    "PCI"
  ],
  "FocusSymbols": [
    "rtl8169_interrupt_msix",
    "rtl8169_interrupt_other",
    "rtl8169_poll_msix_rx",
    "rtl8169_poll_msix_tx",
    "rtl8169_get_channels"
  ],
  "Reasoning": "The patch adds Receive Side Scaling (RSS) and multiple RX queues support to the Realtek r8169 network driver. It introduces new MSI-X interrupt handlers and NAPI polling functions for both RX and TX paths. The driver is reachable via PCI fuzzing.",
  "WorthFuzzing": true
}

21/1 2026/08/14 02:29 action If
0m Args:
{
  "WorthFuzzing": true
}

Results:
null

22/2 2026/08/14 02:29 agent kmsan-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "KMSANReasoning": "The patch series refactors the r8169 driver to support multiple RX queues, RSS, and MSI-X. It introduces new structures for RX rings, RSS data, and NAPI, which are all allocated using zeroing allocation functions (`kcalloc`, `devm_kzalloc`, `devm_alloc_etherdev_mqs`). The RX descriptors are allocated via `dma_alloc_coherent` and explicitly zeroed with `memset`. The ethtool callback `rtl8169_get_channels` only writes to the `ethtool_channels` struct, which is pre-zeroed by the ethtool core before the callback is invoked. Similarly, `rtl8169_get_stats64` updates the `rtnl_link_stats64` struct, which is also pre-zeroed by the networking core. All local variables and stack structures introduced in the patch are properly initialized before use. There are no new IOCTLs, netlink attributes, or other userspace interfaces introduced that could leak uninitialized memory. Any potential bugs introduced by these changes, such as out-of-bounds accesses or use-after-free, will be effectively caught by standard KASAN and other bug detectors. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit b16e1b25c7d702b5dbd0fd5deb3cc7a1653131cf
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 14 02:26:25 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index ec4fc21fa21fb..13372fc55eeef 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -29,6 +29,7 @@
 #include <linux/prefetch.h>
 #include <linux/ipv6.h>
 #include <linux/unaligned.h>
+#include <linux/u64_stats_sync.h>
 #include <net/ip6_checksum.h>
 #include <net/netdev_queues.h>
 #include <net/phy/realtek_phy.h>
@@ -74,9 +75,33 @@
 #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))
+
+/*
+ * Workaround for the hardware DMA prefetcher. The H/W might aggressively
+ * fetch one more descriptor even after hitting the RingEnd mark. We
+ * allocate this extra dummy space as padding to prevent out-of-bounds
+ * access and potential IOMMU faults.
+ */
+#define R8169_RX_RING_BYTES	((NUM_RX_DESC + 1) * 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_QUEUES	16
+#define R8169_MAX_RX_QUEUES	8
+#define R8169_DEFAULT_RX_QUEUES	1
+#define R8169_MAX_TX_QUEUES	1
+#define R8127_MAX_NUM_IRQVEC	32
+#define R8127_MIN_NUM_IRQVEC	30
+#define R8169_IRQ_DEFAULT	1
+#define RTL_RSS_KEY_SIZE	40
+#define RSS_CPU_NUM_MASK	GENMASK(18, 16)
+#define RSS_HASH_MASK		GENMASK(10, 8)
+#define RTL_MAX_INDIRECTION_TABLE_ENTRIES 128
+#define RXS_RSS_UDP		BIT(27)
+#define RXS_RSS_IPV4		BIT(28)
+#define RXS_RSS_IPV6		BIT(29)
+#define RXS_RSS_TCP		BIT(30)
+#define RXS_RSS_L3_TYPE_MASK	(RXS_RSS_IPV4 | RXS_RSS_IPV6)
+#define RXS_RSS_L4_TYPE_MASK	(RXS_RSS_TCP | RXS_RSS_UDP)
 
 #define OCP_STD_PHY_BASE	0xa400
 
@@ -441,11 +466,16 @@ enum rtl8125_registers {
 	TxPoll_8125		= 0x90,
 	LEDSEL3			= 0x96,
 	MAC0_BKP		= 0x19e0,
+	RDSAR_Q1_LOW		= 0x4000,
 	RSS_CTRL_8125		= 0x4500,
 	Q_NUM_CTRL_8125		= 0x4800,
 	EEE_TXIDLE_TIMER_8125	= 0x6048,
+	IMR_CLEAR_VEC_MAP_REG	= 0x0d00,
+	ISR_VEC_MAP_REG		= 0x0d04,
+	IMR_SET_VEC_MAP_REG	= 0x0d0c,
 };
 
+#define MSIX_ID_VEC_MAP_LINKCHG	29
 #define LEDSEL_MASK_8125	0x23f
 
 #define RX_VLAN_INNER_8125	BIT(22)
@@ -474,6 +504,9 @@ enum rtl_register_content {
 	RxRUNT	= (1 << 20),
 	RxCRC	= (1 << 19),
 
+	RXRUNT_RSS	= (1 << 21),
+	RXCRC_RSS	= (1 << 20),
+
 	/* ChipCmdBits */
 	StopReq		= 0x80,
 	CmdReset	= 0x10,
@@ -576,6 +609,23 @@ enum rtl_register_content {
 
 	/* magic enable v2 */
 	MagicPacket_v2	= (1 << 16),	/* Wake up when receives a Magic Packet */
+#define	ISRIMR_LINKCHG	BIT(29)
+#define	ISRIMR_TOK_Q0	BIT(8)
+#define	ISRIMR_ROK_Q0	BIT(0)
+#define RTL_DESC_TYPE_CTRL		0xd8
+#define RSS_KEY_REG			0x4600
+#define RSS_INDIRECTION_TBL_REG		0x4700
+#define RSS_CTRL_TCP_IPV4_SUPP		BIT(0)
+#define RTL_DESC_TYPE_RSS		BIT(1)
+#define RSS_CTRL_IPV4_SUPP		BIT(1)
+#define RSS_CTRL_TCP_IPV6_SUPP		BIT(2)
+#define RSS_CTRL_IPV6_SUPP		BIT(3)
+#define RSS_CTRL_IPV6_EXT_SUPP		BIT(4)
+#define RSS_CTRL_TCP_IPV6_EXT_SUPP	BIT(5)
+#define	RX_RES_RSS			BIT(22)
+#define	RX_RUNT_RSS			BIT(21)
+#define	RX_CRC_RSS			BIT(20)
+#define RTL_RX_Q_NUM_MASK		GENMASK(4, 2)
 };
 
 enum rtl_desc_bit {
@@ -633,6 +683,11 @@ enum rtl_rx_desc_bit {
 #define RxProtoIP	(PID1 | PID0)
 #define RxProtoMask	RxProtoIP
 
+#define	RX_UDPT_DESC_RSS	BIT(19)
+#define	RX_TCPT_DESC_RSS	BIT(18)
+#define	RX_UDPF_DESC_RSS	BIT(16) /* UDP/IP checksum failed */
+#define	RX_TCPF_DESC_RSS	BIT(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 */
@@ -654,9 +709,27 @@ struct TxDesc {
 };
 
 struct RxDesc {
-	__le32 opts1;
-	__le32 opts2;
-	__le64 addr;
+	union {
+		/* RX_DESC_TYPE_DEFAULT */
+		struct {
+			__le32 opts1;
+			__le32 opts2;
+			__le64 addr;
+		};
+
+		/* RX_DESC_TYPE_RSS */
+		struct {
+			union {
+				__le64 rss_addr;
+				struct {
+					__le32 rss_info;
+					__le32 rss_result;
+				} rss_dword;
+			};
+			__le32 rss_opts2;
+			__le32 rss_opts1;
+		};
+	};
 };
 
 struct ring_info {
@@ -728,27 +801,57 @@ enum rtl_dash_type {
 	RTL_DASH_25_BP,
 };
 
+enum rx_desc_type {
+	RX_DESC_TYPE_DEFAULT,
+	RX_DESC_TYPE_RSS,
+};
+
+struct rtl8169_rx_ring {
+	u32 cur_rx;
+	u32 dirty_rx;
+	struct RxDesc *rx_desc_array;
+	dma_addr_t rx_desc_phy_addr[NUM_RX_DESC];
+	dma_addr_t rx_phy_addr;
+	struct page *rx_databuff[NUM_RX_DESC];
+
+	struct {
+		u64 rx_errors;
+		u64 rx_dropped;
+		u64 rx_length_errors;
+		u64 rx_crc_errors;
+		u64 multicast;
+		struct u64_stats_sync syncp;
+	} stats;
+};
+
+struct rtl8169_rss_data {
+	u8 rss_key[RTL_RSS_KEY_SIZE];
+	u8 rss_indir_tbl[RTL_MAX_INDIRECTION_TABLE_ENTRIES];
+	unsigned int hw_supp_indir_tbl_entries;
+};
+
 struct rtl8169_private {
 	void __iomem *mmio_addr;	/* memory map physical address */
 	struct pci_dev *pci_dev;
 	struct net_device *dev;
 	struct phy_device *phydev;
-	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. */
 	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 napi_struct *rtl8169_napi;
+	struct rtl8169_rx_ring *rx_ring;
+	unsigned int num_rx_rings;
 	u16 cp_cmd;
 	u16 tx_lpi_timer;
 	u32 irq_mask;
-	int irq;
+	unsigned int hw_supp_num_rx_queues;
+	struct rtl8169_rss_data *rss_data;
+	unsigned int irq_nvecs;
+	enum rx_desc_type init_rx_desc_type;
 	struct clk *clk;
 
 	struct {
@@ -1578,6 +1681,11 @@ static bool rtl_dash_is_enabled(struct rtl8169_private *tp)
 	}
 }
 
+static bool rtl_hw_support_rss(struct rtl8169_private *tp)
+{
+	return tp->mac_version == RTL_GIGA_MAC_VER_80;
+}
+
 static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp)
 {
 	switch (tp->mac_version) {
@@ -1644,26 +1752,38 @@ 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))
-		RTL_W32(tp, IntrStatus_8125, bits);
-	else
+	if (rtl_is_8125(tp)) {
+		if (tp->irq_nvecs > 1)
+			RTL_W32(tp, ISR_VEC_MAP_REG, bits);
+		else
+			RTL_W32(tp, IntrStatus_8125, bits);
+	} else {
 		RTL_W16(tp, IntrStatus, bits);
+	}
 }
 
 static void rtl_irq_disable(struct rtl8169_private *tp)
 {
-	if (rtl_is_8125(tp))
-		RTL_W32(tp, IntrMask_8125, 0);
-	else
+	if (rtl_is_8125(tp)) {
+		if (tp->irq_nvecs > 1)
+			RTL_W32(tp, IMR_CLEAR_VEC_MAP_REG, 0xffffffff);
+		else
+			RTL_W32(tp, IntrMask_8125, 0);
+	} 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->irq_nvecs > 1)
+			RTL_W32(tp, IMR_SET_VEC_MAP_REG, 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)
@@ -1867,9 +1987,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->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		opts2 = le32_to_cpu(desc->rss_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));
@@ -2416,34 +2547,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 */
@@ -2619,9 +2722,26 @@ 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->rx_desc_array, 0x0, R8169_RX_RING_BYTES);
+	}
+}
+
 static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
 {
-	tp->dirty_tx = tp->cur_tx = tp->cur_rx = 0;
+	tp->dirty_tx = 0;
+	tp->cur_tx = 0;
+
+	for (int i = 0; i < tp->num_rx_rings; i++) {
+		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
+
+		ring->dirty_rx = 0;
+		ring->cur_rx = 0;
+	}
 }
 
 static void rtl_jumbo_config(struct rtl8169_private *tp)
@@ -2680,6 +2800,29 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
 	rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
 }
 
+static void rtl8169_init_rss(struct rtl8169_private *tp)
+{
+	for (int i = 0; i < tp->rss_data->hw_supp_indir_tbl_entries; i++)
+		tp->rss_data->rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, tp->num_rx_rings);
+
+	netdev_rss_key_fill(tp->rss_data->rss_key, RTL_RSS_KEY_SIZE);
+}
+
+static void rtl_setup_rx_params(struct rtl8169_private *tp)
+{
+	tp->num_rx_rings = 1;
+	switch (tp->mac_version) {
+	case RTL_GIGA_MAC_VER_80:
+		tp->hw_supp_num_rx_queues = R8169_MAX_RX_QUEUES;
+		tp->rss_data->hw_supp_indir_tbl_entries = RTL_MAX_INDIRECTION_TABLE_ENTRIES;
+		break;
+	default:
+		tp->hw_supp_num_rx_queues = R8169_DEFAULT_RX_QUEUES;
+		break;
+	}
+	tp->init_rx_desc_type = RX_DESC_TYPE_DEFAULT;
+}
+
 static void rtl_request_firmware(struct rtl8169_private *tp)
 {
 	struct rtl_fw *rtl_fw;
@@ -2802,8 +2945,60 @@ 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)
+{
+	u8 *rss_key = tp->rss_data->rss_key;
+	const u16 rss_key_reg = RSS_KEY_REG;
+
+	/* Write redirection table to HW */
+	for (int i = 0; i < RTL_RSS_KEY_SIZE; i += sizeof(u32))
+		RTL_W32(tp, rss_key_reg + i, get_unaligned_le32(rss_key + i));
+}
+
+static void rtl8169_store_reta(struct rtl8169_private *tp)
+{
+	u8 *indir_tbl = tp->rss_data->rss_indir_tbl;
+	unsigned int i;
+
+	/* Write redirection table to HW */
+	for (i = 0; i < tp->rss_data->hw_supp_indir_tbl_entries; i += 4) {
+		u32 reta = get_unaligned_le32(&indir_tbl[i]);
+
+		RTL_W32(tp, RSS_INDIRECTION_TBL_REG + i, reta);
+	}
+}
+
+static void rtl8169_set_rss_hash_opt(struct rtl8169_private *tp)
+{
+	u32 rss_ctrl;
+
+	rss_ctrl = FIELD_PREP(RSS_CPU_NUM_MASK, ilog2(tp->num_rx_rings));
+
+	/* Perform hash on these packet types */
+	rss_ctrl |= RSS_CTRL_TCP_IPV4_SUPP |
+		    RSS_CTRL_IPV4_SUPP |
+		    RSS_CTRL_IPV6_SUPP |
+		    RSS_CTRL_IPV6_EXT_SUPP |
+		    RSS_CTRL_TCP_IPV6_SUPP |
+		    RSS_CTRL_TCP_IPV6_EXT_SUPP;
+
+	rss_ctrl |= FIELD_PREP(RSS_HASH_MASK,
+			       ilog2(tp->rss_data->hw_supp_indir_tbl_entries));
+
+	RTL_W32(tp, RSS_CTRL_8125, rss_ctrl);
+}
+
+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)
 {
+	struct rtl8169_rx_ring *ring = &tp->rx_ring[0];
+
 	/*
 	 * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh
 	 * register to be written before TxDescAddrLow to work.
@@ -2811,8 +3006,18 @@ static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp)
 	 */
 	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));
+	RTL_W32(tp, RxDescAddrHigh, ((u64)ring->rx_phy_addr) >> 32);
+	RTL_W32(tp, RxDescAddrLow,
+		((u64)ring->rx_phy_addr) & DMA_BIT_MASK(32));
+
+	for (int i = 1; i < tp->num_rx_rings; i++) {
+		unsigned int rdsar_reg = RDSAR_Q1_LOW + (i - 1) * 8;
+		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
+
+		RTL_W32(tp, rdsar_reg + 4, ((u64)ring->rx_phy_addr >> 32));
+		RTL_W32(tp, rdsar_reg,
+			((u64)ring->rx_phy_addr) & DMA_BIT_MASK(32));
+	}
 }
 
 static void rtl8169_set_magic_reg(struct rtl8169_private *tp)
@@ -3858,6 +4063,27 @@ DECLARE_RTL_COND(rtl_mac_ocp_e00e_cond)
 	return r8168_mac_ocp_read(tp, 0xe00e) & BIT(13);
 }
 
+static void rtl8125_set_rx_q_num(struct rtl8169_private *tp)
+{
+	u16 rx_q_num;
+	u16 q_ctrl;
+
+	rx_q_num = ilog2(tp->num_rx_rings);
+	q_ctrl = RTL_R16(tp, Q_NUM_CTRL_8125);
+	q_ctrl &= ~RTL_RX_Q_NUM_MASK;
+	q_ctrl |= FIELD_PREP(RTL_RX_Q_NUM_MASK, rx_q_num);
+	RTL_W16(tp, Q_NUM_CTRL_8125, q_ctrl);
+}
+
+static void rtl8169_hw_enable_vec_mapping(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);
@@ -3866,6 +4092,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->irq_nvecs > 1)
+		rtl8169_hw_enable_vec_mapping(tp);
+
 	/* disable UPS */
 	r8168_mac_ocp_modify(tp, 0xd40a, 0x0010, 0x0000);
 
@@ -3885,6 +4114,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->num_rx_rings > 1) {
+		rtl8125_set_rx_q_num(tp);
+		RTL_W8(tp, RTL_DESC_TYPE_CTRL,
+		       RTL_R8(tp, RTL_DESC_TYPE_CTRL) | RTL_DESC_TYPE_RSS);
+	}
+
 	if (tp->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)
@@ -4121,6 +4357,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->num_rx_rings > 1)
+			rtl_set_rss_config(tp);
+		else
+			RTL_W32(tp, RSS_CTRL_8125, 0x00);
+	}
 	rtl_lock_config_regs(tp);
 
 	rtl_jumbo_config(tp);
@@ -4148,19 +4390,32 @@ 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(struct rtl8169_private *tp, struct RxDesc *desc)
 {
-	u32 eor = le32_to_cpu(desc->opts1) & RingEnd;
+	u32 eor;
 
-	desc->opts2 = 0;
-	/* Force memory writes to complete before releasing descriptor */
-	dma_wmb();
-	WRITE_ONCE(desc->opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		eor = le32_to_cpu(desc->rss_opts1) & RingEnd;
+		desc->rss_opts2 = cpu_to_le32(0);
+		/* Force memory writes to complete before releasing descriptor */
+		dma_wmb();
+		WRITE_ONCE(desc->rss_opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
+		break;
+	default:
+		eor = le32_to_cpu(desc->opts1) & RingEnd;
+		desc->opts2 = cpu_to_le32(0);
+		/* Force memory writes to complete before releasing descriptor */
+		dma_wmb();
+		WRITE_ONCE(desc->opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE));
+		break;
+	}
 }
 
 static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
-					  struct RxDesc *desc)
+					  struct rtl8169_rx_ring *ring, unsigned int index)
 {
+	struct RxDesc *desc = ring->rx_desc_array + index;
 	struct device *d = tp_to_dev(tp);
 	int node = dev_to_node(d);
 	dma_addr_t mapping;
@@ -4177,56 +4432,128 @@ static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
 		return NULL;
 	}
 
-	desc->addr = cpu_to_le64(mapping);
-	rtl8169_mark_to_asic(desc);
+	ring->rx_desc_phy_addr[index] = mapping;
+	if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+		desc->rss_addr = cpu_to_le64(mapping);
+	else
+		desc->addr = cpu_to_le64(mapping);
+	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->rx_desc_phy_addr[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->rx_desc_phy_addr[i] = 0;
+		if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS) {
+			ring->rx_desc_array[i].rss_addr = 0;
+			ring->rx_desc_array[i].rss_opts1 = 0;
+		} else {
+			ring->rx_desc_array[i].addr = 0;
+			ring->rx_desc_array[i].opts1 = 0;
+		}
+	}
+}
+
+static void rtl8169_mark_as_last_descriptor(struct rtl8169_private *tp, struct RxDesc *desc)
+{
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		desc->rss_opts1 |= cpu_to_le32(RingEnd);
+		break;
+	default:
+		desc->opts1 |= cpu_to_le32(RingEnd);
+		break;
 	}
 }
 
-static int rtl8169_rx_fill(struct rtl8169_private *tp)
+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->rx_desc_array[NUM_RX_DESC - 1]);
 
 	return 0;
 }
 
+static int rtl8169_alloc_rx_desc(struct rtl8169_private *tp)
+{
+	struct pci_dev *pdev = tp->pci_dev;
+	struct rtl8169_rx_ring *ring;
+
+	for (int i = 0; i < tp->num_rx_rings; i++) {
+		ring = &tp->rx_ring[i];
+		ring->rx_desc_array = dma_alloc_coherent(&pdev->dev,
+							 R8169_RX_RING_BYTES,
+							 &ring->rx_phy_addr,
+							 GFP_KERNEL);
+		if (!ring->rx_desc_array)
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+static void rtl8169_free_rx_desc(struct rtl8169_private *tp)
+{
+	struct pci_dev *pdev = tp->pci_dev;
+	struct rtl8169_rx_ring *ring;
+
+	for (int i = 0; i < tp->num_rx_rings; i++) {
+		ring = &tp->rx_ring[i];
+		if (ring->rx_desc_array) {
+			dma_free_coherent(&pdev->dev,
+					  R8169_RX_RING_BYTES,
+					  ring->rx_desc_array,
+					  ring->rx_phy_addr);
+			ring->rx_desc_array = NULL;
+		}
+	}
+}
+
 static int rtl8169_init_ring(struct rtl8169_private *tp)
 {
+	int i, ret;
+
 	rtl8169_init_ring_indexes(tp);
+	rtl8169_rx_desc_init(tp);
 
 	memset(tp->tx_skb, 0, sizeof(tp->tx_skb));
-	memset(tp->Rx_databuff, 0, sizeof(tp->Rx_databuff));
 
-	return rtl8169_rx_fill(tp);
+	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));
+		ret = rtl8169_rx_fill(tp, ring);
+		if (ret < 0)
+			goto err_clear;
+	}
+	return 0;
+
+err_clear:
+	while (--i >= 0)
+		rtl8169_rx_clear(tp, &tp->rx_ring[i]);
+	return ret;
 }
 
 static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp, unsigned int entry)
@@ -4266,9 +4593,25 @@ static void rtl8169_tx_clear(struct rtl8169_private *tp)
 	netdev_reset_queue(tp->dev);
 }
 
+static void rtl8169_napi_disable(struct rtl8169_private *tp)
+{
+	int napi_num = min(tp->irq_nvecs, R8169_MAX_QUEUES);
+
+	for (int i = 0; i < napi_num; i++)
+		napi_disable(&tp->rtl8169_napi[i]);
+}
+
+static void rtl8169_napi_enable(struct rtl8169_private *tp)
+{
+	int napi_num = min(tp->irq_nvecs, R8169_MAX_QUEUES);
+
+	for (int i = 0; i < napi_num; i++)
+		napi_enable(&tp->rtl8169_napi[i]);
+}
+
 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();
@@ -4303,18 +4646,30 @@ 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 < NUM_RX_DESC; j++) {
+			dma_addr_t phy_addr = ring->rx_desc_phy_addr[j];
+
+			if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+				ring->rx_desc_array[j].rss_addr = cpu_to_le64(phy_addr);
+			rtl8169_mark_to_asic(tp, ring->rx_desc_array + j);
+		}
+	}
+}
 
+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);
 
-	napi_enable(&tp->napi);
+	rtl8169_napi_enable(tp);
 	rtl_hw_start(tp);
 }
 
@@ -4758,30 +5113,111 @@ 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_hash(struct rtl8169_private *tp,
+				   struct RxDesc *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->rss_dword.rss_info);
+
+	if (!(rss_header_info & RXS_RSS_L3_TYPE_MASK))
+		return;
+
+	hash_val = le32_to_cpu(desc->rss_dword.rss_result);
+
+	skb_set_hash(skb, hash_val,
+		     (RXS_RSS_L4_TYPE_MASK & rss_header_info) ?
+		     PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3);
+}
+
+static inline void rtl8169_rx_csum(struct rtl8169_private *tp,
+				   struct sk_buff *skb,
+				   u32 opts1)
 {
-	u32 status = opts1 & (RxProtoMask | RxCSFailMask);
+	bool csum_ok = false;
 
-	if (status == RxProtoTCP || status == RxProtoUDP)
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		if (((opts1 & RX_TCPT_DESC_RSS) && !(opts1 & RX_TCPF_DESC_RSS)) ||
+		    ((opts1 & RX_UDPT_DESC_RSS) && !(opts1 & RX_UDPF_DESC_RSS)))
+			csum_ok = true;
+		break;
+	default: {
+		u32 status = opts1 & (RxProtoMask | RxCSFailMask);
+
+		if (status == RxProtoTCP || status == RxProtoUDP)
+			csum_ok = true;
+		break;
+	}
+	}
+
+	if (csum_ok)
 		skb->ip_summed = CHECKSUM_UNNECESSARY;
 	else
 		skb_checksum_none_assert(skb);
 }
 
-static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget)
+static __le32 rtl8169_rx_desc_opts1(struct rtl8169_private *tp, struct RxDesc *desc)
+{
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		return READ_ONCE(desc->rss_opts1);
+	default:
+		return READ_ONCE(desc->opts1);
+	}
+}
+
+static bool rtl8169_check_rx_desc_error(struct rtl8169_rx_ring *ring,
+					struct rtl8169_private *tp,
+					u32 status)
+{
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		if (unlikely(status & RX_RES_RSS)) {
+			u64_stats_update_begin(&ring->stats.syncp);
+			if (status & RX_RUNT_RSS)
+				ring->stats.rx_length_errors++;
+			if (status & RX_CRC_RSS)
+				ring->stats.rx_crc_errors++;
+			u64_stats_update_end(&ring->stats.syncp);
+			return true;
+		}
+		break;
+	default:
+		if (unlikely(status & RxRES)) {
+			u64_stats_update_begin(&ring->stats.syncp);
+			if (status & (RxRWT | RxRUNT))
+				ring->stats.rx_length_errors++;
+			if (status & RxCRC)
+				ring->stats.rx_crc_errors++;
+			u64_stats_update_end(&ring->stats.syncp);
+			return true;
+		}
+	}
+	return false;
+}
+
+static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
+		  struct rtl8169_rx_ring *ring, int budget,
+		  struct napi_struct *napi)
 {
 	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 % NUM_RX_DESC;
+		struct RxDesc *desc = ring->rx_desc_array + entry;
 		struct sk_buff *skb;
 		const void *rx_buf;
 		dma_addr_t addr;
 		u32 status;
 
-		status = le32_to_cpu(READ_ONCE(desc->opts1));
+		status = le32_to_cpu(rtl8169_rx_desc_opts1(tp, desc));
 		if (status & DescOwn)
 			break;
 
@@ -4791,20 +5227,24 @@ 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(ring, tp, status)) {
 			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++;
+			u64_stats_update_begin(&ring->stats.syncp);
+			ring->stats.rx_errors++;
+			u64_stats_update_end(&ring->stats.syncp);
 
 			if (!(dev->features & NETIF_F_RXALL))
 				goto release_descriptor;
-			else if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
-				goto release_descriptor;
+
+			if (tp->init_rx_desc_type == RX_DESC_TYPE_DEFAULT) {
+				if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
+					goto release_descriptor;
+			} else {
+				if (!(status & (RXRUNT_RSS | RXCRC_RSS)))
+					goto release_descriptor;
+			}
 		}
 
 		pkt_size = status & GENMASK(13, 0);
@@ -4815,19 +5255,23 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
 		 * They are seen as a symptom of over-mtu sized frames.
 		 */
 		if (unlikely(rtl8169_fragmented_frame(status))) {
-			dev->stats.rx_dropped++;
-			dev->stats.rx_length_errors++;
+			u64_stats_update_begin(&ring->stats.syncp);
+			ring->stats.rx_dropped++;
+			ring->stats.rx_length_errors++;
+			u64_stats_update_end(&ring->stats.syncp);
 			goto release_descriptor;
 		}
 
-		skb = napi_alloc_skb(&tp->napi, pkt_size);
+		skb = napi_alloc_skb(napi, pkt_size);
 		if (unlikely(!skb)) {
-			dev->stats.rx_dropped++;
+			u64_stats_update_begin(&ring->stats.syncp);
+			ring->stats.rx_dropped++;
+			u64_stats_update_end(&ring->stats.syncp);
 			goto release_descriptor;
 		}
 
-		addr = le64_to_cpu(desc->addr);
-		rx_buf = page_address(tp->Rx_databuff[entry]);
+		addr = ring->rx_desc_phy_addr[entry];
+		rx_buf = page_address(ring->rx_databuff[entry]);
 
 		dma_sync_single_for_cpu(d, addr, pkt_size, DMA_FROM_DEVICE);
 		prefetch(rx_buf);
@@ -4836,19 +5280,26 @@ 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);
+		if (tp->num_rx_rings > 1)
+			rtl8169_rx_hash(tp, desc, skb);
+		rtl8169_rx_csum(tp, skb, status);
 		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++;
+		if (skb->pkt_type == PACKET_MULTICAST) {
+			u64_stats_update_begin(&ring->stats.syncp);
+			ring->stats.multicast++;
+			u64_stats_update_end(&ring->stats.syncp);
+		}
 
-		napi_gro_receive(&tp->napi, skb);
+		napi_gro_receive(napi, skb);
 
 		dev_sw_netstats_rx_add(dev, pkt_size);
 release_descriptor:
-		rtl8169_mark_to_asic(desc);
+		if (tp->init_rx_desc_type == RX_DESC_TYPE_RSS)
+			desc->rss_addr = cpu_to_le64(ring->rx_desc_phy_addr[entry]);
+		rtl8169_mark_to_asic(tp, desc);
 	}
 
 	return count;
@@ -4856,8 +5307,12 @@ 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;
-	u32 status = rtl_get_events(tp);
+	struct napi_struct *napi = dev_instance;
+	struct rtl8169_private *tp;
+	u32 status;
+
+	tp = netdev_priv(napi->dev);
+	status = rtl_get_events(tp);
 
 	if ((status & 0xffff) == 0xffff || !(status & tp->irq_mask))
 		return IRQ_NONE;
@@ -4873,13 +5328,114 @@ 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);
 out:
 	rtl_ack_events(tp, status);
 
 	return IRQ_HANDLED;
 }
 
+static void rtl8169_free_one_irq(struct rtl8169_private *tp, int i)
+{
+	if (tp->irq_nvecs > 1) {
+		if (i < R8169_MAX_QUEUES)
+			pci_free_irq(tp->pci_dev, i, &tp->rtl8169_napi[i]);
+		else if (i == MSIX_ID_VEC_MAP_LINKCHG)
+			pci_free_irq(tp->pci_dev, i, tp);
+	} else {
+		pci_free_irq(tp->pci_dev, i, &tp->rtl8169_napi[i]);
+	}
+}
+
+static void rtl8169_free_irq(struct rtl8169_private *tp)
+{
+	for (int i = 0; i < tp->irq_nvecs; i++)
+		rtl8169_free_one_irq(tp, i);
+}
+
+static void rtl8169_disable_hw_interrupt_msix(struct rtl8169_private *tp,
+					      int message_id)
+{
+	RTL_W32(tp, IMR_CLEAR_VEC_MAP_REG, BIT(message_id));
+}
+
+static void rtl8169_clear_hw_isr(struct rtl8169_private *tp, int message_id)
+{
+	RTL_W32(tp, ISR_VEC_MAP_REG, BIT(message_id));
+}
+
+static void rtl8169_enable_hw_interrupt_msix(struct rtl8169_private *tp,
+					     int message_id)
+{
+	RTL_W32(tp, IMR_SET_VEC_MAP_REG, BIT(message_id));
+}
+
+static irqreturn_t rtl8169_interrupt_msix(int irq, void *dev_instance)
+{
+	struct napi_struct *napi = dev_instance;
+	struct net_device *dev = napi->dev;
+	struct rtl8169_private *tp;
+	int message_id;
+
+	tp = netdev_priv(dev);
+	message_id = napi - tp->rtl8169_napi;
+
+	rtl8169_disable_hw_interrupt_msix(tp, message_id);
+	rtl8169_clear_hw_isr(tp, message_id);
+
+	napi_schedule(napi);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t rtl8169_interrupt_other(int irq, void *dev_instance)
+{
+	struct rtl8169_private *tp = dev_instance;
+
+	rtl8169_clear_hw_isr(tp, MSIX_ID_VEC_MAP_LINKCHG);
+	phy_mac_interrupt(tp->phydev);
+	return IRQ_HANDLED;
+}
+
+static int rtl8169_request_irq(struct rtl8169_private *tp)
+{
+	struct net_device *dev = tp->dev;
+	struct napi_struct *napi;
+	int i, rc;
+
+	for (i = 0; i < tp->irq_nvecs; i++) {
+		napi = &tp->rtl8169_napi[i];
+		if (tp->irq_nvecs > 1) {
+			if (i < R8169_MAX_QUEUES)
+				rc = pci_request_irq(tp->pci_dev, i,
+						     rtl8169_interrupt_msix,
+						     NULL, napi, "%s-%d",
+						     dev->name, i);
+			else if (i == MSIX_ID_VEC_MAP_LINKCHG)
+				rc = pci_request_irq(tp->pci_dev, i,
+						     rtl8169_interrupt_other,
+						     NULL, tp, "%s-%d",
+						     dev->name, i);
+			else
+				continue;
+		} else {
+			rc = pci_request_irq(tp->pci_dev, i,
+					     rtl8169_interrupt,
+					     NULL, napi, "%s-%d",
+					     dev->name, i);
+		}
+
+		if (rc)
+			goto free_irq;
+	}
+	return 0;
+
+free_irq:
+	while (--i >= 0)
+		rtl8169_free_one_irq(tp, i);
+	return rc;
+}
+
 static void rtl_task(struct work_struct *work)
 {
 	struct rtl8169_private *tp =
@@ -4914,13 +5470,14 @@ 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 net_device *dev = tp->dev;
-	int work_done;
+	struct rtl8169_private *tp = netdev_priv(napi->dev);
+	struct net_device *dev = napi->dev;
+	int work_done = 0;
 
 	rtl_tx(dev, tp, budget);
 
-	work_done = rtl_rx(dev, tp, budget);
+	/* rtl8169_poll() is used only when there is a single RX ring. */
+	work_done = rtl_rx(dev, tp, &tp->rx_ring[0], budget, napi);
 
 	if (work_done < budget && napi_complete_done(napi, work_done))
 		rtl_irq_enable(tp);
@@ -5035,7 +5592,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);
 
@@ -5051,18 +5608,17 @@ 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]);
 
-	free_irq(tp->irq, tp);
+	rtl8169_free_irq(tp);
 
 	phy_disconnect(tp->phydev);
 
-	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_rx_desc(tp);
 
 	pm_runtime_put_sync(&pdev->dev);
 
@@ -5074,7 +5630,14 @@ static void rtl8169_netpoll(struct net_device *dev)
 {
 	struct rtl8169_private *tp = netdev_priv(dev);
 
-	rtl8169_interrupt(tp->irq, tp);
+	for (int i = 0; i < tp->irq_nvecs; i++) {
+		if (tp->irq_nvecs > 1)
+			rtl8169_interrupt_msix(pci_irq_vector(tp->pci_dev, i),
+					       &tp->rtl8169_napi[i]);
+		else
+			rtl8169_interrupt(pci_irq_vector(tp->pci_dev, i),
+					  &tp->rtl8169_napi[i]);
+	}
 }
 #endif
 
@@ -5082,7 +5645,6 @@ static int rtl_open(struct net_device *dev)
 {
 	struct rtl8169_private *tp = netdev_priv(dev);
 	struct pci_dev *pdev = tp->pci_dev;
-	unsigned long irqflags;
 	int retval = -ENOMEM;
 
 	pm_runtime_get_sync(&pdev->dev);
@@ -5096,10 +5658,8 @@ static int rtl_open(struct net_device *dev)
 	if (!tp->TxDescArray)
 		goto out;
 
-	tp->RxDescArray = dma_alloc_coherent(&pdev->dev, R8169_RX_RING_BYTES,
-					     &tp->RxPhyAddr, GFP_KERNEL);
-	if (!tp->RxDescArray)
-		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)
@@ -5107,8 +5667,7 @@ 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;
 
@@ -5125,15 +5684,13 @@ 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);
+	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;
-err_free_tx_0:
+	rtl8169_free_rx_desc(tp);
 	dma_free_coherent(&pdev->dev, R8169_TX_RING_BYTES, tp->TxDescArray,
 			  tp->TxPhyAddr);
 	tp->TxDescArray = NULL;
@@ -5152,6 +5709,27 @@ rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 	netdev_stats_to_stats64(stats, &dev->stats);
 	dev_fetch_sw_netstats(stats, dev->tstats);
 
+	for (int i = 0; i < tp->num_rx_rings; i++) {
+		u64 errors, dropped, length_errors, crc_errors, multicast;
+		struct rtl8169_rx_ring *ring = &tp->rx_ring[i];
+		unsigned int start;
+
+		do {
+			start = u64_stats_fetch_begin(&ring->stats.syncp);
+			errors = ring->stats.rx_errors;
+			dropped = ring->stats.rx_dropped;
+			length_errors = ring->stats.rx_length_errors;
+			crc_errors = ring->stats.rx_crc_errors;
+			multicast = ring->stats.multicast;
+		} while (u64_stats_fetch_retry(&ring->stats.syncp, start));
+
+		stats->rx_errors += errors;
+		stats->rx_dropped += dropped;
+		stats->rx_length_errors += length_errors;
+		stats->rx_crc_errors += crc_errors;
+		stats->multicast += multicast;
+	}
+
 	/*
 	 * Fetch additional counter values missing in stats collected by driver
 	 * from tally counters.
@@ -5275,6 +5853,16 @@ static void rtl_shutdown(struct pci_dev *pdev)
 		pci_prepare_to_sleep(pdev);
 }
 
+static void r8169_free_napi(struct rtl8169_private *tp)
+{
+	int napi_num = min(tp->irq_nvecs, R8169_MAX_QUEUES);
+
+	for (int i = 0; i < napi_num; i++)
+		netif_napi_del(&tp->rtl8169_napi[i]);
+
+	kfree(tp->rtl8169_napi);
+}
+
 static void rtl_remove_one(struct pci_dev *pdev)
 {
 	struct rtl8169_private *tp = pci_get_drvdata(pdev);
@@ -5289,6 +5877,9 @@ static void rtl_remove_one(struct pci_dev *pdev)
 
 	unregister_netdev(tp->dev);
 
+	r8169_free_napi(tp);
+	kfree(tp->rx_ring);
+
 	if (tp->dash_type != RTL_DASH_NONE)
 		rtl8168_driver_stop(tp);
 
@@ -5320,15 +5911,49 @@ 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->irq_nvecs > 1) {
+		tp->irq_mask = ISRIMR_LINKCHG | ISRIMR_TOK_Q0;
+		for (int i = 0; i < tp->num_rx_rings; i++)
+			tp->irq_mask |= ISRIMR_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 get_max_irq_nvecs(struct rtl8169_private *tp)
+{
+	if (tp->mac_version == RTL_GIGA_MAC_VER_80)
+		return R8127_MAX_NUM_IRQVEC;
+	return R8169_IRQ_DEFAULT;
+}
+
+static int get_min_irq_nvecs(struct rtl8169_private *tp)
+{
+	if (tp->mac_version == RTL_GIGA_MAC_VER_80)
+		return R8127_MIN_NUM_IRQVEC;
+	return R8169_IRQ_DEFAULT;
+}
+
+static void rtl8169_set_rx_ring_num(struct rtl8169_private *tp)
+{
+	if (tp->irq_nvecs >= get_min_irq_nvecs(tp)) {
+		unsigned int rss_queue_num = netif_get_num_default_rss_queues();
+
+		tp->num_rx_rings = rounddown_pow_of_two(min(rss_queue_num,
+							    tp->hw_supp_num_rx_queues));
+		if (tp->num_rx_rings >= 2)
+			tp->init_rx_desc_type = RX_DESC_TYPE_RSS;
+	}
 }
 
 static int rtl_alloc_irq(struct rtl8169_private *tp)
 {
+	struct pci_dev *pdev = tp->pci_dev;
 	unsigned int flags;
+	int nvecs;
 
 	switch (tp->mac_version) {
 	case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06:
@@ -5344,7 +5969,18 @@ 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, get_min_irq_nvecs(tp),
+				      get_max_irq_nvecs(tp), flags);
+
+	if (nvecs < 0)
+		nvecs = pci_alloc_irq_vectors(pdev, 1, 1, flags);
+
+	if (nvecs < 0)
+		return nvecs;
+
+	tp->irq_nvecs = nvecs;
+
+	return 0;
 }
 
 static void rtl_read_mac_address(struct rtl8169_private *tp,
@@ -5599,6 +6235,107 @@ 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 net_device *dev = napi->dev;
+	struct rtl8169_private *tp;
+	int work_done = 0;
+	int message_id;
+
+	tp = netdev_priv(dev);
+	message_id = napi - tp->rtl8169_napi;
+
+	if (message_id < tp->num_rx_rings)
+		work_done += rtl_rx(dev, tp, &tp->rx_ring[message_id],
+				    budget, napi);
+
+	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 net_device *dev = napi->dev;
+	struct rtl8169_private *tp;
+
+	tp = netdev_priv(dev);
+
+	/* Currently r8169 only supports a single Tx ring.
+	 * Therefore, we don't need a per-ring Tx processing loop here.
+	 */
+	rtl_tx(dev, tp, budget);
+
+	if (napi_complete_done(napi, 0))
+		rtl8169_enable_hw_interrupt_msix(tp, (int)(napi - tp->rtl8169_napi));
+
+	return 0;
+}
+
+/* RTL8127 MSI-X vector layout:
+ * Vectors 0 .. (RxQs - 1)		: Rx Queues
+ * Vectors RxQs .. (RxQs + TxQs - 1)	: Tx Queues
+ * NAPI is only allocated for data path
+ */
+static void r8169_init_napi(struct rtl8169_private *tp)
+{
+	int napi_num = min(tp->irq_nvecs, R8169_MAX_QUEUES);
+
+	for (int i = 0; i < napi_num; i++) {
+		int (*poll_fn)(struct napi_struct *, int) = rtl8169_poll;
+
+		if (tp->irq_nvecs > 1) {
+			if (i < R8169_MAX_RX_QUEUES)
+				poll_fn = rtl8169_poll_msix_rx;
+			else
+				poll_fn = rtl8169_poll_msix_tx;
+		}
+		netif_napi_add(tp->dev, &tp->rtl8169_napi[i], poll_fn);
+	}
+}
+
+static void rtl8169_get_channels(struct net_device *dev,
+				 struct ethtool_channels *ch)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	ch->max_rx = tp->hw_supp_num_rx_queues;
+	ch->max_tx = 1;
+
+	ch->rx_count = tp->num_rx_rings;
+	ch->tx_count = 1;
+}
+
+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_channels		= rtl8169_get_channels,
+	.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;
@@ -5609,7 +6346,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;
 
@@ -5689,6 +6429,13 @@ 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);
 
+	if (rtl_hw_support_rss(tp)) {
+		tp->rss_data = devm_kzalloc(&pdev->dev, sizeof(*tp->rss_data),
+					    GFP_KERNEL);
+		if (!tp->rss_data)
+			return -ENOMEM;
+	}
+
 	tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK;
 
 	if (sizeof(dma_addr_t) > 4 && tp->mac_version >= RTL_GIGA_MAC_VER_18 &&
@@ -5703,11 +6450,16 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 	rtl_hw_reset(tp);
 
+	rtl_setup_rx_params(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);
+	rtl8169_set_rx_ring_num(tp);
+
+	if (rtl_hw_support_rss(tp))
+		rtl8169_init_rss(tp);
 
 	INIT_WORK(&tp->wk.work, rtl_task);
 	disable_work(&tp->wk.work);
@@ -5716,13 +6468,16 @@ 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);
-
 	dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
 			   NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
 	dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
 
+	if (rtl_hw_support_rss(tp) && tp->num_rx_rings > 1) {
+		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.
@@ -5778,6 +6533,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 = netif_set_real_num_queues(tp->dev, 1, tp->num_rx_rings);
+	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),
@@ -5792,9 +6551,26 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 	if (rc)
 		return rc;
 
+	tp->rx_ring = kcalloc(tp->num_rx_rings, sizeof(struct rtl8169_rx_ring),
+			      GFP_KERNEL);
+	if (!tp->rx_ring)
+		return -ENOMEM;
+
+	for (int i = 0; i < tp->num_rx_rings; i++)
+		u64_stats_init(&tp->rx_ring[i].stats.syncp);
+
+	tp->rtl8169_napi = kcalloc(tp->irq_nvecs, sizeof(struct napi_struct),
+				   GFP_KERNEL);
+	if (!tp->rtl8169_napi) {
+		kfree(tp->rx_ring);
+		return -ENOMEM;
+	}
+
+	r8169_init_napi(tp);
+
 	rc = register_netdev(dev);
 	if (rc)
-		return rc;
+		goto err_free_napi;
 
 	if (IS_ENABLED(CONFIG_R8169_LEDS)) {
 		if (rtl_is_8125(tp))
@@ -5803,8 +6579,9 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 			tp->leds = rtl8168_init_leds(dev);
 	}
 
-	netdev_info(dev, "%s, %pM, %sXID %x, IRQ %d\n",
-		    chip->name, dev->dev_addr, ext_xid_str, xid, tp->irq);
+	netdev_info(dev, "%s, %pM, %sXID %x, IRQ %d (%d total)\n",
+		    chip->name, dev->dev_addr, ext_xid_str, xid,
+		    pci_irq_vector(pdev, 0), tp->irq_nvecs);
 
 	if (jumbo_max)
 		netdev_info(dev, "jumbo features [frames: %d bytes, tx checksumming: %s]\n",
@@ -5821,6 +6598,11 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 		pm_runtime_put_sync(&pdev->dev);
 
 	return 0;
+
+err_free_napi:
+	r8169_free_napi(tp);
+	kfree(tp->rx_ring);
+	return rc;
 }
 
 static struct pci_driver rtl8169_pci_driver = {

23/3 2026/08/14 02:29 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 24212
							output: 300
							thoughts: 5373
						

Thoughts:
**Analysis of the r8169 Patch Series for KMSAN Fuzzing Justification**

Okay, let's break this down. The core question is whether this patch series, which introduces RSS support, multiple RX queues, and descriptor format changes for the r8169 network driver, warrants a dedicated KMSAN fuzzing session. My assessment is based on the KMSAN's role in detecting uninitialized memory reads and kernel-to-user memory leaks.

First, I examined the changes to `struct RxDesc`. The structure has been modified to accommodate different descriptor types (default and RSS). The RX descriptors are allocated using `dma_alloc_coherent`. Critically, `dma_alloc_coherent` *defaults* to zeroing the allocated memory in current Linux kernels, and the driver also explicitly zeroes the descriptors using `memset`. So, there's no immediate risk of uninitialized memory in those DMA buffers.

Next, I looked at other relevant data structures like `rtl8169_rss_data`, `tp->rx_ring`, and `tp->rtl8169_napi`.  These are allocated with `devm_kzalloc` or `kcalloc`, both of which guarantee zero-initialization. This eliminates the possibility of uninitialized memory issues within those areas.

I then focused on potential data leaks to userspace. The patch adds `rtl8169_get_channels` for ethtool.  However, before this callback is even invoked, the ethtool core itself allocates and zero-initializes the `ethtool_channels` structure. Therefore, the driver is not responsible for filling the structure with initial values, thus preventing info leaks through uninitialized fields. The same is true for `rtl8169_get_stats64` where the storage is zeroed before being populated.

I also analyzed the core RX path (`rtl_rx`) to check for uninitialized stack variables. I found that any local variables, like `opts2`, `rss_header_info`, and `hash_val`,  are *always* assigned a value before use. Similarly, variables used in control flow, like `csum_ok`, `eor`, `flags`, and `rss_queue_num`, are explicitly initialized in all execution paths.

There is a read from the DMA descriptor in the core RX path:
```c
status = le32_to_cpu(rtl8169_rx_desc_opts1(tp, desc));
```
While KMSAN doesn't fully track DMA writes, this isn't the primary concern, as it is DMA memory populated by hardware. This could be a hardware bug or driver logic, which should be caught by KASAN or other standard tools.

Considering that the patch focuses on structure re-use, descriptor format changes, new features and multiple queues, but ensures zero-initialization everywhere, I conclude that it does not introduce any *new* uninitialized memory usage. Standard testing with KASAN and other tools should be sufficient to catch any subtle memory corruption or misuse that might arise. A dedicated KMSAN fuzzing session is not justified in this case.



24/3 2026/08/14 02:30 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch series refactors the r8169 driver to support multiple RX queues, RSS, and MSI-X. It introduces new structures for RX rings, RSS data, and NAPI, which are all allocated using zeroing allocation functions (`kcalloc`, `devm_kzalloc`, `devm_alloc_etherdev_mqs`). The RX descriptors are allocated via `dma_alloc_coherent` and explicitly zeroed with `memset`. The ethtool callback `rtl8169_get_channels` only writes to the `ethtool_channels` struct, which is pre-zeroed by the ethtool core before the callback is invoked. Similarly, `rtl8169_get_stats64` updates the `rtnl_link_stats64` struct, which is also pre-zeroed by the networking core. All local variables and stack structures introduced in the patch are properly initialized before use. There are no new IOCTLs, netlink attributes, or other userspace interfaces introduced that could leak uninitialized memory. Any potential bugs introduced by these changes, such as out-of-bounds accesses or use-after-free, will be effectively caught by standard KASAN and other bug detectors. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch series refactors the r8169 driver to support multiple RX queues, RSS, and MSI-X. It introduces new structures for RX rings, RSS data, and NAPI, which are all allocated using zeroing allocation functions (`kcalloc`, `devm_kzalloc`, `devm_alloc_etherdev_mqs`). The RX descriptors are allocated via `dma_alloc_coherent` and explicitly zeroed with `memset`. The ethtool callback `rtl8169_get_channels` only writes to the `ethtool_channels` struct, which is pre-zeroed by the ethtool core before the callback is invoked. Similarly, `rtl8169_get_stats64` updates the `rtnl_link_stats64` struct, which is also pre-zeroed by the networking core. All local variables and stack structures introduced in the patch are properly initialized before use. There are no new IOCTLs, netlink attributes, or other userspace interfaces introduced that could leak uninitialized memory. Any potential bugs introduced by these changes, such as out-of-bounds accesses or use-after-free, will be effectively caught by standard KASAN and other bug detectors. Therefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)