|
@@ -114,6 +114,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;
|
|
@@ -789,6 +790,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;
|