]> Pileus Git - ~andy/linux/blobdiff - kernel/debug/debug_core.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[~andy/linux] / kernel / debug / debug_core.c
index 1aed37b4c564c843fa2d92a145255de45188200f..5cb7cd1de10c7dd5b3380f69d49854cb539a4011 100644 (file)
@@ -66,7 +66,7 @@ int                           kgdb_connected;
 EXPORT_SYMBOL_GPL(kgdb_connected);
 
 /* All the KGDB handlers are installed */
-static int                     kgdb_io_module_registered;
+int                    kgdb_io_module_registered;
 
 /* Guard for recursive entry */
 static int                     exception_level;
@@ -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;
 
@@ -114,6 +116,7 @@ EXPORT_SYMBOL_GPL(kgdb_active);
  */
 static atomic_t                        passive_cpu_wait[NR_CPUS];
 static atomic_t                        cpu_in_kgdb[NR_CPUS];
+static atomic_t                        kgdb_break_tasklet_var;
 atomic_t                       kgdb_setting_breakpoint;
 
 struct task_struct             *kgdb_usethread;
@@ -203,12 +206,6 @@ int __weak kgdb_skipexception(int exception, struct pt_regs *regs)
        return 0;
 }
 
-void __weak
-kgdb_post_primary_code(struct pt_regs *regs, int e_vector, int err_code)
-{
-       return;
-}
-
 /**
  *     kgdb_disable_hw_debug - Disable hardware debugging while we in kgdb.
  *     @regs: Current &struct pt_regs.
@@ -450,6 +447,10 @@ static int kgdb_reenter_check(struct kgdb_state *ks)
        }
 
        printk(KERN_CRIT "KGDB: re-enter exception: ALL breakpoints killed\n");
+#ifdef CONFIG_KGDB_KDB
+       /* Allow kdb to debug itself one level */
+       return 0;
+#endif
        dump_stack();
        panic("Recursive entry to debugger");
 
@@ -494,6 +495,9 @@ acquirelock:
         */
        atomic_inc(&cpu_in_kgdb[cpu]);
 
+       if (exception_level == 1)
+               goto cpu_master_loop;
+
        /*
         * CPU will loop if it is a slave or request to become a kgdb
         * master cpu and acquire the kgdb_active lock:
@@ -588,7 +592,6 @@ return_normal:
         * At this point the primary processor is completely
         * in the debugger and all secondary CPUs are quiescent
         */
-       kgdb_post_primary_code(ks->linux_regs, ks->ex_vector, ks->err_code);
        dbg_deactivate_sw_breakpoints();
        kgdb_single_step = 0;
        kgdb_contthread = current;
@@ -678,7 +681,6 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
        ks->cpu                 = raw_smp_processor_id();
        ks->ex_vector           = evector;
        ks->signo               = signo;
-       ks->ex_vector           = evector;
        ks->err_code            = ecode;
        ks->kgdb_usethreadid    = 0;
        ks->linux_regs          = regs;
@@ -762,11 +764,42 @@ static struct sysrq_key_op sysrq_dbg_op = {
 };
 #endif
 
+static int kgdb_panic_event(struct notifier_block *self,
+                           unsigned long val,
+                           void *data)
+{
+       if (dbg_kdb_mode)
+               kdb_printf("PANIC: %s\n", (char *)data);
+       kgdb_breakpoint();
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block kgdb_panic_event_nb = {
+       .notifier_call  = kgdb_panic_event,
+       .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
                register_sysrq_key('g', &sysrq_dbg_op);
 #endif
@@ -786,6 +819,8 @@ static void kgdb_unregister_callbacks(void)
         */
        if (kgdb_io_module_registered) {
                kgdb_io_module_registered = 0;
+               atomic_notifier_chain_unregister(&panic_notifier_list,
+                                              &kgdb_panic_event_nb);
                kgdb_arch_exit();
 #ifdef CONFIG_MAGIC_SYSRQ
                unregister_sysrq_key('g', &sysrq_dbg_op);
@@ -797,6 +832,31 @@ static void kgdb_unregister_callbacks(void)
        }
 }
 
+/*
+ * There are times a tasklet needs to be used vs a compiled in
+ * break point so as to cause an exception outside a kgdb I/O module,
+ * such as is the case with kgdboe, where calling a breakpoint in the
+ * I/O driver itself would be fatal.
+ */
+static void kgdb_tasklet_bpt(unsigned long ing)
+{
+       kgdb_breakpoint();
+       atomic_set(&kgdb_break_tasklet_var, 0);
+}
+
+static DECLARE_TASKLET(kgdb_tasklet_breakpoint, kgdb_tasklet_bpt, 0);
+
+void kgdb_schedule_breakpoint(void)
+{
+       if (atomic_read(&kgdb_break_tasklet_var) ||
+               atomic_read(&kgdb_active) != -1 ||
+               atomic_read(&kgdb_setting_breakpoint))
+               return;
+       atomic_inc(&kgdb_break_tasklet_var);
+       tasklet_schedule(&kgdb_tasklet_breakpoint);
+}
+EXPORT_SYMBOL_GPL(kgdb_schedule_breakpoint);
+
 static void kgdb_initial_breakpoint(void)
 {
        kgdb_break_asap = 0;