diff mbox series

r8169: RTL8125 timer experimentation

Message ID 7ea34300-7d55-4411-8ce9-fcc769e05647@gmail.com (mailing list archive)
State Not Applicable
Headers show
Series r8169: RTL8125 timer experimentation | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch

Commit Message

Ken Milmore May 12, 2024, 10:06 p.m. UTC
I have been experimenting with the "interrupt moderation" timer on the RTL8125, based largely on reading the NetBSD "rge" driver source.
This doesn't seem like much of an improvement over a software timer, but the BSD driver as well as the Realtek-supplied r8125 driver both make use of it.

Despite using 32-bit registers it appears that the timer is 16-bit, and runs at 125 MHz.
The default timeout interval used by the aforementioned drivers is 0x2600 which equates to about 78 us.
This interval maybe seems a bit long, but I note that both drivers use a ring buffer size of 1024 vs only 256 in the r8169 driver.

The patch below is just for interest. It modifies r8169 to use the timer when enabling interrupts from rtl8169_poll, following any Tx or Rx work having been done.
The timer interval can be adjusted via a module parameter.
diff mbox series

Patch

diff --git linux-source-6.1~/drivers/net/ethernet/realtek/r8169_main.c linux-source-6.1/drivers/net/ethernet/realtek/r8169_main.c
index 6e34177..1fc470c 100644
--- linux-source-6.1~/drivers/net/ethernet/realtek/r8169_main.c
+++ linux-source-6.1/drivers/net/ethernet/realtek/r8169_main.c
@@ -329,6 +329,8 @@  enum rtl8168_registers {
 enum rtl8125_registers {
 	IntrMask_8125		= 0x38,
 	IntrStatus_8125		= 0x3c,
+	TimerCnt0_8125		= 0x48,
+	TimerInt0_8125		= 0x58,
 	TxPoll_8125		= 0x90,
 	MAC0_BKP		= 0x19e0,
 	EEE_TXIDLE_TIMER_8125	= 0x6048,
@@ -660,6 +662,9 @@  MODULE_FIRMWARE(FIRMWARE_8107E_2);
 MODULE_FIRMWARE(FIRMWARE_8125A_3);
 MODULE_FIRMWARE(FIRMWARE_8125B_2);
 
+static u16 rtl8125_timer_interval __read_mostly = 0x2600;
+module_param(rtl8125_timer_interval, ushort, 0644);
+
 static inline struct device *tp_to_dev(struct rtl8169_private *tp)
 {
 	return &tp->pci_dev->dev;
@@ -1324,6 +1329,26 @@  u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr)
 		RTL_R32(tp, EFUSEAR) & EFUSEAR_DATA_MASK : ~0;
 }
 
+static void rtl8125_hard_irq_enable(struct rtl8169_private *tp)
+{
+	u32 mask = tp->irq_mask & ~PCSTimeout;
+
+	RTL_W32(tp, TimerInt0_8125, 0);
+	RTL_W32(tp, IntrMask_8125, mask);
+}
+
+static void rtl8125_timer_irq_enable(struct rtl8169_private *tp)
+{
+	u16 interval = READ_ONCE(rtl8125_timer_interval);
+
+	if (interval) {
+		RTL_W32(tp, TimerInt0_8125, interval);
+		RTL_W32(tp, TimerCnt0_8125, 1);
+		RTL_W32(tp, IntrMask_8125, PCSTimeout);
+	} else
+		rtl8125_hard_irq_enable(tp);
+}
+
 static u32 rtl_get_events(struct rtl8169_private *tp)
 {
 	if (rtl_is_8125(tp))
@@ -1351,7 +1376,7 @@  static void rtl_irq_disable(struct rtl8169_private *tp)
 static void rtl_irq_enable(struct rtl8169_private *tp)
 {
 	if (rtl_is_8125(tp))
-		RTL_W32(tp, IntrMask_8125, tp->irq_mask);
+		rtl8125_hard_irq_enable(tp);
 	else
 		RTL_W16(tp, IntrMask, tp->irq_mask);
 }
@@ -4430,7 +4455,7 @@  static void rtl8169_pcierr_interrupt(struct net_device *dev)
 	rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
 }
 
-static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
+static bool rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
 		   int budget)
 {
 	unsigned int dirty_tx, bytes_compl = 0, pkts_compl = 0;
@@ -4481,6 +4506,9 @@  static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp,
 		 */
 		if (READ_ONCE(tp->cur_tx) != dirty_tx && skb)
 			rtl8169_doorbell(tp);
+		return true;
+	} else {
+		return false;
 	}
 }
 
@@ -4654,13 +4682,18 @@  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;
+	bool tx_done;
 
-	rtl_tx(dev, tp, budget);
+	tx_done = rtl_tx(dev, tp, budget);
 
 	work_done = rtl_rx(dev, tp, budget);
 
-	if (work_done < budget && napi_complete_done(napi, work_done))
-		rtl_irq_enable(tp);
+	if (work_done < budget && napi_complete_done(napi, work_done)) {
+		if (rtl_is_8125(tp) && (work_done || tx_done))
+			rtl8125_timer_irq_enable(tp);
+		else
+			rtl_irq_enable(tp);
+	}
 
 	return work_done;
 }
@@ -5031,6 +5064,9 @@  static void rtl_set_irq_mask(struct rtl8169_private *tp)
 		tp->irq_mask |= RxFIFOOver;
 	else
 		tp->irq_mask |= RxOverflow;
+
+	if (rtl_is_8125(tp))
+		tp->irq_mask |= PCSTimeout;
 }
 
 static int rtl_alloc_irq(struct rtl8169_private *tp)