summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKay Sievers <kay@vrfy.org>2012-05-29 03:34:17 +0200
committerKay Sievers <kay@vrfy.org>2012-05-29 03:37:09 +0200
commit90d3a50475ca04df6cd0316c81cb8ced0a2f62ca (patch)
treeee4d36c33c11cde179780ef5b94094ac96c92d28
parentd639a17ab4d435a2cebf3bbcabb7e4150efd1661 (diff)
downloadpatches-90d3a50475ca04df6cd0316c81cb8ced0a2f62ca.tar.gz
kmsg dump (entirely untested)
-rw-r--r--kmsg_dump.patch540
-rw-r--r--printk-race.patch6
-rw-r--r--series1
3 files changed, 544 insertions, 3 deletions
diff --git a/kmsg_dump.patch b/kmsg_dump.patch
new file mode 100644
index 0000000..c13a8da
--- /dev/null
+++ b/kmsg_dump.patch
@@ -0,0 +1,540 @@
+---
+ arch/powerpc/platforms/pseries/nvram.c | 60 +---------
+ arch/x86/platform/mrst/Makefile | 2
+ arch/x86/platform/mrst/early_printk_mrst.c | 13 --
+ drivers/mtd/mtdoops.c | 22 ---
+ fs/pstore/platform.c | 34 ++---
+ include/linux/kmsg_dump.h | 39 +++++-
+ kernel/printk.c | 171 ++++++++++++++++++++++++-----
+ 7 files changed, 203 insertions(+), 138 deletions(-)
+
+--- a/arch/powerpc/platforms/pseries/nvram.c
++++ b/arch/powerpc/platforms/pseries/nvram.c
+@@ -68,9 +68,7 @@ static const char *pseries_nvram_os_part
+ };
+
+ static void oops_to_nvram(struct kmsg_dumper *dumper,
+- enum kmsg_dump_reason reason,
+- const char *old_msgs, unsigned long old_len,
+- const char *new_msgs, unsigned long new_len);
++ enum kmsg_dump_reason reason);
+
+ static struct kmsg_dumper nvram_kmsg_dumper = {
+ .dump = oops_to_nvram
+@@ -504,28 +502,6 @@ int __init pSeries_nvram_init(void)
+ }
+
+ /*
+- * Try to capture the last capture_len bytes of the printk buffer. Return
+- * the amount actually captured.
+- */
+-static size_t capture_last_msgs(const char *old_msgs, size_t old_len,
+- const char *new_msgs, size_t new_len,
+- char *captured, size_t capture_len)
+-{
+- if (new_len >= capture_len) {
+- memcpy(captured, new_msgs + (new_len - capture_len),
+- capture_len);
+- return capture_len;
+- } else {
+- /* Grab the end of old_msgs. */
+- size_t old_tail_len = min(old_len, capture_len - new_len);
+- memcpy(captured, old_msgs + (old_len - old_tail_len),
+- old_tail_len);
+- memcpy(captured + old_tail_len, new_msgs, new_len);
+- return old_tail_len + new_len;
+- }
+-}
+-
+-/*
+ * Are we using the ibm,rtas-log for oops/panic reports? And if so,
+ * would logging this oops/panic overwrite an RTAS event that rtas_errd
+ * hasn't had a chance to read and process? Return 1 if so, else 0.
+@@ -541,27 +517,6 @@ static int clobbering_unread_rtas_event(
+ NVRAM_RTAS_READ_TIMEOUT);
+ }
+
+-/* Squeeze out each line's <n> severity prefix. */
+-static size_t elide_severities(char *buf, size_t len)
+-{
+- char *in, *out, *buf_end = buf + len;
+- /* Assume a <n> at the very beginning marks the start of a line. */
+- int newline = 1;
+-
+- in = out = buf;
+- while (in < buf_end) {
+- if (newline && in+3 <= buf_end &&
+- *in == '<' && isdigit(in[1]) && in[2] == '>') {
+- in += 3;
+- newline = 0;
+- } else {
+- newline = (*in == '\n');
+- *out++ = *in++;
+- }
+- }
+- return out - buf;
+-}
+-
+ /* Derived from logfs_compress() */
+ static int nvram_compress(const void *in, void *out, size_t inlen,
+ size_t outlen)
+@@ -619,9 +574,7 @@ static int zip_oops(size_t text_len)
+ * partition. If that's too much, go back and capture uncompressed text.
+ */
+ static void oops_to_nvram(struct kmsg_dumper *dumper,
+- enum kmsg_dump_reason reason,
+- const char *old_msgs, unsigned long old_len,
+- const char *new_msgs, unsigned long new_len)
++ enum kmsg_dump_reason reason)
+ {
+ static unsigned int oops_count = 0;
+ static bool panicking = false;
+@@ -660,14 +613,13 @@ static void oops_to_nvram(struct kmsg_du
+ return;
+
+ if (big_oops_buf) {
+- text_len = capture_last_msgs(old_msgs, old_len,
+- new_msgs, new_len, big_oops_buf, big_oops_buf_sz);
+- text_len = elide_severities(big_oops_buf, text_len);
++ kmsg_dump_get_buffer(dumper, false,
++ big_oops_buf, big_oops_buf_sz, &text_len);
+ rc = zip_oops(text_len);
+ }
+ if (rc != 0) {
+- text_len = capture_last_msgs(old_msgs, old_len,
+- new_msgs, new_len, oops_data, oops_data_sz);
++ kmsg_dump_get_buffer(dumper, true,
++ oops_data, oops_data_sz, &text_len);
+ err_type = ERR_TYPE_KERNEL_PANIC;
+ *oops_len = (u16) text_len;
+ }
+--- a/arch/x86/platform/mrst/Makefile
++++ b/arch/x86/platform/mrst/Makefile
+@@ -1,3 +1,3 @@
+ obj-$(CONFIG_X86_INTEL_MID) += mrst.o
+ obj-$(CONFIG_X86_INTEL_MID) += vrtc.o
+-obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_mrst.o
++obj-y += early_printk_mrst.o
+--- a/arch/x86/platform/mrst/early_printk_mrst.c
++++ b/arch/x86/platform/mrst/early_printk_mrst.c
+@@ -110,19 +110,16 @@ static struct kmsg_dumper dw_dumper;
+ static int dumper_registered;
+
+ static void dw_kmsg_dump(struct kmsg_dumper *dumper,
+- enum kmsg_dump_reason reason,
+- const char *s1, unsigned long l1,
+- const char *s2, unsigned long l2)
++ enum kmsg_dump_reason reason)
+ {
+- int i;
++ static char line[1024];
++ size_t len;
+
+ /* When run to this, we'd better re-init the HW */
+ mrst_early_console_init();
+
+- for (i = 0; i < l1; i++)
+- early_mrst_console.write(&early_mrst_console, s1 + i, 1);
+- for (i = 0; i < l2; i++)
+- early_mrst_console.write(&early_mrst_console, s2 + i, 1);
++ while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len))
++ early_mrst_console.write(&early_mrst_console, line, len);
+ }
+
+ /* Set the ratio rate to 115200, 8n1, IRQ disabled */
+--- a/drivers/mtd/mtdoops.c
++++ b/drivers/mtd/mtdoops.c
+@@ -304,32 +304,17 @@ static void find_next_position(struct mt
+ }
+
+ static void mtdoops_do_dump(struct kmsg_dumper *dumper,
+- enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
+- const char *s2, unsigned long l2)
++ enum kmsg_dump_reason reason)
+ {
+ struct mtdoops_context *cxt = container_of(dumper,
+ struct mtdoops_context, dump);
+- unsigned long s1_start, s2_start;
+- unsigned long l1_cpy, l2_cpy;
+- char *dst;
+-
+- if (reason != KMSG_DUMP_OOPS &&
+- reason != KMSG_DUMP_PANIC)
+- return;
+
+ /* Only dump oopses if dump_oops is set */
+ if (reason == KMSG_DUMP_OOPS && !dump_oops)
+ return;
+
+- dst = cxt->oops_buf + MTDOOPS_HEADER_SIZE; /* Skip the header */
+- l2_cpy = min(l2, record_size - MTDOOPS_HEADER_SIZE);
+- l1_cpy = min(l1, record_size - MTDOOPS_HEADER_SIZE - l2_cpy);
+-
+- s2_start = l2 - l2_cpy;
+- s1_start = l1 - l1_cpy;
+-
+- memcpy(dst, s1 + s1_start, l1_cpy);
+- memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
++ kmsg_dump_get_buffer(dumper, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
++ record_size - MTDOOPS_HEADER_SIZE, NULL);
+
+ /* Panics must be written immediately */
+ if (reason != KMSG_DUMP_OOPS)
+@@ -375,6 +360,7 @@ static void mtdoops_notify_add(struct mt
+ return;
+ }
+
++ cxt->dump.max_reason = KMSG_DUMP_OOPS;
+ cxt->dump.dump = mtdoops_do_dump;
+ err = kmsg_dump_register(&cxt->dump);
+ if (err) {
+--- a/fs/pstore/platform.c
++++ b/fs/pstore/platform.c
+@@ -94,20 +94,15 @@ static const char *get_reason_str(enum k
+ * as we can from the end of the buffer.
+ */
+ static void pstore_dump(struct kmsg_dumper *dumper,
+- enum kmsg_dump_reason reason,
+- const char *s1, unsigned long l1,
+- const char *s2, unsigned long l2)
++ enum kmsg_dump_reason reason)
+ {
+- unsigned long s1_start, s2_start;
+- unsigned long l1_cpy, l2_cpy;
+- unsigned long size, total = 0;
+- char *dst;
++ unsigned long total = 0;
+ const char *why;
+ u64 id;
+- int hsize, ret;
+ unsigned int part = 1;
+ unsigned long flags = 0;
+ int is_locked = 0;
++ int ret;
+
+ why = get_reason_str(reason);
+
+@@ -119,30 +114,25 @@ static void pstore_dump(struct kmsg_dump
+ spin_lock_irqsave(&psinfo->buf_lock, flags);
+ oopscount++;
+ while (total < kmsg_bytes) {
++ char *dst;
++ unsigned long size;
++ int hsize;
++ size_t len;
++
+ dst = psinfo->buf;
+ hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
+ size = psinfo->bufsize - hsize;
+ dst += hsize;
+
+- l2_cpy = min(l2, size);
+- l1_cpy = min(l1, size - l2_cpy);
+-
+- if (l1_cpy + l2_cpy == 0)
++ if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len))
+ break;
+
+- s2_start = l2 - l2_cpy;
+- s1_start = l1 - l1_cpy;
+-
+- memcpy(dst, s1 + s1_start, l1_cpy);
+- memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
+-
+ ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
+- hsize + l1_cpy + l2_cpy, psinfo);
++ hsize + len, psinfo);
+ if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
+ pstore_new_entry = 1;
+- l1 -= l1_cpy;
+- l2 -= l2_cpy;
+- total += l1_cpy + l2_cpy;
++
++ total += hsize + len;
+ part++;
+ }
+ if (in_nmi()) {
+--- a/include/linux/kmsg_dump.h
++++ b/include/linux/kmsg_dump.h
+@@ -21,6 +21,7 @@
+ * is passed to the kernel.
+ */
+ enum kmsg_dump_reason {
++ KMSG_DUMP_UNDEF,
+ KMSG_DUMP_PANIC,
+ KMSG_DUMP_OOPS,
+ KMSG_DUMP_EMERG,
+@@ -31,23 +32,35 @@ enum kmsg_dump_reason {
+
+ /**
+ * struct kmsg_dumper - kernel crash message dumper structure
+- * @dump: The callback which gets called on crashes. The buffer is passed
+- * as two sections, where s1 (length l1) contains the older
+- * messages and s2 (length l2) contains the newer.
+ * @list: Entry in the dumper list (private)
++ * @dump: Call into dumping code which will retrieve the data with
++ * through the record iterator
++ * @max_reason: filter for highest reason number that should be dumped
+ * @registered: Flag that specifies if this is already registered
+ */
+ struct kmsg_dumper {
+- void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason,
+- const char *s1, unsigned long l1,
+- const char *s2, unsigned long l2);
+ struct list_head list;
+- int registered;
++ void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason);
++ enum kmsg_dump_reason max_reason;
++ bool active;
++ bool registered;
++
++ /* private state of the kmsg iterator */
++ u32 cur_idx;
++ u32 next_idx;
++ u64 cur_seq;
++ u64 next_seq;
+ };
+
+ #ifdef CONFIG_PRINTK
+ void kmsg_dump(enum kmsg_dump_reason reason);
+
++bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
++ char *line, size_t size, size_t *len);
++
++bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
++ char *buf, size_t size, size_t *len);
++
+ int kmsg_dump_register(struct kmsg_dumper *dumper);
+
+ int kmsg_dump_unregister(struct kmsg_dumper *dumper);
+@@ -56,6 +69,18 @@ static inline void kmsg_dump(enum kmsg_d
+ {
+ }
+
++bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
++ const char *line, size_t size, size_t *len)
++{
++ return false
++}
++
++bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
++ char *buf, size_t size, size_t *len)
++{
++ return false;
++}
++
+ static inline int kmsg_dump_register(struct kmsg_dumper *dumper)
+ {
+ return -EINVAL;
+--- a/kernel/printk.c
++++ b/kernel/printk.c
+@@ -909,7 +909,7 @@ static int syslog_print_all(char __user
+ /*
+ * Find first record that fits, including all following records,
+ * into the user-provided buffer for this dump.
+- */
++ */
+ seq = clear_seq;
+ idx = clear_idx;
+ while (seq < log_next_seq) {
+@@ -919,6 +919,8 @@ static int syslog_print_all(char __user
+ idx = log_next(idx);
+ seq++;
+ }
++
++ /* move first record forward until length fits into the buffer */
+ seq = clear_seq;
+ idx = clear_idx;
+ while (len > size && seq < log_next_seq) {
+@@ -929,7 +931,7 @@ static int syslog_print_all(char __user
+ seq++;
+ }
+
+- /* last message in this dump */
++ /* last message fitting into this dump */
+ next_seq = log_next_seq;
+
+ len = 0;
+@@ -2305,43 +2307,156 @@ module_param_named(always_kmsg_dump, alw
+ */
+ void kmsg_dump(enum kmsg_dump_reason reason)
+ {
+- u64 idx;
+ struct kmsg_dumper *dumper;
+- const char *s1, *s2;
+- unsigned long l1, l2;
+ unsigned long flags;
+
+ if ((reason > KMSG_DUMP_OOPS) && !always_kmsg_dump)
+ return;
+
+- /* Theoretically, the log could move on after we do this, but
+- there's not a lot we can do about that. The new messages
+- will overwrite the start of what we dump. */
++ rcu_read_lock();
++ list_for_each_entry_rcu(dumper, &dump_list, list) {
++ if (dumper->max_reason && reason > dumper->max_reason)
++ continue;
+
+- raw_spin_lock_irqsave(&logbuf_lock, flags);
+- if (syslog_seq < log_first_seq)
+- idx = syslog_idx;
+- else
+- idx = log_first_idx;
+-
+- if (idx > log_next_idx) {
+- s1 = log_buf;
+- l1 = log_next_idx;
++ /* initialize iterator with data about the stored records */
++ dumper->active = true;
+
+- s2 = log_buf + idx;
+- l2 = log_buf_len - idx;
+- } else {
+- s1 = "";
+- l1 = 0;
++ raw_spin_lock_irqsave(&logbuf_lock, flags);
++ dumper->cur_seq = clear_seq;
++ dumper->cur_idx = clear_idx;
++ dumper->next_seq = log_next_seq;
++ dumper->next_idx = log_next_idx;
++ raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+
+- s2 = log_buf + idx;
+- l2 = log_next_idx - idx;
++ /* invoke dumper which will iterate over records */
++ dumper->dump(dumper, reason);
++
++ /* reset iterator */
++ dumper->active = false;
+ }
++ rcu_read_unlock();
++}
++
++/*
++ * Start at the oldest kmsg record; print one line into provided buffer.
++ * Repeated calls will return the next record. A return value of FALSE
++ * indicates that all records are read including the youngest one.
++ */
++bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
++ char *line, size_t size, size_t *len)
++{
++ unsigned long flags;
++ struct log *msg;
++ size_t l = 0;
++ bool ret = false;
++
++ if (dumper->active)
++ goto out;
++
++ raw_spin_lock_irqsave(&logbuf_lock, flags);
++ if (dumper->cur_seq < log_first_seq) {
++ /* messages are gone, move to first available one */
++ dumper->cur_seq = log_first_seq;
++ dumper->cur_idx = log_first_idx;
++ }
++
++ /* last entry */
++ if (dumper->cur_seq == log_next_seq) {
++ raw_spin_unlock_irqrestore(&logbuf_lock, flags);
++ goto out;
++ }
++
++ msg = log_from_idx(dumper->cur_idx);
++ l = msg_print_text(msg, syslog,
++ line, size);
++
++ dumper->cur_idx = log_next(dumper->cur_idx);
++ dumper->cur_seq++;
++ ret = true;
+ raw_spin_unlock_irqrestore(&logbuf_lock, flags);
++out:
++ if (len)
++ *len = l;
++ return ret;
++}
+
+- rcu_read_lock();
+- list_for_each_entry_rcu(dumper, &dump_list, list)
+- dumper->dump(dumper, reason, s1, l1, s2, l2);
+- rcu_read_unlock();
++/*
++ * Fill the provided buffer with as many of the the youngest kmsg records
++ * that fit into it. Repeated calls will fill the buffer with the next block
++ * of older records not including the former ones. A return value of FALSE
++ * indicate that all records are read, including the oldest one.
++ */
++bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
++ char *buf, size_t size, size_t *len)
++{
++ unsigned long flags;
++ u64 seq;
++ u32 idx;
++ u64 next_seq;
++ u32 next_idx;
++ size_t l = 0;
++ bool ret = false;
++
++ if (dumper->active)
++ goto out;
++
++ raw_spin_lock_irqsave(&logbuf_lock, flags);
++ if (dumper->cur_seq < log_first_seq) {
++ /* messages are gone, move to first available one */
++ dumper->cur_seq = log_first_seq;
++ dumper->cur_idx = log_first_idx;
++ }
++
++ /* last entry */
++ if (dumper->cur_seq == dumper->next_seq) {
++ raw_spin_unlock_irqrestore(&logbuf_lock, flags);
++ goto out;
++ }
++
++ /* calculate length of entire buffer */
++ seq = dumper->cur_seq;
++ idx = dumper->cur_idx;
++ while (seq < dumper->next_seq) {
++ struct log *msg = log_from_idx(idx);
++
++ l += msg_print_text(msg, true, NULL, 0);
++ idx = log_next(idx);
++ seq++;
++ }
++
++ /* move first record forward until length fits into the buffer */
++ seq = dumper->cur_seq;
++ idx = dumper->cur_idx;
++ while (l > size && seq < dumper->next_seq) {
++ struct log *msg = log_from_idx(idx);
++
++ l -= msg_print_text(msg, true, NULL, 0);
++ idx = log_next(idx);
++ seq++;
++ }
++
++ /* last message in next interation */
++ next_seq = seq;
++ next_idx = idx;
++
++ l = 0;
++ while (seq < dumper->next_seq) {
++ struct log *msg = log_from_idx(idx);
++
++ l += msg_print_text(msg, syslog,
++ buf + l, size - l);
++
++ idx = log_next(idx);
++ seq++;
++ }
++
++ dumper->next_seq = next_seq;
++ dumper->next_idx = next_idx;
++ ret = true;
++ raw_spin_unlock_irqrestore(&logbuf_lock, flags);
++out:
++ if (len)
++ *len = l;
++ return ret;
+ }
+ #endif
diff --git a/printk-race.patch b/printk-race.patch
index fff4e95..73dc7f4 100644
--- a/printk-race.patch
+++ b/printk-race.patch
@@ -6,7 +6,7 @@
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
-@@ -490,6 +490,12 @@ config USB_SWITCH_FSA9480
+@@ -508,6 +508,12 @@ config USB_SWITCH_FSA9480
stereo and mono audio, video, microphone and UART data to use
a common connector port.
@@ -21,10 +21,10 @@
source "drivers/misc/cb710/Kconfig"
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
-@@ -48,3 +48,4 @@ obj-y += lis3lv02d/
- obj-y += carma/
+@@ -51,3 +51,4 @@ obj-y += carma/
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
+ obj-$(CONFIG_INTEL_MEI) += mei/
+obj-$(CONFIG_PRINTK_RACE) +=printk-race.o
--- /dev/null
+++ b/drivers/misc/printk-race.c
diff --git a/series b/series
index 24de0b3..d2c7e7f 100644
--- a/series
+++ b/series
@@ -1,2 +1,3 @@
printk-race.patch
+kmsg_dump.patch