xfs: remove nr_to_write writeback windup.
[safe/jmp/linux-2.6] / kernel / debug / debug_core.c
index 375e42f..5cb7cd1 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;
 
@@ -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;
@@ -444,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");
 
@@ -488,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:
@@ -754,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
@@ -778,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);
@@ -789,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;