Skip to contentSkip to conversation

Rework the RTL8139C timer interrupts and enable them by default

PATCH New Tim Deegan 1 participant 1 message Dec 15, 2008

lore
Share & tools
lore

Tim Deegan

author
#0 lore raw

PATCH 1/1 Rework the RTL8139C timer interrupts and enable them by default

Reorganise the RTL8139 rolling timer so that it calculates the counter on read instead of updating it in real time, and only sets a timer when the guest has programmed a timer interrupt.

Enable it by default since it should have no overhead for guests that don't use it, and OpenBSD's rtl8139 driver relies on it.

---
 rtl8139.c |   87 ++++++++++++++++++++++++++++++--------------------------------
 1 file changed, 43 insertions(+), 44 deletions(-)
rtl8139.c +43 -44
Index: hw/rtl8139.c
===================================================================
@@ -59,8 +59,8 @@
 /* Calculate CRCs properly on Rx packets */
 #define RTL8139_CALCULATE_RXCRC 1
 
-/* Uncomment to enable on-board timer interrupts */
-//#define RTL8139_ONBOARD_TIMER 1
+/* Enable on-board timer interrupts */
+#define RTL8139_ONBOARD_TIMER 1
 
 #if defined(RTL8139_CALCULATE_RXCRC)
 /* For crc32 */
@@ -497,6 +497,37 @@
 
 } RTL8139State;
 
+/* Fix up the TCTR field to match the current system time */
+static inline void rtl8139_update_tctr(RTL8139State *s, int64_t current_time)
+{
+    if (s->clock_enabled)
+        s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY, ticks_per_sec);
+}
+
+/* Queue a timer interrupt based on the state of the TCTR and TimerInt */
+static inline void rtl8139_set_tctr_timer(RTL8139State *s)
+{
+#if RTL8139_ONBOARD_TIMER
+    int64_t next_time;
+    int64_t curr_time = qemu_get_clock(vm_clock);
+
+    if (!s->clock_enabled || s->TimerInt == 0) 
+    {
+        DEBUG_PRINT(("RTL8139: >>> set timer: clock not running or timeout not set\n"));
+        return;
+    }
+
+    rtl8139_update_tctr(s, curr_time);
+    next_time = curr_time +
+        muldiv64((1 + s->TimerInt - s->TCTR), ticks_per_sec, PCI_FREQUENCY);
+
+    if (next_time <= curr_time)
+        next_time = curr_time + 1;
+
+    qemu_mod_timer(s->timer, next_time);
+#endif
+}
+
 static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command)
 {
     DEBUG_PRINT(("RTL8139: eeprom command 0x%02x\n", command));
@@ -2788,11 +2819,13 @@
             DEBUG_PRINT(("RTL8139: TCTR Timer reset on write\n"));
             s->TCTR = 0;
             s->TCTR_base = qemu_get_clock(vm_clock);
+            rtl8139_set_tctr_timer(s);
             break;
 
         case FlashReg:
             DEBUG_PRINT(("RTL8139: FlashReg TimerInt write val=0x%08x\n", val));
             s->TimerInt = val;
+            rtl8139_set_tctr_timer(s);
             break;
 
         default:
@@ -3002,6 +3035,7 @@
             break;
 
         case Timer:
+            rtl8139_update_tctr(s, qemu_get_clock(vm_clock));
             ret = s->TCTR;
             DEBUG_PRINT(("RTL8139: TCTR Timer read val=0x%08x\n", ret));
             break;
@@ -3287,6 +3321,7 @@
         qemu_get_be32s(f, &s->TimerInt);
         s->TCTR_base=qemu_get_be64(f);
 
+        rtl8139_set_tctr_timer(s);
         RTL8139TallyCounters_load(f, &s->tally_counters);
     }
     else
@@ -3347,56 +3382,23 @@
     rtl8139_mmio_writel,
 };
 
-static inline int64_t rtl8139_get_next_tctr_time(RTL8139State *s, int64_t current_time)
-{
-    int64_t next_time = current_time +
-        muldiv64(1, ticks_per_sec, PCI_FREQUENCY);
-    if (next_time <= current_time)
-        next_time = current_time + 1;
-    return next_time;
-}
-
 #ifdef RTL8139_ONBOARD_TIMER
 static void rtl8139_timer(void *opaque)
 {
     RTL8139State *s = opaque;
 
-    int is_timeout = 0;
-
-    int64_t  curr_time;
-    uint32_t curr_tick;
-
-    if (!s->clock_enabled)
+    if (!s->clock_enabled || s->TimerInt == 0)
     {
-        DEBUG_PRINT(("RTL8139: >>> timer: clock is not running\n"));
+        DEBUG_PRINT(("RTL8139: >>> timer: clock not running or timeout not set\n"));
         return;
     }
 
-    curr_time = qemu_get_clock(vm_clock);
+    s->IntrStatus |= PCSTimeout;
+    rtl8139_update_irq(s);
 
-    curr_tick = muldiv64(curr_time - s->TCTR_base, PCI_FREQUENCY, ticks_per_sec);
+    rtl8139_set_tctr_timer(s);
 
-    if (s->TimerInt && curr_tick >= s->TimerInt)
-    {
-        if (s->TCTR < s->TimerInt || curr_tick < s->TCTR)
-        {
-            is_timeout = 1;
-        }
-    }
-
-    s->TCTR = curr_tick;
-
-//  DEBUG_PRINT(("RTL8139: >>> timer: tick=%08u\n", s->TCTR));
-
-    if (is_timeout)
-    {
-        DEBUG_PRINT(("RTL8139: >>> timer: timeout tick=%08u\n", s->TCTR));
-        s->IntrStatus |= PCSTimeout;
-        rtl8139_update_irq(s);
-    }
-
-    qemu_mod_timer(s->timer,
-        rtl8139_get_next_tctr_time(s,curr_time));
+    DEBUG_PRINT(("RTL8139: >>> timer: timeout tick=%08u (%08u)\n", s->TCTR, s->TimerInt));
 }
 #endif /* RTL8139_ONBOARD_TIMER */
 
@@ -3458,8 +3460,5 @@
 
 #ifdef RTL8139_ONBOARD_TIMER
     s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s);
-
-    qemu_mod_timer(s->timer,
-        rtl8139_get_next_tctr_time(s,qemu_get_clock(vm_clock)));
 #endif /* RTL8139_ONBOARD_TIMER */
 }