aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJim Keniston <jkenisto@us.ibm.com>2011-07-25 03:54:50 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-19 19:19:46 -0400
commit6c493685f1b209dd4ae41eb52c818cf12da20def (patch)
treee524f18276205cecb1552baca95cb5dc7d981b2b /arch
parent7392769365f32c82340f184f93408b12dc3da4dc (diff)
powerpc/nvram: Add compression to fit more oops output into NVRAM
Capture more than twice as much text from the printk buffer, and compress it to fit it in the lnx,oops-log NVRAM partition. You can view the compressed text using the new (as of July 20) --unzip option of the nvram command in the powerpc-utils package. [BenH: Added select of ZLIB_DEFLATE] Signed-off-by: Jim Keniston <jkenisto@us.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/include/asm/rtas.h6
-rw-r--r--arch/powerpc/platforms/pseries/Kconfig1
-rw-r--r--arch/powerpc/platforms/pseries/nvram.c171
3 files changed, 169 insertions, 9 deletions
diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index 58625d1e7802..41f69ae79d4e 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -249,10 +249,12 @@ extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal);
249#define ERR_FLAG_ALREADY_LOGGED 0x0 249#define ERR_FLAG_ALREADY_LOGGED 0x0
250#define ERR_FLAG_BOOT 0x1 /* log was pulled from NVRAM on boot */ 250#define ERR_FLAG_BOOT 0x1 /* log was pulled from NVRAM on boot */
251#define ERR_TYPE_RTAS_LOG 0x2 /* from rtas event-scan */ 251#define ERR_TYPE_RTAS_LOG 0x2 /* from rtas event-scan */
252#define ERR_TYPE_KERNEL_PANIC 0x4 /* from panic() */ 252#define ERR_TYPE_KERNEL_PANIC 0x4 /* from die()/panic() */
253#define ERR_TYPE_KERNEL_PANIC_GZ 0x8 /* ditto, compressed */
253 254
254/* All the types and not flags */ 255/* All the types and not flags */
255#define ERR_TYPE_MASK (ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC) 256#define ERR_TYPE_MASK \
257 (ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC | ERR_TYPE_KERNEL_PANIC_GZ)
256 258
257#define RTAS_DEBUG KERN_DEBUG "RTAS: " 259#define RTAS_DEBUG KERN_DEBUG "RTAS: "
258 260
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 05cf4769b88c..c81f6bb9c10f 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -15,6 +15,7 @@ config PPC_PSERIES
15 select PPC_UDBG_16550 15 select PPC_UDBG_16550
16 select PPC_NATIVE 16 select PPC_NATIVE
17 select PPC_PCI_CHOICE if EXPERT 17 select PPC_PCI_CHOICE if EXPERT
18 select ZLIB_DEFLATE
18 default y 19 default y
19 20
20config PPC_SPLPAR 21config PPC_SPLPAR
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 00cc3a094885..a76b22844d18 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -18,6 +18,8 @@
18#include <linux/spinlock.h> 18#include <linux/spinlock.h>
19#include <linux/slab.h> 19#include <linux/slab.h>
20#include <linux/kmsg_dump.h> 20#include <linux/kmsg_dump.h>
21#include <linux/ctype.h>
22#include <linux/zlib.h>
21#include <asm/uaccess.h> 23#include <asm/uaccess.h>
22#include <asm/nvram.h> 24#include <asm/nvram.h>
23#include <asm/rtas.h> 25#include <asm/rtas.h>
@@ -78,8 +80,41 @@ static struct kmsg_dumper nvram_kmsg_dumper = {
78#define NVRAM_RTAS_READ_TIMEOUT 5 /* seconds */ 80#define NVRAM_RTAS_READ_TIMEOUT 5 /* seconds */
79static unsigned long last_unread_rtas_event; /* timestamp */ 81static unsigned long last_unread_rtas_event; /* timestamp */
80 82
81/* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */ 83/*
82static char *oops_buf; 84 * For capturing and compressing an oops or panic report...
85
86 * big_oops_buf[] holds the uncompressed text we're capturing.
87 *
88 * oops_buf[] holds the compressed text, preceded by a prefix.
89 * The prefix is just a u16 holding the length of the compressed* text.
90 * (*Or uncompressed, if compression fails.) oops_buf[] gets written
91 * to NVRAM.
92 *
93 * oops_len points to the prefix. oops_data points to the compressed text.
94 *
95 * +- oops_buf
96 * | +- oops_data
97 * v v
98 * +------------+-----------------------------------------------+
99 * | length | text |
100 * | (2 bytes) | (oops_data_sz bytes) |
101 * +------------+-----------------------------------------------+
102 * ^
103 * +- oops_len
104 *
105 * We preallocate these buffers during init to avoid kmalloc during oops/panic.
106 */
107static size_t big_oops_buf_sz;
108static char *big_oops_buf, *oops_buf;
109static u16 *oops_len;
110static char *oops_data;
111static size_t oops_data_sz;
112
113/* Compression parameters */
114#define COMPR_LEVEL 6
115#define WINDOW_BITS 12
116#define MEM_LEVEL 4
117static struct z_stream_s stream;
83 118
84static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) 119static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
85{ 120{
@@ -387,11 +422,44 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
387 sizeof(rtas_log_partition)); 422 sizeof(rtas_log_partition));
388 } 423 }
389 oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL); 424 oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL);
425 if (!oops_buf) {
426 pr_err("nvram: No memory for %s partition\n",
427 oops_log_partition.name);
428 return;
429 }
430 oops_len = (u16*) oops_buf;
431 oops_data = oops_buf + sizeof(u16);
432 oops_data_sz = oops_log_partition.size - sizeof(u16);
433
434 /*
435 * Figure compression (preceded by elimination of each line's <n>
436 * severity prefix) will reduce the oops/panic report to at most
437 * 45% of its original size.
438 */
439 big_oops_buf_sz = (oops_data_sz * 100) / 45;
440 big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
441 if (big_oops_buf) {
442 stream.workspace = kmalloc(zlib_deflate_workspacesize(
443 WINDOW_BITS, MEM_LEVEL), GFP_KERNEL);
444 if (!stream.workspace) {
445 pr_err("nvram: No memory for compression workspace; "
446 "skipping compression of %s partition data\n",
447 oops_log_partition.name);
448 kfree(big_oops_buf);
449 big_oops_buf = NULL;
450 }
451 } else {
452 pr_err("No memory for uncompressed %s data; "
453 "skipping compression\n", oops_log_partition.name);
454 stream.workspace = NULL;
455 }
456
390 rc = kmsg_dump_register(&nvram_kmsg_dumper); 457 rc = kmsg_dump_register(&nvram_kmsg_dumper);
391 if (rc != 0) { 458 if (rc != 0) {
392 pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); 459 pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
393 kfree(oops_buf); 460 kfree(oops_buf);
394 return; 461 kfree(big_oops_buf);
462 kfree(stream.workspace);
395 } 463 }
396} 464}
397 465
@@ -473,7 +541,83 @@ static int clobbering_unread_rtas_event(void)
473 NVRAM_RTAS_READ_TIMEOUT); 541 NVRAM_RTAS_READ_TIMEOUT);
474} 542}
475 543
476/* our kmsg_dump callback */ 544/* Squeeze out each line's <n> severity prefix. */
545static size_t elide_severities(char *buf, size_t len)
546{
547 char *in, *out, *buf_end = buf + len;
548 /* Assume a <n> at the very beginning marks the start of a line. */
549 int newline = 1;
550
551 in = out = buf;
552 while (in < buf_end) {
553 if (newline && in+3 <= buf_end &&
554 *in == '<' && isdigit(in[1]) && in[2] == '>') {
555 in += 3;
556 newline = 0;
557 } else {
558 newline = (*in == '\n');
559 *out++ = *in++;
560 }
561 }
562 return out - buf;
563}
564
565/* Derived from logfs_compress() */
566static int nvram_compress(const void *in, void *out, size_t inlen,
567 size_t outlen)
568{
569 int err, ret;
570
571 ret = -EIO;
572 err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
573 MEM_LEVEL, Z_DEFAULT_STRATEGY);
574 if (err != Z_OK)
575 goto error;
576
577 stream.next_in = in;
578 stream.avail_in = inlen;
579 stream.total_in = 0;
580 stream.next_out = out;
581 stream.avail_out = outlen;
582 stream.total_out = 0;
583
584 err = zlib_deflate(&stream, Z_FINISH);
585 if (err != Z_STREAM_END)
586 goto error;
587
588 err = zlib_deflateEnd(&stream);
589 if (err != Z_OK)
590 goto error;
591
592 if (stream.total_out >= stream.total_in)
593 goto error;
594
595 ret = stream.total_out;
596error:
597 return ret;
598}
599
600/* Compress the text from big_oops_buf into oops_buf. */
601static int zip_oops(size_t text_len)
602{
603 int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len,
604 oops_data_sz);
605 if (zipped_len < 0) {
606 pr_err("nvram: compression failed; returned %d\n", zipped_len);
607 pr_err("nvram: logging uncompressed oops/panic report\n");
608 return -1;
609 }
610 *oops_len = (u16) zipped_len;
611 return 0;
612}
613
614/*
615 * This is our kmsg_dump callback, called after an oops or panic report
616 * has been written to the printk buffer. We want to capture as much
617 * of the printk buffer as possible. First, capture as much as we can
618 * that we think will compress sufficiently to fit in the lnx,oops-log
619 * partition. If that's too much, go back and capture uncompressed text.
620 */
477static void oops_to_nvram(struct kmsg_dumper *dumper, 621static void oops_to_nvram(struct kmsg_dumper *dumper,
478 enum kmsg_dump_reason reason, 622 enum kmsg_dump_reason reason,
479 const char *old_msgs, unsigned long old_len, 623 const char *old_msgs, unsigned long old_len,
@@ -482,6 +626,8 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
482 static unsigned int oops_count = 0; 626 static unsigned int oops_count = 0;
483 static bool panicking = false; 627 static bool panicking = false;
484 size_t text_len; 628 size_t text_len;
629 unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ;
630 int rc = -1;
485 631
486 switch (reason) { 632 switch (reason) {
487 case KMSG_DUMP_RESTART: 633 case KMSG_DUMP_RESTART:
@@ -509,8 +655,19 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
509 if (clobbering_unread_rtas_event()) 655 if (clobbering_unread_rtas_event())
510 return; 656 return;
511 657
512 text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len, 658 if (big_oops_buf) {
513 oops_buf, oops_log_partition.size); 659 text_len = capture_last_msgs(old_msgs, old_len,
660 new_msgs, new_len, big_oops_buf, big_oops_buf_sz);
661 text_len = elide_severities(big_oops_buf, text_len);
662 rc = zip_oops(text_len);
663 }
664 if (rc != 0) {
665 text_len = capture_last_msgs(old_msgs, old_len,
666 new_msgs, new_len, oops_data, oops_data_sz);
667 err_type = ERR_TYPE_KERNEL_PANIC;
668 *oops_len = (u16) text_len;
669 }
670
514 (void) nvram_write_os_partition(&oops_log_partition, oops_buf, 671 (void) nvram_write_os_partition(&oops_log_partition, oops_buf,
515 (int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count); 672 (int) (sizeof(*oops_len) + *oops_len), err_type, ++oops_count);
516} 673}