AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/06/29 07:30 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "R8169"
  ],
  "FocusSymbols": [
    "rtl8169_set_channels",
    "rtl8169_realloc_rx",
    "rtl_set_rss_config",
    "rtl8169_init_rss",
    "rtl8169_set_rx_ring_num",
    "rtl_alloc_irq",
    "rtl8169_alloc_rx_desc",
    "rtl8169_free_rx_desc",
    "r8169_init_napi"
  ],
  "Reasoning": "The patch introduces functional changes to the r8169 network driver, adding support for Receive Side Scaling (RSS), multiple RX queues, and MSI-X interrupts. These changes involve complex resource allocation, hardware configuration, and ethtool operations, which are reachable via PCI device initialization and user-space configuration, making them suitable for fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/06/29 07:30 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 69bc733f9f41eeeaa57b3638f54aa9382e6857ec\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Jun 29 07:30:44 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 ec4fc21fa21fb0..9194c2142f95d0 100644\n--- a/drivers/net/ethernet/realtek/r8169_main.c\n+++ b/drivers/net/ethernet/realtek/r8169_main.c\n@@ -74,9 +74,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_RX_QUEUES\t8\n+#define R8127_MAX_TX_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 +465,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@@ -576,6 +605,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 +679,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 +705,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 +797,49 @@ 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 index;\t\t\t\t\t/* Rx queue index */\n+\tu32 cur_rx;\t\t\t\t\t/* Index of next Rx pkt. */\n+\tu32 dirty_rx;\t\t\t\t\t/* Index for recycling. */\n+\tstruct RxDesc *rx_desc_array;\t\t\t/* array of Rx Desc*/\n+\tdma_addr_t rx_desc_phy_addr[NUM_RX_DESC];\t/* Rx data buffer physical dma address */\n+\tdma_addr_t rx_phy_addr;\t\t\t\t/* Rx desc physical address */\n+\tstruct page *rx_databuff[NUM_RX_DESC];\t\t/* Rx data buffers */\n+};\n+\n+struct rtl8169_rss_data {\n+\tu32 rss_key[RTL_RSS_KEY_SIZE / sizeof(u32)];\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[R8169_MAX_RX_QUEUES];\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 +1669,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 +1740,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 +1975,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 +2535,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 +2710,27 @@ 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\tring-\u003eindex = i;\n+\t}\n }\n \n static void rtl_jumbo_config(struct rtl8169_private *tp)\n@@ -2680,6 +2789,34 @@ 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_set_rss_indir_tbl(struct rtl8169_private *tp,\n+\t\t\t\t      unsigned int num_rx_rings)\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, num_rx_rings);\n+}\n+\n+static void rtl8169_init_rss(struct rtl8169_private *tp)\n+{\n+\trtl8169_set_rss_indir_tbl(tp, tp-\u003enum_rx_rings);\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 +2939,63 @@ 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+\tu32 num_entries = RTL_RSS_KEY_SIZE / sizeof(u32);\n+\tu32 *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 num_entries; i++)\n+\t\tRTL_W32(tp, rss_key_reg + (i * 4), 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 = (u32)indir_tbl[i] |\n+\t\t\t   (u32)indir_tbl[i + 1] \u003c\u003c 8 |\n+\t\t\t   (u32)indir_tbl[i + 2] \u003c\u003c 16 |\n+\t\t\t   (u32)indir_tbl[i + 3] \u003c\u003c 24;\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 +3003,16 @@ 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, ((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, ((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 +4058,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 +4087,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 +4109,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 +4352,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 +4385,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 +4427,129 @@ 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+\t\t\t     enum rx_desc_type desc_type)\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 (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, tp-\u003einit_rx_desc_type);\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 (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], tp-\u003einit_rx_desc_type);\n+\treturn ret;\n }\n \n static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp, unsigned int entry)\n@@ -4266,9 +4589,21 @@ 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+\tfor (int i = 0; i \u003c tp-\u003eirq_nvecs; i++)\n+\t\tnapi_disable(\u0026tp-\u003ertl8169_napi[i]);\n+}\n+\n+static void rtl8169_napi_enable(struct rtl8169_private *tp)\n+{\n+\tfor (int i = 0; i \u003c tp-\u003eirq_nvecs; 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 +4638,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 +5105,104 @@ 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 status = opts1 \u0026 (RxProtoMask | RxCSFailMask);\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+\tbool csum_ok = false;\n+\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\tif (opts1 == RxProtoTCP || opts1 == RxProtoUDP)\n+\t\t\tcsum_ok = true;\n+\t\tbreak;\n+\t}\n \n-\tif (status == RxProtoTCP || status == RxProtoUDP)\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 net_device *dev,\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\tif (status \u0026 RX_RUNT_RSS)\n+\t\t\t\tdev-\u003estats.rx_length_errors++;\n+\t\t\tif (status \u0026 RX_CRC_RSS)\n+\t\t\t\tdev-\u003estats.rx_crc_errors++;\n+\t\t\treturn true;\n+\t\t}\n+\t\tbreak;\n+\tdefault:\n+\t\tif (unlikely(status \u0026 RxRES)) {\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\treturn true;\n+\t\t}\n+\t\tbreak;\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, 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,19 +5212,16 @@ 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(dev, 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 \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\tif ((status \u0026 RxRWT || !(status \u0026 (RxRUNT | RxCRC))) \u0026\u0026\n+\t\t\t    tp-\u003einit_rx_desc_type == RX_DESC_TYPE_DEFAULT)\n \t\t\t\tgoto release_descriptor;\n \t\t}\n \n@@ -4820,14 +5238,14 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget\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\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 +5254,23 @@ 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 \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 +5278,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 +5299,86 @@ 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_irq(struct rtl8169_private *tp)\n+{\n+\tfor (int i = 0; i \u003c tp-\u003eirq_nvecs; i++) {\n+\t\tstruct napi_struct *napi = \u0026tp-\u003ertl8169_napi[i];\n+\n+\t\tpci_free_irq(tp-\u003epci_dev, i, napi);\n+\t}\n+}\n+\n+static void rtl8169_disable_hw_interrupt_msix(struct rtl8169_private *tp, 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, 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+\tif (message_id == MSIX_ID_VEC_MAP_LINKCHG) {\n+\t\trtl8169_clear_hw_isr(tp, message_id);\n+\t\tphy_mac_interrupt(tp-\u003ephydev);\n+\t\treturn IRQ_HANDLED;\n+\t}\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 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\trc = pci_request_irq(tp-\u003epci_dev, i, rtl8169_interrupt_msix,\n+\t\t\t\t\t     NULL, napi, \"%s-%d\", dev-\u003ename, i);\n+\t\telse\n+\t\t\trc = pci_request_irq(tp-\u003epci_dev, i, rtl8169_interrupt,\n+\t\t\t\t\t     NULL, napi, \"%s-%d\", dev-\u003ename, i);\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\tpci_free_irq(tp-\u003epci_dev, i, \u0026tp-\u003ertl8169_napi[i]);\n+\treturn rc;\n+}\n+\n static void rtl_task(struct work_struct *work)\n {\n \tstruct rtl8169_private *tp =\n@@ -4914,13 +5413,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 +5535,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 +5551,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], tp-\u003einit_rx_desc_type);\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 +5573,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 +5588,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 +5601,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 +5610,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 +5627,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], tp-\u003einit_rx_desc_type);\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@@ -5275,6 +5775,14 @@ 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+\tfor (int i = 0; i \u003c tp-\u003eirq_nvecs; 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 +5797,8 @@ static void rtl_remove_one(struct pci_dev *pdev)\n \n \tunregister_netdev(tp-\u003edev);\n \n+\tr8169_free_napi(tp);\n+\n \tif (tp-\u003edash_type != RTL_DASH_NONE)\n \t\trtl8168_driver_stop(tp);\n \n@@ -5320,15 +5830,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 +5888,17 @@ 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), 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 +6153,241 @@ 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], 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+\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+static int rtl8169_poll_msix_other(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+\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+ * Vector (RxQs + TxQs) and up\t\t: Other events (Link status(29), etc.)\n+ */\n+static void r8169_init_napi(struct rtl8169_private *tp)\n+{\n+\tfor (int i = 0; i \u003c tp-\u003eirq_nvecs; 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 if (i \u003c R8169_MAX_RX_QUEUES + R8127_MAX_TX_QUEUES)\n+\t\t\t\tpoll_fn = rtl8169_poll_msix_tx;\n+\t\t\telse\n+\t\t\t\tpoll_fn = rtl8169_poll_msix_other;\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 int rtl8169_realloc_rx(struct rtl8169_private *tp,\n+\t\t\t      struct rtl8169_rx_ring *new_rx,\n+\t\t\t      int new_count)\n+{\n+\tint i, ret;\n+\n+\tfor (i = 0; i \u003c new_count; i++) {\n+\t\tstruct rtl8169_rx_ring *ring = \u0026new_rx[i];\n+\n+\t\tring-\u003erx_desc_array = dma_alloc_coherent(\u0026tp-\u003epci_dev-\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\tret = -ENOMEM;\n+\t\t\tgoto err_free;\n+\t\t}\n+\n+\t\tmemset(ring-\u003erx_databuff, 0, sizeof(ring-\u003erx_databuff));\n+\t\tret = rtl8169_rx_fill(tp, ring);\n+\t\tif (ret) {\n+\t\t\tdma_free_coherent(\u0026tp-\u003epci_dev-\u003edev, R8169_RX_RING_BYTES,\n+\t\t\t\t\t  ring-\u003erx_desc_array, ring-\u003erx_phy_addr);\n+\t\t\tgoto err_free;\n+\t\t}\n+\t}\n+\treturn 0;\n+\n+err_free:\n+\twhile (--i \u003e= 0) {\n+\t\trtl8169_rx_clear(tp, \u0026new_rx[i], tp-\u003einit_rx_desc_type);\n+\t\tdma_free_coherent(\u0026tp-\u003epci_dev-\u003edev, R8169_RX_RING_BYTES,\n+\t\t\t\t  new_rx[i].rx_desc_array, new_rx[i].rx_phy_addr);\n+\t}\n+\treturn ret;\n+}\n+\n+static int rtl8169_set_channels(struct net_device *dev,\n+\t\t\t\tstruct ethtool_channels *ch)\n+{\n+\tstruct rtl8169_private *tp = netdev_priv(dev);\n+\tbool if_running = netif_running(dev);\n+\tenum rx_desc_type old_rx_desc_type;\n+\tenum rx_desc_type new_desc_type;\n+\tstruct rtl8169_rx_ring *new_rx;\n+\tint i, ret;\n+\n+\tif (ch-\u003erx_count == tp-\u003enum_rx_rings)\n+\t\treturn 0;\n+\n+\told_rx_desc_type = tp-\u003einit_rx_desc_type;\n+\n+\tif (!rtl_hw_support_rss(tp)) {\n+\t\tnetdev_warn(dev, \"This chip does not support multiple channels/RSS.\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (ch-\u003erx_count \u003e tp-\u003ehw_supp_num_rx_queues || !is_power_of_2(ch-\u003erx_count) ||\n+\t    tp-\u003eirq_nvecs \u003c get_min_irq_nvecs(tp))\n+\t\treturn -EINVAL;\n+\n+\tnew_desc_type = ch-\u003erx_count \u003e 1 ? RX_DESC_TYPE_RSS : RX_DESC_TYPE_DEFAULT;\n+\n+\tif (!if_running) {\n+\t\tret = netif_set_real_num_rx_queues(dev, ch-\u003erx_count);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\ttp-\u003enum_rx_rings = ch-\u003erx_count;\n+\t\ttp-\u003einit_rx_desc_type = new_desc_type;\n+\n+\t\trtl8169_set_rss_indir_tbl(tp, tp-\u003enum_rx_rings);\n+\t\trtl_set_irq_mask(tp);\n+\t\treturn 0;\n+\t}\n+\n+\tnew_rx = kzalloc_objs(*new_rx, R8169_MAX_RX_QUEUES);\n+\tif (!new_rx)\n+\t\treturn -ENOMEM;\n+\n+\tnetif_stop_queue(dev);\n+\trtl8169_down(tp);\n+\n+\tret = netif_set_real_num_rx_queues(dev, ch-\u003erx_count);\n+\tif (ret)\n+\t\tgoto err_up;\n+\n+\ttp-\u003einit_rx_desc_type = new_desc_type;\n+\n+\tret = rtl8169_realloc_rx(tp, new_rx, ch-\u003erx_count);\n+\tif (ret)\n+\t\tgoto err_reset;\n+\n+\tfor (i = 0; i \u003c tp-\u003enum_rx_rings; i++)\n+\t\trtl8169_rx_clear(tp, \u0026tp-\u003erx_ring[i], old_rx_desc_type);\n+\trtl8169_free_rx_desc(tp);\n+\n+\ttp-\u003enum_rx_rings = ch-\u003erx_count;\n+\n+\tmemset(tp-\u003erx_ring, 0, sizeof(tp-\u003erx_ring));\n+\tmemcpy(tp-\u003erx_ring, new_rx, sizeof(*new_rx) * ch-\u003erx_count);\n+\n+\trtl8169_set_rss_indir_tbl(tp, tp-\u003enum_rx_rings);\n+\trtl_set_irq_mask(tp);\n+\n+\trtl8169_up(tp);\n+\tnetif_start_queue(dev);\n+\n+\tkfree(new_rx);\n+\n+\treturn 0;\n+\n+err_reset:\n+\tif (netif_set_real_num_rx_queues(dev, tp-\u003enum_rx_rings))\n+\t\tnetdev_err(dev, \"Failed to revert rx_queues, state might be inconsistent!\\n\");\n+\ttp-\u003einit_rx_desc_type = old_rx_desc_type;\n+err_up:\n+\trtl8169_up(tp);\n+\tnetif_start_queue(dev);\n+\tkfree(new_rx);\n+\n+\treturn ret;\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.set_channels\t\t= rtl8169_set_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 +6398,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 +6481,12 @@ 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), 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 +6501,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 +6519,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 +6584,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 +6602,15 @@ 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-\u003ertl8169_napi = kcalloc(tp-\u003eirq_nvecs, sizeof(struct napi_struct), GFP_KERNEL);\n+\tif (!tp-\u003ertl8169_napi)\n+\t\treturn -ENOMEM;\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 +6619,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 +6638,10 @@ 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+\treturn rc;\n }\n \n static struct pci_driver rtl8169_pci_driver = {\n"
}

