x86, kgdb, init: Add early and late debug states
authorJason Wessel <jason.wessel@windriver.com>
Fri, 21 May 2010 02:04:29 +0000 (21:04 -0500)
committerJason Wessel <jason.wessel@windriver.com>
Fri, 21 May 2010 02:04:29 +0000 (21:04 -0500)
The kernel debugger can operate well before mm_init(), but the x86
hardware breakpoint code which uses the perf api requires that the
kernel allocators are initialized.

This means the kernel debug core needs to provide an optional arch
specific call back to allow the initialization functions to run after
the kernel has been further initialized.

The kdb shell already had a similar restriction with an early
initialization and late initialization.  The kdb_init() was moved into
the debug core's version of the late init which is called
dbg_late_init();

CC: kgdb-bugreport@lists.sourceforge.net
Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
arch/x86/kernel/kgdb.c
include/linux/kgdb.h
init/main.c
kernel/debug/debug_core.c

index 95b89d4cb8f18f1595a430e0e6eaf8ac5f513487..2b71ec41869ff5df2a6e2cd99d594e7698ec311f 100644 (file)
@@ -595,15 +595,16 @@ static struct notifier_block kgdb_notifier = {
  *     specific callbacks.
  */
 int kgdb_arch_init(void)
+{
+       return register_die_notifier(&kgdb_notifier);
+}
+
+void kgdb_arch_late(void)
 {
        int i, cpu;
-       int ret;
        struct perf_event_attr attr;
        struct perf_event **pevent;
 
-       ret = register_die_notifier(&kgdb_notifier);
-       if (ret != 0)
-               return ret;
        /*
         * Pre-allocate the hw breakpoint structions in the non-atomic
         * portion of kgdb because this operation requires mutexs to
@@ -615,12 +616,15 @@ int kgdb_arch_init(void)
        attr.bp_type = HW_BREAKPOINT_W;
        attr.disabled = 1;
        for (i = 0; i < 4; i++) {
+               if (breakinfo[i].pev)
+                       continue;
                breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL);
                if (IS_ERR(breakinfo[i].pev)) {
-                       printk(KERN_ERR "kgdb: Could not allocate hw breakpoints\n");
+                       printk(KERN_ERR "kgdb: Could not allocate hw"
+                              "breakpoints\nDisabling the kernel debugger\n");
                        breakinfo[i].pev = NULL;
                        kgdb_arch_exit();
-                       return -1;
+                       return;
                }
                for_each_online_cpu(cpu) {
                        pevent = per_cpu_ptr(breakinfo[i].pev, cpu);
@@ -631,7 +635,6 @@ int kgdb_arch_init(void)
                        }
                }
        }
-       return ret;
 }
 
 /**
index 6c784ab6856ac006803c52d7d064595262caf345..9340f34d1bb5e7af04ad247d119d341e9d5cd0a6 100644 (file)
@@ -207,6 +207,17 @@ extern int kgdb_validate_break_address(unsigned long addr);
 extern int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr);
 extern int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle);
 
+/**
+ *     kgdb_arch_late - Perform any architecture specific initalization.
+ *
+ *     This function will handle the late initalization of any
+ *     architecture specific callbacks.  This is an optional function for
+ *     handling things like late initialization of hw breakpoints.  The
+ *     default implementation does nothing.
+ */
+extern void kgdb_arch_late(void);
+
+
 /**
  * struct kgdb_arch - Describe architecture specific values.
  * @gdb_bpt_instr: The instruction to trigger a breakpoint.
@@ -285,7 +296,10 @@ extern int                 kgdb_single_step;
 extern atomic_t                        kgdb_active;
 #define in_dbg_master() \
        (raw_smp_processor_id() == atomic_read(&kgdb_active))
+extern bool dbg_is_early;
+extern void __init dbg_late_init(void);
 #else /* ! CONFIG_KGDB */
 #define in_dbg_master() (0)
+#define dbg_late_init()
 #endif /* ! CONFIG_KGDB */
 #endif /* _KGDB_H_ */
index 372771333d983d1eb692032ac47db70ef080924c..22881b5e95e3b00e010b407310539fbd4e1a8a57 100644 (file)
@@ -62,7 +62,7 @@
 #include <linux/sched.h>
 #include <linux/signal.h>
 #include <linux/idr.h>
-#include <linux/kdb.h>
+#include <linux/kgdb.h>
 #include <linux/ftrace.h>
 #include <linux/async.h>
 #include <linux/kmemcheck.h>
@@ -676,7 +676,7 @@ asmlinkage void __init start_kernel(void)
        buffer_init();
        key_init();
        security_init();
-       kdb_init(KDB_INIT_FULL);
+       dbg_late_init();
        vfs_caches_init(totalram_pages);
        signals_init();
        /* rootfs populating might need page-writeback */
index 64b5588c96389228c01dca2932c0fb9625ca18a8..5cb7cd1de10c7dd5b3380f69d49854cb539a4011 100644 (file)
@@ -78,6 +78,8 @@ static DEFINE_SPINLOCK(kgdb_registration_lock);
 static int kgdb_con_registered;
 /* determine if kgdb console output should be used */
 static int kgdb_use_con;
+/* Flag for alternate operations for early debugging */
+bool dbg_is_early = true;
 /* Next cpu to become the master debug core */
 int dbg_switch_cpu;
 
@@ -777,11 +779,25 @@ static struct notifier_block kgdb_panic_event_nb = {
        .priority       = INT_MAX,
 };
 
+void __weak kgdb_arch_late(void)
+{
+}
+
+void __init dbg_late_init(void)
+{
+       dbg_is_early = false;
+       if (kgdb_io_module_registered)
+               kgdb_arch_late();
+       kdb_init(KDB_INIT_FULL);
+}
+
 static void kgdb_register_callbacks(void)
 {
        if (!kgdb_io_module_registered) {
                kgdb_io_module_registered = 1;
                kgdb_arch_init();
+               if (!dbg_is_early)
+                       kgdb_arch_late();
                atomic_notifier_chain_register(&panic_notifier_list,
                                               &kgdb_panic_event_nb);
 #ifdef CONFIG_MAGIC_SYSRQ