Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 3160499aacb81f6735941eb4c372d87a > files > 86

kvm-83-164.el5_5.30.src.rpm

From 0ad7dd7088e4cbe77f0c875f5ac10faadf792d87 Mon Sep 17 00:00:00 2001
From: Gleb Natapov <gleb@redhat.com>
Date: Wed, 22 Apr 2009 11:16:39 +0300
Subject: [PATCH 08/11] Change RTC time drift IRQ re-injection logic

Currently IRQ are reinjected as soon as they are acknowledged to
the RTC, but Windows sometimes do acknowledgement in a loop with
global interrupt disabled waiting for interrupt to be cleared and
it does not mask RTC vector in PIC/APIC while doing this. In such
situation interrupt injection always fails and RTC interrupt is never
cleared.

Instead of reinjecting coalesced IRQs on acknowledgement the patch below
reinjects them by accelerating RTC clock a bit. This way RTC interrupt
is not constantly raced after coalesced interrupt.

Patch Status: waiting for upstream review.
BZ: 494299

Signed-off-by: Gleb Natapov <gleb@redhat.com>
Message-ID: <20090422081639.GB24095@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
RH-Upstream-status: submitted
Acked-by: Dor Laor <dlaor@redhat.com>
Acked-by: Marcelo Tosatti <mtosatti@redhat.com>
Acked-by: Avi Kivity <avi@redhat.com>
Bugzilla: 497886
---
 qemu/hw/mc146818rtc.c |   60 +++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 46 insertions(+), 14 deletions(-)

diff --git a/qemu/hw/mc146818rtc.c b/qemu/hw/mc146818rtc.c
index 24ace10..dd5a695 100644
--- a/qemu/hw/mc146818rtc.c
+++ b/qemu/hw/mc146818rtc.c
@@ -70,6 +70,7 @@ struct RTCState {
 #ifdef TARGET_I386
     uint32_t irq_coalesced;
     uint32_t period;
+    QEMUTimer *coalesced_timer;
 #endif
     QEMUTimer *second_timer;
     QEMUTimer *second_timer2;
@@ -90,6 +91,37 @@ static void rtc_irq_raise(qemu_irq irq) {
 static void rtc_set_time(RTCState *s);
 static void rtc_copy_date(RTCState *s);
 
+#ifdef TARGET_I386
+static void rtc_coalesced_timer_update(RTCState *s)
+{
+    if (s->irq_coalesced == 0) {
+        qemu_del_timer(s->coalesced_timer);
+    } else {
+        /* divide each RTC interval to 2 - 8 smaller intervals */
+        int c = MIN(s->irq_coalesced, 7) + 1; 
+        int64_t next_clock = qemu_get_clock(vm_clock) +
+		muldiv64(s->period / c, ticks_per_sec, 32768);
+        qemu_mod_timer(s->coalesced_timer, next_clock);
+    }
+}
+
+static void rtc_coalesced_timer(void *opaque)
+{
+    RTCState *s = opaque;
+
+    if (s->irq_coalesced != 0) {
+        apic_reset_irq_delivered();
+        s->cmos_data[RTC_REG_C] |= 0xc0;
+        rtc_irq_raise(s->irq);
+        if (apic_get_irq_delivered()) {
+            s->irq_coalesced--;
+        }
+    }
+
+    rtc_coalesced_timer_update(s);
+}
+#endif
+
 static void rtc_timer_update(RTCState *s, int64_t current_time)
 {
     int period_code, period;
@@ -131,13 +163,17 @@ static void rtc_periodic_timer(void *opaque)
     RTCState *s = opaque;
 
     rtc_timer_update(s, s->next_periodic_time);
+    s->cmos_data[RTC_REG_C] |= 0xc0;
 #ifdef TARGET_I386
-    if ((s->cmos_data[RTC_REG_C] & 0xc0) && rtc_td_hack) {
-        s->irq_coalesced++;
-        return;
-    }
+    if(rtc_td_hack) {
+        apic_reset_irq_delivered();
+        rtc_irq_raise(s->irq);
+        if (!apic_get_irq_delivered()) {
+            s->irq_coalesced++;
+            rtc_coalesced_timer_update(s);
+        }
+    } else
 #endif
-    s->cmos_data[RTC_REG_C] |= 0xc0;
     rtc_irq_raise(s->irq);
 }
 
@@ -408,15 +444,6 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
         case RTC_REG_C:
             ret = s->cmos_data[s->cmos_index];
             qemu_irq_lower(s->irq);
-#ifdef TARGET_I386
-            if(s->irq_coalesced) {
-                apic_reset_irq_delivered();
-                qemu_irq_raise(s->irq);
-                if (apic_get_irq_delivered())
-                    s->irq_coalesced--;
-                break;
-            }
-#endif
             s->cmos_data[RTC_REG_C] = 0x00;
             break;
         default:
@@ -529,6 +556,7 @@ static int rtc_load_td(QEMUFile *f, void *opaque, int version_id)
 
     s->irq_coalesced = qemu_get_be32(f);
     s->period = qemu_get_be32(f);
+    rtc_coalesced_timer_update(s);
     return 0;
 }
 #endif
@@ -551,6 +579,10 @@ RTCState *rtc_init(int base, qemu_irq irq)
 
     s->periodic_timer = qemu_new_timer(vm_clock,
                                        rtc_periodic_timer, s);
+#ifdef TARGET_I386
+    if (rtc_td_hack)
+        s->coalesced_timer = qemu_new_timer(vm_clock, rtc_coalesced_timer, s);
+#endif
     s->second_timer = qemu_new_timer(vm_clock,
                                      rtc_update_second, s);
     s->second_timer2 = qemu_new_timer(vm_clock,
-- 
1.6.1