x86/irq: Seperate unused system vectors from spurious entry again

Quite some time ago the interrupt entry stubs for unused vectors in the
system vector range got removed and directly mapped to the spurious
interrupt vector entry point.

Sounds reasonable, but it's subtly broken. The spurious interrupt vector
entry point pushes vector number 0xFF on the stack which makes the whole
logic in __smp_spurious_interrupt() pointless.

As a consequence any spurious interrupt which comes from a vector != 0xFF
is treated as a real spurious interrupt (vector 0xFF) and not
acknowledged. That subsequently stalls all interrupt vectors of equal and
lower priority, which brings the system to a grinding halt.

This can happen because even on 64-bit the system vector space is not
guaranteed to be fully populated. A full compile time handling of the
unused vectors is not possible because quite some of them are conditonally
populated at runtime.

Bring the entry stubs back, which wastes 160 bytes if all stubs are unused,
but gains the proper handling back. There is no point to selectively spare
some of the stubs which are known at compile time as the required code in
the IDT management would be way larger and convoluted.

Do not route the spurious entries through common_interrupt and do_IRQ() as
the original code did. Route it to smp_spurious_interrupt() which evaluates
the vector number and acts accordingly now that the real vector numbers are
handed in.

Fixup the pr_warn so the actual spurious vector (0xff) is clearly
distiguished from the other vectors and also note for the vectored case
whether it was pending in the ISR or not.

 "Spurious APIC interrupt (vector 0xFF) on CPU#0, should never happen."
 "Spurious interrupt vector 0xed on CPU#1. Acked."
 "Spurious interrupt vector 0xee on CPU#1. Not pending!."

Fixes: 2414e021ac ("x86: Avoid building unused IRQ entry stubs")
Reported-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Jan Beulich <jbeulich@suse.com>
Link: https://lkml.kernel.org/r/20190628111440.550568228@linutronix.de
This commit is contained in:
Thomas Gleixner 2019-06-28 13:11:54 +02:00
parent b7107a67f0
commit f8a8fe61fe
5 changed files with 77 additions and 17 deletions

View File

@ -1104,6 +1104,30 @@ ENTRY(irq_entries_start)
.endr .endr
END(irq_entries_start) END(irq_entries_start)
#ifdef CONFIG_X86_LOCAL_APIC
.align 8
ENTRY(spurious_entries_start)
vector=FIRST_SYSTEM_VECTOR
.rept (NR_VECTORS - FIRST_SYSTEM_VECTOR)
pushl $(~vector+0x80) /* Note: always in signed byte range */
vector=vector+1
jmp common_spurious
.align 8
.endr
END(spurious_entries_start)
common_spurious:
ASM_CLAC
addl $-0x80, (%esp) /* Adjust vector into the [-256, -1] range */
SAVE_ALL switch_stacks=1
ENCODE_FRAME_POINTER
TRACE_IRQS_OFF
movl %esp, %eax
call smp_spurious_interrupt
jmp ret_from_intr
ENDPROC(common_interrupt)
#endif
/* /*
* the CPU automatically disables interrupts when executing an IRQ vector, * the CPU automatically disables interrupts when executing an IRQ vector,
* so IRQ-flags tracing has to follow that: * so IRQ-flags tracing has to follow that:

View File

@ -375,6 +375,18 @@ ENTRY(irq_entries_start)
.endr .endr
END(irq_entries_start) END(irq_entries_start)
.align 8
ENTRY(spurious_entries_start)
vector=FIRST_SYSTEM_VECTOR
.rept (NR_VECTORS - FIRST_SYSTEM_VECTOR)
UNWIND_HINT_IRET_REGS
pushq $(~vector+0x80) /* Note: always in signed byte range */
jmp common_spurious
.align 8
vector=vector+1
.endr
END(spurious_entries_start)
.macro DEBUG_ENTRY_ASSERT_IRQS_OFF .macro DEBUG_ENTRY_ASSERT_IRQS_OFF
#ifdef CONFIG_DEBUG_ENTRY #ifdef CONFIG_DEBUG_ENTRY
pushq %rax pushq %rax
@ -571,10 +583,20 @@ _ASM_NOKPROBE(interrupt_entry)
/* Interrupt entry/exit. */ /* Interrupt entry/exit. */
/* /*
* The interrupt stubs push (~vector+0x80) onto the stack and * The interrupt stubs push (~vector+0x80) onto the stack and
* then jump to common_interrupt. * then jump to common_spurious/interrupt.
*/ */
common_spurious:
addq $-0x80, (%rsp) /* Adjust vector to [-256, -1] range */
call interrupt_entry
UNWIND_HINT_REGS indirect=1
call smp_spurious_interrupt /* rdi points to pt_regs */
jmp ret_from_intr
END(common_spurious)
_ASM_NOKPROBE(common_spurious)
/* common_interrupt is a hotpath. Align it */
.p2align CONFIG_X86_L1_CACHE_SHIFT .p2align CONFIG_X86_L1_CACHE_SHIFT
common_interrupt: common_interrupt:
addq $-0x80, (%rsp) /* Adjust vector to [-256, -1] range */ addq $-0x80, (%rsp) /* Adjust vector to [-256, -1] range */

