diff mbox

[PATCHv3,3/3] KGDB: Allow registering multiple I/O ops.

Message ID 1330313411-845-4-git-send-email-andrey.warkentin@gmail.com
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

Andrei Warkentin Feb. 27, 2012, 3:30 a.m. UTC
This allows multiple I/O ops, which is useful, if you want
to be able to support debugging say both via console and
via network (or 1394, dbgp, etc.)

Tested with kgdboc and netkgdb.

Signed-off-by: Andrei Warkentin <andrey.warkentin@gmail.com>
---
 drivers/usb/early/ehci-dbgp.c |    4 +-
 include/linux/kgdb.h          |    6 ++-
 kernel/debug/debug_core.c     |  130 +++++++++++++++++++++++++++--------------
 kernel/debug/gdbstub.c        |   43 ++++++-------
 kernel/debug/kdb/kdb_io.c     |   26 ++++-----
 5 files changed, 125 insertions(+), 84 deletions(-)
diff mbox

Patch

diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c
index 1fc8f12..e5db14a 100644
--- a/drivers/usb/early/ehci-dbgp.c
+++ b/drivers/usb/early/ehci-dbgp.c
@@ -97,7 +97,8 @@  static inline u32 dbgp_len_update(u32 x, u32 len)
 
 #ifdef CONFIG_KGDB
 static struct kgdb_io kgdbdbgp_io_ops;
-#define dbgp_kgdb_mode (dbg_io_ops == &kgdbdbgp_io_ops)
+static int kgdb_registered = 0;
+#define dbgp_kgdb_mode (kgdb_registered)
 #else
 #define dbgp_kgdb_mode (0)
 #endif
@@ -1051,6 +1052,7 @@  static int __init kgdbdbgp_parse_config(char *str)
 		kgdbdbgp_wait_time = simple_strtoul(ptr, &ptr, 10);
 	}
 	kgdb_register_io_module(&kgdbdbgp_io_ops);
+	kgdb_registered = 1;
 	kgdbdbgp_io_ops.is_console = early_dbgp_console.index != -1;
 
 	return 0;
diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h
index fa39183..c92cd30 100644
--- a/include/linux/kgdb.h
+++ b/include/linux/kgdb.h
@@ -17,6 +17,7 @@ 
 #include <linux/linkage.h>
 #include <linux/init.h>
 #include <linux/atomic.h>
+#include <linux/list.h>
 #ifdef CONFIG_HAVE_ARCH_KGDB
 #include <asm/kgdb.h>
 #endif
@@ -276,6 +277,7 @@  struct kgdb_io {
 	void			(*pre_exception) (void);
 	void			(*post_exception) (void);
 	int			is_console;
+	struct list_head	list;
 };
 
 extern struct kgdb_arch		arch_kgdb_ops;
@@ -284,7 +286,9 @@  extern unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs);
 
 extern int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_ops);
 extern void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops);
-extern struct kgdb_io *dbg_io_ops;
+extern int dbg_io_get_char(void);
+extern void dbg_io_put_char(u8, bool);
+extern void dbg_io_flush(void);
 
 extern int kgdb_hex2long(char **ptr, unsigned long *long_val);
 extern char *kgdb_mem2hex(char *mem, char *buf, int count);
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c
index 0d7c087..bd29f92 100644
--- a/kernel/debug/debug_core.c
+++ b/kernel/debug/debug_core.c
@@ -72,8 +72,8 @@  int			kgdb_io_module_registered;
 /* Guard for recursive entry */
 static int			exception_level;
 
-struct kgdb_io		*dbg_io_ops;
-static DEFINE_SPINLOCK(kgdb_registration_lock);
+static DEFINE_MUTEX(kgdb_registration_lock);
+static LIST_HEAD(dbg_io_list);
 
 /* kgdb console driver is loaded */
 static int kgdb_con_registered;
