diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-17 16:03:05 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-17 16:03:05 -0400 |
commit | 6b6e177d632ee251c7c78d8f266a851ab9704879 (patch) | |
tree | 6feec072e7dd7eb32249369a1a80e6a904307e34 /arch/tile/kernel | |
parent | bfaf245022b4b8661af2e35f467cf0e91943c24c (diff) | |
parent | 128f3cb9398b5eeb4ee04b60bd5e314f5c122821 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile
Pull arch/tile updates from Chris Metcalf:
"These are mostly nohz_full changes, plus a smattering of minor fixes
(notably a couple for ftrace)"
* git://git.kernel.org/pub/scm/linux/kernel/git/cmetcalf/linux-tile:
tile: nohz: warn if nohz_full uses hypervisor shared cores
tile: ftrace: fix function_graph tracer issues
tile: map data region shadow of kernel as R/W
tile: support CONTEXT_TRACKING and thus NOHZ_FULL
tile: support arch_irq_work_raise
arch: tile: fix null pointer dereference on pt_regs pointer
tile/elf: reorganize notify_exec()
tile: use si_int instead of si_ptr for compat_siginfo
Diffstat (limited to 'arch/tile/kernel')
-rw-r--r-- | arch/tile/kernel/compat_signal.c | 11 | ||||
-rw-r--r-- | arch/tile/kernel/ftrace.c | 6 | ||||
-rw-r--r-- | arch/tile/kernel/mcount_64.S | 7 | ||||
-rw-r--r-- | arch/tile/kernel/process.c | 12 | ||||
-rw-r--r-- | arch/tile/kernel/ptrace.c | 22 | ||||
-rw-r--r-- | arch/tile/kernel/setup.c | 23 | ||||
-rw-r--r-- | arch/tile/kernel/single_step.c | 3 | ||||
-rw-r--r-- | arch/tile/kernel/smp.c | 32 | ||||
-rw-r--r-- | arch/tile/kernel/stack.c | 15 | ||||
-rw-r--r-- | arch/tile/kernel/traps.c | 16 | ||||
-rw-r--r-- | arch/tile/kernel/unaligned.c | 22 |
11 files changed, 131 insertions, 38 deletions
diff --git a/arch/tile/kernel/compat_signal.c b/arch/tile/kernel/compat_signal.c index 5cbc864398d3..e8c2c04143cd 100644 --- a/arch/tile/kernel/compat_signal.c +++ b/arch/tile/kernel/compat_signal.c @@ -68,7 +68,7 @@ int copy_siginfo_to_user32(struct compat_siginfo __user *to, const siginfo_t *fr if (from->si_code < 0) { err |= __put_user(from->si_pid, &to->si_pid); err |= __put_user(from->si_uid, &to->si_uid); - err |= __put_user(ptr_to_compat(from->si_ptr), &to->si_ptr); + err |= __put_user(from->si_int, &to->si_int); } else { /* * First 32bits of unions are always present: @@ -93,8 +93,7 @@ int copy_siginfo_to_user32(struct compat_siginfo __user *to, const siginfo_t *fr break; case __SI_TIMER >> 16: err |= __put_user(from->si_overrun, &to->si_overrun); - err |= __put_user(ptr_to_compat(from->si_ptr), - &to->si_ptr); + err |= __put_user(from->si_int, &to->si_int); break; /* This is not generated by the kernel as of now. */ case __SI_RT >> 16: @@ -110,19 +109,19 @@ int copy_siginfo_to_user32(struct compat_siginfo __user *to, const siginfo_t *fr int copy_siginfo_from_user32(siginfo_t *to, struct compat_siginfo __user *from) { int err; - u32 ptr32; if (!access_ok(VERIFY_READ, from, sizeof(struct compat_siginfo))) return -EFAULT; + memset(to, 0, sizeof(*to)); + err = __get_user(to->si_signo, &from->si_signo); err |= __get_user(to->si_errno, &from->si_errno); err |= __get_user(to->si_code, &from->si_code); err |= __get_user(to->si_pid, &from->si_pid); err |= __get_user(to->si_uid, &from->si_uid); - err |= __get_user(ptr32, &from->si_ptr); - to->si_ptr = compat_ptr(ptr32); + err |= __get_user(to->si_int, &from->si_int); return err; } diff --git a/arch/tile/kernel/ftrace.c b/arch/tile/kernel/ftrace.c index 8d52d83cc516..0c0996175b1e 100644 --- a/arch/tile/kernel/ftrace.c +++ b/arch/tile/kernel/ftrace.c @@ -74,7 +74,11 @@ static unsigned long ftrace_gen_branch(unsigned long pc, unsigned long addr, create_JumpOff_X1(pcrel_by_instr); } - if (addr == FTRACE_ADDR) { + /* + * Also put { move r10, lr; jal ftrace_stub } in a bundle, which + * is used to replace the instruction in address ftrace_call. + */ + if (addr == FTRACE_ADDR || addr == (unsigned long)ftrace_stub) { /* opcode: or r10, lr, zero */ opcode_x0 = create_Dest_X0(10) | diff --git a/arch/tile/kernel/mcount_64.S b/arch/tile/kernel/mcount_64.S index 3c2b8d5e1d1a..6c6702451962 100644 --- a/arch/tile/kernel/mcount_64.S +++ b/arch/tile/kernel/mcount_64.S @@ -81,7 +81,12 @@ STD_ENTRY(ftrace_caller) /* arg1: self return address */ /* arg2: parent's return address */ - { move r0, lr; move r1, r10 } + /* arg3: ftrace_ops */ + /* arg4: regs (but make it NULL) */ + { move r0, lr; moveli r2, hw2_last(function_trace_op) } + { move r1, r10; shl16insli r2, r2, hw1(function_trace_op) } + { movei r3, 0; shl16insli r2, r2, hw0(function_trace_op) } + ld r2,r2 .global ftrace_call ftrace_call: 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 @@ #include <linux/kernel.h> #include <linux/tracehook.h> #include <linux/signal.h> +#include <linux/context_tracking.h> #include <asm/stack.h> #include <asm/switch_to.h> #include <asm/homecache.h> @@ -474,6 +475,8 @@ int do_work_pending(struct pt_regs *regs, u32 thread_info_flags) if (!user_mode(regs)) return 0; + user_exit(); + /* Enable interrupts; they are disabled again on return to caller. */ local_irq_enable(); @@ -496,11 +499,12 @@ int do_work_pending(struct pt_regs *regs, u32 thread_info_flags) tracehook_notify_resume(regs); return 1; } - if (thread_info_flags & _TIF_SINGLESTEP) { + if (thread_info_flags & _TIF_SINGLESTEP) single_step_once(regs); - return 0; - } - panic("work_pending: bad flags %#x\n", thread_info_flags); + + user_enter(); + + return 0; } 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 @@ #include <linux/regset.h> #include <linux/elf.h> #include <linux/tracehook.h> +#include <linux/context_tracking.h> #include <asm/traps.h> #include <arch/chip.h> @@ -252,12 +253,21 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, int do_syscall_trace_enter(struct pt_regs *regs) { - if (test_thread_flag(TIF_SYSCALL_TRACE)) { + u32 work = ACCESS_ONCE(current_thread_info()->flags); + + /* + * If TIF_NOHZ is set, we are required to call user_exit() before + * doing anything that could touch RCU. + */ + if (work & _TIF_NOHZ) + user_exit(); + + if (work & _TIF_SYSCALL_TRACE) { if (tracehook_report_syscall_entry(regs)) regs->regs[TREG_SYSCALL_NR] = -1; } - if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) + if (work & _TIF_SYSCALL_TRACEPOINT) trace_sys_enter(regs, regs->regs[TREG_SYSCALL_NR]); return regs->regs[TREG_SYSCALL_NR]; @@ -268,6 +278,12 @@ void do_syscall_trace_exit(struct pt_regs *regs) long errno; /* + * We may come here right after calling schedule_user() + * in which case we can be in RCU user mode. + */ + user_exit(); + + /* * The standard tile calling convention returns the value (or negative * errno) in r0, and zero (or positive errno) in r1. * 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) /* Handle synthetic interrupt delivered only by the simulator. */ void __kprobes do_breakpoint(struct pt_regs* regs, int fault_num) { + enum ctx_state prev_state = exception_enter(); send_sigtrap(current, regs); + exception_exit(prev_state); } diff --git a/arch/tile/kernel/setup.c b/arch/tile/kernel/setup.c index f1f579914952..7833b2ccdfbc 100644 --- a/arch/tile/kernel/setup.c +++ b/arch/tile/kernel/setup.c @@ -32,6 +32,7 @@ #include <linux/hugetlb.h> #include <linux/start_kernel.h> #include <linux/screen_info.h> +#include <linux/tick.h> #include <asm/setup.h> #include <asm/sections.h> #include <asm/cacheflush.h> @@ -1390,6 +1391,28 @@ static int __init dataplane(char *str) early_param("dataplane", dataplane); +#ifdef CONFIG_NO_HZ_FULL +/* Warn if hypervisor shared cpus are marked as nohz_full. */ +static int __init check_nohz_full_cpus(void) +{ + struct cpumask shared; + int cpu; + + if (hv_inquire_tiles(HV_INQ_TILES_SHARED, + (HV_VirtAddr) shared.bits, sizeof(shared)) < 0) { + pr_warn("WARNING: No support for inquiring hv shared tiles\n"); + return 0; + } + for_each_cpu(cpu, &shared) { + if (tick_nohz_full_cpu(cpu)) + pr_warn("WARNING: nohz_full cpu %d receives hypervisor interrupts!\n", + cpu); + } + return 0; +} +arch_initcall(check_nohz_full_cpus); +#endif + #ifdef CONFIG_CMDLINE_BOOL static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE; #endif 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 @@ #include <linux/types.h> #include <linux/err.h> #include <linux/prctl.h> +#include <linux/context_tracking.h> #include <asm/cacheflush.h> #include <asm/traps.h> #include <asm/uaccess.h> @@ -738,6 +739,7 @@ static DEFINE_PER_CPU(unsigned long, ss_saved_pc); void gx_singlestep_handle(struct pt_regs *regs, int fault_num) { + enum ctx_state prev_state = exception_enter(); unsigned long *ss_pc = this_cpu_ptr(&ss_saved_pc); struct thread_info *info = (void *)current_thread_info(); 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) __insn_mtspr(SPR_SINGLE_STEP_CONTROL_K, control); send_sigtrap(current, regs); } + exception_exit(prev_state); } diff --git a/arch/tile/kernel/smp.c b/arch/tile/kernel/smp.c index d3c4ed780ce2..07e3ff5cc740 100644 --- a/arch/tile/kernel/smp.c +++ b/arch/tile/kernel/smp.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> +#include <linux/irq_work.h> #include <linux/module.h> #include <asm/cacheflush.h> #include <asm/homecache.h> @@ -33,6 +34,8 @@ EXPORT_SYMBOL(smp_topology); static unsigned long __iomem *ipi_mappings[NR_CPUS]; #endif +/* Does messaging work correctly to the local cpu? */ +bool self_interrupt_ok; /* * Top-level send_IPI*() functions to send messages to other cpus. @@ -147,6 +150,10 @@ void evaluate_message(int tag) generic_smp_call_function_single_interrupt(); break; + case MSG_TAG_IRQ_WORK: /* Invoke IRQ work */ + irq_work_run(); + break; + default: panic("Unknown IPI message tag %d", tag); break; @@ -186,6 +193,15 @@ void flush_icache_range(unsigned long start, unsigned long end) EXPORT_SYMBOL(flush_icache_range); +#ifdef CONFIG_IRQ_WORK +void arch_irq_work_raise(void) +{ + if (arch_irq_work_has_interrupt()) + send_IPI_single(smp_processor_id(), MSG_TAG_IRQ_WORK); +} +#endif + + /* Called when smp_send_reschedule() triggers IRQ_RESCHEDULE. */ static irqreturn_t handle_reschedule_ipi(int irq, void *token) { @@ -203,8 +219,22 @@ static struct irqaction resched_action = { void __init ipi_init(void) { + int cpu = smp_processor_id(); + HV_Recipient recip = { .y = cpu_y(cpu), .x = cpu_x(cpu), + .state = HV_TO_BE_SENT }; + int tag = MSG_TAG_CALL_FUNCTION_SINGLE; + + /* + * Test if we can message ourselves for arch_irq_work_raise. + * This functionality is only available in the Tilera hypervisor + * in versions 4.3.4 and following. + */ + if (hv_send_message(&recip, 1, (HV_VirtAddr)&tag, sizeof(tag)) == 1) + self_interrupt_ok = true; + else + pr_warn("Older hypervisor: disabling fast irq_work_raise\n"); + #if CHIP_HAS_IPI() - int cpu; /* Map IPI trigger MMIO addresses. */ for_each_possible_cpu(cpu) { HV_Coord tile; diff --git a/arch/tile/kernel/stack.c b/arch/tile/kernel/stack.c index 7ff5afdbd3aa..c42dce50acd8 100644 --- a/arch/tile/kernel/stack.c +++ b/arch/tile/kernel/stack.c @@ -108,14 +108,15 @@ static struct pt_regs *valid_fault_handler(struct KBacktraceIterator* kbt) p->sp < PAGE_OFFSET && p->sp != 0) { if (kbt->verbose) pr_err(" <%s while in user mode>\n", fault); - } else if (kbt->verbose) { - pr_err(" (odd fault: pc %#lx, sp %#lx, ex1 %#lx?)\n", - p->pc, p->sp, p->ex1); - p = NULL; + } else { + if (kbt->verbose) + pr_err(" (odd fault: pc %#lx, sp %#lx, ex1 %#lx?)\n", + p->pc, p->sp, p->ex1); + return NULL; } - if (!kbt->profile || ((1ULL << p->faultnum) & QUEUED_INTERRUPTS) == 0) - return p; - return NULL; + if (kbt->profile && ((1ULL << p->faultnum) & QUEUED_INTERRUPTS) != 0) + return NULL; + return p; } /* Is the pc pointing to a sigreturn trampoline? */ 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 @@ #include <linux/reboot.h> #include <linux/uaccess.h> #include <linux/ptrace.h> +#include <linux/context_tracking.h> #include <asm/stack.h> #include <asm/traps.h> #include <asm/setup.h> @@ -253,6 +254,7 @@ static int do_bpt(struct pt_regs *regs) void __kprobes do_trap(struct pt_regs *regs, int fault_num, unsigned long reason) { + enum ctx_state prev_state = exception_enter(); siginfo_t info = { 0 }; int signo, code; unsigned long address = 0; @@ -261,7 +263,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, /* Handle breakpoints, etc. */ if (is_kernel && fault_num == INT_ILL && do_bpt(regs)) - return; + goto done; /* Re-enable interrupts, if they were previously enabled. */ if (!(regs->flags & PT_FLAGS_DISABLE_IRQ)) @@ -275,7 +277,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, const char *name; char buf[100]; if (fixup_exception(regs)) /* ILL_TRANS or UNALIGN_DATA */ - return; + goto done; if (fault_num >= 0 && fault_num < ARRAY_SIZE(int_name) && int_name[fault_num] != NULL) @@ -294,7 +296,6 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, fault_num, name, regs->pc, buf); show_regs(regs); do_exit(SIGKILL); /* FIXME: implement i386 die() */ - return; } switch (fault_num) { @@ -308,7 +309,6 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, pr_err("Unreadable instruction for INT_ILL: %#lx\n", regs->pc); do_exit(SIGKILL); - return; } if (!special_ill(instr, &signo, &code)) { signo = SIGILL; @@ -319,7 +319,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, case INT_GPV: #if CHIP_HAS_TILE_DMA() if (retry_gpv(reason)) - return; + goto done; #endif /*FALLTHROUGH*/ case INT_UDN_ACCESS: @@ -346,7 +346,7 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, if (!state || (void __user *)(regs->pc) != state->buffer) { single_step_once(regs); - return; + goto done; } } #endif @@ -380,7 +380,6 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, #endif default: panic("Unexpected do_trap interrupt number %d", fault_num); - return; } info.si_signo = signo; @@ -391,6 +390,9 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, if (signo != SIGTRAP) trace_unhandled_signal("trap", regs, address, signo); force_sig_info(signo, &info, current); + +done: + exception_exit(prev_state); } 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 @@ #include <linux/module.h> #include <linux/compat.h> #include <linux/prctl.h> +#include <linux/context_tracking.h> #include <asm/cacheflush.h> #include <asm/traps.h> #include <asm/uaccess.h> @@ -1448,6 +1449,7 @@ void jit_bundle_gen(struct pt_regs *regs, tilegx_bundle_bits bundle, void do_unaligned(struct pt_regs *regs, int vecnum) { + enum ctx_state prev_state = exception_enter(); tilegx_bundle_bits __user *pc; tilegx_bundle_bits bundle; struct thread_info *info = current_thread_info(); @@ -1487,12 +1489,11 @@ void do_unaligned(struct pt_regs *regs, int vecnum) (int)unaligned_fixup, (unsigned long long)regs->ex1, (unsigned long long)regs->pc); - return; + } else { + /* Not fixable. Go panic. */ + panic("Unalign exception in Kernel. pc=%lx", + regs->pc); } - /* Not fixable. Go panic. */ - panic("Unalign exception in Kernel. pc=%lx", - regs->pc); - return; } else { /* * Try to fix the exception. If we can't, panic the @@ -1501,8 +1502,8 @@ void do_unaligned(struct pt_regs *regs, int vecnum) bundle = GX_INSN_BSWAP( *((tilegx_bundle_bits *)(regs->pc))); jit_bundle_gen(regs, bundle, align_ctl); - return; } + goto done; } /* @@ -1526,7 +1527,7 @@ void do_unaligned(struct pt_regs *regs, int vecnum) trace_unhandled_signal("unaligned fixup trap", regs, 0, SIGBUS); force_sig_info(info.si_signo, &info, current); - return; + goto done; } @@ -1543,7 +1544,7 @@ void do_unaligned(struct pt_regs *regs, int vecnum) trace_unhandled_signal("segfault in unalign fixup", regs, (unsigned long)info.si_addr, SIGSEGV); force_sig_info(info.si_signo, &info, current); - return; + goto done; } if (!info->unalign_jit_base) { @@ -1578,7 +1579,7 @@ void do_unaligned(struct pt_regs *regs, int vecnum) if (IS_ERR((void __force *)user_page)) { pr_err("Out of kernel pages trying do_mmap\n"); - return; + goto done; } /* Save the address in the thread_info struct */ @@ -1591,6 +1592,9 @@ void do_unaligned(struct pt_regs *regs, int vecnum) /* Generate unalign JIT */ jit_bundle_gen(regs, GX_INSN_BSWAP(bundle), align_ctl); + +done: + exception_exit(prev_state); } #endif /* __tilegx__ */ |