x86: Plug racy xAPIC access of CPU hotplug code
PATCH 1/1 x86: Plug racy xAPIC access of CPU hotplug code
apic_icr_write and its users in smpboot.c were apparently written under the assumption that this code would only run during early boot. But nowadays we also execute it when onlining a CPU later on while the system is fully running. That will make wakeup_cpu_via_init_nmi and, thus, also native_apic_icr_write run in plain process context. If we migrate the caller to a different CPU at the wrong time or interrupt it and write to ICR/ICR2 to send unrelated IPIs, we can end up sending INIT, SIPI or NMIs to wrong CPUs.
Fix this by disabling interrupts during the write to the ICR halves and disable preemption around waiting for ICR availability and using it.
--- arch/x86/kernel/apic/apic.c | 4 ++++ arch/x86/kernel/smpboot.c | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-)
arch/x86/kernel/apic/apic.c +4 -0
@@ -283,8 +283,12 @@ u32 native_safe_apic_wait_icr_idle(void)void native_apic_icr_write (u32 low,u32 id) { +unsigned long flags; + +local_irq_save (flags);apic_write (APIC_ICR2 ,SET_APIC_DEST_FIELD (id));apic_write (APIC_ICR , low); +local_irq_restore (flags); }u64 native_apic_icr_read (void )
arch/x86/kernel/smpboot.c +9 -2
@@ -701,11 +701,15 @@ wakeup_cpu_via_init_nmi(int cpu, unsigned long start_ip, int apicid,int id;int boot_error; +preempt_disable (); +/* * Wake up AP by INIT, INIT, STARTUP sequence. */ -if (cpu) -return wakeup_secondary_cpu_via_init (apicid, start_ip); +if (cpu) { + boot_error =wakeup_secondary_cpu_via_init (apicid, start_ip); + goto out; + }/* * Wake up BSP by nmi.
@@ -725,6 +729,9 @@ wakeup_cpu_via_init_nmi(int cpu, unsigned long start_ip, int apicid, boot_error =wakeup_secondary_cpu_via_nmi (id, start_ip); } +out: +preempt_enable (); +return boot_error; }
Signature · 1 line
1.8.1.1.298.ge7eed54
Quoted 11 lines · Jan Kiszka
apic_icr_writeand its users insmpboot.cwere apparently written under the assumption that this code would only run during early boot. But nowadays we also execute it when onlining a CPU later on while the system is fully running. That will makewakeup_cpu_via_init_nmiand, thus, alsonative_apic_icr_writerun in plain process context. If we migrate the caller to a different CPU at the wrong time or interrupt it and write to ICR/ICR2 to send unrelated IPIs, we can end up sending INIT, SIPI or NMIs to wrong CPUs.Fix this by disabling interrupts during the write to the ICR halves and disable preemption around waiting for ICR availability and using it.
If you just want to disable migration use get_cpu()/put_cpu()
-Andi
Quoted 10 lines · Andi Kleen
Quoted 9 lines · Jan Kiszka
apic_icr_writeand its users insmpboot.cwere apparently written under the assumption that this code would only run during early boot. But nowadays we also execute it when onlining a CPU later on while the system is fully running. That will makewakeup_cpu_via_init_nmiand, thus, alsonative_apic_icr_writerun in plain process context. If we migrate the caller to a different CPU at the wrong time or interrupt it and write to ICR/ICR2 to send unrelated IPIs, we can end up sending INIT, SIPI or NMIs to wrong CPUs.
Quoted 4 lines
Quoted 2 lines
Fix this by disabling interrupts during the write to the ICR halves and disable preemption around waiting for ICR availability and using it.
If you just want to disable migration use get_cpu()/put_cpu()
Fine with me if that is now preferred. Will that be the upstream way of -rt's migrate_disable()?
Jan
Signature · 2 lines
Siemens AG, Corporate Technology, CT RTC ITP SES-DE Corporate Competence Center Embedded Linux
Quoted 11 lines · Jan Kiszka
Quoted 10 lines · Andi Kleen
Quoted 9 lines · Jan Kiszka
apic_icr_writeand its users insmpboot.cwere apparently written under the assumption that this code would only run during early boot. But nowadays we also execute it when onlining a CPU later on while the system is fully running. That will makewakeup_cpu_via_init_nmiand, thus, alsonative_apic_icr_writerun in plain process context. If we migrate the caller to a different CPU at the wrong time or interrupt it and write to ICR/ICR2 to send unrelated IPIs, we can end up sending INIT, SIPI or NMIs to wrong CPUs.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Fix this by disabling interrupts during the write to the ICR halves and disable preemption around waiting for ICR availability and using it.
If you just want to disable migration use get_cpu()/put_cpu()
Fine with me if that is now preferred. Will that be the upstream way of -rt's
migrate_disable()?
Your original patch is fine, the suggestion to do ICR accesses with just preemption disabled is crap and is really asking for trouble: if some IRQ comes in at that point after all then it might cause all sorts of hard to debug problems (hangs, delays, missed IPIs, etc.).
Thanks,
Ingo
Quoted 19 lines · Ingo Molnar
Quoted 16 lines · Jan Kiszka
Quoted 14 lines · Andi Kleen
Quoted 11 lines · Jan Kiszka
apic_icr_writeand its users insmpboot.cwere apparently written under the assumption that this code would only run during early boot. But nowadays we also execute it when onlining a CPU later on while the system is fully running. That will makewakeup_cpu_via_init_nmiand, thus, alsonative_apic_icr_writerun in plain process context. If we migrate the caller to a different CPU at the wrong time or interrupt it and write to ICR/ICR2 to send unrelated IPIs, we can end up sending INIT, SIPI or NMIs to wrong CPUs.Fix this by disabling interrupts during the write to the ICR halves and disable preemption around waiting for ICR availability and using it.
If you just want to disable migration use get_cpu()/put_cpu()
Quoted 7 lines
Quoted 2 lines
Fine with me if that is now preferred. Will that be the upstream way of -rt's
migrate_disable()?Your original patch is fine, the suggestion to do ICR accesses with just preemption disabled is crap and is really asking for trouble: if some IRQ comes in at that point after all then it might cause all sorts of hard to debug problems (hangs, delays, missed IPIs, etc.).
Of course, we still need irqs off during ICR writes. I thought Andi was just suggesting to replace preempt_disable with get_cpu, maybe to document why we are disabling preemption here.
Jan
Signature · 2 lines
Siemens AG, Corporate Technology, CT RTC ITP SES-DE Corporate Competence Center Embedded Linux
Quoted 3 lines
Of course, we still need irqs off during ICR writes. I thought Andi was just suggesting to replace
preempt_disablewithget_cpu, maybe to document why we are disabling preemption here.
Yes that was my point.
Also with irq-off you of course still always have races against the NMI-level machine check.
-Andi
Quoted 2 lines · Andi Kleen
Also with irq-off you of course still always have races against the NMI-level machine check.
The self-IPI triggered over NMI won't touch the high-part of the ICR and will properly wait for ICR to become free again. So we are safe.
Jan
Signature · 2 lines
Siemens AG, Corporate Technology, CT RTC ITP SES-DE Corporate Competence Center Embedded Linux
Quoted 59 lines · Jan Kiszka
apic_icr_writeand its users insmpboot.cwere apparently written under the assumption that this code would only run during early boot. But nowadays we also execute it when onlining a CPU later on while the system is fully running. That will makewakeup_cpu_via_init_nmiand, thus, alsonative_apic_icr_writerun in plain process context. If we migrate the caller to a different CPU at the wrong time or interrupt it and write to ICR/ICR2 to send unrelated IPIs, we can end up sending INIT, SIPI or NMIs to wrong CPUs.Fix this by disabling interrupts during the write to the ICR halves and disable preemption around waiting for ICR availability and using it.
Signed-off-by Jan Kiszka <[email protected]>--- arch/x86/kernel/apic/apic.c | 4 ++++ arch/x86/kernel/smpboot.c | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-)arch/x86/kernel/apic/apic.c +4
-0@@ -283,8 +283,12 @@ u32 native_safe_apic_wait_icr_idle(void)void native_apic_icr_write (u32 low,u32 id) { +unsigned long flags; + +local_irq_save (flags);apic_write (APIC_ICR2 ,SET_APIC_DEST_FIELD (id));apic_write (APIC_ICR , low); +local_irq_restore (flags); }u64 native_apic_icr_read (void )arch/x86/kernel/smpboot.c +6
-2@@ -701,11 +701,15 @@ wakeup_cpu_via_init_nmi(int cpu, unsigned long start_ip, int apicid,int id;int boot_error; +preempt_disable (); +/* * Wake up AP by INIT, INIT, STARTUP sequence. */ -if (cpu) -return wakeup_secondary_cpu_via_init (apicid, start_ip); +if (cpu) { + boot_error =wakeup_secondary_cpu_via_init (apicid, start_ip); + goto out; + }/* * Wake up BSP by nmi.
arch/x86/kernel/smpboot.c wakeup_cpu_via_init_nmi(int cpu, unsigned long start_ip, int apicid, · line 737
Quoted 9 lines
arch/x86/kernel/smpboot.c +3
-0@@ -725,6 +729,9 @@ wakeup_cpu_via_init_nmi(int cpu, unsigned long start_ip, int apicid, boot_error =wakeup_secondary_cpu_via_nmi (id, start_ip); } +out: +preempt_enable (); +return boot_error; }
What's the status of this? Waiting for further review, or is it queued somewhere by now? Would be good to have in 3.14, and then also in stable kernels.
Jan
Patch fixes bug https://bugzilla.redhat.com/show_bug.cgi?id=1073568
Before it was possible to hang guest in several minutes, now reproducer runs for several hours without any issue. So,
tip-bot for Jan Kiszka
merge bot[tip:x86/apic] x86/apic: Plug racy xAPIC access of CPU hotplug code
In reply to Jan Kiszkax86/apic: Plug racy xAPIC access of CPU hotplug code
Commit-ID: ea7bdc65bca8cf837a63e0ff7b75daed83222511 Gitweb: http://git.kernel.org/tip/ea7bdc65bca8cf837a63e0ff7b75daed83222511 Author: Jan Kiszka <[email protected]> AuthorDate: Mon, 27 Jan 2014 20:14:06 +0100 Committer: Ingo Molnar <[email protected]> CommitDate: Tue, 11 Mar 2014 12:03:31 +0100
x86/apic: Plug racy xAPIC access of CPU hotplug code
apic_icr_write() and its users in smpboot.c were apparently written under the assumption that this code would only run during early boot. But nowadays we also execute it when onlining a CPU later on while the system is fully running. That will make wakeup_cpu_via_init_nmi and, thus, also native_apic_icr_write run in plain process context. If we migrate the caller to a different CPU at the wrong time or interrupt it and write to ICR/ICR2 to send unrelated IPIs, we can end up sending INIT, SIPI or NMIs to wrong CPUs.
Fix this by disabling interrupts during the write to the ICR halves and disable preemption around waiting for ICR availability and using it.
--- arch/x86/kernel/apic/apic.c | 4 ++++ arch/x86/kernel/smpboot.c | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-)
arch/x86/kernel/apic/apic.c +4 -0
@@ -286,8 +286,12 @@ u32 native_safe_apic_wait_icr_idle(void)void native_apic_icr_write (u32 low,u32 id) { +unsigned long flags; + +local_irq_save (flags);apic_write (APIC_ICR2 ,SET_APIC_DEST_FIELD (id));apic_write (APIC_ICR , low); +local_irq_restore (flags); }u64 native_apic_icr_read (void )
arch/x86/kernel/smpboot.c +9 -2
@@ -702,11 +702,15 @@ wakeup_cpu_via_init_nmi(int cpu, unsigned long start_ip, int apicid,int id;int boot_error; +preempt_disable (); +/* * Wake up AP by INIT, INIT, STARTUP sequence. */ -if (cpu) -return wakeup_secondary_cpu_via_init (apicid, start_ip); +if (cpu) { + boot_error =wakeup_secondary_cpu_via_init (apicid, start_ip); + goto out; + }/* * Wake up BSP by nmi.
@@ -726,6 +730,9 @@ wakeup_cpu_via_init_nmi(int cpu, unsigned long start_ip, int apicid, boot_error =wakeup_secondary_cpu_via_nmi (id, start_ip); } +out: +preempt_enable (); +return boot_error; }