aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/smp.c
diff options
context:
space:
mode:
authorDon Zickus <dzickus@redhat.com>2012-05-11 14:41:14 -0400
committerIngo Molnar <mingo@kernel.org>2012-05-14 05:49:37 -0400
commit7d007d21e539dbecb6942c5734e6649f720982cf (patch)
treebe2def495b0dda661075e1446caead8fca20642d /arch/x86/kernel/smp.c
parent5d2b86d90f7cc4a41316cef3d41560da6141f45c (diff)
x86/reboot: Use NMI to assist in shutting down if IRQ fails
For v3.3, I added code to use the NMI to stop other cpus in the panic case. The idea was to make sure all cpus on the system were definitely halted to help serialize the panic path to execute the rest of the code on a single cpu. The main problem it was trying to solve was how to stop a cpu that was spinning with its irqs disabled. A IPI irq would be stuck and couldn't get in there, but an NMI could. Things were great until we had another conversation about some pstore changes. Because some of the backend pstore still uses spinlocks to protect the device access, things could get ugly if a panic happened and we were stuck spinning on a lock. Now with the NMI shutting down cpus, we could assume no other cpus were running and just bust the spin lock and proceed. The counter argument was, well if you do that the backend could be in a screwed up state and you might not be able to save anything as a result. If we could have just given the cpu a little more time to finish things, we could have grabbed the spin lock cleanly and everything would have been fine. Well, how do give a cpu a 'little more time' in the panic case? For the most part you can't without spinning on the lock and even in that case, how long do you spin for? So instead of making it ugly in the pstore code, just mimic the idea that stop_machine had, which is block on an IRQ IPI until the remote cpu has re-enabled interrupts and left the critical region. Which is what happens now using REBOOT_IRQ. Then leave the NMI case for those cpus that are truly stuck after a short time. This leaves the current behaviour alone and just handle a corner case. Most systems should never have to enter the NMI code and if they do, print out a message in case the NMI itself causes another issue. Signed-off-by: Don Zickus <dzickus@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/1336761675-24296-3-git-send-email-dzickus@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel/smp.c')
-rw-r--r--arch/x86/kernel/smp.c61
1 files changed, 56 insertions, 5 deletions
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c
index 6d20f523bc4e..228e7405511a 100644
--- a/arch/x86/kernel/smp.c
+++ b/arch/x86/kernel/smp.c
@@ -29,6 +29,7 @@
29#include <asm/mmu_context.h> 29#include <asm/mmu_context.h>
30#include <asm/proto.h> 30#include <asm/proto.h>
31#include <asm/apic.h> 31#include <asm/apic.h>
32#include <asm/nmi.h>
32/* 33/*
33 * Some notes on x86 processor bugs affecting SMP operation: 34 * Some notes on x86 processor bugs affecting SMP operation:
34 * 35 *
@@ -108,6 +109,8 @@
108 * about nothing of note with C stepping upwards. 109 * about nothing of note with C stepping upwards.
109 */ 110 */
110 111
112static atomic_t stopping_cpu = ATOMIC_INIT(-1);
113
111/* 114/*
112 * this function sends a 'reschedule' IPI to another CPU. 115 * this function sends a 'reschedule' IPI to another CPU.
113 * it goes straight through and wastes no time serializing 116 * it goes straight through and wastes no time serializing
@@ -148,6 +151,17 @@ void native_send_call_func_ipi(const struct cpumask *mask)
148 free_cpumask_var(allbutself); 151 free_cpumask_var(allbutself);
149} 152}
150 153
154static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs)
155{
156 /* We are registered on stopping cpu too, avoid spurious NMI */
157 if (raw_smp_processor_id() == atomic_read(&stopping_cpu))
158 return NMI_HANDLED;
159
160 stop_this_cpu(NULL);
161
162 return NMI_HANDLED;
163}
164
151/* 165/*
152 * this function calls the 'stop' function on all other CPUs in the system. 166 * this function calls the 'stop' function on all other CPUs in the system.
153 */ 167 */
@@ -171,13 +185,25 @@ static void native_stop_other_cpus(int wait)
171 /* 185 /*
172 * Use an own vector here because smp_call_function 186 * Use an own vector here because smp_call_function
173 * does lots of things not suitable in a panic situation. 187 * does lots of things not suitable in a panic situation.
174 * On most systems we could also use an NMI here, 188 */
175 * but there are a few systems around where NMI 189
176 * is problematic so stay with an non NMI for now 190 /*
177 * (this implies we cannot stop CPUs spinning with irq off 191 * We start by using the REBOOT_VECTOR irq.
178 * currently) 192 * The irq is treated as a sync point to allow critical
193 * regions of code on other cpus to release their spin locks
194 * and re-enable irqs. Jumping straight to an NMI might
195 * accidentally cause deadlocks with further shutdown/panic
196 * code. By syncing, we give the cpus up to one second to
197 * finish their work before we force them off with the NMI.
179 */ 198 */
180 if (num_online_cpus() > 1) { 199 if (num_online_cpus() > 1) {
200 /* did someone beat us here? */
201 if (atomic_cmpxchg(&stopping_cpu, -1, safe_smp_processor_id()) != -1)
202 return;
203
204 /* sync above data before sending IRQ */
205 wmb();
206
181 apic->send_IPI_allbutself(REBOOT_VECTOR); 207 apic->send_IPI_allbutself(REBOOT_VECTOR);
182 208
183 /* 209 /*
@@ -188,7 +214,32 @@ static void native_stop_other_cpus(int wait)
188 while (num_online_cpus() > 1 && (wait || timeout--)) 214 while (num_online_cpus() > 1 && (wait || timeout--))
189 udelay(1); 215 udelay(1);
190 } 216 }
217
218 /* if the REBOOT_VECTOR didn't work, try with the NMI */
219 if ((num_online_cpus() > 1)) {
220 if (register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback,
221 NMI_FLAG_FIRST, "smp_stop"))
222 /* Note: we ignore failures here */
223 /* Hope the REBOOT_IRQ is good enough */
224 goto finish;
225
226 /* sync above data before sending IRQ */
227 wmb();
228
229 pr_emerg("Shutting down cpus with NMI\n");
230
231 apic->send_IPI_allbutself(NMI_VECTOR);
232
233 /*
234 * Don't wait longer than a 10 ms if the caller
235 * didn't ask us to wait.
236 */
237 timeout = USEC_PER_MSEC * 10;
238 while (num_online_cpus() > 1 && (wait || timeout--))
239 udelay(1);
240 }
191 241
242finish:
192 local_irq_save(flags); 243 local_irq_save(flags);
193 disable_local_APIC(); 244 disable_local_APIC();
194 local_irq_restore(flags); 245 local_irq_restore(flags);