diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2012-07-11 14:26:35 -0400 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2012-09-26 09:47:07 -0400 |
commit | 6ba3c97a38803883c2eee489505796cb0a727122 (patch) | |
tree | b6400bf2e10bf8d55b63eae35ddb93f3a9e9b270 /arch/x86/kernel/traps.c | |
parent | ef3f628872c838933a279d0d7e63e707783c9710 (diff) |
x86: Exception hooks for userspace RCU extended QS
Add necessary hooks to x86 exception for userspace
RCU extended quiescent state support.
This includes traps, page fault, debug exceptions, etc...
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Alessio Igor Bogani <abogani@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Avi Kivity <avi@redhat.com>
Cc: Chris Metcalf <cmetcalf@tilera.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: Geoff Levand <geoff@infradead.org>
Cc: Gilad Ben Yossef <gilad@benyossef.com>
Cc: Hakan Akkan <hakanakkan@gmail.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Josh Triplett <josh@joshtriplett.org>
Cc: Kevin Hilman <khilman@ti.com>
Cc: Max Krasnyansky <maxk@qualcomm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephen Hemminger <shemminger@vyatta.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Sven-Thorsten Dietrich <thebigcorporation@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'arch/x86/kernel/traps.c')
-rw-r--r-- | arch/x86/kernel/traps.c | 81 |
1 files changed, 55 insertions, 26 deletions
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 74850e559449..378967578f22 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c | |||
@@ -55,6 +55,7 @@ | |||
55 | #include <asm/i387.h> | 55 | #include <asm/i387.h> |
56 | #include <asm/fpu-internal.h> | 56 | #include <asm/fpu-internal.h> |
57 | #include <asm/mce.h> | 57 | #include <asm/mce.h> |
58 | #include <asm/rcu.h> | ||
58 | 59 | ||
59 | #include <asm/mach_traps.h> | 60 | #include <asm/mach_traps.h> |
60 | 61 | ||
@@ -180,11 +181,15 @@ vm86_trap: | |||
180 | #define DO_ERROR(trapnr, signr, str, name) \ | 181 | #define DO_ERROR(trapnr, signr, str, name) \ |
181 | dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ | 182 | dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ |
182 | { \ | 183 | { \ |
183 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ | 184 | exception_enter(regs); \ |
184 | == NOTIFY_STOP) \ | 185 | if (notify_die(DIE_TRAP, str, regs, error_code, \ |
186 | trapnr, signr) == NOTIFY_STOP) { \ | ||
187 | exception_exit(regs); \ | ||
185 | return; \ | 188 | return; \ |
189 | } \ | ||
186 | conditional_sti(regs); \ | 190 | conditional_sti(regs); \ |
187 | do_trap(trapnr, signr, str, regs, error_code, NULL); \ | 191 | do_trap(trapnr, signr, str, regs, error_code, NULL); \ |
192 | exception_exit(regs); \ | ||
188 | } | 193 | } |
189 | 194 | ||
190 | #define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ | 195 | #define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ |
@@ -195,11 +200,15 @@ dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ | |||
195 | info.si_errno = 0; \ | 200 | info.si_errno = 0; \ |
196 | info.si_code = sicode; \ | 201 | info.si_code = sicode; \ |
197 | info.si_addr = (void __user *)siaddr; \ | 202 | info.si_addr = (void __user *)siaddr; \ |
198 | if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ | 203 | exception_enter(regs); \ |
199 | == NOTIFY_STOP) \ | 204 | if (notify_die(DIE_TRAP, str, regs, error_code, \ |
205 | trapnr, signr) == NOTIFY_STOP) { \ | ||
206 | exception_exit(regs); \ | ||
200 | return; \ | 207 | return; \ |
208 | } \ | ||
201 | conditional_sti(regs); \ | 209 | conditional_sti(regs); \ |
202 | do_trap(trapnr, signr, str, regs, error_code, &info); \ | 210 | do_trap(trapnr, signr, str, regs, error_code, &info); \ |
211 | exception_exit(regs); \ | ||
203 | } | 212 | } |
204 | 213 | ||
205 | DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV, | 214 | DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV, |
@@ -222,12 +231,14 @@ DO_ERROR_INFO(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check, | |||
222 | /* Runs on IST stack */ | 231 | /* Runs on IST stack */ |
223 | dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code) | 232 | dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code) |
224 | { | 233 | { |
234 | exception_enter(regs); | ||
225 | if (notify_die(DIE_TRAP, "stack segment", regs, error_code, | 235 | if (notify_die(DIE_TRAP, "stack segment", regs, error_code, |
226 | X86_TRAP_SS, SIGBUS) == NOTIFY_STOP) | 236 | X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) { |
227 | return; | 237 | preempt_conditional_sti(regs); |
228 | preempt_conditional_sti(regs); | 238 | do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL); |
229 | do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL); | 239 | preempt_conditional_cli(regs); |
230 | preempt_conditional_cli(regs); | 240 | } |
241 | exception_exit(regs); | ||
231 | } | 242 | } |
232 | 243 | ||
233 | dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) | 244 | dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) |
@@ -235,6 +246,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) | |||
235 | static const char str[] = "double fault"; | 246 | static const char str[] = "double fault"; |
236 | struct task_struct *tsk = current; | 247 | struct task_struct *tsk = current; |
237 | 248 | ||
249 | exception_enter(regs); | ||
238 | /* Return not checked because double check cannot be ignored */ | 250 | /* Return not checked because double check cannot be ignored */ |
239 | notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV); | 251 | notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV); |
240 | 252 | ||
@@ -255,27 +267,28 @@ do_general_protection(struct pt_regs *regs, long error_code) | |||
255 | { | 267 | { |
256 | struct task_struct *tsk; | 268 | struct task_struct *tsk; |
257 | 269 | ||
270 | exception_enter(regs); | ||
258 | conditional_sti(regs); | 271 | conditional_sti(regs); |
259 | 272 | ||
260 | #ifdef CONFIG_X86_32 | 273 | #ifdef CONFIG_X86_32 |
261 | if (regs->flags & X86_VM_MASK) { | 274 | if (regs->flags & X86_VM_MASK) { |
262 | local_irq_enable(); | 275 | local_irq_enable(); |
263 | handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); | 276 | handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); |
264 | return; | 277 | goto exit; |
265 | } | 278 | } |
266 | #endif | 279 | #endif |
267 | 280 | ||
268 | tsk = current; | 281 | tsk = current; |
269 | if (!user_mode(regs)) { | 282 | if (!user_mode(regs)) { |
270 | if (fixup_exception(regs)) | 283 | if (fixup_exception(regs)) |
271 | return; | 284 | goto exit; |
272 | 285 | ||
273 | tsk->thread.error_code = error_code; | 286 | tsk->thread.error_code = error_code; |
274 | tsk->thread.trap_nr = X86_TRAP_GP; | 287 | tsk->thread.trap_nr = X86_TRAP_GP; |
275 | if (!notify_die(DIE_GPF, "general protection fault", regs, error_code, | 288 | if (notify_die(DIE_GPF, "general protection fault", regs, error_code, |
276 | X86_TRAP_GP, SIGSEGV) == NOTIFY_STOP) | 289 | X86_TRAP_GP, SIGSEGV) != NOTIFY_STOP) |
277 | die("general protection fault", regs, error_code); | 290 | die("general protection fault", regs, error_code); |
278 | return; | 291 | goto exit; |
279 | } | 292 | } |
280 | 293 | ||
281 | tsk->thread.error_code = error_code; | 294 | tsk->thread.error_code = error_code; |
@@ -291,7 +304,8 @@ do_general_protection(struct pt_regs *regs, long error_code) | |||
291 | } | 304 | } |
292 | 305 | ||
293 | force_sig(SIGSEGV, tsk); | 306 | force_sig(SIGSEGV, tsk); |
294 | return; | 307 | exit: |
308 | exception_exit(regs); | ||
295 | } | 309 | } |
296 | 310 | ||
297 | /* May run on IST stack. */ | 311 | /* May run on IST stack. */ |
@@ -306,15 +320,16 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co | |||
306 | ftrace_int3_handler(regs)) | 320 | ftrace_int3_handler(regs)) |
307 | return; | 321 | return; |
308 | #endif | 322 | #endif |
323 | exception_enter(regs); | ||
309 | #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP | 324 | #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP |
310 | if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, | 325 | if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, |
311 | SIGTRAP) == NOTIFY_STOP) | 326 | SIGTRAP) == NOTIFY_STOP) |
312 | return; | 327 | goto exit; |
313 | #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */ | 328 | #endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */ |
314 | 329 | ||
315 | if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, | 330 | if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, |
316 | SIGTRAP) == NOTIFY_STOP) | 331 | SIGTRAP) == NOTIFY_STOP) |
317 | return; | 332 | goto exit; |
318 | 333 | ||
319 | /* | 334 | /* |
320 | * Let others (NMI) know that the debug stack is in use | 335 | * Let others (NMI) know that the debug stack is in use |
@@ -325,6 +340,8 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co | |||
325 | do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL); | 340 | do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL); |
326 | preempt_conditional_cli(regs); | 341 | preempt_conditional_cli(regs); |
327 | debug_stack_usage_dec(); | 342 | debug_stack_usage_dec(); |
343 | exit: | ||
344 | exception_exit(regs); | ||
328 | } | 345 | } |
329 | 346 | ||
330 | #ifdef CONFIG_X86_64 | 347 | #ifdef CONFIG_X86_64 |
@@ -385,6 +402,8 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | |||
385 | unsigned long dr6; | 402 | unsigned long dr6; |
386 | int si_code; | 403 | int si_code; |
387 | 404 | ||
405 | exception_enter(regs); | ||
406 | |||
388 | get_debugreg(dr6, 6); | 407 | get_debugreg(dr6, 6); |
389 | 408 | ||
390 | /* Filter out all the reserved bits which are preset to 1 */ | 409 | /* Filter out all the reserved bits which are preset to 1 */ |
@@ -400,7 +419,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | |||
400 | 419 | ||
401 | /* Catch kmemcheck conditions first of all! */ | 420 | /* Catch kmemcheck conditions first of all! */ |
402 | if ((dr6 & DR_STEP) && kmemcheck_trap(regs)) | 421 | if ((dr6 & DR_STEP) && kmemcheck_trap(regs)) |
403 | return; | 422 | goto exit; |
404 | 423 | ||
405 | /* DR6 may or may not be cleared by the CPU */ | 424 | /* DR6 may or may not be cleared by the CPU */ |
406 | set_debugreg(0, 6); | 425 | set_debugreg(0, 6); |
@@ -415,7 +434,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | |||
415 | 434 | ||
416 | if (notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code, | 435 | if (notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code, |
417 | SIGTRAP) == NOTIFY_STOP) | 436 | SIGTRAP) == NOTIFY_STOP) |
418 | return; | 437 | goto exit; |
419 | 438 | ||
420 | /* | 439 | /* |
421 | * Let others (NMI) know that the debug stack is in use | 440 | * Let others (NMI) know that the debug stack is in use |
@@ -431,7 +450,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | |||
431 | X86_TRAP_DB); | 450 | X86_TRAP_DB); |
432 | preempt_conditional_cli(regs); | 451 | preempt_conditional_cli(regs); |
433 | debug_stack_usage_dec(); | 452 | debug_stack_usage_dec(); |
434 | return; | 453 | goto exit; |
435 | } | 454 | } |
436 | 455 | ||
437 | /* | 456 | /* |
@@ -452,7 +471,8 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) | |||
452 | preempt_conditional_cli(regs); | 471 | preempt_conditional_cli(regs); |
453 | debug_stack_usage_dec(); | 472 | debug_stack_usage_dec(); |
454 | 473 | ||
455 | return; | 474 | exit: |
475 | exception_exit(regs); | ||
456 | } | 476 | } |
457 | 477 | ||
458 | /* | 478 | /* |
@@ -549,14 +569,17 @@ dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code) | |||
549 | #ifdef CONFIG_X86_32 | 569 | #ifdef CONFIG_X86_32 |
550 | ignore_fpu_irq = 1; | 570 | ignore_fpu_irq = 1; |
551 | #endif | 571 | #endif |
552 | 572 | exception_enter(regs); | |
553 | math_error(regs, error_code, X86_TRAP_MF); | 573 | math_error(regs, error_code, X86_TRAP_MF); |
574 | exception_exit(regs); | ||
554 | } | 575 | } |
555 | 576 | ||
556 | dotraplinkage void | 577 | dotraplinkage void |
557 | do_simd_coprocessor_error(struct pt_regs *regs, long error_code) | 578 | do_simd_coprocessor_error(struct pt_regs *regs, long error_code) |
558 | { | 579 | { |
580 | exception_enter(regs); | ||
559 | math_error(regs, error_code, X86_TRAP_XF); | 581 | math_error(regs, error_code, X86_TRAP_XF); |
582 | exception_exit(regs); | ||
560 | } | 583 | } |
561 | 584 | ||
562 | dotraplinkage void | 585 | dotraplinkage void |
@@ -623,6 +646,7 @@ EXPORT_SYMBOL_GPL(math_state_restore); | |||
623 | dotraplinkage void __kprobes | 646 | dotraplinkage void __kprobes |
624 | do_device_not_available(struct pt_regs *regs, long error_code) | 647 | do_device_not_available(struct pt_regs *regs, long error_code) |
625 | { | 648 | { |
649 | exception_enter(regs); | ||
626 | #ifdef CONFIG_MATH_EMULATION | 650 | #ifdef CONFIG_MATH_EMULATION |
627 | if (read_cr0() & X86_CR0_EM) { | 651 | if (read_cr0() & X86_CR0_EM) { |
628 | struct math_emu_info info = { }; | 652 | struct math_emu_info info = { }; |
@@ -631,6 +655,7 @@ do_device_not_available(struct pt_regs *regs, long error_code) | |||
631 | 655 | ||
632 | info.regs = regs; | 656 | info.regs = regs; |
633 | math_emulate(&info); | 657 | math_emulate(&info); |
658 | exception_exit(regs); | ||
634 | return; | 659 | return; |
635 | } | 660 | } |
636 | #endif | 661 | #endif |
@@ -638,12 +663,15 @@ do_device_not_available(struct pt_regs *regs, long error_code) | |||
638 | #ifdef CONFIG_X86_32 | 663 | #ifdef CONFIG_X86_32 |
639 | conditional_sti(regs); | 664 | conditional_sti(regs); |
640 | #endif | 665 | #endif |
666 | exception_exit(regs); | ||
641 | } | 667 | } |
642 | 668 | ||
643 | #ifdef CONFIG_X86_32 | 669 | #ifdef CONFIG_X86_32 |
644 | dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) | 670 | dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) |
645 | { | 671 | { |
646 | siginfo_t info; | 672 | siginfo_t info; |
673 | |||
674 | exception_enter(regs); | ||
647 | local_irq_enable(); | 675 | local_irq_enable(); |
648 | 676 | ||
649 | info.si_signo = SIGILL; | 677 | info.si_signo = SIGILL; |
@@ -651,10 +679,11 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) | |||
651 | info.si_code = ILL_BADSTK; | 679 | info.si_code = ILL_BADSTK; |
652 | info.si_addr = NULL; | 680 | info.si_addr = NULL; |
653 | if (notify_die(DIE_TRAP, "iret exception", regs, error_code, | 681 | if (notify_die(DIE_TRAP, "iret exception", regs, error_code, |
654 | X86_TRAP_IRET, SIGILL) == NOTIFY_STOP) | 682 | X86_TRAP_IRET, SIGILL) != NOTIFY_STOP) { |
655 | return; | 683 | do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code, |
656 | do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code, | 684 | &info); |
657 | &info); | 685 | } |
686 | exception_exit(regs); | ||
658 | } | 687 | } |
659 | #endif | 688 | #endif |
660 | 689 | ||