@@ -384,7 +384,7 @@  setundefined:
  */
 static int kgdb_io_ready(int print_wait)
 {
-	if (!dbg_io_ops)
+	if (list_empty(&dbg_io_list))
 		return 0;
 	if (kgdb_connected)
 		return 1;
@@ -455,6 +455,26 @@  static void dbg_touch_watchdogs(void)
 	rcu_cpu_stall_reset();
 }
 
+void dbg_io_run_pre(void)
+{
+	struct kgdb_io *kio;
+
+	list_for_each_entry(kio, &dbg_io_list, list) {
+		if (kio->pre_exception)
+			kio->pre_exception();
+	}
+}
+
+void dbg_io_run_post(void)
+{
+	struct kgdb_io *kio;
+
+	list_for_each_entry_reverse(kio, &dbg_io_list, list) {
+		if (kio->post_exception)
+			kio->post_exception();
+	}
+}
+
 static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs,
 		int exception_state)
 {
@@ -565,8 +585,7 @@  return_normal:
 		goto kgdb_restore;
 
 	/* Call the I/O driver's pre_exception routine */
-	if (dbg_io_ops->pre_exception)
-		dbg_io_ops->pre_exception();
+	dbg_io_run_pre();
 
 	/*
 	 * Get the passive CPU lock which will hold all the non-primary
@@ -625,8 +644,7 @@  cpu_master_loop:
 	}
 
 	/* Call the I/O driver's post_exception routine */
-	if (dbg_io_ops->post_exception)
-		dbg_io_ops->post_exception();
+	dbg_io_run_post();
 
 	if (!kgdb_single_step) {
 		raw_spin_unlock(&dbg_slave_lock);
@@ -734,7 +752,7 @@  static struct console kgdbcons = {
 #ifdef CONFIG_MAGIC_SYSRQ
 static void sysrq_handle_dbg(int key)
 {
-	if (!dbg_io_ops) {
+	if (list_empty(&dbg_io_list)) {
 		printk(KERN_CRIT "ERROR: No KGDB I/O module available\n");
 		return;
 	}
@@ -867,38 +885,35 @@  static void kgdb_initial_breakpoint(void)
 int kgdb_register_io_module(struct kgdb_io *new_dbg_io_ops)
 {
 	int err;
+	int first;
 
-	spin_lock(&kgdb_registration_lock);
-
-	if (dbg_io_ops) {
-		spin_unlock(&kgdb_registration_lock);
-
-		printk(KERN_ERR "kgdb: Another I/O driver is already "
-				"registered with KGDB.\n");
-		return -EBUSY;
-	}
+	BUG_ON(!new_dbg_io_ops->read_char);
+	BUG_ON(!new_dbg_io_ops->write_char);
+	mutex_lock(&kgdb_registration_lock);
+	first = list_empty(&dbg_io_list);
 
 	if (new_dbg_io_ops->init) {
 		err = new_dbg_io_ops->init();
 		if (err) {
-			spin_unlock(&kgdb_registration_lock);
+			mutex_unlock(&kgdb_registration_lock);
 			return err;
 		}
 	}
 
-	dbg_io_ops = new_dbg_io_ops;
+	list_add(&new_dbg_io_ops->list, &dbg_io_list);
+	if (first) {
+		/* Arm KGDB now. */
+		kgdb_register_callbacks();
 
-	spin_unlock(&kgdb_registration_lock);
+		if (kgdb_break_asap)
+			kgdb_initial_breakpoint();
+	}
+
+	mutex_unlock(&kgdb_registration_lock);
 
 	printk(KERN_INFO "kgdb: Registered I/O driver %s.\n",
 	       new_dbg_io_ops->name);
 
-	/* Arm KGDB now. */
-	kgdb_register_callbacks();
-
-	if (kgdb_break_asap)
-		kgdb_initial_breakpoint();
-
 	return 0;
 }
 EXPORT_SYMBOL_GPL(kgdb_register_io_module);
@@ -913,37 +928,64 @@  void kgdb_unregister_io_module(struct kgdb_io *old_dbg_io_ops)
 {
 	BUG_ON(kgdb_connected);
 
-	/*
-	 * KGDB is no longer able to communicate out, so
-	 * unregister our callbacks and reset state.
-	 */
-	kgdb_unregister_callbacks();
+	mutex_lock(&kgdb_registration_lock);
+	if (list_is_singular(&dbg_io_list)) {
+		/*
+		 * KGDB is no longer able to communicate out, so
+		 * unregister our callbacks and reset state.
+		 */
+		kgdb_unregister_callbacks();
+		printk(KERN_INFO
+		       "kgdb: debugger disabled.\n");
+	}
 
-	spin_lock(&kgdb_registration_lock);
+	list_del(&old_dbg_io_ops->list);
+	mutex_unlock(&kgdb_registration_lock);
 
-	WARN_ON_ONCE(dbg_io_ops != old_dbg_io_ops);
-	dbg_io_ops = NULL;
+	printk(KERN_INFO
+	       "kgdb: Unregistered I/O driver %s.\n",
+	       old_dbg_io_ops->name);
 
-	spin_unlock(&kgdb_registration_lock);
 
-	printk(KERN_INFO
-		"kgdb: Unregistered I/O driver %s, debugger disabled.\n",
-		old_dbg_io_ops->name);
 }
 EXPORT_SYMBOL_GPL(kgdb_unregister_io_module);
 
 int dbg_io_get_char(void)
 {
-	int ret = dbg_io_ops->read_char();
-	if (ret == NO_POLL_CHAR)
-		return -1;
-	if (!dbg_kdb_mode)
+	struct kgdb_io *kio;
+	int ret = NO_POLL_CHAR;
+
+	list_for_each_entry(kio, &dbg_io_list, list) {
+		ret = kio->read_char();
+		if (ret == NO_POLL_CHAR)
+			continue;
+		if (!dbg_kdb_mode)
+			return ret;
+		if (ret == 127)
+			return 8;
 		return ret;
-	if (ret == 127)
-		return 8;
+	}
 	return ret;
 }
 
+void dbg_io_flush(void)
+{
+	struct kgdb_io *kio;
+
+	list_for_each_entry(kio, &dbg_io_list, list)
+		if (kio->flush)
+			kio->flush();
+}
+
+void dbg_io_put_char(u8 data, bool skip_con)
+{
+	struct kgdb_io *kio;
+
+	list_for_each_entry(kio, &dbg_io_list, list)
+		if (!kio->is_console || !skip_con)
+			kio->write_char(data);
+}
+
 /**
  * kgdb_breakpoint - generate breakpoint exception
  *
diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c
index c22d8c2..cf4fdfd 100644
--- a/kernel/debug/gdbstub.c
+++ b/kernel/debug/gdbstub.c
@@ -79,9 +79,9 @@  static int gdbstub_read_wait(void)
 #else
 static int gdbstub_read_wait(void)
 {
-	int ret = dbg_io_ops->read_char();
+	int ret = dbg_io_get_char();
 	while (ret == NO_POLL_CHAR)
-		ret = dbg_io_ops->read_char();
+		ret = dbg_io_get_char();
 	return ret;
 }
 #endif
@@ -125,12 +125,11 @@  static void get_packet(char *buffer)
 
 			if (checksum != xmitcsum)
 				/* failed checksum */
-				dbg_io_ops->write_char('-');
+				dbg_io_put_char('-', false);
 			else
 				/* successful transfer */
-				dbg_io_ops->write_char('+');
-			if (dbg_io_ops->flush)
-				dbg_io_ops->flush();
+				dbg_io_put_char('+', false);
+			dbg_io_flush();
 		}
 		buffer[count] = 0;
 	} while (checksum != xmitcsum);
@@ -150,21 +149,20 @@  static void put_packet(char *buffer)
 	 * $<packet info>#<checksum>.
 	 */
 	while (1) {
-		dbg_io_ops->write_char('$');
+		dbg_io_put_char('$', false);
 		checksum = 0;
 		count = 0;
 
 		while ((ch = buffer[count])) {
-			dbg_io_ops->write_char(ch);
+			dbg_io_put_char(ch, false);
 			checksum += ch;
 			count++;
 		}
 
-		dbg_io_ops->write_char('#');
-		dbg_io_ops->write_char(hex_asc_hi(checksum));
-		dbg_io_ops->write_char(hex_asc_lo(checksum));
-		if (dbg_io_ops->flush)
-			dbg_io_ops->flush();
+		dbg_io_put_char('#', false);
+		dbg_io_put_char(hex_asc_hi(checksum), false);
+		dbg_io_put_char(hex_asc_lo(checksum), false);
+		dbg_io_flush();
 
 		/* Now see what we get in reply. */
 		ch = gdbstub_read_wait();
@@ -183,9 +181,8 @@  static void put_packet(char *buffer)
 		 * packet.
 		 */
 		if (ch == '$') {
-			dbg_io_ops->write_char('-');
-			if (dbg_io_ops->flush)
-				dbg_io_ops->flush();
+			dbg_io_put_char('-', false);
+			dbg_io_flush();
 			return;
 		}
 	}
@@ -1097,7 +1094,7 @@  int gdbstub_state(struct kgdb_state *ks, char *cmd)
 		gdbstub_prev_in_buf_pos = 0;
 		return 0;
 	}
-	dbg_io_ops->write_char('+');
+	dbg_io_put_char('+', false);
 	put_packet(remcom_out_buffer);
 	return 0;
 }
@@ -1115,19 +1112,19 @@  void gdbstub_exit(int status)
 	buffer[1] = hex_asc_hi(status);
 	buffer[2] = hex_asc_lo(status);
 
-	dbg_io_ops->write_char('$');
+	dbg_io_put_char('$', false);
 	checksum = 0;
 
 	for (loop = 0; loop < 3; loop++) {
 		ch = buffer[loop];
 		checksum += ch;
-		dbg_io_ops->write_char(ch);
+		dbg_io_put_char(ch, false);
 	}
 
-	dbg_io_ops->write_char('#');
-	dbg_io_ops->write_char(hex_asc_hi(checksum));
-	dbg_io_ops->write_char(hex_asc_lo(checksum));
+	dbg_io_put_char('#', false);
+	dbg_io_put_char(hex_asc_hi(checksum), false);
+	dbg_io_put_char(hex_asc_lo(checksum), false);
 
 	/* make sure the output is flushed, lest the bootloader clobber it */
-	dbg_io_ops->flush();
+	dbg_io_flush();
 }
diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index 5eb7e23..35ef3cb 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -689,14 +689,11 @@  kdb_printit:
 	if (!dbg_kdb_mode && kgdb_connected) {
 		gdbstub_msg_write(kdb_buffer, retlen);
 	} else {
-		if (!dbg_io_ops->is_console) {
-			len = strlen(kdb_buffer);
-			cp = kdb_buffer;
-			while (len--) {
-				dbg_io_ops->write_char(*cp);
-				cp++;
-			}
-		}
+		len = strlen(kdb_buffer);
+		cp = kdb_buffer;
+		while (len--)
+			dbg_io_put_char(*cp++, true);
+
 		while (c) {
 			c->write(c, kdb_buffer, retlen);
 			touch_nmi_watchdog();
@@ -743,14 +740,13 @@  kdb_printit:
 		kdb_input_flush();
 		c = console_drivers;
 
-		if (!dbg_io_ops->is_console) {
-			len = strlen(moreprompt);
-			cp = moreprompt;
-			while (len--) {
-				dbg_io_ops->write_char(*cp);
-				cp++;
-			}
+		len = strlen(moreprompt);
+		cp = moreprompt;
+		while (len--) {
+			dbg_io_put_char(*cp, true);
+			cp++;
 		}
+
 		while (c) {
 			c->write(c, moreprompt, strlen(moreprompt));
 			touch_nmi_watchdog();