fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / ia64 / kernel / traps.c
index e82ad78..d8590f9 100644 (file)
@@ -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>
 #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)
 {
@@ -76,18 +94,22 @@ die (const char *str, struct pt_regs *regs, long err)
                .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");
@@ -95,6 +117,10 @@ die (const char *str, struct pt_regs *regs, long err)
        bust_spinlocks(0);
        die.lock_owner = -1;
        spin_unlock_irq(&die.lock);
+
+       if (panic_on_oops)
+               panic("Fatal exception");
+
        do_exit(SIGSEGV);
 }
 
@@ -106,7 +132,7 @@ die_if_kernel (char *str, struct pt_regs *regs, long err)
 }
 
 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;
@@ -119,6 +145,9 @@ ia64_bad_break (unsigned long break_num, struct pt_regs *regs)
 
        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;
@@ -178,6 +207,9 @@ ia64_bad_break (unsigned long break_num, struct pt_regs *regs)
                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;
                }
        }
@@ -202,13 +234,21 @@ disabled_fph_fault (struct pt_regs *regs)
 
        /* 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);
@@ -226,6 +266,7 @@ disabled_fph_fault (struct pt_regs *regs)
                 */
                psr->mfh = 1;
        }
+       preempt_enable_no_resched();
 }
 
 static inline int
@@ -267,6 +308,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.
  */
@@ -276,8 +326,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))
@@ -285,14 +333,38 @@ 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
+                        * 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, &regs->cr_ipsr, &regs->ar_fpsr, &isr, &regs->pr,
@@ -388,7 +460,7 @@ ia64_illegal_op_fault (unsigned long ec, long arg1, long arg2, long arg3,
        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)
@@ -494,12 +566,15 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
                if (fsys_mode(current, &regs)) {
                        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!",
-                                   &regs, 0);
+                               set_thread_flag(TIF_DB_DISABLED);
+                               ia64_psr(&regs)->db = 0;
+                               ia64_psr(&regs)->lp = 1;
                                return;
                        }
                        /* re-do the system call via break 0x100000: */
@@ -523,6 +598,9 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
                      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", &regs, vector, siginfo.si_code, SIGTRAP)
+                               == NOTIFY_STOP)
+                       return;
                siginfo.si_signo = SIGTRAP;
                siginfo.si_errno = 0;
                siginfo.si_addr  = (void __user *) ifa;
@@ -550,10 +628,19 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long 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(&regs)->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(&regs)->lp = 0;
                        return;