| From 8c3ba8d049247dc06b6dcee1711a11b26647aa44 Mon Sep 17 00:00:00 2001 |
| From: Kerstin Jonsson <kerstin.jonsson@ericsson.com> |
| Date: Mon, 24 May 2010 12:13:15 -0700 |
| Subject: x86, apic: ack all pending irqs when crashed/on kexec |
| |
| From: Kerstin Jonsson <kerstin.jonsson@ericsson.com> |
| |
| commit 8c3ba8d049247dc06b6dcee1711a11b26647aa44 upstream. |
| |
| When the SMP kernel decides to crash_kexec() the local APICs may have |
| pending interrupts in their vector tables. |
| |
| The setup routine for the local APIC has a deficient mechanism for |
| clearing these interrupts, it only handles interrupts that has already |
| been dispatched to the local core for servicing (the ISR register) safely, |
| it doesn't consider lower prioritized queued interrupts stored in the IRR |
| register. |
| |
| If you have more than one pending interrupt within the same 32 bit word in |
| the LAPIC vector table registers you may find yourself entering the IO |
| APIC setup with pending interrupts left in the LAPIC. This is a situation |
| for wich the IO APIC setup is not prepared. Depending of what/which |
| interrupt vector/vectors are stuck in the APIC tables your system may show |
| various degrees of malfunctioning. That was the reason why the |
| check_timer() failed in our system, the timer interrupts was blocked by |
| pending interrupts from the old kernel when routed trough the IO APIC. |
| |
| Additional comment from Jiri Bohac: |
| ============== |
| If this should go into stable release, |
| I'd add some kind of limit on the number of iterations, just to be safe from |
| hard to debug lock-ups: |
| |
| +if (loops++ > MAX_LOOPS) { |
| + printk("LAPIC pending clean-up") |
| + break; |
| +} |
| while (queued); |
| |
| with MAX_LOOPS something like 1E9 this would leave plenty of time for the |
| pending IRQs to be cleared and would and still cause at most a second of delay |
| if the loop were to lock-up for whatever reason. |
| |
| [trenn@suse.de: |
| |
| V2: Use tsc if avail to bail out after 1 sec due to possible virtual |
| apic_read calls which may take rather long (suggested by: Avi Kivity |
| <avi@redhat.com>) If no tsc is available bail out quickly after |
| cpu_khz, if we broke out too early and still have irqs pending (which |
| should never happen?) we still get a WARN_ON... |
| |
| V3: - Fixed indentation -> checkpatch clean |
| - max_loops must be signed |
| |
| V4: - Fix typo, mixed up tsc and ntsc in first rdtscll() call |
| |
| V5: Adjust WARN_ON() condition to also catch error in cpu_has_tsc case] |
| |
| Cc: <jbohac@novell.com> |
| Cc: Yinghai Lu <yinghai@kernel.org> |
| Cc: Kerstin Jonsson <kerstin.jonsson@ericsson.com> |
| Cc: Avi Kivity <avi@redhat.com> |
| Cc: Suresh Siddha <suresh.b.siddha@intel.com> |
| Tested-by: Eric W. Biederman <ebiederm@xmission.com> |
| Signed-off-by: Thomas Renninger <trenn@suse.de> |
| LKML-Reference: <201005241913.o4OJDGWM010865@imap1.linux-foundation.org> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: H. Peter Anvin <hpa@linux.intel.com> |
| Cc: Thomas Renninger <trenn@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| arch/x86/kernel/apic/apic.c | 41 +++++++++++++++++++++++++++++++++-------- |
| 1 file changed, 33 insertions(+), 8 deletions(-) |
| |
| --- a/arch/x86/kernel/apic/apic.c |
| +++ b/arch/x86/kernel/apic/apic.c |
| @@ -51,6 +51,7 @@ |
| #include <asm/smp.h> |
| #include <asm/mce.h> |
| #include <asm/kvm_para.h> |
| +#include <asm/tsc.h> |
| |
| unsigned int num_processors; |
| |
| @@ -1151,8 +1152,13 @@ static void __cpuinit lapic_setup_esr(vo |
| */ |
| void __cpuinit setup_local_APIC(void) |
| { |
| - unsigned int value; |
| - int i, j; |
| + unsigned int value, queued; |
| + int i, j, acked = 0; |
| + unsigned long long tsc = 0, ntsc; |
| + long long max_loops = cpu_khz; |
| + |
| + if (cpu_has_tsc) |
| + rdtscll(tsc); |
| |
| if (disable_apic) { |
| arch_disable_smp_support(); |
| @@ -1204,13 +1210,32 @@ void __cpuinit setup_local_APIC(void) |
| * the interrupt. Hence a vector might get locked. It was noticed |
| * for timer irq (vector 0x31). Issue an extra EOI to clear ISR. |
| */ |
| - for (i = APIC_ISR_NR - 1; i >= 0; i--) { |
| - value = apic_read(APIC_ISR + i*0x10); |
| - for (j = 31; j >= 0; j--) { |
| - if (value & (1<<j)) |
| - ack_APIC_irq(); |
| + do { |
| + queued = 0; |
| + for (i = APIC_ISR_NR - 1; i >= 0; i--) |
| + queued |= apic_read(APIC_IRR + i*0x10); |
| + |
| + for (i = APIC_ISR_NR - 1; i >= 0; i--) { |
| + value = apic_read(APIC_ISR + i*0x10); |
| + for (j = 31; j >= 0; j--) { |
| + if (value & (1<<j)) { |
| + ack_APIC_irq(); |
| + acked++; |
| + } |
| + } |
| } |
| - } |
| + if (acked > 256) { |
| + printk(KERN_ERR "LAPIC pending interrupts after %d EOI\n", |
| + acked); |
| + break; |
| + } |
| + if (cpu_has_tsc) { |
| + rdtscll(ntsc); |
| + max_loops = (cpu_khz << 10) - (ntsc - tsc); |
| + } else |
| + max_loops--; |
| + } while (queued && max_loops > 0); |
| + WARN_ON(max_loops <= 0); |
| |
| /* |
| * Now that we are all set up, enable the APIC |