2/1 2026/06/29 07:30 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "R8169"
  ],
  "FocusSymbols": [
    "rtl8169_set_channels",
    "rtl8169_realloc_rx",
    "rtl_set_rss_config",
    "rtl8169_init_rss",
    "rtl8169_set_rx_ring_num",
    "rtl_alloc_irq",
    "rtl8169_alloc_rx_desc",
    "rtl8169_free_rx_desc",
    "r8169_init_napi"
  ],
  "Reasoning": "The patch introduces functional changes to the r8169 network driver, adding support for Receive Side Scaling (RSS), multiple RX queues, and MSI-X interrupts. These changes involve complex resource allocation, hardware configuration, and ethtool operations, which are reachable via PCI device initialization and user-space configuration, making them suitable for 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).

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.
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.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. 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 69bc733f9f41eeeaa57b3638f54aa9382e6857ec
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Jun 29 07:30:44 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 ec4fc21fa21fb0..9194c2142f95d0 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -74,9 +74,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_RX_QUEUES	8
+#define R8127_MAX_TX_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 +465,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)
@@ -576,6 +605,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 +679,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 +705,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 +797,49 @@ 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 index;					/* Rx queue index */
+	u32 cur_rx;					/* Index of next Rx pkt. */
+	u32 dirty_rx;					/* Index for recycling. */
+	struct RxDesc *rx_desc_array;			/* array of Rx Desc*/
+	dma_addr_t rx_desc_phy_addr[NUM_RX_DESC];	/* Rx data buffer physical dma address */
+	dma_addr_t rx_phy_addr;				/* Rx desc physical address */
+	struct page *rx_databuff[NUM_RX_DESC];		/* Rx data buffers */
+};
+
+struct rtl8169_rss_data {
+	u32 rss_key[RTL_RSS_KEY_SIZE / sizeof(u32)];
+	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[R8169_MAX_RX_QUEUES];
+	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 +1669,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 +1740,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 +1975,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 +2535,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 +2710,27 @@ 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;
+		ring->index = i;
+	}
 }
 
 static void rtl_jumbo_config(struct rtl8169_private *tp)