View File

@ -150,6 +150,8 @@ extern char irq_entries_start[];
#define trace_irq_entries_start irq_entries_start #define trace_irq_entries_start irq_entries_start
#endif #endif
extern char spurious_entries_start[];
#define VECTOR_UNUSED NULL #define VECTOR_UNUSED NULL
#define VECTOR_SHUTDOWN ((void *)~0UL) #define VECTOR_SHUTDOWN ((void *)~0UL)
#define VECTOR_RETRIGGERED ((void *)~1UL) #define VECTOR_RETRIGGERED ((void *)~1UL)

View File

@ -2068,21 +2068,32 @@ __visible void __irq_entry smp_spurious_interrupt(struct pt_regs *regs)
entering_irq(); entering_irq();
trace_spurious_apic_entry(vector); trace_spurious_apic_entry(vector);
/*
* Check if this really is a spurious interrupt and ACK it
* if it is a vectored one. Just in case...
* Spurious interrupts should not be ACKed.
*/
v = apic_read(APIC_ISR + ((vector & ~0x1f) >> 1));
if (v & (1 << (vector & 0x1f)))
ack_APIC_irq();
inc_irq_stat(irq_spurious_count); inc_irq_stat(irq_spurious_count);
/* see sw-dev-man vol 3, chapter 7.4.13.5 */ /*
pr_info("spurious APIC interrupt through vector %02x on CPU#%d, " * If this is a spurious interrupt then do not acknowledge
"should never happen.\n", vector, smp_processor_id()); */
if (vector == SPURIOUS_APIC_VECTOR) {
/* See SDM vol 3 */
pr_info("Spurious APIC interrupt (vector 0xFF) on CPU#%d, should never happen.\n",
smp_processor_id());
goto out;
}
/*
* If it is a vectored one, verify it's set in the ISR. If set,
* acknowledge it.
*/
v = apic_read(APIC_ISR + ((vector & ~0x1f) >> 1));
if (v & (1 << (vector & 0x1f))) {
pr_info("Spurious interrupt (vector 0x%02x) on CPU#%d. Acked\n",
vector, smp_processor_id());
ack_APIC_irq();
} else {
pr_info("Spurious interrupt (vector 0x%02x) on CPU#%d. Not pending!\n",
vector, smp_processor_id());
}
out:
trace_spurious_apic_exit(vector); trace_spurious_apic_exit(vector);
exiting_irq(); exiting_irq();
} }

View File

@ -320,7 +320,8 @@ void __init idt_setup_apic_and_irq_gates(void)
#ifdef CONFIG_X86_LOCAL_APIC #ifdef CONFIG_X86_LOCAL_APIC
for_each_clear_bit_from(i, system_vectors, NR_VECTORS) { for_each_clear_bit_from(i, system_vectors, NR_VECTORS) {
set_bit(i, system_vectors); set_bit(i, system_vectors);
set_intr_gate(i, spurious_interrupt); entry = spurious_entries_start + 8 * (i - FIRST_SYSTEM_VECTOR);
set_intr_gate(i, entry);
} }
#endif #endif
} }