aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorAndy Green <andy@openmoko.com>2008-11-19 17:09:52 +0000
committerAndy Green <agreen@pads.home.warmcat.com>2008-11-19 17:09:52 +0000
commit6d3fb8064a9336438137d7987bb1e58433528d59 (patch)
tree122c31d6b17d6aec7881bd03cdaec59958b29da1 /kernel
parent7e7270be85be64eeee2196bbc8cd877395870f40 (diff)
introduce-resume-exception-capture.patch
This patch introduces a new resume debugging concept: if we get an OOPS inbetween starting suspend and finishing resume, it uses a new "emergency spew" device similar to BUT NOT REQUIRING CONFIG_DEBUG_LL to dump the syslog buffer and then the OOPS on the debug device defined by the existing CONFIG_DEBUG_S3C_UART index. But neither CONFIG_DEBUG_LL nor the S3C low level configs are needed to use this feature. Another difference between this feature and CONFIG_DEBUG_LL is that it does not affect resume timing, ordering or UART traffic UNLESS there is an OOPS during resume. The patch adds three global exports, one to say if we are inside suspend / resume, and two callbacks for printk() to use to init and dump the emergency data. The callbacks are set in s3c serial device init, but the whole structure is arch independent. Signed-off-by: Andy Green <andy@openmoko.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/power/main.c7
-rw-r--r--kernel/printk.c41
2 files changed, 48 insertions, 0 deletions
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 19122cf6d82..3fdc1f42376 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -132,6 +132,9 @@ static inline int suspend_test(int level) { return 0; }
#endif /* CONFIG_PM_SLEEP */
+int global_inside_suspend;
+EXPORT_SYMBOL(global_inside_suspend);
+
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_PM_TEST_SUSPEND
@@ -322,6 +325,8 @@ int suspend_devices_and_enter(suspend_state_t state)
if (!suspend_ops)
return -ENOSYS;
+ global_inside_suspend = 1;
+
if (suspend_ops->begin) {
error = suspend_ops->begin(state);
if (error)
@@ -365,6 +370,8 @@ int suspend_devices_and_enter(suspend_state_t state)
Close:
if (suspend_ops->end)
suspend_ops->end();
+ global_inside_suspend = 0;
+
return error;
Recover_platform:
diff --git a/kernel/printk.c b/kernel/printk.c
index f492f1583d7..c81ad8638aa 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -32,8 +32,12 @@
#include <linux/security.h>
#include <linux/bootmem.h>
#include <linux/syscalls.h>
+#include <linux/jiffies.h>
+#include <linux/suspend.h>
#include <asm/uaccess.h>
+#include <asm/plat-s3c24xx/neo1973.h>
+#include <asm/arch/gta02.h>
/*
* Architectures can override it:
@@ -67,6 +71,12 @@ int console_printk[4] = {
int oops_in_progress;
EXPORT_SYMBOL(oops_in_progress);
+void (*printk_emergency_debug_spew_init)(void) = NULL;
+EXPORT_SYMBOL(printk_emergency_debug_spew_init);
+
+void (*printk_emergency_debug_spew_send_string)(const char *) = NULL;
+EXPORT_SYMBOL(printk_emergency_debug_spew_send_string);
+
/*
* console_sem protects the console_drivers list, and also
* provides serialisation for access to the entire console
@@ -668,6 +678,37 @@ asmlinkage int vprintk(const char *fmt, va_list args)
printed_len += vscnprintf(printk_buf + printed_len,
sizeof(printk_buf) - printed_len, fmt, args);
+ /* if you're debugging resume, the normal methods can change resume
+ * ordering behaviours because their debugging output is synchronous
+ * (ie, CONFIG_DEBUG_LL). If your problem is an OOPS, this code
+ * will not affect the speed and duration and ordering of resume
+ * actions, but will give you a chance to read the full undumped
+ * syslog AND the OOPS data when it happens
+ *
+ * if you support it, your debug device init can override the exported
+ * emergency_debug_spew_init and emergency_debug_spew_send_string to
+ * usually force polling or bitbanging on your debug console device
+ */
+ if (oops_in_progress && global_inside_suspend &&
+ printk_emergency_debug_spew_init &&
+ printk_emergency_debug_spew_send_string) {
+ unsigned long cur_index;
+ char ch[2];
+
+ if (global_inside_suspend == 1) {
+ (printk_emergency_debug_spew_init)();
+
+ ch[1] = '\0';
+ cur_index = con_start;
+ while (cur_index != log_end) {
+ ch[0] = LOG_BUF(cur_index);
+ (printk_emergency_debug_spew_send_string)(ch);
+ cur_index++;
+ }
+ global_inside_suspend++; /* only once */
+ }
+ (printk_emergency_debug_spew_send_string)(printk_buf);
+ }
/*
* Copy the output into log_buf. If the caller didn't provide