@@ -2680,6 +2789,34 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
 	rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
 }
 
+static void rtl8169_set_rss_indir_tbl(struct rtl8169_private *tp,
+				      unsigned int num_rx_rings)
+{
+	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, num_rx_rings);
+}
+
+static void rtl8169_init_rss(struct rtl8169_private *tp)
+{
+	rtl8169_set_rss_indir_tbl(tp, 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 +2939,63 @@ 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)
+{
+	u32 num_entries = RTL_RSS_KEY_SIZE / sizeof(u32);
+	u32 *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 < num_entries; i++)
+		RTL_W32(tp, rss_key_reg + (i * 4), 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 = (u32)indir_tbl[i] |
+			   (u32)indir_tbl[i + 1] << 8 |
+			   (u32)indir_tbl[i + 2] << 16 |
+			   (u32)indir_tbl[i + 3] << 24;
+		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 +3003,16 @@ 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 +4058,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 +4087,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 +4109,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 +4352,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 +4385,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 +4427,129 @@ 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,
+			     enum rx_desc_type desc_type)
 {
 	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 (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, tp->init_rx_desc_type);
 			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 (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], tp->init_rx_desc_type);
+	return ret;
 }
 
 static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp, unsigned int entry)
@@ -4266,9 +4589,21 @@ static void rtl8169_tx_clear(struct rtl8169_private *tp)
 	netdev_reset_queue(tp->dev);
 }
 
