]> err.no Git - linux-2.6/commitdiff
x86: add a list for custom page fault handlers.
authorPekka Paalanen <pq@iki.fi>
Mon, 12 May 2008 19:20:56 +0000 (21:20 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 23 May 2008 19:16:38 +0000 (21:16 +0200)
Provides kernel modules a way to register custom page fault handlers.
On every page fault this will call a list of registered functions. The
functions may handle the fault and force do_page_fault() to return
immediately.

This functionality is similar to the now removed page fault notifiers.
Custom page fault handlers are used by debugging and reverse engineering
tools. Mmiotrace is one such tool and a patch to add it into the tree
will follow.

The custom page fault handlers are called earlier in do_page_fault()
than the page fault notifiers were.

Signed-off-by: Pekka Paalanen <pq@iki.fi>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/Kconfig.debug
arch/x86/mm/fault.c
include/asm-x86/kdebug.h

index ac1e31ba4795cb0f067a280826da257432f93f33..9431a8399844e1d96c235bdb6ca7821b93eea914 100644 (file)
@@ -168,6 +168,14 @@ config IOMMU_LEAK
          Add a simple leak tracer to the IOMMU code. This is useful when you
          are debugging a buggy device driver that leaks IOMMU mappings.
 
+config PAGE_FAULT_HANDLERS
+       bool "Custom page fault handlers"
+       depends on DEBUG_KERNEL
+       help
+         Allow the use of custom page fault handlers. A kernel module may
+         register a function that is called on every page fault. Custom
+         handlers are used by some debugging and reverse engineering tools.
+
 #
 # IO delay types:
 #
index fd7e1798c75a20049a97046db333868eb4bbd6c5..343f5c1aacc89e63417ff6b42dd8d3c2f95a3e43 100644 (file)
 #define PF_RSVD                (1<<3)
 #define PF_INSTR       (1<<4)
 
+#ifdef CONFIG_PAGE_FAULT_HANDLERS
+static HLIST_HEAD(pf_handlers); /* protected by RCU */
+static DEFINE_SPINLOCK(pf_handlers_writer);
+
+void register_page_fault_handler(struct pf_handler *new_pfh)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&pf_handlers_writer, flags);
+       hlist_add_head_rcu(&new_pfh->hlist, &pf_handlers);
+       spin_unlock_irqrestore(&pf_handlers_writer, flags);
+}
+EXPORT_SYMBOL_GPL(register_page_fault_handler);
+
+/**
+ * unregister_page_fault_handler:
+ * The caller must ensure @old_pfh is not in use anymore before freeing it.
+ * This function does not guarantee it. The list of handlers is protected by
+ * RCU, so you can do this by e.g. calling synchronize_rcu().
+ */
+void unregister_page_fault_handler(struct pf_handler *old_pfh)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&pf_handlers_writer, flags);
+       hlist_del_rcu(&old_pfh->hlist);
+       spin_unlock_irqrestore(&pf_handlers_writer, flags);
+}
+EXPORT_SYMBOL_GPL(unregister_page_fault_handler);
+#endif
+
+/* returns non-zero if do_page_fault() should return */
+static int handle_custom_pf(struct pt_regs *regs, unsigned long error_code,
+                                                       unsigned long address)
+{
+#ifdef CONFIG_PAGE_FAULT_HANDLERS
+       int ret = 0;
+       struct pf_handler *cur;
+       struct hlist_node *ncur;
+
+       if (hlist_empty(&pf_handlers))
+               return 0;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(cur, ncur, &pf_handlers, hlist) {
+               ret = cur->handler(regs, error_code, address);
+               if (ret)
+                       break;
+       }
+       rcu_read_unlock();
+       return ret;
+#else
+       return 0;
+#endif
+}
+
 static inline int notify_page_fault(struct pt_regs *regs)
 {
 #ifdef CONFIG_KPROBES
@@ -601,6 +655,8 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code)
 
        if (notify_page_fault(regs))
                return;
+       if (handle_custom_pf(regs, error_code, address))
+               return;
 
        /*
         * We fault-in kernel-space virtual memory on-demand. The
index 96651bb59ba18c6f8506d985f9c98a757617a338..a80f2d6cc737684b0b3078febb88cd4b430ea284 100644 (file)
@@ -35,4 +35,13 @@ extern void show_regs(struct pt_regs *regs);
 extern unsigned long oops_begin(void);
 extern void oops_end(unsigned long, struct pt_regs *, int signr);
 
+struct pf_handler {
+       struct hlist_node hlist;
+       int (*handler)(struct pt_regs *regs, unsigned long error_code,
+                                               unsigned long address);
+};
+
+extern void register_page_fault_handler(struct pf_handler *new_pfh);
+extern void unregister_page_fault_handler(struct pf_handler *old_pfh);
+
 #endif