diff mbox

[4/4] qemu-timer: Fix timers for w32

Message ID 1302460123-25704-5-git-send-email-weil@mail.berlios.de
State Superseded
Headers show

Commit Message

Stefan Weil April 10, 2011, 6:28 p.m. UTC
Commit 68c23e5520e8286d79d96ab47c0ea722ceb75041 removed the
multimedia timer, but this timer is needed for certain
Linux kernels. Otherwise Linux boot stops with this error:

    MP-BIOS bug: 8254 timer not connected to IO-APIC

So the multimedia timer is added again here.

Cc: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Stefan Weil <weil@mail.berlios.de>
---
 qemu-timer.c |   96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 96 insertions(+), 0 deletions(-)

Comments

Paolo Bonzini April 11, 2011, 7:36 a.m. UTC | #1
On 04/10/2011 08:28 PM, Stefan Weil wrote:
> Commit 68c23e5520e8286d79d96ab47c0ea722ceb75041 removed the
> multimedia timer, but this timer is needed for certain
> Linux kernels. Otherwise Linux boot stops with this error:
>
>      MP-BIOS bug: 8254 timer not connected to IO-APIC
>
> So the multimedia timer is added again here.

Which distribution and Windows version is that?  Also, have they tried 
the non-dynticks timer (win32)?

Paolo
Stefan Weil April 11, 2011, 5:21 p.m. UTC | #2
Am 11.04.2011 09:36, schrieb Paolo Bonzini:
> On 04/10/2011 08:28 PM, Stefan Weil wrote:
>> Commit 68c23e5520e8286d79d96ab47c0ea722ceb75041 removed the
>> multimedia timer, but this timer is needed for certain
>> Linux kernels. Otherwise Linux boot stops with this error:
>>
>>      MP-BIOS bug: 8254 timer not connected to IO-APIC
>>
>> So the multimedia timer is added again here.
>
> Which distribution and Windows version is that?  Also, have they tried 
> the non-dynticks timer (win32)?
>
> Paolo

The bug was reported for a tinycore 3.5.1 guest (linux kernel 3.6.33-3).
The iso image is available from 
http://distro.ibiblio.org/tinycorelinux/3.x/release/.
QEMU was running on XP SP3.

Other Linux live CD-ROMs (e.g. FC14) were reported to show the same bug
when run as guest.

APIC can be disabled (kernel parameter), without APIC there is no problem.

I see the same bug here with two XP hosts and also tried both timer variants
of current QEMU (without a difference).

I don't get the bug when running on a Linux host using wine.

Regards, Stefan
Jan Kiszka April 12, 2011, 12:36 p.m. UTC | #3
On 2011-04-11 19:21, Stefan Weil wrote:
> Am 11.04.2011 09:36, schrieb Paolo Bonzini:
>> On 04/10/2011 08:28 PM, Stefan Weil wrote:
>>> Commit 68c23e5520e8286d79d96ab47c0ea722ceb75041 removed the
>>> multimedia timer, but this timer is needed for certain
>>> Linux kernels. Otherwise Linux boot stops with this error:
>>>
>>>      MP-BIOS bug: 8254 timer not connected to IO-APIC
>>>
>>> So the multimedia timer is added again here.
>>
>> Which distribution and Windows version is that?  Also, have they tried
>> the non-dynticks timer (win32)?
>>
>> Paolo
> 
> The bug was reported for a tinycore 3.5.1 guest (linux kernel 3.6.33-3).
> The iso image is available from
> http://distro.ibiblio.org/tinycorelinux/3.x/release/.
> QEMU was running on XP SP3.
> 
> Other Linux live CD-ROMs (e.g. FC14) were reported to show the same bug
> when run as guest.
> 
> APIC can be disabled (kernel parameter), without APIC there is no problem.
> 
> I see the same bug here with two XP hosts and also tried both timer
> variants
> of current QEMU (without a difference).
> 
> I don't get the bug when running on a Linux host using wine.

Passing no_timer_check to the Linux guest should work around the issue
as well. But this feature is only available since 2.6.20 (excluding
popular legacy 2.6.16 kernels).

Jan
Paolo Bonzini April 12, 2011, 12:40 p.m. UTC | #4
On 04/12/2011 02:36 PM, Jan Kiszka wrote:
>> I see the same bug here with two XP hosts and also tried both timer
>> variants
>> of current QEMU (without a difference).
>>
>> I don't get the bug when running on a Linux host using wine.
>
> Passing no_timer_check to the Linux guest should work around the issue
> as well. But this feature is only available since 2.6.20 (excluding
> popular legacy 2.6.16 kernels).

I think adding back MM timers is fine, especially since a bug that only 
happens under native Windows is a bug I'm unlikely to look at...