+static void rtl8169_napi_disable(struct rtl8169_private *tp)
+{
+	for (int i = 0; i < tp->irq_nvecs; i++)
+		napi_disable(&tp->rtl8169_napi[i]);
+}
+
+static void rtl8169_napi_enable(struct rtl8169_private *tp)
+{
+	for (int i = 0; i < tp->irq_nvecs; 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 +4638,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 +5105,104 @@ 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 status = opts1 & (RxProtoMask | RxCSFailMask);
+	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)
+{
+	bool csum_ok = false;
+
+	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:
+		if (opts1 == RxProtoTCP || opts1 == RxProtoUDP)
+			csum_ok = true;
+		break;
+	}
 
-	if (status == RxProtoTCP || status == RxProtoUDP)
+	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 net_device *dev,
+					struct rtl8169_private *tp,
+					u32 status)
+{
+	switch (tp->init_rx_desc_type) {
+	case RX_DESC_TYPE_RSS:
+		if (unlikely(status & RX_RES_RSS)) {
+			if (status & RX_RUNT_RSS)
+				dev->stats.rx_length_errors++;
+			if (status & RX_CRC_RSS)
+				dev->stats.rx_crc_errors++;
+			return true;
+		}
+		break;
+	default:
+		if (unlikely(status & RxRES)) {
+			if (status & (RxRWT | RxRUNT))
+				dev->stats.rx_length_errors++;
+			if (status & RxCRC)
+				dev->stats.rx_crc_errors++;
+			return true;
+		}
+		break;
+	}
+	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,19 +5212,16 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
 		 */
 		dma_rmb();
 
-		if (unlikely(status & RxRES)) {
+		if (rtl8169_check_rx_desc_error(dev, tp, status)) {
 			if (net_ratelimit())
 				netdev_warn(dev, "Rx ERROR. status = %08x\n",
 					    status);
 			dev->stats.rx_errors++;
-			if (status & (RxRWT | RxRUNT))
-				dev->stats.rx_length_errors++;
-			if (status & RxCRC)
-				dev->stats.rx_crc_errors++;
 
 			if (!(dev->features & NETIF_F_RXALL))
 				goto release_descriptor;
-			else if (status & RxRWT || !(status & (RxRUNT | RxCRC)))
+			if ((status & RxRWT || !(status & (RxRUNT | RxCRC))) &&
+			    tp->init_rx_desc_type == RX_DESC_TYPE_DEFAULT)
 				goto release_descriptor;
 		}
 
@@ -4820,14 +5238,14 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
 			goto release_descriptor;
 		}
 
-		skb = napi_alloc_skb(&tp->napi, pkt_size);
+		skb = napi_alloc_skb(napi, pkt_size);
 		if (unlikely(!skb)) {
 			dev->stats.rx_dropped++;
 			goto release_descriptor;
 		}
 
-		addr = le64_to_cpu(desc->addr);
-		rx_buf = page_address(tp->Rx_databuff[entry]);
+		addr = ring->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 +5254,23 @@ 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++;
 
-		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 +5278,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 +5299,86 @@ 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_irq(struct rtl8169_private *tp)
+{
+	for (int i = 0; i < tp->irq_nvecs; i++) {
+		struct napi_struct *napi = &tp->rtl8169_napi[i];
+
+		pci_free_irq(tp->pci_dev, i, napi);
+	}
+}
+
+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;
+
+	if (message_id == MSIX_ID_VEC_MAP_LINKCHG) {
+		rtl8169_clear_hw_isr(tp, message_id);
+		phy_mac_interrupt(tp->phydev);
+		return IRQ_HANDLED;
+	}
+
+	rtl8169_disable_hw_interrupt_msix(tp, message_id);
+	rtl8169_clear_hw_isr(tp, message_id);
+
+	napi_schedule(napi);
+
+	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)
+			rc = pci_request_irq(tp->pci_dev, i, rtl8169_interrupt_msix,
+					     NULL, napi, "%s-%d", dev->name, i);
+		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)
+		pci_free_irq(tp->pci_dev, i, &tp->rtl8169_napi[i]);
+	return rc;
+}
+
 static void rtl_task(struct work_struct *work)
 {
 	struct rtl8169_private *tp =
@@ -4914,13 +5413,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 +5535,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 +5551,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], tp->init_rx_desc_type);
 
