x86: optimize page faults like all other achitectures and kill notifier cruft
authorChristoph Hellwig <hch@lst.de>
Tue, 16 Oct 2007 08:24:07 +0000 (01:24 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Tue, 16 Oct 2007 16:42:50 +0000 (09:42 -0700)
x86(-64) are the last architectures still using the page fault notifier
cruft for the kprobes page fault hook.  This patch converts them to the
proper direct calls, and removes the now unused pagefault notifier bits
aswell as the cruft in kprobes.c that was related to this mess.

I know Andi didn't really like this, but all other architecture maintainers
agreed the direct calls are much better and besides the obvious cruft
removal a common way of dealing with kprobes across architectures is
important aswell.

[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: fix sparc64]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Cc: Andi Kleen <ak@suse.de>
Cc: <linux-arch@vger.kernel.org>
Cc: Prasanna S Panchamukhi <prasanna@in.ibm.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
20 files changed:
arch/x86/kernel/kprobes_32.c
arch/x86/kernel/kprobes_64.c
arch/x86/mm/fault_32.c
arch/x86/mm/fault_64.c
include/asm-avr32/kdebug.h
include/asm-avr32/kprobes.h
include/asm-ia64/kdebug.h
include/asm-ia64/kprobes.h
include/asm-powerpc/kdebug.h
include/asm-powerpc/kprobes.h
include/asm-s390/kdebug.h
include/asm-s390/kprobes.h
include/asm-sh/kdebug.h
include/asm-sparc64/kdebug.h
include/asm-sparc64/kprobes.h
include/asm-x86/kdebug_32.h
include/asm-x86/kdebug_64.h
include/asm-x86/kprobes_32.h
include/asm-x86/kprobes_64.h
kernel/kprobes.c

index e7d0d3c2ef6473faeb460e7e77e20e579eedeb23..06b86e5617f62c739bdc4b448985d46462078273 100644 (file)
@@ -584,7 +584,7 @@ out:
        return 1;
 }
 