Paolo
Jan Kiszka April 12, 2011, 12:59 p.m. UTC | #5
On 2011-04-12 14:40, Paolo Bonzini wrote:
> On 04/12/2011 02:36 PM, Jan Kiszka wrote:
>>> I see the same bug here with two XP hosts and also tried both timer
>>> variants
>>> of current QEMU (without a difference).
>>>
>>> I don't get the bug when running on a Linux host using wine.
>>
>> Passing no_timer_check to the Linux guest should work around the issue
>> as well. But this feature is only available since 2.6.20 (excluding
>> popular legacy 2.6.16 kernels).
> 
> I think adding back MM timers is fine, especially since a bug that only 
> happens under native Windows is a bug I'm unlikely to look at...

That wasn't meant as a vote against changing the Windows code paths,
just as a further hint how to work-around virtualization related effects.

Even with high-res timers, you may hit that issue once in a while, also
on Linux hosts. When using KVM, latest Linux guest will notice that they
are running on a hypervisor and should skip the test automatically IIRC.

Jan
diff mbox

Patch

diff --git a/qemu-timer.c b/qemu-timer.c
index 26e5ea3..a57197b 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -209,6 +209,10 @@  static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t)
 
 #ifdef _WIN32
 
+static int mm_start_timer(struct qemu_alarm_timer *t);
+static void mm_stop_timer(struct qemu_alarm_timer *t);
+static void mm_rearm_timer(struct qemu_alarm_timer *t);
+
 static int win32_start_timer(struct qemu_alarm_timer *t);
 static void win32_stop_timer(struct qemu_alarm_timer *t);
 static void win32_rearm_timer(struct qemu_alarm_timer *t);
@@ -301,6 +305,8 @@  static struct qemu_alarm_timer alarm_timers[] = {
 #endif
     {"unix", unix_start_timer, unix_stop_timer, NULL},
 #else
+    {"mmtimer", mm_start_timer, mm_stop_timer, NULL},
+    {"mmtimer2", mm_start_timer, mm_stop_timer, mm_rearm_timer},
     {"dynticks", win32_start_timer, win32_stop_timer, win32_rearm_timer},
     {"win32", win32_start_timer, win32_stop_timer, NULL},
 #endif
@@ -949,6 +955,96 @@  static void unix_stop_timer(struct qemu_alarm_timer *t)
 
 #ifdef _WIN32
 
+static MMRESULT mm_timer;
+static unsigned mm_period;
+
+static void CALLBACK mm_alarm_handler(UINT uTimerID, UINT uMsg,
+                                      DWORD_PTR dwUser, DWORD_PTR dw1,
+                                      DWORD_PTR dw2)
+{
+    struct qemu_alarm_timer *t = alarm_timer;
+    if (!t) {
+        return;
+    }
+    if (alarm_has_dynticks(t) || qemu_next_alarm_deadline() <= 0) {
+        t->expired = alarm_has_dynticks(t);
+        t->pending = 1;
+        qemu_notify_event();
+    }
+}
+
+static int mm_start_timer(struct qemu_alarm_timer *t)
+{
+    TIMECAPS tc;
+    UINT flags;
+
+    memset(&tc, 0, sizeof(tc));
+    timeGetDevCaps(&tc, sizeof(tc));
+
+    mm_period = tc.wPeriodMin;
+    timeBeginPeriod(mm_period);
+
+    flags = TIME_CALLBACK_FUNCTION;
+    if (alarm_has_dynticks(t)) {
+        flags |= TIME_ONESHOT;
+    } else {
+        flags |= TIME_PERIODIC;
+    }
+
+    mm_timer = timeSetEvent(1,                  /* interval (ms) */
+                            mm_period,          /* resolution */
+                            mm_alarm_handler,   /* function */
+                            (DWORD)t,           /* parameter */
+                            flags);
+
+    if (!mm_timer) {
+        fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n",
+                GetLastError());
+        timeEndPeriod(mm_period);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void mm_stop_timer(struct qemu_alarm_timer *t)
+{
+    timeKillEvent(mm_timer);
+    timeEndPeriod(mm_period);
+}
+
+static void mm_rearm_timer(struct qemu_alarm_timer *t)
+{
+    int nearest_delta_ms;
+
+    assert(alarm_has_dynticks(t));
+    if (!active_timers[QEMU_CLOCK_REALTIME] &&
+        !active_timers[QEMU_CLOCK_VIRTUAL] &&
+        !active_timers[QEMU_CLOCK_HOST]) {
+        return;
+    }
+
+    timeKillEvent(mm_timer);
+
+    nearest_delta_ms = (qemu_next_alarm_deadline() + 999999) / 1000000;
+    if (nearest_delta_ms < 1) {
+        nearest_delta_ms = 1;
+    }
+    mm_timer = timeSetEvent(nearest_delta_ms,
+                            mm_period,
+                            mm_alarm_handler,
+                            (DWORD)t,
+                            TIME_ONESHOT | TIME_CALLBACK_FUNCTION);
+
+    if (!mm_timer) {
+        fprintf(stderr, "Failed to re-arm win32 alarm timer %ld\n",
+                GetLastError());
+
+        timeEndPeriod(mm_period);
+        exit(1);
+    }
+}
+
 static int win32_start_timer(struct qemu_alarm_timer *t)
 {
     HANDLE hTimer;