* 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>
#include <linux/vt_kern.h> /* For unblank_screen() */
#include <linux/module.h> /* for EXPORT_SYMBOL */
#include <linux/hardirq.h>
+#include <linux/kprobes.h>
+#include <linux/delay.h> /* for ssleep() */
#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;
fpswa_interface_t *fpswa_interface;
EXPORT_SYMBOL(fpswa_interface);
+ATOMIC_NOTIFIER_HEAD(ia64die_chain);
+
+int
+register_die_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&ia64die_chain, nb);
+}
+EXPORT_SYMBOL_GPL(register_die_notifier);
+
+int
+unregister_die_notifier(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&ia64die_chain, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_die_notifier);
+
void __init
trap_init (void)
{
.lock_owner_depth = 0
};
static int die_counter;
+ int cpu = get_cpu();
- if (die.lock_owner != smp_processor_id()) {
+ if (die.lock_owner != cpu) {
console_verbose();
spin_lock_irq(&die.lock);
- die.lock_owner = smp_processor_id();
+ die.lock_owner = cpu;
die.lock_owner_depth = 0;
bust_spinlocks(1);
}
+ put_cpu();
if (++die.lock_owner_depth < 3) {
- printk("%s[%d]: %s %ld [%d]\n",
- current->comm, current->pid, str, err, ++die_counter);
+ printk("%s[%d[#%u]]: %s %ld [%d]\n",
+ current->comm, current->pid, current->xid,
+ str, err, ++die_counter);
+ (void) notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
show_regs(regs);
} else
printk(KERN_ERR "Recursive die() failure, output suppressed\n");
bust_spinlocks(0);
die.lock_owner = -1;
spin_unlock_irq(&die.lock);
+
+ if (panic_on_oops)
+ panic("Fatal exception");
+
do_exit(SIGSEGV);
}
}
void
-ia64_bad_break (unsigned long break_num, struct pt_regs *regs)
+__kprobes ia64_bad_break (unsigned long break_num, struct pt_regs *regs)
{
siginfo_t siginfo;
int sig, code;
switch (break_num) {
case 0: /* unknown error (used by GCC for __builtin_abort()) */
+ if (notify_die(DIE_BREAK, "break 0", regs, break_num, TRAP_BRKPT, SIGTRAP)
+ == NOTIFY_STOP)
+ return;
die_if_kernel("bugcheck!", regs, break_num);
sig = SIGILL; code = ILL_ILLOPC;
break;
if (break_num < 0x80000) {
sig = SIGILL; code = __ILL_BREAK;
} else {
+ if (notify_die(DIE_BREAK, "bad break", regs, break_num, TRAP_BRKPT, SIGTRAP)
+ == NOTIFY_STOP)
+ return;
sig = SIGTRAP; code = TRAP_BRKPT;
}
}
/* first, grant user-level access to fph partition: */
psr->dfh = 0;
+
+ /*
+ * Make sure that no other task gets in on this processor
+ * while we're claiming the FPU
+ */
+ preempt_disable();
#ifndef CONFIG_SMP
{
struct task_struct *fpu_owner
= (struct task_struct *)ia64_get_kr(IA64_KR_FPU_OWNER);
- if (ia64_is_local_fpu_owner(current))
+ if (ia64_is_local_fpu_owner(current)) {
+ preempt_enable_no_resched();
return;
+ }
if (fpu_owner)
ia64_flush_fph(fpu_owner);
*/
psr->mfh = 1;
}
+ preempt_enable_no_resched();
}
static inline int
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.
*/
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))
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
+ * reseting 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[#%u]): floating-point assist fault at ip %016lx, isr %016lx\n",
+ current->comm, current->pid, current->xid,
+ regs->cr_iip + ia64_psr(regs)->ri, isr);
+ }
+ }
}
exception = fp_emulate(fp_fault, bundle, ®s->cr_ipsr, ®s->ar_fpsr, &isr, ®s->pr,
return rv;
}
-void
+void __kprobes
ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
unsigned long iim, unsigned long itir, long arg5, long arg6,
long arg7, struct pt_regs regs)
if (fsys_mode(current, ®s)) {
extern char __kernel_syscall_via_break[];
/*
- * Got a trap in fsys-mode: Taken Branch Trap and Single Step trap
- * need special handling; Debug trap is not supposed to happen.
+ * Got a trap in fsys-mode: Taken Branch Trap
+ * and Single Step trap need special handling;
+ * Debug trap is ignored (we disable it here
+ * and re-enable it in the lower-privilege trap).
*/
if (unlikely(vector == 29)) {
- die("Got debug trap in fsys-mode---not supposed to happen!",
- ®s, 0);
+ set_thread_flag(TIF_DB_DISABLED);
+ ia64_psr(®s)->db = 0;
+ ia64_psr(®s)->lp = 1;
return;
}
/* re-do the system call via break 0x100000: */
case 35: siginfo.si_code = TRAP_BRANCH; ifa = 0; break;
case 36: siginfo.si_code = TRAP_TRACE; ifa = 0; break;
}
+ if (notify_die(DIE_FAULT, "ia64_fault", ®s, vector, siginfo.si_code, SIGTRAP)
+ == NOTIFY_STOP)
+ return;
siginfo.si_signo = SIGTRAP;
siginfo.si_errno = 0;
siginfo.si_addr = (void __user *) ifa;
case 34:
if (isr & 0x2) {
/* Lower-Privilege Transfer Trap */
+
+ /* If we disabled debug traps during an fsyscall,
+ * re-enable them here.
+ */
+ if (test_thread_flag(TIF_DB_DISABLED)) {
+ clear_thread_flag(TIF_DB_DISABLED);
+ ia64_psr(®s)->db = 1;
+ }
+
/*
- * Just clear PSR.lp and then return immediately: all the
- * interesting work (e.g., signal delivery is done in the kernel
- * exit path).
+ * Just clear PSR.lp and then return immediately:
+ * all the interesting work (e.g., signal delivery)
+ * is done in the kernel exit path.
*/
ia64_psr(®s)->lp = 0;
return;