-static int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
 {
        struct kprobe *cur = kprobe_running();
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
@@ -666,7 +666,6 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
                        ret = NOTIFY_STOP;
                break;
        case DIE_GPF:
-       case DIE_PAGE_FAULT:
                /* kprobe_running() needs smp_processor_id() */
                preempt_disable();
                if (kprobe_running() &&
index 62e28e52d784c8da494ebb6d2e045f5d8a9cb2b6..7c16506d681fc56f985c65f2c4b22dea7a5019ea 100644 (file)
@@ -657,7 +657,6 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
                        ret = NOTIFY_STOP;
                break;
        case DIE_GPF:
-       case DIE_PAGE_FAULT:
                /* kprobe_running() needs smp_processor_id() */
                preempt_disable();
                if (kprobe_running() &&
index fcb38e7f35434113896abc3caad887c9aad665d1..be72c2a5b03b4a1e4fea65b3431faf21d1491d3a 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/kprobes.h>
 #include <linux/uaccess.h>
 #include <linux/kdebug.h>
+#include <linux/kprobes.h>
 
 #include <asm/system.h>
 #include <asm/desc.h>
 
 extern void die(const char *,struct pt_regs *,long);
 
-static ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
-
-int register_page_fault_notifier(struct notifier_block *nb)
+#ifdef CONFIG_KPROBES
+static inline int notify_page_fault(struct pt_regs *regs)
 {
-       vmalloc_sync_all();
-       return atomic_notifier_chain_register(&notify_page_fault_chain, nb);
-}
-EXPORT_SYMBOL_GPL(register_page_fault_notifier);
+       int ret = 0;
+
+       /* kprobe_running() needs smp_processor_id() */
+       if (!user_mode_vm(regs)) {
+               preempt_disable();
+               if (kprobe_running() && kprobe_fault_handler(regs, 14))
+                       ret = 1;
+               preempt_enable();
+       }
 
-int unregister_page_fault_notifier(struct notifier_block *nb)
-{
-       return atomic_notifier_chain_unregister(&notify_page_fault_chain, nb);
+       return ret;
 }
-EXPORT_SYMBOL_GPL(unregister_page_fault_notifier);
-
-static inline int notify_page_fault(struct pt_regs *regs, long err)
+#else
+static inline int notify_page_fault(struct pt_regs *regs)
 {
-       struct die_args args = {
-               .regs = regs,
-               .str = "page fault",
-               .err = err,
-               .trapnr = 14,
-               .signr = SIGSEGV
-       };
-       return atomic_notifier_call_chain(&notify_page_fault_chain,
-                                         DIE_PAGE_FAULT, &args);
+       return 0;
 }
+#endif
 
 /*
  * Return EIP plus the CS segment base.  The segment limit is also
@@ -331,7 +326,7 @@ fastcall void __kprobes do_page_fault(struct pt_regs *regs,
        if (unlikely(address >= TASK_SIZE)) {
                if (!(error_code & 0x0000000d) && vmalloc_fault(address) >= 0)
                        return;
-               if (notify_page_fault(regs, error_code) == NOTIFY_STOP)
+               if (notify_page_fault(regs))
                        return;
                /*
                 * Don't take the mm semaphore here. If we fixup a prefetch
@@ -340,7 +335,7 @@ fastcall void __kprobes do_page_fault(struct pt_regs *regs,
                goto bad_area_nosemaphore;
        }
 
-       if (notify_page_fault(regs, error_code) == NOTIFY_STOP)
+       if (notify_page_fault(regs))
                return;
 
        /* It's safe to allow irq's after cr2 has been saved and the vmalloc
index 54816adb8e933c8c09e41afcf3008331afb5adc6..5e0e54906c488fe5b7266d013c072672a606ba29 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/kprobes.h>
 #include <linux/uaccess.h>
 #include <linux/kdebug.h>
+#include <linux/kprobes.h>
 
 #include <asm/system.h>
 #include <asm/pgalloc.h>
 #define PF_RSVD        (1<<3)
 #define PF_INSTR       (1<<4)
 
-static ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
-
-/* Hook to register for page fault notifications */
-int register_page_fault_notifier(struct notifier_block *nb)
+#ifdef CONFIG_KPROBES
+static inline int notify_page_fault(struct pt_regs *regs)
 {
-       vmalloc_sync_all();
-       return atomic_notifier_chain_register(&notify_page_fault_chain, nb);
-}
-EXPORT_SYMBOL_GPL(register_page_fault_notifier);
+       int ret = 0;
+
+       /* kprobe_running() needs smp_processor_id() */
+       if (!user_mode(regs)) {
+               preempt_disable();
+               if (kprobe_running() && kprobe_fault_handler(regs, 14))
+                       ret = 1;
+               preempt_enable();
+       }
 
-int unregister_page_fault_notifier(struct notifier_block *nb)
-{
-       return atomic_notifier_chain_unregister(&notify_page_fault_chain, nb);
+       return ret;
 }
-EXPORT_SYMBOL_GPL(unregister_page_fault_notifier);
-
-static inline int notify_page_fault(struct pt_regs *regs, long err)
+#else
+static inline int notify_page_fault(struct pt_regs *regs)
 {
-       struct die_args args = {
-               .regs = regs,
-               .str = "page fault",
-               .err = err,
-               .trapnr = 14,
-               .signr = SIGSEGV
-       };
-       return atomic_notifier_call_chain(&notify_page_fault_chain,
-                                         DIE_PAGE_FAULT, &args);
+       return 0;
 }
+#endif
 
 /* Sometimes the CPU reports invalid exceptions on prefetch.
    Check that here and ignore.
@@ -345,7 +339,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
                        if (vmalloc_fault(address) >= 0)
                                return;
                }
-               if (notify_page_fault(regs, error_code) == NOTIFY_STOP)
+               if (notify_page_fault(regs))
                        return;
                /*
                 * Don't take the mm semaphore here. If we fixup a prefetch
@@ -354,7 +348,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
                goto bad_area_nosemaphore;
        }
 
-       if (notify_page_fault(regs, error_code) == NOTIFY_STOP)
+       if (notify_page_fault(regs))
                return;
 
        if (likely(regs->eflags & X86_EFLAGS_IF))
index 7f54e2b15d136ab5e2ee2234ef49fd0700ab2473..fd7e99046b2f366be239078692c957381c223bc6 100644 (file)
@@ -1,26 +1,10 @@
 #ifndef __ASM_AVR32_KDEBUG_H
 #define __ASM_AVR32_KDEBUG_H
 
-#include <linux/notifier.h>
-
 /* Grossly misnamed. */
 enum die_val {
        DIE_BREAKPOINT,
        DIE_SSTEP,
 };
 
-/*
- * These are only here because kprobes.c wants them to implement a
- * blatant layering violation.  Will hopefully go away soon once all
- * architectures are updated.
- */
-static inline int register_page_fault_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
-static inline int unregister_page_fault_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
-
 #endif /* __ASM_AVR32_KDEBUG_H */
index 190a6377c80969f7b5ef5313457b42fec539a99d..0f3e636e6e4dda8d36c89b50f697777f59bdf88d 100644 (file)
@@ -17,8 +17,6 @@ typedef u16   kprobe_opcode_t;
 #define BREAKPOINT_INSTRUCTION 0xd673  /* breakpoint */
 #define MAX_INSN_SIZE          2
 
-#define ARCH_INACTIVE_KPROBE_COUNT 1
-
 #define arch_remove_kprobe(p)  do { } while (0)
 
 /* Architecture specific copy of original instruction */
index 320cd8e754ea44a9841e874251f2e7113721e84b..35e49407d06c6bdada5952abca95a9a45786c23d 100644 (file)
  * 2005-Oct    Keith Owens <kaos@sgi.com>.  Expand notify_die to cover more
  *             events.
  */
-#include <linux/notifier.h>
-
-/*
- * These are only here because kprobes.c wants them to implement a
- * blatant layering violation.  Will hopefully go away soon once all
- * architectures are updated.
- */
-static inline int register_page_fault_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
-static inline int unregister_page_fault_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
 
 enum die_val {
        DIE_BREAK = 1,
index 067d9dea68f9d627f41ba634e7169185bafcf5e4..6c79edf24d73adc1572b885417394bcb6c02ae13 100644 (file)
@@ -83,7 +83,6 @@ struct kprobe_ctlblk {
 };
 
 #define ARCH_SUPPORTS_KRETPROBES
-#define  ARCH_INACTIVE_KPROBE_COUNT 1
 
 #define SLOT0_OPCODE_SHIFT     (37)
 #define SLOT1_p1_OPCODE_SHIFT  (37 - (64-46))
index 295f0162c60827a750664b9ece653d134f1a277f..ae6d206728afc476d015b15ee2a9f5e2588f467e 100644 (file)
@@ -2,25 +2,6 @@
 #define _ASM_POWERPC_KDEBUG_H
 #ifdef __KERNEL__
 
-/* nearly identical to x86_64/i386 code */
-
-#include <linux/notifier.h>
-
-/*
- * These are only here because kprobes.c wants them to implement a
- * blatant layering violation.  Will hopefully go away soon once all
- * architectures are updated.
- */
-static inline int register_page_fault_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
-static inline int unregister_page_fault_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
-extern struct atomic_notifier_head powerpc_die_chain;
-
 /* Grossly misnamed. */
 enum die_val {
        DIE_OOPS = 1,
index 8b08b447d6f31e2e300e39b3813dd090b7c2233f..c16973d5de6287dc4c14d1572065d747c719416e 100644 (file)
@@ -81,7 +81,6 @@ typedef unsigned int kprobe_opcode_t;
 #endif
 
 #define ARCH_SUPPORTS_KRETPROBES
-#define  ARCH_INACTIVE_KPROBE_COUNT 1
 #define flush_insn_slot(p)     do { } while (0)
 
 void kretprobe_trampoline(void);
index 04418af08f8511a6eea7c1d0ad09e04b49979e5e..40db27cd6e608df9c54285c6e1b52c6d572277f4 100644 (file)
@@ -4,24 +4,9 @@
 /*
  * Feb 2006 Ported to s390 <grundym@us.ibm.com>
  */
-#include <linux/notifier.h>
 
 struct pt_regs;
 
-/*
- * These are only here because kprobes.c wants them to implement a
- * blatant layering violation. Will hopefully go away soon once all
- * architectures are updated.
- */
-static inline int register_page_fault_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
-static inline int unregister_page_fault_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
-
 enum die_val {
        DIE_OOPS = 1,
        DIE_BPT,
index 340ba10446ea46d2aeca453903532bc537627610..8bc67cc9ffd2972a64f9da56564892e54d48d7c5 100644 (file)
@@ -47,7 +47,6 @@ typedef u16 kprobe_opcode_t;
        : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
 
 #define ARCH_SUPPORTS_KRETPROBES
-#define ARCH_INACTIVE_KPROBE_COUNT 0
 
 #define KPROBE_SWAP_INST       0x10
 
index 382cfc7deb732b233170b3e1ec69edb6d14abb17..49cd69051a8896392fa1c9335c1770c802d1aa15 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef __ASM_SH_KDEBUG_H
 #define __ASM_SH_KDEBUG_H
 
-#include <linux/notifier.h>
-
 /* Grossly misnamed. */
 enum die_val {
        DIE_TRAP,
index 9974c7b0aebc18563568cd50a5b5c2c457e5b908..f905b773235a2f977db3383559629d4fbd3e4fda 100644 (file)
@@ -1,26 +1,8 @@
 #ifndef _SPARC64_KDEBUG_H
 #define _SPARC64_KDEBUG_H
 
-/* Nearly identical to x86_64/i386 code. */
-
-#include <linux/notifier.h>
-
 struct pt_regs;
 
-/*
- * These are only here because kprobes.c wants them to implement a
- * blatant layering violation.  Will hopefully go away soon once all
- * architectures are updated.
- */
-static inline int register_page_fault_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
-static inline int unregister_page_fault_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
-
 extern void bad_trap(struct pt_regs *, long);
 
 /* Grossly misnamed. */
index 7f6774dca5f49f778c434c055ab7355e3711a4c4..a04145b77f9635e8a7a30504bef7f1b2cfdcc1e1 100644 (file)
@@ -11,7 +11,6 @@ typedef u32 kprobe_opcode_t;
 #define MAX_INSN_SIZE 2
 
 #define arch_remove_kprobe(p)  do {} while (0)
-#define  ARCH_INACTIVE_KPROBE_COUNT 0
 
 #define flush_insn_slot(p)             \
 do {   flushi(&(p)->ainsn.insn[0]);    \
index a185b5f73e7ff466322cbaa8c8b9743925cce11d..181d437eef4bd059e320155d1a39dab15d15c649 100644 (file)
@@ -5,14 +5,9 @@
  * Aug-05 2004 Ported by Prasanna S Panchamukhi <prasanna@in.ibm.com>
  * from x86_64 architecture.
  */
-#include <linux/notifier.h>
 
 struct pt_regs;
 
-extern int register_page_fault_notifier(struct notifier_block *);
-extern int unregister_page_fault_notifier(struct notifier_block *);
-
-
 /* Grossly misnamed. */
 enum die_val {
        DIE_OOPS = 1,
@@ -27,7 +22,6 @@ enum die_val {
        DIE_GPF,
        DIE_CALL,
        DIE_NMI_IPI,
-       DIE_PAGE_FAULT,
 };
 
 #endif
index d7e2bcf49e4f0ee5ab826b8a2108c2f03f0d0b43..df413e05375e7f6b927c6128c38442d8c3474c56 100644 (file)
@@ -1,13 +1,10 @@
 #ifndef _X86_64_KDEBUG_H
 #define _X86_64_KDEBUG_H 1
 
-#include <linux/notifier.h>
+#include <linux/compiler.h>
 
 struct pt_regs;
 
-extern int register_page_fault_notifier(struct notifier_block *);
-extern int unregister_page_fault_notifier(struct notifier_block *);
-
 /* Grossly misnamed. */
 enum die_val {
        DIE_OOPS = 1,
@@ -22,7 +19,6 @@ enum die_val {
        DIE_GPF,
        DIE_CALL,
        DIE_NMI_IPI,
-       DIE_PAGE_FAULT,
 };
 
 extern void printk_address(unsigned long address);
index 06f7303c30ca15d111061cfcace36ec043b9362d..f2489d07ce88cdef0dec6d5d5d605a0d84a3c4f0 100644 (file)
@@ -43,7 +43,6 @@ typedef u8 kprobe_opcode_t;
        : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
 
 #define ARCH_SUPPORTS_KRETPROBES
-#define  ARCH_INACTIVE_KPROBE_COUNT 0
 #define flush_insn_slot(p)     do { } while (0)
 
 void arch_remove_kprobe(struct kprobe *p);
@@ -89,4 +88,5 @@ static inline void restore_interrupts(struct pt_regs *regs)
 
 extern int kprobe_exceptions_notify(struct notifier_block *self,
                                    unsigned long val, void *data);
+extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
 #endif                         /* _ASM_KPROBES_H */
index 7db825403e013129655fab261035b1391944c888..3f495e5308b13499f4d7122e2bb90510bc293417 100644 (file)
@@ -42,7 +42,6 @@ typedef u8 kprobe_opcode_t;
        : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
 
 #define ARCH_SUPPORTS_KRETPROBES
-#define  ARCH_INACTIVE_KPROBE_COUNT 1
 
 void kretprobe_trampoline(void);
 extern void arch_remove_kprobe(struct kprobe *p);
index 4b8a4493c541873a946e9d62161459fa4f94c35c..f9798ff7899f4a610823fd641fe5e2dde0e746f7 100644 (file)
@@ -64,7 +64,6 @@
 
 static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE];
 static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
-static atomic_t kprobe_count;
 
 /* NOTE: change this value only with kprobe_mutex held */
 static bool kprobe_enabled;
@@ -73,11 +72,6 @@ DEFINE_MUTEX(kprobe_mutex);          /* Protects kprobe_table */
 DEFINE_SPINLOCK(kretprobe_lock);       /* Protects kretprobe_inst_table */
 static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL;
 
-static struct notifier_block kprobe_page_fault_nb = {
-       .notifier_call = kprobe_exceptions_notify,
-       .priority = 0x7fffffff /* we need to notified first */
-};
-
 #ifdef __ARCH_WANT_KPROBES_INSN_SLOT
 /*
  * kprobe->ainsn.insn points to the copy of the instruction to be
@@ -556,8 +550,6 @@ static int __kprobes __register_kprobe(struct kprobe *p,
        old_p = get_kprobe(p->addr);
        if (old_p) {
                ret = register_aggr_kprobe(old_p, p);
-               if (!ret)
-                       atomic_inc(&kprobe_count);
                goto out;
        }
 
@@ -569,13 +561,9 @@ static int __kprobes __register_kprobe(struct kprobe *p,
        hlist_add_head_rcu(&p->hlist,
                       &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
 
-       if (kprobe_enabled) {
-               if (atomic_add_return(1, &kprobe_count) == \
-                               (ARCH_INACTIVE_KPROBE_COUNT + 1))
-                       register_page_fault_notifier(&kprobe_page_fault_nb);
-
+       if (kprobe_enabled)
                arch_arm_kprobe(p);
-       }
+
 out:
        mutex_unlock(&kprobe_mutex);
 
@@ -658,16 +646,6 @@ valid_p:
                }
                mutex_unlock(&kprobe_mutex);
        }
-
-       /* Call unregister_page_fault_notifier()
-        * if no probes are active
-        */
-       mutex_lock(&kprobe_mutex);
-       if (atomic_add_return(-1, &kprobe_count) == \
-                               ARCH_INACTIVE_KPROBE_COUNT)
-               unregister_page_fault_notifier(&kprobe_page_fault_nb);
-       mutex_unlock(&kprobe_mutex);
-       return;
 }
 
 static struct notifier_block kprobe_exceptions_nb = {
@@ -815,7 +793,6 @@ static int __init init_kprobes(void)
                INIT_HLIST_HEAD(&kprobe_table[i]);
                INIT_HLIST_HEAD(&kretprobe_inst_table[i]);
        }
-       atomic_set(&kprobe_count, 0);
 
        /* By default, kprobes are enabled */
        kprobe_enabled = true;
@@ -921,13 +898,6 @@ static void __kprobes enable_all_kprobes(void)
        if (kprobe_enabled)
                goto already_enabled;
 
-       /*
-        * Re-register the page fault notifier only if there are any
-        * active probes at the time of enabling kprobes globally
-        */
-       if (atomic_read(&kprobe_count) > ARCH_INACTIVE_KPROBE_COUNT)
-               register_page_fault_notifier(&kprobe_page_fault_nb);
-
        for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
                head = &kprobe_table[i];
                hlist_for_each_entry_rcu(p, node, head, hlist)
@@ -968,10 +938,7 @@ static void __kprobes disable_all_kprobes(void)
        mutex_unlock(&kprobe_mutex);
        /* Allow all currently running kprobes to complete */
        synchronize_sched();
-
-       mutex_lock(&kprobe_mutex);
-       /* Unconditionally unregister the page_fault notifier */
-       unregister_page_fault_notifier(&kprobe_page_fault_nb);
+       return;
 
 already_disabled:
        mutex_unlock(&kprobe_mutex);