Hack for qemu's missed LAPIC timers
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 3 Dec 2013 06:50:33 +0000 (22:50 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 16 Jan 2014 21:07:51 +0000 (13:07 -0800)
If your system hangs and needs keyboard input (or network packets) to
wake up, try turning on LOUSY_APIC_TIMER, and report your results to the
mailing list.  We've seen this on qemu with KVM disabled.

Here's part of the email with details from debugging this:

------------------------------
> still, the question is whether or not __trigger was sitting there as
> an RKM while we halted the core, or if the timer IRQ didn't fire
> until we hit the KB.  i'll poke around a bit - i should be able to
> check the RKM queue from qemu.

turns out that we don't have any pending RKMs when everything is
halted (looked at the memory from qemu), which means the timer interrupt
isn't going off (since it should enqueue an RKM).  then once we hit a
key (like CTRL-Q), the timer interrupt does fire before the KB/serial
IRQ; we see the RKM __trigger_tchain queued (via the monitor/kfunc).

so it looks like the timer IRQ should fire, but doesn't.  and then when
we fire some other IRQ, like serial/KB, the timer IRQ does fire.  i
haven't been able to inspect the LAPIC from qemu - that's not the sort
of thing they'd emulate in physical memory for the monitor, i guess.

anyway, perhaps qemu with-no-kvm doesn't like something about our local
apic timer IRQs?  since qemu doesn't report an always running APIC, it's
possible for the timer to stop in deep C states or speedstep
transitions (SDM 3a 10.5.4).  though even when we -enable-kvm, qemu
also reports no "always running APIC."  maybe qemu is getting confused
and turning something off, or i screwed up some initialization?  Or
perhaps qemu doesn't like our use of "hlt", and has some race of it's
own where halt and alarms happen concurrently.

whenever we do break it out (via KB, for instance), we actually get two
IRQs delivered (handle_irq() runs twice from the moment i hit ctrl-q
til we get into the monitor (which happens directly from ctrl-q's IRQ
handler)).  so probably the first IRQ is the LAPIC timer, and the second
is the keyboard.

incidentally, any IRQ, not just a keyboard/serial IRQ, is enough to
break qemu out of its funk.  so instead of holding down the space bar
with a power supply, you can keep trying to telnet to localhost:5555.
=)

kern/arch/x86/Kconfig
kern/arch/x86/apic.c

index f15a4a7..d55bdca 100644 (file)
@@ -37,6 +37,19 @@ endmenu
 
 menu "x86 Hacks"
 
+config LOUSY_LAPIC_TIMER
+       bool "Lousy Local APIC Timer"
+       default n
+       help
+               This turns our one-shot APIC timer into a periodic timer.  If your
+               system seems to lock up until you hit the keyboard, say 'Y' here and
+               report the results.
+
+               Qemu without KVM had issues firing a one-shot LAPIC timer (the timer IRQ
+               would only go off when some other IRQ fired), but it worked with a
+               periodic tick.  Since we aggressively disarm the timer, this config
+               shouldn't be a performance hit.
+
 config NOMTRRS
        bool "Disable MTRRs"
        default n
index 9b3abb9..38e3d27 100644 (file)
@@ -167,6 +167,11 @@ bool ipi_is_pending(uint8_t vector)
  */
 void __lapic_set_timer(uint32_t ticks, uint8_t vec, bool periodic, uint8_t div)
 {
+#ifdef CONFIG_LOUSY_LAPIC_TIMER
+       /* qemu without kvm seems to delay timer IRQs on occasion, and needs extra
+        * IRQs from any source to get them delivered.  periodic does the trick. */
+       periodic = TRUE;
+#endif
        // clears bottom bit and then set divider
        write_mmreg32(LAPIC_TIMER_DIVIDE, (read_mmreg32(LAPIC_TIMER_DIVIDE) &~0xf) |
                      (div & 0xf));