diff options
| author | Chris Metcalf <cmetcalf@ezchip.com> | 2015-03-23 14:23:58 -0400 |
|---|---|---|
| committer | Chris Metcalf <cmetcalf@ezchip.com> | 2015-04-17 14:01:10 -0400 |
| commit | 49e4e15619cd7cd9fc275d460fae2a95c1337fcc (patch) | |
| tree | 700e24bb8f72a7662e7d4ae26d847e908d08de92 /arch/tile/kernel | |
| parent | b340c656af6317e28b466996a72cca019d97b42d (diff) | |
tile: support CONTEXT_TRACKING and thus NOHZ_FULL
Add the TIF_NOHZ flag appropriately.
Add call to user_exit() on entry to do_work_pending() and on entry
to syscalls via do_syscall_trace_enter(), and also the top of
do_syscall_trace_exit() just because it's done in x86.
Add call to user_enter() at the bottom of do_work_pending() once we
have no more work to do before returning to userspace.
Wrap all the trap code in exception_enter() / exception_exit().
Signed-off-by: Chris Metcalf <cmetcalf@ezchip.com>
Acked-by: Frederic Weisbecker <fweisbec@gmail.com>
Diffstat (limited to 'arch/tile/kernel')
| -rw-r--r-- | arch/tile/kernel/process.c | 12 | ||||
| -rw-r--r-- | arch/tile/kernel/ptrace.c | 22 | ||||
| -rw-r--r-- | arch/tile/kernel/single_step.c | 3 | ||||
| -rw-r--r-- | arch/tile/kernel/traps.c | 16 | ||||
| -rw-r--r-- | arch/tile/kernel/unaligned.c | 22 |
5 files changed, 53 insertions, 22 deletions
diff --git a/arch/tile/kernel/process.c b/arch/tile/kernel/process.c index 48e5773dd0b7..b403c2e3e263 100644 --- a/arch/tile/kernel/process.c +++ b/arch/tile/kernel/process.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/kernel.h> | 27 | #include <linux/kernel.h> |
| 28 | #include <linux/tracehook.h> | 28 | #include <linux/tracehook.h> |
| 29 | #include <linux/signal.h> | 29 | #include <linux/signal.h> |
| 30 | #include <linux/context_tracking.h> | ||
| 30 | #include <asm/stack.h> | 31 | #include <asm/stack.h> |
| 31 | #include <asm/switch_to.h> | 32 | #include <asm/switch_to.h> |
| 32 | #include <asm/homecache.h> | 33 | #include <asm/homecache.h> |
| @@ -474,6 +475,8 @@ int do_work_pending(struct pt_regs *regs, u32 thread_info_flags) | |||
| 474 | if (!user_mode(regs)) | 475 | if (!user_mode(regs)) |
| 475 | return 0; | 476 | return 0; |
| 476 | 477 | ||
| 478 | user_exit(); | ||
| 479 | |||
| 477 | /* Enable interrupts; they are disabled again on return to caller. */ | 480 | /* Enable interrupts; they are disabled again on return to caller. */ |
| 478 | local_irq_enable(); | 481 | local_irq_enable(); |
| 479 | 482 | ||
| @@ -496,11 +499,12 @@ int do_work_pending(struct pt_regs *regs, u32 thread_info_flags) | |||
| 496 | tracehook_notify_resume(regs); | 499 | tracehook_notify_resume(regs); |
| 497 | return 1; | 500 | return 1; |
| 498 | } | 501 | } |
| 499 | if (thread_info_flags & _TIF_SINGLESTEP) { | 502 | if (thread_info_flags & _TIF_SINGLESTEP) |
| 500 | single_step_once(regs); | 503 | single_step_once(regs); |
| 501 | return 0; | 504 | |
| 502 | } | 505 | user_enter(); |
| 503 | panic("work_pending: bad flags %#x\n", thread_info_flags); | 506 | |
| 507 | return 0; | ||
| 504 | } | 508 | } |
| 505 | 509 | ||
| 506 | unsigned long get_wchan(struct task_struct *p) | 510 | unsigned long get_wchan(struct task_struct *p) |
diff --git a/arch/tile/kernel/ptrace.c b/arch/tile/kernel/ptrace.c index de98c6ddf136..f84eed8243da 100644 --- a/arch/tile/kernel/ptrace.c +++ b/arch/tile/kernel/ptrace.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include <linux/regset.h> | 22 | #include <linux/regset.h> |
| 23 | #include <linux/elf.h> | 23 | #include <linux/elf.h> |
| 24 | #include <linux/tracehook.h> | 24 | #include <linux/tracehook.h> |
| 25 | #include <linux/context_tracking.h> | ||
| 25 | #include <asm/traps.h> | 26 | #include <asm/traps.h> |
| 26 | #include <arch/chip.h> | 27 | #include <arch/chip.h> |
| 27 | 28 | ||
| @@ -252,12 +253,21 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
| 252 | 253 | ||
| 253 | int do_syscall_trace_enter(struct pt_regs *regs) | 254 | int do_syscall_trace_enter(struct pt_regs *regs) |
| 254 | { | 255 | { |
| 255 | if (test_thread_flag(TIF_SYSCALL_TRACE)) { | 256 | u32 work = ACCESS_ONCE(current_thread_info()->flags); |
| 257 | |||
| 258 | /* | ||
| 259 | * If TIF_NOHZ is set, we are required to call user_exit() before | ||
| 260 | * doing anything that could touch RCU. | ||
| 261 | */ | ||
| 262 | if (work & _TIF_NOHZ) | ||
| 263 | user_exit(); | ||
| 264 | |||
| 265 | if (work & _TIF_SYSCALL_TRACE) { | ||
| 256 | if (tracehook_report_syscall_entry(regs)) | 266 | if (tracehook_report_syscall_entry(regs)) |
| 257 | regs->regs[TREG_SYSCALL_NR] = -1; | 267 | regs->regs[TREG_SYSCALL_NR] = -1; |
| 258 | } | 268 | } |
| 259 | 269 | ||
| 260 | if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) | 270 | if (work & _TIF_SYSCALL_TRACEPOINT) |
| 261 | trace_sys_enter(regs, regs->regs[TREG_SYSCALL_NR]); | 271 | trace_sys_enter(regs, regs->regs[TREG_SYSCALL_NR]); |
| 262 | 272 | ||
| 263 | return regs->regs[TREG_SYSCALL_NR]; | 273 | return regs->regs[TREG_SYSCALL_NR]; |
| @@ -268,6 +278,12 @@ void do_syscall_trace_exit(struct pt_regs *regs) | |||
| 268 | long errno; | 278 | long errno; |
| 269 | 279 | ||
| 270 | /* | 280 | /* |
| 281 | * We may come here right after calling schedule_user() | ||
| 282 | * in which case we can be in RCU user mode. | ||
| 283 | */ | ||
| 284 | user_exit(); | ||
| 285 | |||
| 286 | /* | ||
| 271 | * The standard tile calling convention returns the value (or negative | 287 | * The standard tile calling convention returns the value (or negative |
| 272 | * errno) in r0, and zero (or positive errno) in r1. | 288 | * errno) in r0, and zero (or positive errno) in r1. |
| 273 | * It saves a couple of cycles on the hot path to do this work in | 289 | * It saves a couple of cycles on the hot path to do this work in |
| @@ -303,5 +319,7 @@ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs) | |||
| 303 | /* Handle synthetic interrupt delivered only by the simulator. */ | 319 | /* Handle synthetic interrupt delivered only by the simulator. */ |
| 304 | void __kprobes do_breakpoint(struct pt_regs* regs, int fault_num) | 320 | void __kprobes do_breakpoint(struct pt_regs* regs, int fault_num) |
| 305 | { | 321 | { |
| 322 | enum ctx_state prev_state = exception_enter(); | ||
| 306 | send_sigtrap(current, regs); | 323 | send_sigtrap(current, regs); |
| 324 | exception_exit(prev_state); | ||
| 307 | } | 325 | } |
diff --git a/arch/tile/kernel/single_step.c b/arch/tile/kernel/single_step.c index 862973074bf9..53f7b9def07b 100644 --- a/arch/tile/kernel/single_step.c +++ b/arch/tile/kernel/single_step.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <linux/types.h> | 23 | #include <linux/types.h> |
| 24 | #include <linux/err.h> | 24 | #include <linux/err.h> |
| 25 | #include <linux/prctl.h> | 25 | #include <linux/prctl.h> |
| 26 | #include <linux/context_tracking.h> | ||
| 26 | #include <asm/cacheflush.h> | 27 | #include <asm/cacheflush.h> |
| 27 | #include <asm/traps.h> | 28 | #include <asm/traps.h> |
| 28 | #include <asm/uaccess.h> | 29 | #include <asm/uaccess.h> |
| @@ -738,6 +739,7 @@ static DEFINE_PER_CPU(unsigned long, ss_saved_pc); | |||
| 738 | 739 | ||
| 739 | void gx_singlestep_handle(struct pt_regs *regs, int fault_num) | 740 | void gx_singlestep_handle(struct pt_regs *regs, int fault_num) |
| 740 | { | 741 | { |
| 742 | enum ctx_state prev_state = exception_enter(); | ||
| 741 | unsigned long *ss_pc = this_cpu_ptr(&ss_saved_pc); | 743 | unsigned long *ss_pc = this_cpu_ptr(&ss_saved_pc); |
| 742 | struct thread_info *info = (void *)current_thread_info(); | 744 | struct thread_info *info = (void *)current_thread_info(); |
| 743 | int is_single_step = test_ti_thread_flag(info, TIF_SINGLESTEP); | 745 | int is_single_step = test_ti_thread_flag(info, TIF_SINGLESTEP); |
| @@ -754,6 +756,7 @@ void gx_singlestep_handle(struct pt_regs *regs, int fault_num) | |||
| 754 | __insn_mtspr(SPR_SINGLE_STEP_CONTROL_K, control); | 756 | __insn_mtspr(SPR_SINGLE_STEP_CONTROL_K, control); |
| 755 | send_sigtrap(current, regs); | 757 | send_sigtrap(current, regs); |
| 756 | } | 758 | } |
| 759 | exception_exit(prev_state); | ||
| 757 | } | 760 | } |
| 758 | 761 | ||
| 759 | 762 | ||
diff --git a/arch/tile/kernel/traps.c b/arch/tile/kernel/traps.c index bf841ca517bb..312fc134c1cb 100644 --- a/arch/tile/kernel/traps.c +++ b/arch/tile/kernel/traps.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/reboot.h> | 20 | #include <linux/reboot.h> |
| 21 | #include <linux/uaccess.h> | 21 | #include <linux/uaccess.h> |
| 22 | #include <linux/ptrace.h> | 22 | #include <linux/ptrace.h> |
| 23 | #include <linux/context_tracking.h> | ||
| 23 | #include <asm/stack.h> | 24 | #include <asm/stack.h> |
| 24 | #include <asm/traps.h> | 25 | #include <asm/traps.h> |
| 25 | #include <asm/setup.h> | 26 | #include <asm/setup.h> |
| @@ -253,6 +254,7 @@ static int do_bpt(struct pt_regs *regs) | |||
| 253 | void __kprobes do_trap(struct pt_regs *regs, int fault_num, | 254 | void __kprobes do_trap(struct pt_regs *regs, int fault_num, |
| 254 | unsigned long reason) | 255 | unsigned long reason) |
| 255 | { | 256 | { |
| 257 | enum ctx_state prev_state = exception_enter(); | ||
| 256 | siginfo_t info = { 0 }; | 258 | siginfo_t info = { 0 }; |
| 257 | int signo, code; | 259 | int signo, code; |
| 258 | unsigned long address = 0; | 260 | unsigned long address = 0; |
| @@ -261,7 +263,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
| 261 | 263 | ||
| 262 | /* Handle breakpoints, etc. */ | 264 | /* Handle breakpoints, etc. */ |
| 263 | if (is_kernel && fault_num == INT_ILL && do_bpt(regs)) | 265 | if (is_kernel && fault_num == INT_ILL && do_bpt(regs)) |
| 264 | return; | 266 | goto done; |
| 265 | 267 | ||
| 266 | /* Re-enable interrupts, if they were previously enabled. */ | 268 | /* Re-enable interrupts, if they were previously enabled. */ |
| 267 | if (!(regs->flags & PT_FLAGS_DISABLE_IRQ)) | 269 | if (!(regs->flags & PT_FLAGS_DISABLE_IRQ)) |
| @@ -275,7 +277,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
| 275 | const char *name; | 277 | const char *name; |
| 276 | char buf[100]; | 278 | char buf[100]; |
| 277 | if (fixup_exception(regs)) /* ILL_TRANS or UNALIGN_DATA */ | 279 | if (fixup_exception(regs)) /* ILL_TRANS or UNALIGN_DATA */ |
| 278 | return; | 280 | goto done; |
| 279 | if (fault_num >= 0 && | 281 | if (fault_num >= 0 && |
| 280 | fault_num < ARRAY_SIZE(int_name) && | 282 | fault_num < ARRAY_SIZE(int_name) && |
| 281 | int_name[fault_num] != NULL) | 283 | int_name[fault_num] != NULL) |
| @@ -294,7 +296,6 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
| 294 | fault_num, name, regs->pc, buf); | 296 | fault_num, name, regs->pc, buf); |
| 295 | show_regs(regs); | 297 | show_regs(regs); |
| 296 | do_exit(SIGKILL); /* FIXME: implement i386 die() */ | 298 | do_exit(SIGKILL); /* FIXME: implement i386 die() */ |
| 297 | return; | ||
| 298 | } | 299 | } |
| 299 | 300 | ||
| 300 | switch (fault_num) { | 301 | switch (fault_num) { |
| @@ -308,7 +309,6 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
| 308 | pr_err("Unreadable instruction for INT_ILL: %#lx\n", | 309 | pr_err("Unreadable instruction for INT_ILL: %#lx\n", |
| 309 | regs->pc); | 310 | regs->pc); |
| 310 | do_exit(SIGKILL); | 311 | do_exit(SIGKILL); |
| 311 | return; | ||
| 312 | } | 312 | } |
| 313 | if (!special_ill(instr, &signo, &code)) { | 313 | if (!special_ill(instr, &signo, &code)) { |
| 314 | signo = SIGILL; | 314 | signo = SIGILL; |
| @@ -319,7 +319,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
| 319 | case INT_GPV: | 319 | case INT_GPV: |
| 320 | #if CHIP_HAS_TILE_DMA() | 320 | #if CHIP_HAS_TILE_DMA() |
| 321 | if (retry_gpv(reason)) | 321 | if (retry_gpv(reason)) |
| 322 | return; | 322 | goto done; |
| 323 | #endif | 323 | #endif |
| 324 | /*FALLTHROUGH*/ | 324 | /*FALLTHROUGH*/ |
| 325 | case INT_UDN_ACCESS: | 325 | case INT_UDN_ACCESS: |
| @@ -346,7 +346,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
| 346 | if (!state || | 346 | if (!state || |
| 347 | (void __user *)(regs->pc) != state->buffer) { | 347 | (void __user *)(regs->pc) != state->buffer) { |
| 348 | single_step_once(regs); | 348 | single_step_once(regs); |
| 349 | return; | 349 | goto done; |
| 350 | } | 350 | } |
| 351 | } | 351 | } |
| 352 | #endif | 352 | #endif |
| @@ -380,7 +380,6 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
| 380 | #endif | 380 | #endif |
| 381 | default: | 381 | default: |
| 382 | panic("Unexpected do_trap interrupt number %d", fault_num); | 382 | panic("Unexpected do_trap interrupt number %d", fault_num); |
| 383 | return; | ||
| 384 | } | 383 | } |
| 385 | 384 | ||
| 386 | info.si_signo = signo; | 385 | info.si_signo = signo; |
| @@ -391,6 +390,9 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
| 391 | if (signo != SIGTRAP) | 390 | if (signo != SIGTRAP) |
| 392 | trace_unhandled_signal("trap", regs, address, signo); | 391 | trace_unhandled_signal("trap", regs, address, signo); |
| 393 | force_sig_info(signo, &info, current); | 392 | force_sig_info(signo, &info, current); |
| 393 | |||
| 394 | done: | ||
| 395 | exception_exit(prev_state); | ||
| 394 | } | 396 | } |
| 395 | 397 | ||
| 396 | void kernel_double_fault(int dummy, ulong pc, ulong lr, ulong sp, ulong r52) | 398 | void kernel_double_fault(int dummy, ulong pc, ulong lr, ulong sp, ulong r52) |
diff --git a/arch/tile/kernel/unaligned.c b/arch/tile/kernel/unaligned.c index 7d9a83be0aca..d075f92ccee0 100644 --- a/arch/tile/kernel/unaligned.c +++ b/arch/tile/kernel/unaligned.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
| 26 | #include <linux/compat.h> | 26 | #include <linux/compat.h> |
| 27 | #include <linux/prctl.h> | 27 | #include <linux/prctl.h> |
| 28 | #include <linux/context_tracking.h> | ||
| 28 | #include <asm/cacheflush.h> | 29 | #include <asm/cacheflush.h> |
| 29 | #include <asm/traps.h> | 30 | #include <asm/traps.h> |
| 30 | #include <asm/uaccess.h> | 31 | #include <asm/uaccess.h> |
| @@ -1448,6 +1449,7 @@ void jit_bundle_gen(struct pt_regs *regs, tilegx_bundle_bits bundle, | |||
| 1448 | 1449 | ||
| 1449 | void do_unaligned(struct pt_regs *regs, int vecnum) | 1450 | void do_unaligned(struct pt_regs *regs, int vecnum) |
| 1450 | { | 1451 | { |
| 1452 | enum ctx_state prev_state = exception_enter(); | ||
| 1451 | tilegx_bundle_bits __user *pc; | 1453 | tilegx_bundle_bits __user *pc; |
| 1452 | tilegx_bundle_bits bundle; | 1454 | tilegx_bundle_bits bundle; |
| 1453 | struct thread_info *info = current_thread_info(); | 1455 | struct thread_info *info = current_thread_info(); |
| @@ -1487,12 +1489,11 @@ void do_unaligned(struct pt_regs *regs, int vecnum) | |||
| 1487 | (int)unaligned_fixup, | 1489 | (int)unaligned_fixup, |
| 1488 | (unsigned long long)regs->ex1, | 1490 | (unsigned long long)regs->ex1, |
| 1489 | (unsigned long long)regs->pc); | 1491 | (unsigned long long)regs->pc); |
| 1490 | return; | 1492 | } else { |
| 1493 | /* Not fixable. Go panic. */ | ||
| 1494 | panic("Unalign exception in Kernel. pc=%lx", | ||
| 1495 | regs->pc); | ||
| 1491 | } | 1496 | } |
| 1492 | /* Not fixable. Go panic. */ | ||
| 1493 | panic("Unalign exception in Kernel. pc=%lx", | ||
| 1494 | regs->pc); | ||
| 1495 | return; | ||
| 1496 | } else { | 1497 | } else { |
| 1497 | /* | 1498 | /* |
| 1498 | * Try to fix the exception. If we can't, panic the | 1499 | * Try to fix the exception. If we can't, panic the |
| @@ -1501,8 +1502,8 @@ void do_unaligned(struct pt_regs *regs, int vecnum) | |||
| 1501 | bundle = GX_INSN_BSWAP( | 1502 | bundle = GX_INSN_BSWAP( |
| 1502 | *((tilegx_bundle_bits *)(regs->pc))); | 1503 | *((tilegx_bundle_bits *)(regs->pc))); |
| 1503 | jit_bundle_gen(regs, bundle, align_ctl); | 1504 | jit_bundle_gen(regs, bundle, align_ctl); |
| 1504 | return; | ||
| 1505 | } | 1505 | } |
| 1506 | goto done; | ||
| 1506 | } | 1507 | } |
| 1507 | 1508 | ||
| 1508 | /* | 1509 | /* |
| @@ -1526,7 +1527,7 @@ void do_unaligned(struct pt_regs *regs, int vecnum) | |||
| 1526 | 1527 | ||
| 1527 | trace_unhandled_signal("unaligned fixup trap", regs, 0, SIGBUS); | 1528 | trace_unhandled_signal("unaligned fixup trap", regs, 0, SIGBUS); |
| 1528 | force_sig_info(info.si_signo, &info, current); | 1529 | force_sig_info(info.si_signo, &info, current); |
| 1529 | return; | 1530 | goto done; |
| 1530 | } | 1531 | } |
| 1531 | 1532 | ||
| 1532 | 1533 | ||
| @@ -1543,7 +1544,7 @@ void do_unaligned(struct pt_regs *regs, int vecnum) | |||
| 1543 | trace_unhandled_signal("segfault in unalign fixup", regs, | 1544 | trace_unhandled_signal("segfault in unalign fixup", regs, |
| 1544 | (unsigned long)info.si_addr, SIGSEGV); | 1545 | (unsigned long)info.si_addr, SIGSEGV); |
| 1545 | force_sig_info(info.si_signo, &info, current); | 1546 | force_sig_info(info.si_signo, &info, current); |
| 1546 | return; | 1547 | goto done; |
| 1547 | } | 1548 | } |
| 1548 | 1549 | ||
| 1549 | if (!info->unalign_jit_base) { | 1550 | if (!info->unalign_jit_base) { |
| @@ -1578,7 +1579,7 @@ void do_unaligned(struct pt_regs *regs, int vecnum) | |||
| 1578 | 1579 | ||
| 1579 | if (IS_ERR((void __force *)user_page)) { | 1580 | if (IS_ERR((void __force *)user_page)) { |
| 1580 | pr_err("Out of kernel pages trying do_mmap\n"); | 1581 | pr_err("Out of kernel pages trying do_mmap\n"); |
| 1581 | return; | 1582 | goto done; |
| 1582 | } | 1583 | } |
| 1583 | 1584 | ||
| 1584 | /* Save the address in the thread_info struct */ | 1585 | /* Save the address in the thread_info struct */ |
| @@ -1591,6 +1592,9 @@ void do_unaligned(struct pt_regs *regs, int vecnum) | |||
| 1591 | 1592 | ||
| 1592 | /* Generate unalign JIT */ | 1593 | /* Generate unalign JIT */ |
| 1593 | jit_bundle_gen(regs, GX_INSN_BSWAP(bundle), align_ctl); | 1594 | jit_bundle_gen(regs, GX_INSN_BSWAP(bundle), align_ctl); |
| 1595 | |||
| 1596 | done: | ||
| 1597 | exception_exit(prev_state); | ||
| 1594 | } | 1598 | } |
| 1595 | 1599 | ||
| 1596 | #endif /* __tilegx__ */ | 1600 | #endif /* __tilegx__ */ |
