diff options
Diffstat (limited to 'arch/sparc64')
-rw-r--r-- | arch/sparc64/Kconfig | 1 | ||||
-rw-r--r-- | arch/sparc64/kernel/compat_audit.c | 2 | ||||
-rw-r--r-- | arch/sparc64/kernel/entry.h | 3 | ||||
-rw-r--r-- | arch/sparc64/kernel/irq.c | 19 | ||||
-rw-r--r-- | arch/sparc64/kernel/of_device.c | 5 | ||||
-rw-r--r-- | arch/sparc64/kernel/process.c | 110 | ||||
-rw-r--r-- | arch/sparc64/kernel/ptrace.c | 32 | ||||
-rw-r--r-- | arch/sparc64/kernel/rtrap.S | 6 | ||||
-rw-r--r-- | arch/sparc64/kernel/signal.c | 13 | ||||
-rw-r--r-- | arch/sparc64/kernel/signal32.c | 3 | ||||
-rw-r--r-- | arch/sparc64/kernel/smp.c | 298 | ||||
-rw-r--r-- | arch/sparc64/kernel/sparc64_ksyms.c | 1 | ||||
-rw-r--r-- | arch/sparc64/kernel/syscalls.S | 4 | ||||
-rw-r--r-- | arch/sparc64/kernel/traps.c | 10 | ||||
-rw-r--r-- | arch/sparc64/mm/tsb.c | 5 | ||||
-rw-r--r-- | arch/sparc64/mm/ultra.S | 42 |
16 files changed, 246 insertions, 308 deletions
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index 7c88263256a..923a98959fa 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -17,6 +17,7 @@ config SPARC64 select HAVE_LMB select HAVE_ARCH_KGDB select USE_GENERIC_SMP_HELPERS if SMP + select HAVE_ARCH_TRACEHOOK config GENERIC_TIME bool diff --git a/arch/sparc64/kernel/compat_audit.c b/arch/sparc64/kernel/compat_audit.c index c1979482aa9..c831b0a4e66 100644 --- a/arch/sparc64/kernel/compat_audit.c +++ b/arch/sparc64/kernel/compat_audit.c @@ -1,4 +1,4 @@ -#include <asm-sparc/unistd.h> +#include <asm/unistd_32.h> unsigned sparc32_dir_class[] = { #include <asm-generic/audit_dir_write.h> diff --git a/arch/sparc64/kernel/entry.h b/arch/sparc64/kernel/entry.h index 32fbab62085..fc294a29289 100644 --- a/arch/sparc64/kernel/entry.h +++ b/arch/sparc64/kernel/entry.h @@ -22,8 +22,7 @@ extern void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long thread_info_flags); -extern asmlinkage void syscall_trace(struct pt_regs *regs, - int syscall_exit_p); +extern asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p); extern void bad_trap_tl1(struct pt_regs *regs, long lvl); diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index c481673d249..ba43d85e8dd 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -915,12 +915,18 @@ static void __init sun4v_init_mondo_queues(void) alloc_one_mondo(&tb->nonresum_mondo_pa, tb->nonresum_qmask); alloc_one_kbuf(&tb->nonresum_kernel_buf_pa, tb->nonresum_qmask); + } +} + +static void __init init_send_mondo_info(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + struct trap_per_cpu *tb = &trap_block[cpu]; init_cpu_send_mondo_info(tb); } - - /* Load up the boot cpu's entries. */ - sun4v_register_mondo_queues(hard_smp_processor_id()); } static struct irqaction timer_irq_action = { @@ -949,6 +955,13 @@ void __init init_IRQ(void) if (tlb_type == hypervisor) sun4v_init_mondo_queues(); + init_send_mondo_info(); + + if (tlb_type == hypervisor) { + /* Load up the boot cpu's entries. */ + sun4v_register_mondo_queues(hard_smp_processor_id()); + } + /* We need to clear any IRQ's pending in the soft interrupt * registers, a spurious one could be left around from the * PROM timer which we just disabled. diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c index 4fd48ab7dda..f8b50cbf4bf 100644 --- a/arch/sparc64/kernel/of_device.c +++ b/arch/sparc64/kernel/of_device.c @@ -56,9 +56,6 @@ struct of_device *of_find_device_by_node(struct device_node *dp) EXPORT_SYMBOL(of_find_device_by_node); #ifdef CONFIG_PCI -struct bus_type isa_bus_type; -EXPORT_SYMBOL(isa_bus_type); - struct bus_type ebus_bus_type; EXPORT_SYMBOL(ebus_bus_type); #endif @@ -842,8 +839,6 @@ static int __init of_bus_driver_init(void) err = of_bus_type_init(&of_platform_bus_type, "of"); #ifdef CONFIG_PCI if (!err) - err = of_bus_type_init(&isa_bus_type, "isa"); - if (!err) err = of_bus_type_init(&ebus_bus_type, "ebus"); #endif #ifdef CONFIG_SBUS diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 8a9cd3e165b..7f5debdc5fe 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -52,8 +52,6 @@ #include <asm/irq_regs.h> #include <asm/smp.h> -/* #define VERBOSE_SHOWREGS */ - static void sparc64_yield(int cpu) { if (tlb_type != hypervisor) @@ -213,22 +211,8 @@ static void show_regwindow(struct pt_regs *regs) printk("I7: <%pS>\n", (void *) rwk->ins[7]); } -#ifdef CONFIG_SMP -static DEFINE_SPINLOCK(regdump_lock); -#endif - -void __show_regs(struct pt_regs * regs) +void show_regs(struct pt_regs *regs) { -#ifdef CONFIG_SMP - unsigned long flags; - - /* Protect against xcall ipis which might lead to livelock on the lock */ - __asm__ __volatile__("rdpr %%pstate, %0\n\t" - "wrpr %0, %1, %%pstate" - : "=r" (flags) - : "i" (PSTATE_IE)); - spin_lock(®dump_lock); -#endif printk("TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %08x %s\n", regs->tstate, regs->tpc, regs->tnpc, regs->y, print_tainted()); printk("TPC: <%pS>\n", (void *) regs->tpc); @@ -246,64 +230,24 @@ void __show_regs(struct pt_regs * regs) regs->u_regs[15]); printk("RPC: <%pS>\n", (void *) regs->u_regs[15]); show_regwindow(regs); -#ifdef CONFIG_SMP - spin_unlock(®dump_lock); - __asm__ __volatile__("wrpr %0, 0, %%pstate" - : : "r" (flags)); -#endif } -#ifdef VERBOSE_SHOWREGS -static void idump_from_user (unsigned int *pc) -{ - int i; - int code; - - if((((unsigned long) pc) & 3)) - return; - - pc -= 3; - for(i = -3; i < 6; i++) { - get_user(code, pc); - printk("%c%08x%c",i?' ':'<',code,i?' ':'>'); - pc++; - } - printk("\n"); -} -#endif +struct global_reg_snapshot global_reg_snapshot[NR_CPUS]; +static DEFINE_SPINLOCK(global_reg_snapshot_lock); -void show_regs(struct pt_regs *regs) +static bool kstack_valid(struct thread_info *tp, struct reg_window *rw) { -#ifdef VERBOSE_SHOWREGS - extern long etrap, etraptl1; -#endif - __show_regs(regs); -#if 0 -#ifdef CONFIG_SMP - { - extern void smp_report_regs(void); + unsigned long thread_base, fp; - smp_report_regs(); - } -#endif -#endif + thread_base = (unsigned long) tp; + fp = (unsigned long) rw; -#ifdef VERBOSE_SHOWREGS - if (regs->tpc >= &etrap && regs->tpc < &etraptl1 && - regs->u_regs[14] >= (long)current - PAGE_SIZE && - regs->u_regs[14] < (long)current + 6 * PAGE_SIZE) { - printk ("*********parent**********\n"); - __show_regs((struct pt_regs *)(regs->u_regs[14] + PTREGS_OFF)); - idump_from_user(((struct pt_regs *)(regs->u_regs[14] + PTREGS_OFF))->tpc); - printk ("*********endpar**********\n"); - } -#endif + if (fp < (thread_base + sizeof(struct thread_info)) || + fp >= (thread_base + THREAD_SIZE)) + return false; + return true; } -#ifdef CONFIG_MAGIC_SYSRQ -struct global_reg_snapshot global_reg_snapshot[NR_CPUS]; -static DEFINE_SPINLOCK(global_reg_snapshot_lock); - static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs, int this_cpu) { @@ -315,14 +259,22 @@ static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs, global_reg_snapshot[this_cpu].o7 = regs->u_regs[UREG_I7]; if (regs->tstate & TSTATE_PRIV) { + struct thread_info *tp = current_thread_info(); struct reg_window *rw; rw = (struct reg_window *) (regs->u_regs[UREG_FP] + STACK_BIAS); - global_reg_snapshot[this_cpu].i7 = rw->ins[6]; - } else + if (kstack_valid(tp, rw)) { + global_reg_snapshot[this_cpu].i7 = rw->ins[7]; + rw = (struct reg_window *) + (rw->ins[6] + STACK_BIAS); + if (kstack_valid(tp, rw)) + global_reg_snapshot[this_cpu].rpc = rw->ins[7]; + } + } else { global_reg_snapshot[this_cpu].i7 = 0; - + global_reg_snapshot[this_cpu].rpc = 0; + } global_reg_snapshot[this_cpu].thread = tp; } @@ -341,7 +293,7 @@ static void __global_reg_poll(struct global_reg_snapshot *gp) } } -static void sysrq_handle_globreg(int key, struct tty_struct *tty) +void __trigger_all_cpu_backtrace(void) { struct thread_info *tp = current_thread_info(); struct pt_regs *regs = get_irq_regs(); @@ -375,13 +327,14 @@ static void sysrq_handle_globreg(int key, struct tty_struct *tty) ((tp && tp->task) ? tp->task->pid : -1)); if (gp->tstate & TSTATE_PRIV) { - printk(" TPC[%pS] O7[%pS] I7[%pS]\n", + printk(" TPC[%pS] O7[%pS] I7[%pS] RPC[%pS]\n", (void *) gp->tpc, (void *) gp->o7, - (void *) gp->i7); + (void *) gp->i7, + (void *) gp->rpc); } else { - printk(" TPC[%lx] O7[%lx] I7[%lx]\n", - gp->tpc, gp->o7, gp->i7); + printk(" TPC[%lx] O7[%lx] I7[%lx] RPC[%lx]\n", + gp->tpc, gp->o7, gp->i7, gp->rpc); } } @@ -390,6 +343,13 @@ static void sysrq_handle_globreg(int key, struct tty_struct *tty) spin_unlock_irqrestore(&global_reg_snapshot_lock, flags); } +#ifdef CONFIG_MAGIC_SYSRQ + +static void sysrq_handle_globreg(int key, struct tty_struct *tty) +{ + __trigger_all_cpu_backtrace(); +} + static struct sysrq_key_op sparc_globalreg_op = { .handler = sysrq_handle_globreg, .help_msg = "Globalregs", diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index f6c9fc92921..bd578cc4856 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -23,6 +23,7 @@ #include <linux/audit.h> #include <linux/signal.h> #include <linux/regset.h> +#include <linux/tracehook.h> #include <linux/compat.h> #include <linux/elf.h> @@ -1049,8 +1050,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) return ret; } -asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p) +asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) { + int ret = 0; + /* do the secure computing check first */ secure_computing(regs->u_regs[UREG_G1]); @@ -1064,27 +1067,14 @@ asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p) audit_syscall_exit(result, regs->u_regs[UREG_I0]); } - if (!(current->ptrace & PT_PTRACED)) - goto out; - - if (!test_thread_flag(TIF_SYSCALL_TRACE)) - goto out; - - ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0)); - - /* - * this isn't the same as continuing with a signal, but it will do - * for normal use. strace only continues with a signal if the - * stopping signal is not SIGTRAP. -brl - */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code = 0; + if (test_thread_flag(TIF_SYSCALL_TRACE)) { + if (syscall_exit_p) + tracehook_report_syscall_exit(regs, 0); + else + ret = tracehook_report_syscall_entry(regs); } -out: - if (unlikely(current->audit_context) && !syscall_exit_p) + if (unlikely(current->audit_context) && !syscall_exit_p && !ret) audit_syscall_entry((test_thread_flag(TIF_32BIT) ? AUDIT_ARCH_SPARC : AUDIT_ARCH_SPARC64), @@ -1093,4 +1083,6 @@ out: regs->u_regs[UREG_I1], regs->u_regs[UREG_I2], regs->u_regs[UREG_I3]); + + return ret; } diff --git a/arch/sparc64/kernel/rtrap.S b/arch/sparc64/kernel/rtrap.S index c6fc695fe1f..97a993c1f7f 100644 --- a/arch/sparc64/kernel/rtrap.S +++ b/arch/sparc64/kernel/rtrap.S @@ -46,7 +46,7 @@ __handle_user_windows: wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate ldx [%g6 + TI_FLAGS], %l0 -1: andcc %l0, _TIF_SIGPENDING, %g0 +1: andcc %l0, _TIF_DO_NOTIFY_RESUME_MASK, %g0 be,pt %xcc, __handle_user_windows_continue nop mov %l5, %o1 @@ -86,7 +86,7 @@ __handle_perfctrs: wrpr %g0, RTRAP_PSTATE, %pstate wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate ldx [%g6 + TI_FLAGS], %l0 -1: andcc %l0, _TIF_SIGPENDING, %g0 +1: andcc %l0, _TIF_DO_NOTIFY_RESUME_MASK, %g0 be,pt %xcc, __handle_perfctrs_continue sethi %hi(TSTATE_PEF), %o0 @@ -195,7 +195,7 @@ __handle_preemption_continue: andcc %l1, %o0, %g0 andcc %l0, _TIF_NEED_RESCHED, %g0 bne,pn %xcc, __handle_preemption - andcc %l0, _TIF_SIGPENDING, %g0 + andcc %l0, _TIF_DO_NOTIFY_RESUME_MASK, %g0 bne,pn %xcc, __handle_signal __handle_signal_continue: ldub [%g6 + TI_WSAVED], %o2 diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index 9667e96fd51..ec82d76dc6f 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -2,7 +2,7 @@ * arch/sparc64/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1995, 2008 David S. Miller (davem@davemloft.net) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -17,6 +17,7 @@ #include <linux/errno.h> #include <linux/wait.h> #include <linux/ptrace.h> +#include <linux/tracehook.h> #include <linux/unistd.h> #include <linux/mm.h> #include <linux/tty.h> @@ -89,7 +90,9 @@ asmlinkage void sparc64_set_context(struct pt_regs *regs) err |= __get_user(regs->u_regs[UREG_G4], (&(*grp)[MC_G4])); err |= __get_user(regs->u_regs[UREG_G5], (&(*grp)[MC_G5])); err |= __get_user(regs->u_regs[UREG_G6], (&(*grp)[MC_G6])); - err |= __get_user(regs->u_regs[UREG_G7], (&(*grp)[MC_G7])); + + /* Skip %g7 as that's the thread register in userspace. */ + err |= __get_user(regs->u_regs[UREG_I0], (&(*grp)[MC_O0])); err |= __get_user(regs->u_regs[UREG_I1], (&(*grp)[MC_O1])); err |= __get_user(regs->u_regs[UREG_I2], (&(*grp)[MC_O2])); @@ -574,6 +577,8 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) * clear the TS_RESTORE_SIGMASK flag. */ current_thread_info()->status &= ~TS_RESTORE_SIGMASK; + + tracehook_signal_handler(signr, &info, &ka, regs, 0); return; } if (restart_syscall && @@ -605,4 +610,8 @@ void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long { if (thread_info_flags & _TIF_SIGPENDING) do_signal(regs, orig_i0); + if (thread_info_flags & _TIF_NOTIFY_RESUME) { + clear_thread_flag(TIF_NOTIFY_RESUME); + tracehook_notify_resume(regs); + } } diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index 97cdd1bf4a1..ba5b09ad666 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -19,6 +19,7 @@ #include <linux/binfmts.h> #include <linux/compat.h> #include <linux/bitops.h> +#include <linux/tracehook.h> #include <asm/uaccess.h> #include <asm/ptrace.h> @@ -794,6 +795,8 @@ void do_signal32(sigset_t *oldset, struct pt_regs * regs, * clear the TS_RESTORE_SIGMASK flag. */ current_thread_info()->status &= ~TS_RESTORE_SIGMASK; + + tracehook_signal_handler(signr, &info, &ka, regs, 0); return; } if (restart_syscall && diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 7cf72b4bb10..27b81775a4d 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -459,27 +459,35 @@ again: } } -static inline void spitfire_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t mask) +static void spitfire_xcall_deliver(struct trap_per_cpu *tb, int cnt) { + u64 *mondo, data0, data1, data2; + u16 *cpu_list; u64 pstate; int i; __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); - for_each_cpu_mask(i, mask) - spitfire_xcall_helper(data0, data1, data2, pstate, i); + cpu_list = __va(tb->cpu_list_pa); + mondo = __va(tb->cpu_mondo_block_pa); + data0 = mondo[0]; + data1 = mondo[1]; + data2 = mondo[2]; + for (i = 0; i < cnt; i++) + spitfire_xcall_helper(data0, data1, data2, pstate, cpu_list[i]); } /* Cheetah now allows to send the whole 64-bytes of data in the interrupt * packet, but we have no use for that. However we do take advantage of * the new pipelining feature (ie. dispatch to multiple cpus simultaneously). */ -static void cheetah_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t mask) +static void cheetah_xcall_deliver(struct trap_per_cpu *tb, int cnt) { - u64 pstate, ver, busy_mask; int nack_busy_id, is_jbus, need_more; + u64 *mondo, pstate, ver, busy_mask; + u16 *cpu_list; - if (cpus_empty(mask)) - return; + cpu_list = __va(tb->cpu_list_pa); + mondo = __va(tb->cpu_mondo_block_pa); /* Unfortunately, someone at Sun had the brilliant idea to make the * busy/nack fields hard-coded by ITID number for this Ultra-III @@ -502,7 +510,7 @@ retry: "stxa %2, [%5] %6\n\t" "membar #Sync\n\t" : /* no outputs */ - : "r" (data0), "r" (data1), "r" (data2), + : "r" (mondo[0]), "r" (mondo[1]), "r" (mondo[2]), "r" (0x40), "r" (0x50), "r" (0x60), "i" (ASI_INTR_W)); @@ -511,11 +519,16 @@ retry: { int i; - for_each_cpu_mask(i, mask) { - u64 target = (i << 14) | 0x70; + for (i = 0; i < cnt; i++) { + u64 target, nr; + nr = cpu_list[i]; + if (nr == 0xffff) + continue; + + target = (nr << 14) | 0x70; if (is_jbus) { - busy_mask |= (0x1UL << (i * 2)); + busy_mask |= (0x1UL << (nr * 2)); } else { target |= (nack_busy_id << 24); busy_mask |= (0x1UL << @@ -549,11 +562,13 @@ retry: __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); if (unlikely(need_more)) { - int i, cnt = 0; - for_each_cpu_mask(i, mask) { - cpu_clear(i, mask); - cnt++; - if (cnt == 32) + int i, this_cnt = 0; + for (i = 0; i < cnt; i++) { + if (cpu_list[i] == 0xffff) + continue; + cpu_list[i] = 0xffff; + this_cnt++; + if (this_cnt == 32) break; } goto retry; @@ -584,16 +599,20 @@ retry: /* Clear out the mask bits for cpus which did not * NACK us. */ - for_each_cpu_mask(i, mask) { - u64 check_mask; + for (i = 0; i < cnt; i++) { + u64 check_mask, nr; + + nr = cpu_list[i]; + if (nr == 0xffff) + continue; if (is_jbus) - check_mask = (0x2UL << (2*i)); + check_mask = (0x2UL << (2*nr)); else check_mask = (0x2UL << this_busy_nack); if ((dispatch_stat & check_mask) == 0) - cpu_clear(i, mask); + cpu_list[i] = 0xffff; this_busy_nack += 2; if (this_busy_nack == 64) break; @@ -605,47 +624,17 @@ retry: } /* Multi-cpu list version. */ -static void hypervisor_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t mask) +static void hypervisor_xcall_deliver(struct trap_per_cpu *tb, int cnt) { - struct trap_per_cpu *tb; + int retries, this_cpu, prev_sent, i, saw_cpu_error; + unsigned long status; u16 *cpu_list; - u64 *mondo; - cpumask_t error_mask; - unsigned long flags, status; - int cnt, retries, this_cpu, prev_sent, i; - - if (cpus_empty(mask)) - return; - - /* We have to do this whole thing with interrupts fully disabled. - * Otherwise if we send an xcall from interrupt context it will - * corrupt both our mondo block and cpu list state. - * - * One consequence of this is that we cannot use timeout mechanisms - * that depend upon interrupts being delivered locally. So, for - * example, we cannot sample jiffies and expect it to advance. - * - * Fortunately, udelay() uses %stick/%tick so we can use that. - */ - local_irq_save(flags); this_cpu = smp_processor_id(); - tb = &trap_block[this_cpu]; - - mondo = __va(tb->cpu_mondo_block_pa); - mondo[0] = data0; - mondo[1] = data1; - mondo[2] = data2; - wmb(); cpu_list = __va(tb->cpu_list_pa); - /* Setup the initial cpu list. */ - cnt = 0; - for_each_cpu_mask(i, mask) - cpu_list[cnt++] = i; - - cpus_clear(error_mask); + saw_cpu_error = 0; retries = 0; prev_sent = 0; do { @@ -690,10 +679,9 @@ static void hypervisor_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t continue; err = sun4v_cpu_state(cpu); - if (err >= 0 && - err == HV_CPU_STATE_ERROR) { + if (err == HV_CPU_STATE_ERROR) { + saw_cpu_error = (cpu + 1); cpu_list[i] = 0xffff; - cpu_set(cpu, error_mask); } } } else if (unlikely(status != HV_EWOULDBLOCK)) @@ -717,32 +705,24 @@ static void hypervisor_xcall_deliver(u64 data0, u64 data1, u64 data2, cpumask_t } } while (1); - local_irq_restore(flags); - - if (unlikely(!cpus_empty(error_mask))) + if (unlikely(saw_cpu_error)) goto fatal_mondo_cpu_error; return; fatal_mondo_cpu_error: printk(KERN_CRIT "CPU[%d]: SUN4V mondo cpu error, some target cpus " - "were in error state\n", - this_cpu); - printk(KERN_CRIT "CPU[%d]: Error mask [ ", this_cpu); - for_each_cpu_mask(i, error_mask) - printk("%d ", i); - printk("]\n"); + "(including %d) were in error state\n", + this_cpu, saw_cpu_error - 1); return; fatal_mondo_timeout: - local_irq_restore(flags); printk(KERN_CRIT "CPU[%d]: SUN4V mondo timeout, no forward " " progress after %d retries.\n", this_cpu, retries); goto dump_cpu_list_and_out; fatal_mondo_error: - local_irq_restore(flags); printk(KERN_CRIT "CPU[%d]: Unexpected SUN4V mondo error %lu\n", this_cpu, status); printk(KERN_CRIT "CPU[%d]: Args were cnt(%d) cpulist_pa(%lx) " @@ -756,58 +736,93 @@ dump_cpu_list_and_out: printk("]\n"); } -/* Send cross call to all processors mentioned in MASK - * except self. +static void (*xcall_deliver_impl)(struct trap_per_cpu *, int); + +static void xcall_deliver(u64 data0, u64 data1, u64 data2, const cpumask_t *mask) +{ + struct trap_per_cpu *tb; + int this_cpu, i, cnt; + unsigned long flags; + u16 *cpu_list; + u64 *mondo; + + /* We have to do this whole thing with interrupts fully disabled. + * Otherwise if we send an xcall from interrupt context it will + * corrupt both our mondo block and cpu list state. + * + * One consequence of this is that we cannot use timeout mechanisms + * that depend upon interrupts being delivered locally. So, for + * example, we cannot sample jiffies and expect it to advance. + * + * Fortunately, udelay() uses %stick/%tick so we can use that. + */ + local_irq_save(flags); + + this_cpu = smp_processor_id(); + tb = &trap_block[this_cpu]; + + mondo = __va(tb->cpu_mondo_block_pa); + mondo[0] = data0; + mondo[1] = data1; + mondo[2] = data2; + wmb(); + + cpu_list = __va(tb->cpu_list_pa); + + /* Setup the initial cpu list. */ + cnt = 0; + for_each_cpu_mask_nr(i, *mask) { + if (i == this_cpu || !cpu_online(i)) + continue; + cpu_list[cnt++] = i; + } + + if (cnt) + xcall_deliver_impl(tb, cnt); + + local_irq_restore(flags); +} + +/* Send cross call to all processors mentioned in MASK_P + * except self. Really, there are only two cases currently, + * "&cpu_online_map" and "&mm->cpu_vm_mask". */ -static void smp_cross_call_masked(unsigned long *func, u32 ctx, u64 data1, u64 data2, cpumask_t mask) +static void smp_cross_call_masked(unsigned long *func, u32 ctx, u64 data1, u64 data2, const cpumask_t *mask) { u64 data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff)); - int this_cpu = get_cpu(); - - cpus_and(mask, mask, cpu_online_map); - cpu_clear(this_cpu, mask); - if (tlb_type == spitfire) - spitfire_xcall_deliver(data0, data1, data2, mask); - else if (tlb_type == cheetah || tlb_type == cheetah_plus) - cheetah_xcall_deliver(data0, data1, data2, mask); - else - hypervisor_xcall_deliver(data0, data1, data2, mask); - /* NOTE: Caller runs local copy on master. */ + xcall_deliver(data0, data1, data2, mask); +} - put_cpu(); +/* Send cross call to all processors except self. */ +static void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2) +{ + smp_cross_call_masked(func, ctx, data1, data2, &cpu_online_map); } extern unsigned long xcall_sync_tick; static void smp_start_sync_tick_client(int cpu) { - cpumask_t mask = cpumask_of_cpu(cpu); - - smp_cross_call_masked(&xcall_sync_tick, - 0, 0, 0, mask); + xcall_deliver((u64) &xcall_sync_tick, 0, 0, + &cpumask_of_cpu(cpu)); } extern unsigned long xcall_call_function; void arch_send_call_function_ipi(cpumask_t mask) { - smp_cross_call_masked(&xcall_call_function, 0, 0, 0, mask); + xcall_deliver((u64) &xcall_call_function, 0, 0, &mask); } extern unsigned long xcall_call_function_single; void arch_send_call_function_single_ipi(int cpu) { - cpumask_t mask = cpumask_of_cpu(cpu); - - smp_cross_call_masked(&xcall_call_function_single, 0, 0, 0, mask); + xcall_deliver((u64) &xcall_call_function_single, 0, 0, + &cpumask_of_cpu(cpu)); } -/* Send cross call to all processors except self. */ -#define smp_cross_call(func, ctx, data1, data2) \ - smp_cross_call_masked(func, ctx, data1, data2, cpu_online_map) - void smp_call_function_client(int irq, struct pt_regs *regs) { clear_softint(1 << irq); @@ -843,7 +858,6 @@ void smp_tsb_sync(struct mm_struct *mm) extern unsigned long xcall_flush_tlb_mm; extern unsigned long xcall_flush_tlb_pending; extern unsigned long xcall_flush_tlb_kernel_range; -extern unsigned long xcall_report_regs; #ifdef CONFIG_MAGIC_SYSRQ extern unsigned long xcall_fetch_glob_regs; #endif @@ -878,7 +892,6 @@ static inline void __local_flush_dcache_page(struct page *page) void smp_flush_dcache_page_impl(struct page *page, int cpu) { - cpumask_t mask = cpumask_of_cpu(cpu); int this_cpu; if (tlb_type == hypervisor) @@ -894,29 +907,24 @@ void smp_flush_dcache_page_impl(struct page *page, int cpu) __local_flush_dcache_page(page); } else if (cpu_online(cpu)) { void *pg_addr = page_address(page); - u64 data0; + u64 data0 = 0; if (tlb_type == spitfire) { - data0 = - ((u64)&xcall_flush_dcache_page_spitfire); + data0 = ((u64)&xcall_flush_dcache_page_spitfire); if (page_mapping(page) != NULL) data0 |= ((u64)1 << 32); - spitfire_xcall_deliver(data0, - __pa(pg_addr), - (u64) pg_addr, - mask); } else if (tlb_type == cheetah || tlb_type == cheetah_plus) { #ifdef DCACHE_ALIASING_POSSIBLE - data0 = - ((u64)&xcall_flush_dcache_page_cheetah); - cheetah_xcall_deliver(data0, - __pa(pg_addr), - 0, mask); + data0 = ((u64)&xcall_flush_dcache_page_cheetah); #endif } + if (data0) { + xcall_deliver(data0, __pa(pg_addr), + (u64) pg_addr, &cpumask_of_cpu(cpu)); #ifdef CONFIG_DEBUG_DCFLUSH - atomic_inc(&dcpage_flushes_xcall); + atomic_inc(&dcpage_flushes_xcall); #endif + } } put_cpu(); @@ -924,66 +932,41 @@ void smp_flush_dcache_page_impl(struct page *page, int cpu) void flush_dcache_page_all(struct mm_struct *mm, struct page *page) { - void *pg_addr = page_address(page); - cpumask_t mask = cpu_online_map; - u64 data0; + void *pg_addr; int this_cpu; + u64 data0; if (tlb_type == hypervisor) return; this_cpu = get_cpu(); - cpu_clear(this_cpu, mask); - #ifdef CONFIG_DEBUG_DCFLUSH atomic_inc(&dcpage_flushes); #endif - if (cpus_empty(mask)) - goto flush_self; + data0 = 0; + pg_addr = page_address(page); if (tlb_type == spitfire) { data0 = ((u64)&xcall_flush_dcache_page_spitfire); if (page_mapping(page) != NULL) data0 |= ((u64)1 << 32); - spitfire_xcall_deliver(data0, - __pa(pg_addr), - (u64) pg_addr, - mask); } else if (tlb_type == cheetah || tlb_type == cheetah_plus) { #ifdef DCACHE_ALIASING_POSSIBLE data0 = ((u64)&xcall_flush_dcache_page_cheetah); - cheetah_xcall_deliver(data0, - __pa(pg_addr), - 0, mask); #endif } + if (data0) { + xcall_deliver(data0, __pa(pg_addr), + (u64) pg_addr, &cpu_online_map); #ifdef CONFIG_DEBUG_DCFLUSH - atomic_inc(&dcpage_flushes_xcall); + atomic_inc(&dcpage_flushes_xcall); #endif - flush_self: + } __local_flush_dcache_page(page); put_cpu(); } -static void __smp_receive_signal_mask(cpumask_t mask) -{ - smp_cross_call_masked(&xcall_receive_signal, 0, 0, 0, mask); -} - -void smp_receive_signal(int cpu) -{ - cpumask_t mask = cpumask_of_cpu(cpu); - - if (cpu_online(cpu)) - __smp_receive_signal_mask(mask); -} - -void smp_receive_signal_client(int irq, struct pt_regs *regs) -{ - clear_softint(1 << irq); -} - void smp_new_mmu_context_version_client(int irq, struct pt_regs *regs) { struct mm_struct *mm; @@ -1022,11 +1005,6 @@ void kgdb_roundup_cpus(unsigned long flags) } #endif -void smp_report_regs(void) -{ - smp_cross_call(&xcall_report_regs, 0, 0, 0); -} - #ifdef CONFIG_MAGIC_SYSRQ void smp_fetch_global_regs(void) { @@ -1089,7 +1067,7 @@ void smp_flush_tlb_mm(struct mm_struct *mm) smp_cross_call_masked(&xcall_flush_tlb_mm, ctx, 0, 0, - mm->cpu_vm_mask); + &mm->cpu_vm_mask); local_flush_and_out: __flush_tlb_mm(ctx, SECONDARY_CONTEXT); @@ -1107,7 +1085,7 @@ void smp_flush_tlb_pending(struct mm_struct *mm, unsigned long nr, unsigned long else smp_cross_call_masked(&xcall_flush_tlb_pending, ctx, nr, (unsigned long) vaddrs, - mm->cpu_vm_mask); + &mm->cpu_vm_mask); __flush_tlb_pending(ctx, nr, vaddrs); @@ -1208,6 +1186,16 @@ void __devinit smp_prepare_boot_cpu(void) { } +void __init smp_setup_processor_id(void) +{ + if (tlb_type == spitfire) + xcall_deliver_impl = spitfire_xcall_deliver; + else if (tlb_type == cheetah || tlb_type == cheetah_plus) + xcall_deliver_impl = cheetah_xcall_deliver; + else + xcall_deliver_impl = hypervisor_xcall_deliver; +} + void __devinit smp_fill_in_sib_core_maps(void) { unsigned int i; @@ -1376,7 +1364,13 @@ void __init smp_cpus_done(unsigned int max_cpus) void smp_send_reschedule(int cpu) { - smp_receive_signal(cpu); + xcall_deliver((u64) &xcall_receive_signal, 0, 0, + &cpumask_of_cpu(cpu)); +} + +void smp_receive_signal_client(int irq, struct pt_regs *regs) +{ + clear_softint(1 << irq); } /* This is a nop because we capture all other cpus diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 504e678ee12..0804f71df6c 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -68,7 +68,6 @@ extern void *__memscan_zero(void *, size_t); extern void *__memscan_generic(void *, int, size_t); extern int __memcmp(const void *, const void *, __kernel_size_t); extern __kernel_size_t strlen(const char *); -extern void show_regs(struct pt_regs *); extern void syscall_trace(struct pt_regs *, int); extern void sys_sigsuspend(void); extern int compat_sys_ioctl(unsigned int fd, unsigned int cmd, u32 arg); diff --git a/arch/sparc64/kernel/syscalls.S b/arch/sparc64/kernel/syscalls.S index db19ed67acf..a2f24270ed8 100644 --- a/arch/sparc64/kernel/syscalls.S +++ b/arch/sparc64/kernel/syscalls.S @@ -162,6 +162,8 @@ linux_syscall_trace32: add %sp, PTREGS_OFF, %o0 call syscall_trace clr %o1 + brnz,pn %o0, 3f + mov -ENOSYS, %o0 srl %i0, 0, %o0 srl %i4, 0, %o4 srl %i1, 0, %o1 @@ -173,6 +175,8 @@ linux_syscall_trace: add %sp, PTREGS_OFF, %o0 call syscall_trace clr %o1 + brnz,pn %o0, 3f + mov -ENOSYS, %o0 mov %i0, %o0 mov %i1, %o1 mov %i2, %o2 diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index bd30ecba563..404e8561e2d 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1777,7 +1777,7 @@ static void sun4v_log_error(struct pt_regs *regs, struct sun4v_error_entry *ent, pfx, ent->err_raddr, ent->err_size, ent->err_cpu); - __show_regs(regs); + show_regs(regs); if ((cnt = atomic_read(ocnt)) != 0) { atomic_set(ocnt, 0); @@ -2177,7 +2177,6 @@ static inline struct reg_window *kernel_stack_up(struct reg_window *rw) void die_if_kernel(char *str, struct pt_regs *regs) { static int die_counter; - extern void smp_report_regs(void); int count = 0; /* Amuse the user. */ @@ -2190,7 +2189,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) printk("%s(%d): %s [#%d]\n", current->comm, task_pid_nr(current), str, ++die_counter); notify_die(DIE_OOPS, str, regs, 0, 255, SIGSEGV); __asm__ __volatile__("flushw"); - __show_regs(regs); + show_regs(regs); add_taint(TAINT_DIE); if (regs->tstate & TSTATE_PRIV) { struct reg_window *rw = (struct reg_window *) @@ -2215,11 +2214,6 @@ void die_if_kernel(char *str, struct pt_regs *regs) } user_instruction_dump ((unsigned int __user *) regs->tpc); } -#if 0 -#ifdef CONFIG_SMP - smp_report_regs(); -#endif -#endif if (regs->tstate & TSTATE_PRIV) do_exit(SIGKILL); do_exit(SIGSEGV); diff --git a/arch/sparc64/mm/tsb.c b/arch/sparc64/mm/tsb.c index 3547937b17a..587f8efb2e0 100644 --- a/arch/sparc64/mm/tsb.c +++ b/arch/sparc64/mm/tsb.c @@ -1,9 +1,10 @@ /* arch/sparc64/mm/tsb.c * - * Copyright (C) 2006 David S. Miller <davem@davemloft.net> + * Copyright (C) 2006, 2008 David S. Miller <davem@davemloft.net> */ #include <linux/kernel.h> +#include <linux/preempt.h> #include <asm/system.h> #include <asm/page.h> #include <asm/tlbflush.h> @@ -415,7 +416,9 @@ retry_tsb_alloc: tsb_context_switch(mm); /* Now force other processors to do the same. */ + preempt_disable(); smp_tsb_sync(mm); + preempt_enable(); /* Now it is safe to free the old tsb. */ kmem_cache_free(tsb_caches[old_cache_index], old_tsb); diff --git a/arch/sparc64/mm/ultra.S b/arch/sparc64/mm/ultra.S index 4c8ca131ffa..ff1dc44d363 100644 --- a/arch/sparc64/mm/ultra.S +++ b/arch/sparc64/mm/ultra.S @@ -480,41 +480,6 @@ xcall_sync_tick: b rtrap_xcall ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1 - /* NOTE: This is SPECIAL!! We do etrap/rtrap however - * we choose to deal with the "BH's run with - * %pil==15" problem (described in asm/pil.h) - * by just invoking rtrap directly past where - * BH's are checked for. - * - * We do it like this because we do not want %pil==15 - * lockups to prevent regs being reported. - */ - .globl xcall_report_regs -xcall_report_regs: - -661: rdpr %pstate, %g2 - wrpr %g2, PSTATE_IG | PSTATE_AG, %pstate - .section .sun4v_2insn_patch, "ax" - .word 661b - nop - nop - .previous - - rdpr %pil, %g2 - wrpr %g0, 15, %pil - sethi %hi(109f), %g7 - b,pt %xcc, etrap_irq -109: or %g7, %lo(109b), %g7 -#ifdef CONFIG_TRACE_IRQFLAGS - call trace_hardirqs_off - nop -#endif - call __show_regs - add %sp, PTREGS_OFF, %o0 - /* Has to be a non-v9 branch due to the large distance. */ - b rtrap_xcall - ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1 - #ifdef CONFIG_MAGIC_SYSRQ .globl xcall_fetch_glob_regs xcall_fetch_glob_regs: @@ -531,6 +496,13 @@ xcall_fetch_glob_regs: stx %g7, [%g1 + GR_SNAP_TNPC] stx %o7, [%g1 + GR_SNAP_O7] stx %i7, [%g1 + GR_SNAP_I7] + /* Don't try this at home kids... */ + rdpr %cwp, %g2 + sub %g2, 1, %g7 + wrpr %g7, %cwp + mov %i7, %g7 + wrpr %g2, %cwp + stx %g7, [%g1 + GR_SNAP_RPC] sethi %hi(trap_block), %g7 or %g7, %lo(trap_block), %g7 sllx %g2, TRAP_BLOCK_SZ_SHIFT, %g2 |