-	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 +5573,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 +5588,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 +5601,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 +5610,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 +5627,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], tp->init_rx_desc_type);
 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;
@@ -5275,6 +5775,14 @@ static void rtl_shutdown(struct pci_dev *pdev)
 		pci_prepare_to_sleep(pdev);
 }
 
+static void r8169_free_napi(struct rtl8169_private *tp)
+{
+	for (int i = 0; i < tp->irq_nvecs; 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 +5797,8 @@ static void rtl_remove_one(struct pci_dev *pdev)
 
 	unregister_netdev(tp->dev);
 
+	r8169_free_napi(tp);
+
 	if (tp->dash_type != RTL_DASH_NONE)
 		rtl8168_driver_stop(tp);
 
@@ -5320,15 +5830,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 +5888,17 @@ 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 +6153,241 @@ 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);
+
+	rtl_tx(dev, tp, budget);
+
+	if (napi_complete_done(napi, 0))
+		rtl8169_enable_hw_interrupt_msix(tp, (int)(napi - tp->rtl8169_napi));
+
+	return 0;
+}
+
+static int rtl8169_poll_msix_other(struct napi_struct *napi, int budget)
+{
+	struct net_device *dev = napi->dev;
+	struct rtl8169_private *tp;
+
+	tp = netdev_priv(dev);
+
+	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
+ * Vector (RxQs + TxQs) and up		: Other events (Link status(29), etc.)
+ */
+static void r8169_init_napi(struct rtl8169_private *tp)
+{
+	for (int i = 0; i < tp->irq_nvecs; 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 if (i < R8169_MAX_RX_QUEUES + R8127_MAX_TX_QUEUES)
+				poll_fn = rtl8169_poll_msix_tx;
+			else
+				poll_fn = rtl8169_poll_msix_other;
+		}
+		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 int rtl8169_realloc_rx(struct rtl8169_private *tp,
+			      struct rtl8169_rx_ring *new_rx,
+			      int new_count)
+{
+	int i, ret;
+
+	for (i = 0; i < new_count; i++) {
+		struct rtl8169_rx_ring *ring = &new_rx[i];
+
+		ring->rx_desc_array = dma_alloc_coherent(&tp->pci_dev->dev,
+							 R8169_RX_RING_BYTES,
+							 &ring->rx_phy_addr,
+							 GFP_KERNEL);
+		if (!ring->rx_desc_array) {
+			ret = -ENOMEM;
+			goto err_free;
+		}
+
+		memset(ring->rx_databuff, 0, sizeof(ring->rx_databuff));
+		ret = rtl8169_rx_fill(tp, ring);
+		if (ret) {
+			dma_free_coherent(&tp->pci_dev->dev, R8169_RX_RING_BYTES,
+					  ring->rx_desc_array, ring->rx_phy_addr);
+			goto err_free;
+		}
+	}
+	return 0;
+
+err_free:
+	while (--i >= 0) {
+		rtl8169_rx_clear(tp, &new_rx[i], tp->init_rx_desc_type);
+		dma_free_coherent(&tp->pci_dev->dev, R8169_RX_RING_BYTES,
+				  new_rx[i].rx_desc_array, new_rx[i].rx_phy_addr);
+	}
+	return ret;
+}
+
+static int rtl8169_set_channels(struct net_device *dev,
+				struct ethtool_channels *ch)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	bool if_running = netif_running(dev);
+	enum rx_desc_type old_rx_desc_type;
+	enum rx_desc_type new_desc_type;
+	struct rtl8169_rx_ring *new_rx;
+	int i, ret;
+
+	if (ch->rx_count == tp->num_rx_rings)
+		return 0;
+
+	old_rx_desc_type = tp->init_rx_desc_type;
+
+	if (!rtl_hw_support_rss(tp)) {
+		netdev_warn(dev, "This chip does not support multiple channels/RSS.\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (ch->rx_count > tp->hw_supp_num_rx_queues || !is_power_of_2(ch->rx_count) ||
+	    tp->irq_nvecs < get_min_irq_nvecs(tp))
+		return -EINVAL;
+
+	new_desc_type = ch->rx_count > 1 ? RX_DESC_TYPE_RSS : RX_DESC_TYPE_DEFAULT;
+
+	if (!if_running) {
+		ret = netif_set_real_num_rx_queues(dev, ch->rx_count);
+		if (ret)
+			return ret;
+
+		tp->num_rx_rings = ch->rx_count;
+		tp->init_rx_desc_type = new_desc_type;
+
+		rtl8169_set_rss_indir_tbl(tp, tp->num_rx_rings);
+		rtl_set_irq_mask(tp);
+		return 0;
+	}
+
+	new_rx = kzalloc_objs(*new_rx, R8169_MAX_RX_QUEUES);
+	if (!new_rx)
+		return -ENOMEM;
+
+	netif_stop_queue(dev);
+	rtl8169_down(tp);
+
+	ret = netif_set_real_num_rx_queues(dev, ch->rx_count);
+	if (ret)
+		goto err_up;
+
+	tp->init_rx_desc_type = new_desc_type;
+
+	ret = rtl8169_realloc_rx(tp, new_rx, ch->rx_count);
+	if (ret)
+		goto err_reset;
+
+	for (i = 0; i < tp->num_rx_rings; i++)
+		rtl8169_rx_clear(tp, &tp->rx_ring[i], old_rx_desc_type);
+	rtl8169_free_rx_desc(tp);
+
+	tp->num_rx_rings = ch->rx_count;
+
+	memset(tp->rx_ring, 0, sizeof(tp->rx_ring));
+	memcpy(tp->rx_ring, new_rx, sizeof(*new_rx) * ch->rx_count);
+
+	rtl8169_set_rss_indir_tbl(tp, tp->num_rx_rings);
+	rtl_set_irq_mask(tp);
+
+	rtl8169_up(tp);
+	netif_start_queue(dev);
+
+	kfree(new_rx);
+
+	return 0;
+
+err_reset:
+	if (netif_set_real_num_rx_queues(dev, tp->num_rx_rings))
+		netdev_err(dev, "Failed to revert rx_queues, state might be inconsistent!\n");
+	tp->init_rx_desc_type = old_rx_desc_type;
+err_up:
+	rtl8169_up(tp);
+	netif_start_queue(dev);
+	kfree(new_rx);
+
+	return ret;
+}
+
+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,
+	.set_channels		= rtl8169_set_channels,
+	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
+	.set_link_ksettings	= rtl8169_set_link_ksettings,
+	.get_ringparam		= rtl8169_get_ringparam,
+	.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 +6398,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 +6481,12 @@ 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 +6501,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 +6519,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 +6584,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 +6602,15 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 	if (rc)
 		return rc;
 
+	tp->rtl8169_napi = kcalloc(tp->irq_nvecs, sizeof(struct napi_struct), GFP_KERNEL);
+	if (!tp->rtl8169_napi)
+		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 +6619,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 +6638,10 @@ 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);
+	return rc;
 }
 
 static struct pci_driver rtl8169_pci_driver = {

3/2 2026/06/29 07:30 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 24250
							output: 20
							thoughts: 143
						

Thoughts:
**Analyzing the r8169 Driver Patch**

Okay, so I've got this patch in front of me for the `r8169` network driver. Intriguing, it's introducing some pretty substantial changes: RSS support and multiple RX queues. Looking at the file modifications, it's all centered around `drivers/net/ethernet/realtek/r8169_main.c` which, as expected, is the core of the driver for Realtek Ethernet controllers. This is where the real meat of the logic resides.

The patch itself seems significant; it adds multiple RX rings, which suggests deeper parallel processing on the receive side. MSI-X support is also a good addition, indicating more sophisticated interrupt handling. RSS configuration is crucial for this kind of setup; it means we can distribute incoming traffic efficiently across those new queues, leveraging the CPU cores effectively. And, of course, NAPI for each RX queue - that's standard practice, critical for performance in these modern drivers.

This all points to substantial modification of the reachable core driver logic. Now, before I dive too deeply into the code itself, I need to confirm that `r8169` is even configured into the kernel. It's usually enabled with something like `CONFIG_R8169`. Time to fire up `git grep` and double-check, make sure we're actually working with a relevant configuration.



4/2 2026/06/29 07:30 tool grepper
0m Args:
{
  "Expression": "config R8169"
}

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/06/29 07:30 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 24917
							output: 23
							thoughts: 1398
						

Thoughts:
**Fuzzing the R8169 Driver: A Plan of Attack**

Okay, `R8169`. This looks like a promising target. The initial question is whether I can reach this in a virtualized environment. QEMU gives me `rtl8139`, but does it support the R8169 specifically, or potentially even `r8168` or `r8125`? No, QEMU doesn't natively emulate `r8169`. However, syzkaller does offer a path to fuzz PCI devices. It uses the `syz_pci_device` pseudo-syscall or virtual PCI injection. Right, syzkaller has PCI fuzzing. Good.

`r8169` is a PCI driver. That means I can leverage syzkaller's `syz_pci_*` pseudo-syscalls, or `vhci` if it were USB. For PCI though, I'm thinking I'll use `syz_pci_device`. I need to double-check those instructions. Those instructions mention things that are impossible to reach in virtualized environments like GCE or QEMU, even when using software-emulated hardware. But, does syzkaller emulate PCI devices? Yes, the syzkaller PCI fuzzer, which includes `syz_pci_device_add` and similar calls. Or `syz_pci_bus`.

So, can I reach it? Yes, syzkaller can definitely fuzz PCI drivers. Is this fuzzing effort worthwhile? Absolutely. It represents a functional change in a network driver, and those are always interesting.

Now, let's identify the specific functions that are modified and where I should focus my attention. The patch modifies a significant number of functions:

*   `rtl8169_set_channels`
*   `rtl8169_realloc_rx`
*   `rtl8169_get_channels`
*   `r8169_init_napi`
*   `rtl8169_poll_msix_rx`
*   `rtl8169_poll_msix_tx`
*   `rtl8169_poll_msix_other`
*   `rtl8169_interrupt_msix`
*   `rtl8169_request_irq`
*   `rtl8169_free_irq`
*   `rtl_rx`
*   `rtl8169_rx_fill`
*   `rtl8169_rx_clear`
*   `rtl8169_alloc_rx_data`
*   `rtl8169_alloc_rx_desc`
*   `rtl8169_free_rx_desc`
*   `rtl8169_init_ring`
*   `rtl8169_rx_desc_reset`
*   `rtl8169_rx_hash`
*   `rtl8169_rx_csum`
*   `rtl8169_check_rx_desc_error`
*   `rtl_set_rss_config`
*   `rtl8169_set_rss_hash_opt`
*   `rtl8169_store_reta`
*   `rtl8169_store_rss_key`
*   `rtl_setup_rx_params`
*   `rtl8169_init_rss`
*   `rtl8169_set_rss_indir_tbl`
*   `rtl8125_set_rx_q_num`
*   `rtl8169_hw_enable_vec_mapping`
*   `rtl_hw_start_8125_common`
*   `rtl_hw_start`
*   `rtl8169_change_mtu`
*   `rtl8169_set_rx_ring_num`
*   `rtl_alloc_irq`
*   `rtl_init_one`
*   `rtl_remove_one`
*   `rtl_open`
*   `rtl8169_close`

I need to quickly weed out the hot-path functions to avoid wasting time:

*   `rtl_rx`
*   `rtl8169_poll_msix_rx`
*   `rtl8169_poll_msix_tx`
*   `rtl8169_poll_msix_other


6/2 2026/06/29 07:31 tool grepper
0m Args:
{
  "Expression": "rtl8169_set_channels"
}

Results:
{
  "Output": "drivers/net/ethernet/realtek/r8169_main.c=6237=static int rtl8169_realloc_rx(struct rtl8169_private *tp,\n--\ndrivers/net/ethernet/realtek/r8169_main.c-6273-\ndrivers/net/ethernet/realtek/r8169_main.c:6274:static int rtl8169_set_channels(struct net_device *dev,\ndrivers/net/ethernet/realtek/r8169_main.c-6275-\t\t\t\tstruct ethtool_channels *ch)\n--\ndrivers/net/ethernet/realtek/r8169_main.c=6361=static const struct ethtool_ops rtl8169_ethtool_ops = {\n--\ndrivers/net/ethernet/realtek/r8169_main.c-6379-\t.get_channels\t\t= rtl8169_get_channels,\ndrivers/net/ethernet/realtek/r8169_main.c:6380:\t.set_channels\t\t= rtl8169_set_channels,\ndrivers/net/ethernet/realtek/r8169_main.c-6381-\t.get_link_ksettings\t= phy_ethtool_get_link_ksettings,\n"
}

7/2 2026/06/29 07:31 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 27720
							output: 220
							thoughts: 541
						

Thoughts:
**Fuzzing the `r8169` Driver: A Targeted Approach**

Okay, so this patch introduces some interesting functional changes to the `r8169` driver, specifically RSS support, multiple RX queues, and MSI-X support. This is right up Syzkaller's alley – PCI device initialization and `ethtool` operations are reachable, making it a prime candidate for fuzzing. Definitely worth pursuing.

I'm thinking, given the scope, we should enable `CONFIG_R8169`. It's a fairly straightforward driver, a standard PCI network driver so there shouldn't be any sneaky dependencies I have to worry about.

Now, let's nail down the fuzzing targets. My initial thought was to cast a wide net, so I have a list of symbols I should focus on. But I want to be efficient, so I'll trim down the list to what's most crucial:

*   `rtl8169_set_channels`
*   `rtl8169_realloc_rx`
*   `rtl_set_rss_config`
*   `rtl8169_init_rss`
*   `rtl8169_set_rx_ring_num`
*   `rtl_alloc_irq`
*   `rtl8169_alloc_rx_desc`
*   `rtl8169_free_rx_desc`
*   `r8169_init_napi`

These should cover the core functionality of the new RSS and RX queue features. Focusing on these symbols, I expect we'll get good coverage and find interesting edge cases quickly. Now, let's call `set-results`.



8/2 2026/06/29 07:31 tool set-results
0m Args:
{
  "EnableConfigs": [
    "R8169"
  ],
  "FocusSymbols": [
    "rtl8169_set_channels",
    "rtl8169_realloc_rx",
    "rtl_set_rss_config",
    "rtl8169_init_rss",
    "rtl8169_set_rx_ring_num",
    "rtl_alloc_irq",
    "rtl8169_alloc_rx_desc",
    "rtl8169_free_rx_desc",
    "r8169_init_napi"
  ],
  "Reasoning": "The patch introduces functional changes to the r8169 network driver, adding support for Receive Side Scaling (RSS), multiple RX queues, and MSI-X interrupts. These changes involve complex resource allocation, hardware configuration, and ethtool operations, which are reachable via PCI device initialization and user-space configuration, making them suitable for fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "R8169"
  ],
  "FocusSymbols": [
    "rtl8169_set_channels",
    "rtl8169_realloc_rx",
    "rtl_set_rss_config",
    "rtl8169_init_rss",
    "rtl8169_set_rx_ring_num",
    "rtl_alloc_irq",
    "rtl8169_alloc_rx_desc",
    "rtl8169_free_rx_desc",
    "r8169_init_napi"
  ],
  "Reasoning": "The patch introduces functional changes to the r8169 network driver, adding support for Receive Side Scaling (RSS), multiple RX queues, and MSI-X interrupts. These changes involve complex resource allocation, hardware configuration, and ethtool operations, which are reachable via PCI device initialization and user-space configuration, making them suitable for fuzzing.",
  "WorthFuzzing": true
}

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)