diff options
Diffstat (limited to 'arch/ia64/kernel/traps.c')
| -rw-r--r-- | arch/ia64/kernel/traps.c | 162 |
1 files changed, 77 insertions, 85 deletions
diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index 55391901b01..d3636e67a98 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -7,7 +7,6 @@ * 05/12/00 grao <goutham.rao@intel.com> : added isr in siginfo for SIGFPE */ -#include <linux/config.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/sched.h> @@ -16,35 +15,18 @@ #include <linux/module.h> /* for EXPORT_SYMBOL */ #include <linux/hardirq.h> #include <linux/kprobes.h> +#include <linux/delay.h> /* for ssleep() */ +#include <linux/kdebug.h> #include <asm/fpswa.h> -#include <asm/ia32.h> #include <asm/intrinsics.h> #include <asm/processor.h> #include <asm/uaccess.h> -#include <asm/kdebug.h> - -extern spinlock_t timerlist_lock; +#include <asm/setup.h> fpswa_interface_t *fpswa_interface; EXPORT_SYMBOL(fpswa_interface); -struct notifier_block *ia64die_chain; - -int -register_die_notifier(struct notifier_block *nb) -{ - return notifier_chain_register(&ia64die_chain, nb); -} -EXPORT_SYMBOL_GPL(register_die_notifier); - -int -unregister_die_notifier(struct notifier_block *nb) -{ - return notifier_chain_unregister(&ia64die_chain, nb); -} -EXPORT_SYMBOL_GPL(unregister_die_notifier); - void __init trap_init (void) { @@ -53,35 +35,7 @@ trap_init (void) fpswa_interface = __va(ia64_boot_param->fpswa); } -/* - * Unlock any spinlocks which will prevent us from getting the message out (timerlist_lock - * is acquired through the console unblank code) - */ -void -bust_spinlocks (int yes) -{ - int loglevel_save = console_loglevel; - - if (yes) { - oops_in_progress = 1; - return; - } - -#ifdef CONFIG_VT - unblank_screen(); -#endif - oops_in_progress = 0; - /* - * OK, the message is on the console. Now we call printk() without - * oops_in_progress set so that printk will give klogd a poke. Hold onto - * your hats... - */ - console_loglevel = 15; /* NMI oopser may have shut the console up */ - printk(" "); - console_loglevel = loglevel_save; -} - -void +int die (const char *str, struct pt_regs *regs, long err) { static struct { @@ -89,9 +43,9 @@ die (const char *str, struct pt_regs *regs, long err) u32 lock_owner; int lock_owner_depth; } die = { - .lock = SPIN_LOCK_UNLOCKED, - .lock_owner = -1, - .lock_owner_depth = 0 + .lock = __SPIN_LOCK_UNLOCKED(die.lock), + .lock_owner = -1, + .lock_owner_depth = 0 }; static int die_counter; int cpu = get_cpu(); @@ -107,23 +61,36 @@ die (const char *str, struct pt_regs *regs, long err) if (++die.lock_owner_depth < 3) { printk("%s[%d]: %s %ld [%d]\n", - current->comm, current->pid, str, err, ++die_counter); - (void) notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV); - show_regs(regs); + current->comm, task_pid_nr(current), str, err, ++die_counter); + if (notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV) + != NOTIFY_STOP) + show_regs(regs); + else + regs = NULL; } else printk(KERN_ERR "Recursive die() failure, output suppressed\n"); bust_spinlocks(0); die.lock_owner = -1; + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die.lock); + + if (!regs) + return 1; + + if (panic_on_oops) + panic("Fatal exception"); + do_exit(SIGSEGV); + return 0; } -void +int die_if_kernel (char *str, struct pt_regs *regs, long err) { if (!user_mode(regs)) - die(str, regs, err); + return die(str, regs, err); + return 0; } void @@ -143,7 +110,8 @@ __kprobes ia64_bad_break (unsigned long break_num, struct pt_regs *regs) if (notify_die(DIE_BREAK, "break 0", regs, break_num, TRAP_BRKPT, SIGTRAP) == NOTIFY_STOP) return; - die_if_kernel("bugcheck!", regs, break_num); + if (die_if_kernel("bugcheck!", regs, break_num)) + return; sig = SIGILL; code = ILL_ILLOPC; break; @@ -196,8 +164,9 @@ __kprobes ia64_bad_break (unsigned long break_num, struct pt_regs *regs) break; default: - if (break_num < 0x40000 || break_num > 0x100000) - die_if_kernel("Bad break", regs, break_num); + if ((break_num < 0x40000 || break_num > 0x100000) + && die_if_kernel("Bad break", regs, break_num)) + return; if (break_num < 0x80000) { sig = SIGILL; code = __ILL_BREAK; @@ -303,6 +272,15 @@ fp_emulate (int fp_fault, void *bundle, long *ipsr, long *fpsr, long *isr, long return ret.status; } +struct fpu_swa_msg { + unsigned long count; + unsigned long time; +}; +static DEFINE_PER_CPU(struct fpu_swa_msg, cpulast); +DECLARE_PER_CPU(struct fpu_swa_msg, cpulast); +static struct fpu_swa_msg last __cacheline_aligned; + + /* * Handle floating-point assist faults and traps. */ @@ -312,8 +290,6 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr) long exception, bundle[2]; unsigned long fault_ip; struct siginfo siginfo; - static int fpu_swa_count = 0; - static unsigned long last_time; fault_ip = regs->cr_iip; if (!fp_fault && (ia64_psr(regs)->ri == 0)) @@ -321,14 +297,37 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr) if (copy_from_user(bundle, (void __user *) fault_ip, sizeof(bundle))) return -1; - if (jiffies - last_time > 5*HZ) - fpu_swa_count = 0; - if ((fpu_swa_count < 4) && !(current->thread.flags & IA64_THREAD_FPEMU_NOPRINT)) { - last_time = jiffies; - ++fpu_swa_count; - printk(KERN_WARNING - "%s(%d): floating-point assist fault at ip %016lx, isr %016lx\n", - current->comm, current->pid, regs->cr_iip + ia64_psr(regs)->ri, isr); + if (!(current->thread.flags & IA64_THREAD_FPEMU_NOPRINT)) { + unsigned long count, current_jiffies = jiffies; + struct fpu_swa_msg *cp = &__get_cpu_var(cpulast); + + if (unlikely(current_jiffies > cp->time)) + cp->count = 0; + if (unlikely(cp->count < 5)) { + cp->count++; + cp->time = current_jiffies + 5 * HZ; + + /* minimize races by grabbing a copy of count BEFORE checking last.time. */ + count = last.count; + barrier(); + + /* + * Lower 4 bits are used as a count. Upper bits are a sequence + * number that is updated when count is reset. The cmpxchg will + * fail is seqno has changed. This minimizes mutiple cpus + * resetting the count. + */ + if (current_jiffies > last.time) + (void) cmpxchg_acq(&last.count, count, 16 + (count & ~15)); + + /* used fetchadd to atomically update the count */ + if ((last.count & 15) < 5 && (ia64_fetchadd(1, &last.count, acq) & 15) < 5) { + last.time = current_jiffies + 5 * HZ; + printk(KERN_WARNING + "%s(%d): floating-point assist fault at ip %016lx, isr %016lx\n", + current->comm, task_pid_nr(current), regs->cr_iip + ia64_psr(regs)->ri, isr); + } + } } exception = fp_emulate(fp_fault, bundle, ®s->cr_ipsr, ®s->ar_fpsr, &isr, ®s->pr, @@ -413,14 +412,15 @@ ia64_illegal_op_fault (unsigned long ec, long arg1, long arg2, long arg3, #endif sprintf(buf, "IA-64 Illegal operation fault"); - die_if_kernel(buf, ®s, 0); + rv.fkt = 0; + if (die_if_kernel(buf, ®s, 0)) + return rv; memset(&si, 0, sizeof(si)); si.si_signo = SIGILL; si.si_code = ILL_ILLOPC; si.si_addr = (void __user *) (regs.cr_iip + ia64_psr(®s)->ri); force_sig_info(SIGILL, &si, current); - rv.fkt = 0; return rv; } @@ -464,7 +464,7 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, if (code == 8) { # ifdef CONFIG_IA64_PRINT_HAZARDS printk("%s[%d]: possible hazard @ ip=%016lx (pr = %016lx)\n", - current->comm, current->pid, + current->comm, task_pid_nr(current), regs.cr_iip + ia64_psr(®s)->ri, regs.pr); # endif return; @@ -626,21 +626,13 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, break; case 45: -#ifdef CONFIG_IA32_SUPPORT - if (ia32_exception(®s, isr) == 0) - return; -#endif printk(KERN_ERR "Unexpected IA-32 exception (Trap 45)\n"); printk(KERN_ERR " iip - 0x%lx, ifa - 0x%lx, isr - 0x%lx\n", iip, ifa, isr); force_sig(SIGSEGV, current); - break; + return; case 46: -#ifdef CONFIG_IA32_SUPPORT - if (ia32_intercept(®s, isr) == 0) - return; -#endif printk(KERN_ERR "Unexpected IA-32 intercept trap (Trap 46)\n"); printk(KERN_ERR " iip - 0x%lx, ifa - 0x%lx, isr - 0x%lx, iim - 0x%lx\n", iip, ifa, isr, iim); @@ -655,6 +647,6 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, sprintf(buf, "Fault %lu", vector); break; } - die_if_kernel(buf, ®s, error); - force_sig(SIGILL, current); + if (!die_if_kernel(buf, ®s, error)) + force_sig(SIGILL, current); } |
