From: Kris Van Hees Date: Thu, 22 Dec 2016 07:20:57 +0000 (-0500) Subject: dtrace: add support for passing return value from trap handlers X-Git-Tag: v4.1.12-92~25^2~1 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=559ab88733463614f050818c4e3a519d5bf665d4;p=users%2Fjedix%2Flinux-maple.git dtrace: add support for passing return value from trap handlers Prior to this patch, trap handlers were called to service traps without any mechanism to report back to the lowest level trap entry point. The DTrace FBT implementation on x86 needs to be able to do just that because FBT probes are enabled by replacing a one-byte assembler instruction with a one-byte instruction that causes a trap. After the trap is handled, we need to emulate the instruction that was replaced prior to returning to the original instruction stream. Because different instructions may occur at FBT probe points, we need to be able to report back to the trap entry point which instruction was replaced by the trap. Handlers that do not use notify_die() always return 0. Those who do use notify_die() to call handlers have been modified to return the value that the handler passed on its return. Signed-off-by: Kris Van Hees Reviewed-By: Dan Duval Orabug: 25312278 --- diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index 1f5a86d518db..f0552a007adc 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h @@ -213,8 +213,8 @@ extern void mce_disable_bank(int bank); */ /* Call the installed machine check handler for this CPU setup. */ -extern void (*machine_check_vector)(struct pt_regs *, long error_code); -void do_machine_check(struct pt_regs *, long); +extern int (*machine_check_vector)(struct pt_regs *, long error_code); +int do_machine_check(struct pt_regs *, long); /* * Threshold handler diff --git a/arch/x86/include/asm/traps.h b/arch/x86/include/asm/traps.h index 4e49d7dff78e..bb3af3db6f22 100644 --- a/arch/x86/include/asm/traps.h +++ b/arch/x86/include/asm/traps.h @@ -56,41 +56,41 @@ asmlinkage void trace_page_fault(void); #define trace_async_page_fault async_page_fault #endif -dotraplinkage void do_divide_error(struct pt_regs *, long); -dotraplinkage void do_debug(struct pt_regs *, long); -dotraplinkage void do_nmi(struct pt_regs *, long); -dotraplinkage void do_int3(struct pt_regs *, long); -dotraplinkage void do_overflow(struct pt_regs *, long); -dotraplinkage void do_bounds(struct pt_regs *, long); -dotraplinkage void do_invalid_op(struct pt_regs *, long); -dotraplinkage void do_device_not_available(struct pt_regs *, long); -dotraplinkage void do_coprocessor_segment_overrun(struct pt_regs *, long); -dotraplinkage void do_invalid_TSS(struct pt_regs *, long); -dotraplinkage void do_segment_not_present(struct pt_regs *, long); -dotraplinkage void do_stack_segment(struct pt_regs *, long); +dotraplinkage int do_divide_error(struct pt_regs *, long); +dotraplinkage int do_debug(struct pt_regs *, long); +dotraplinkage int do_nmi(struct pt_regs *, long); +dotraplinkage int do_int3(struct pt_regs *, long); +dotraplinkage int do_overflow(struct pt_regs *, long); +dotraplinkage int do_bounds(struct pt_regs *, long); +dotraplinkage int do_invalid_op(struct pt_regs *, long); +dotraplinkage int do_device_not_available(struct pt_regs *, long); +dotraplinkage int do_coprocessor_segment_overrun(struct pt_regs *, long); +dotraplinkage int do_invalid_TSS(struct pt_regs *, long); +dotraplinkage int do_segment_not_present(struct pt_regs *, long); +dotraplinkage int do_stack_segment(struct pt_regs *, long); #ifdef CONFIG_X86_64 -dotraplinkage void do_double_fault(struct pt_regs *, long); +dotraplinkage int do_double_fault(struct pt_regs *, long); asmlinkage struct pt_regs *sync_regs(struct pt_regs *); #endif -dotraplinkage void do_general_protection(struct pt_regs *, long); -dotraplinkage void do_page_fault(struct pt_regs *, unsigned long); +dotraplinkage int do_general_protection(struct pt_regs *, long); +dotraplinkage int do_page_fault(struct pt_regs *, unsigned long); #ifdef CONFIG_TRACING -dotraplinkage void trace_do_page_fault(struct pt_regs *, unsigned long); +dotraplinkage int trace_do_page_fault(struct pt_regs *, unsigned long); #else -static inline void trace_do_page_fault(struct pt_regs *regs, unsigned long error) +static inline int trace_do_page_fault(struct pt_regs *regs, unsigned long error) { - do_page_fault(regs, error); + return do_page_fault(regs, error); } #endif -dotraplinkage void do_spurious_interrupt_bug(struct pt_regs *, long); -dotraplinkage void do_coprocessor_error(struct pt_regs *, long); -dotraplinkage void do_alignment_check(struct pt_regs *, long); +dotraplinkage int do_spurious_interrupt_bug(struct pt_regs *, long); +dotraplinkage int do_coprocessor_error(struct pt_regs *, long); +dotraplinkage int do_alignment_check(struct pt_regs *, long); #ifdef CONFIG_X86_MCE -dotraplinkage void do_machine_check(struct pt_regs *, long); +dotraplinkage int do_machine_check(struct pt_regs *, long); #endif -dotraplinkage void do_simd_coprocessor_error(struct pt_regs *, long); +dotraplinkage int do_simd_coprocessor_error(struct pt_regs *, long); #ifdef CONFIG_X86_32 -dotraplinkage void do_iret_error(struct pt_regs *, long); +dotraplinkage int do_iret_error(struct pt_regs *, long); #endif static inline int get_si_code(unsigned long condition) diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 20190bdac9d5..d99513d1b523 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -1022,7 +1022,7 @@ static void mce_clear_state(unsigned long *toclear) * MCE broadcast. However some CPUs might be broken beyond repair, * so be always careful when synchronizing with others. */ -void do_machine_check(struct pt_regs *regs, long error_code) +int do_machine_check(struct pt_regs *regs, long error_code) { struct mca_config *cfg = &mca_cfg; struct mce m, *final; @@ -1205,6 +1205,7 @@ out: ist_end_non_atomic(); done: ist_exit(regs, prev_state); + return 0; } EXPORT_SYMBOL_GPL(do_machine_check); @@ -1672,14 +1673,15 @@ static void __mcheck_cpu_init_timer(void) } /* Handle unconfigured int18 (should never happen) */ -static void unexpected_machine_check(struct pt_regs *regs, long error_code) +static int unexpected_machine_check(struct pt_regs *regs, long error_code) { pr_err("CPU#%d: Unexpected int18 (Machine Check)\n", smp_processor_id()); + return 0; } /* Call the installed machine check handler for this CPU setup. */ -void (*machine_check_vector)(struct pt_regs *, long error_code) = +int (*machine_check_vector)(struct pt_regs *, long error_code) = unexpected_machine_check; /* diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index e2532a7c7475..db13325f8254 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -254,7 +254,7 @@ u32 kvm_read_and_reset_pf_reason(void) EXPORT_SYMBOL_GPL(kvm_read_and_reset_pf_reason); NOKPROBE_SYMBOL(kvm_read_and_reset_pf_reason); -dotraplinkage void +dotraplinkage int do_async_page_fault(struct pt_regs *regs, unsigned long error_code) { enum ctx_state prev_state; @@ -277,6 +277,8 @@ do_async_page_fault(struct pt_regs *regs, unsigned long error_code) rcu_irq_exit(); break; } + + return 0; } NOKPROBE_SYMBOL(do_async_page_fault); diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index d05bd2e2ee91..684bb77a7811 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c @@ -478,12 +478,12 @@ static DEFINE_PER_CPU(unsigned long, nmi_cr2); static DEFINE_PER_CPU(int, update_debug_stack); #endif -dotraplinkage notrace void +dotraplinkage notrace int do_nmi(struct pt_regs *regs, long error_code) { if (this_cpu_read(nmi_state) != NMI_NOT_RUNNING) { this_cpu_write(nmi_state, NMI_LATCHED); - return; + return 0; } this_cpu_write(nmi_state, NMI_EXECUTING); this_cpu_write(nmi_cr2, read_cr2()); @@ -522,6 +522,7 @@ nmi_restart: write_cr2(this_cpu_read(nmi_cr2)); if (this_cpu_dec_return(nmi_state)) goto nmi_restart; + return 0; } NOKPROBE_SYMBOL(do_nmi); diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 324ab5247687..9a94ec19be08 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -286,26 +286,28 @@ do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, } NOKPROBE_SYMBOL(do_trap); -static void do_error_trap(struct pt_regs *regs, long error_code, char *str, +static int do_error_trap(struct pt_regs *regs, long error_code, char *str, unsigned long trapnr, int signr) { enum ctx_state prev_state = exception_enter(); siginfo_t info; + int ret; - if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) != - NOTIFY_STOP) { + ret = notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr); + if ((ret & NOTIFY_STOP_MASK) != NOTIFY_STOP_MASK) { conditional_sti(regs); do_trap(trapnr, signr, str, regs, error_code, fill_trap_info(regs, signr, trapnr, &info)); } exception_exit(prev_state); + return notifier_to_errno(ret); } #define DO_ERROR(trapnr, signr, str, name) \ -dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ +dotraplinkage int do_##name(struct pt_regs *regs, long error_code) \ { \ - do_error_trap(regs, error_code, str, trapnr, signr); \ + return do_error_trap(regs, error_code, str, trapnr, signr); \ } DO_ERROR(X86_TRAP_DE, SIGFPE, "divide error", divide_error) @@ -319,7 +321,7 @@ DO_ERROR(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check) #ifdef CONFIG_X86_64 /* Runs on IST stack */ -dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) +dotraplinkage int do_double_fault(struct pt_regs *regs, long error_code) { static const char str[] = "double fault"; struct task_struct *tsk = current; @@ -347,7 +349,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) regs->ip = (unsigned long)general_protection; regs->sp = (unsigned long)&normal_regs->orig_ax; - return; + return 0; } #endif @@ -366,10 +368,12 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) */ for (;;) die(str, regs, error_code); + + return 0; } #endif -dotraplinkage void do_bounds(struct pt_regs *regs, long error_code) +dotraplinkage int do_bounds(struct pt_regs *regs, long error_code) { struct task_struct *tsk = current; struct xsave_struct *xsave_buf; @@ -439,7 +443,7 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code) exit: exception_exit(prev_state); - return; + return 0; exit_trap: /* * This path out is for all the cases where we could not @@ -450,13 +454,15 @@ exit_trap: */ do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, NULL); exception_exit(prev_state); + return 0; } -dotraplinkage void +dotraplinkage int do_general_protection(struct pt_regs *regs, long error_code) { struct task_struct *tsk; enum ctx_state prev_state; + int ret = 0; prev_state = exception_enter(); conditional_sti(regs); @@ -474,8 +480,9 @@ do_general_protection(struct pt_regs *regs, long error_code) tsk->thread.error_code = error_code; tsk->thread.trap_nr = X86_TRAP_GP; - if (notify_die(DIE_GPF, "general protection fault", regs, error_code, - X86_TRAP_GP, SIGSEGV) != NOTIFY_STOP) + ret = notify_die(DIE_GPF, "general protection fault", regs, + error_code, X86_TRAP_GP, SIGSEGV); + if ((ret & NOTIFY_STOP_MASK) != NOTIFY_STOP_MASK) die("general protection fault", regs, error_code); goto exit; } @@ -495,13 +502,15 @@ do_general_protection(struct pt_regs *regs, long error_code) force_sig_info(SIGSEGV, SEND_SIG_PRIV, tsk); exit: exception_exit(prev_state); + return notifier_to_errno(ret); } NOKPROBE_SYMBOL(do_general_protection); /* May run on IST stack. */ -dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) +dotraplinkage int notrace do_int3(struct pt_regs *regs, long error_code) { enum ctx_state prev_state; + int ret = 0; #ifdef CONFIG_DYNAMIC_FTRACE /* @@ -510,10 +519,10 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) */ if (unlikely(atomic_read(&modifying_ftrace_code)) && ftrace_int3_handler(regs)) - return; + return 0; #endif if (poke_int3_handler(regs)) - return; + return 0; prev_state = ist_enter(regs); #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP @@ -527,9 +536,12 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) goto exit; #endif - if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, - SIGTRAP) == NOTIFY_STOP) + ret = notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP, + SIGTRAP); + if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) { + ret = notifier_to_errno(ret); goto exit; + } /* * Let others (NMI) know that the debug stack is in use @@ -542,6 +554,7 @@ dotraplinkage void notrace do_int3(struct pt_regs *regs, long error_code) debug_stack_usage_dec(); exit: ist_exit(regs, prev_state); + return ret; } NOKPROBE_SYMBOL(do_int3); @@ -614,7 +627,7 @@ NOKPROBE_SYMBOL(fixup_bad_iret); * * May run on IST stack. */ -dotraplinkage void do_debug(struct pt_regs *regs, long error_code) +dotraplinkage int do_debug(struct pt_regs *regs, long error_code) { struct task_struct *tsk = current; enum ctx_state prev_state; @@ -698,6 +711,7 @@ dotraplinkage void do_debug(struct pt_regs *regs, long error_code) exit: ist_exit(regs, prev_state); + return 0; } NOKPROBE_SYMBOL(do_debug); @@ -790,16 +804,17 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr) force_sig_info(SIGFPE, &info, task); } -dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code) +dotraplinkage int do_coprocessor_error(struct pt_regs *regs, long error_code) { enum ctx_state prev_state; prev_state = exception_enter(); math_error(regs, error_code, X86_TRAP_MF); exception_exit(prev_state); + return 0; } -dotraplinkage void +dotraplinkage int do_simd_coprocessor_error(struct pt_regs *regs, long error_code) { enum ctx_state prev_state; @@ -807,9 +822,10 @@ do_simd_coprocessor_error(struct pt_regs *regs, long error_code) prev_state = exception_enter(); math_error(regs, error_code, X86_TRAP_XF); exception_exit(prev_state); + return 0; } -dotraplinkage void +dotraplinkage int do_spurious_interrupt_bug(struct pt_regs *regs, long error_code) { conditional_sti(regs); @@ -817,6 +833,7 @@ do_spurious_interrupt_bug(struct pt_regs *regs, long error_code) /* No need to warn about this any longer. */ pr_info("Ignoring P6 Local APIC Spurious Interrupt Bug...\n"); #endif + return 0; } asmlinkage __visible void __attribute__((weak)) smp_thermal_interrupt(void) @@ -869,7 +886,7 @@ void math_state_restore(void) } EXPORT_SYMBOL_GPL(math_state_restore); -dotraplinkage void +dotraplinkage int do_device_not_available(struct pt_regs *regs, long error_code) { enum ctx_state prev_state; @@ -886,7 +903,7 @@ do_device_not_available(struct pt_regs *regs, long error_code) info.regs = regs; math_emulate(&info); exception_exit(prev_state); - return; + return 0; } #endif math_state_restore(); /* interrupts still off */ @@ -894,6 +911,7 @@ do_device_not_available(struct pt_regs *regs, long error_code) conditional_sti(regs); #endif exception_exit(prev_state); + return 0; } NOKPROBE_SYMBOL(do_device_not_available); diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 08fbe2d9344d..9cf6ad536ad6 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -1302,7 +1302,7 @@ good_area: } NOKPROBE_SYMBOL(__do_page_fault); -dotraplinkage void notrace +dotraplinkage int notrace do_page_fault(struct pt_regs *regs, unsigned long error_code) { unsigned long address = read_cr2(); /* Get the faulting address */ @@ -1319,6 +1319,7 @@ do_page_fault(struct pt_regs *regs, unsigned long error_code) prev_state = exception_enter(); __do_page_fault(regs, error_code, address); exception_exit(prev_state); + return 0; } NOKPROBE_SYMBOL(do_page_fault); @@ -1333,7 +1334,7 @@ trace_page_fault_entries(unsigned long address, struct pt_regs *regs, trace_page_fault_kernel(address, regs, error_code); } -dotraplinkage void notrace +dotraplinkage int notrace trace_do_page_fault(struct pt_regs *regs, unsigned long error_code) { /* @@ -1349,6 +1350,7 @@ trace_do_page_fault(struct pt_regs *regs, unsigned long error_code) trace_page_fault_entries(address, regs, error_code); __do_page_fault(regs, error_code, address); exception_exit(prev_state); + return 0; } NOKPROBE_SYMBOL(trace_do_page_fault); #endif /* CONFIG_TRACING */