Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jim Keniston and committed by
Benjamin Herrenschmidt
6c493685 73927693

+169 -9
+4 -2
arch/powerpc/include/asm/rtas.h
··· 249 249 #define ERR_FLAG_ALREADY_LOGGED 0x0 250 250 #define ERR_FLAG_BOOT 0x1 /* log was pulled from NVRAM on boot */ 251 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 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 259 #define RTAS_DEBUG KERN_DEBUG "RTAS: " 258 260
+1
arch/powerpc/platforms/pseries/Kconfig
··· 15 15 select PPC_UDBG_16550 16 16 select PPC_NATIVE 17 17 select PPC_PCI_CHOICE if EXPERT 18 + select ZLIB_DEFLATE 18 19 default y 19 20 20 21 config PPC_SPLPAR
+164 -7
arch/powerpc/platforms/pseries/nvram.c
··· 18 18 #include <linux/spinlock.h> 19 19 #include <linux/slab.h> 20 20 #include <linux/kmsg_dump.h> 21 + #include <linux/ctype.h> 22 + #include <linux/zlib.h> 21 23 #include <asm/uaccess.h> 22 24 #include <asm/nvram.h> 23 25 #include <asm/rtas.h> ··· 80 78 #define NVRAM_RTAS_READ_TIMEOUT 5 /* seconds */ 81 79 static unsigned long last_unread_rtas_event; /* timestamp */ 82 80 83 - /* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */ 84 - static char *oops_buf; 81 + /* 82 + * For capturing and compressing an oops or panic report... 83 + 84 + * big_oops_buf[] holds the uncompressed text we're capturing. 85 + * 86 + * oops_buf[] holds the compressed text, preceded by a prefix. 87 + * The prefix is just a u16 holding the length of the compressed* text. 88 + * (*Or uncompressed, if compression fails.) oops_buf[] gets written 89 + * to NVRAM. 90 + * 91 + * oops_len points to the prefix. oops_data points to the compressed text. 92 + * 93 + * +- oops_buf 94 + * | +- oops_data 95 + * v v 96 + * +------------+-----------------------------------------------+ 97 + * | length | text | 98 + * | (2 bytes) | (oops_data_sz bytes) | 99 + * +------------+-----------------------------------------------+ 100 + * ^ 101 + * +- oops_len 102 + * 103 + * We preallocate these buffers during init to avoid kmalloc during oops/panic. 104 + */ 105 + static size_t big_oops_buf_sz; 106 + static char *big_oops_buf, *oops_buf; 107 + static u16 *oops_len; 108 + static char *oops_data; 109 + static size_t oops_data_sz; 110 + 111 + /* Compression parameters */ 112 + #define COMPR_LEVEL 6 113 + #define WINDOW_BITS 12 114 + #define MEM_LEVEL 4 115 + static struct z_stream_s stream; 85 116 86 117 static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) 87 118 { ··· 422 387 sizeof(rtas_log_partition)); 423 388 } 424 389 oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL); 390 + if (!oops_buf) { 391 + pr_err("nvram: No memory for %s partition\n", 392 + oops_log_partition.name); 393 + return; 394 + } 395 + oops_len = (u16*) oops_buf; 396 + oops_data = oops_buf + sizeof(u16); 397 + oops_data_sz = oops_log_partition.size - sizeof(u16); 398 + 399 + /* 400 + * Figure compression (preceded by elimination of each line's <n> 401 + * severity prefix) will reduce the oops/panic report to at most 402 + * 45% of its original size. 403 + */ 404 + big_oops_buf_sz = (oops_data_sz * 100) / 45; 405 + big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); 406 + if (big_oops_buf) { 407 + stream.workspace = kmalloc(zlib_deflate_workspacesize( 408 + WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); 409 + if (!stream.workspace) { 410 + pr_err("nvram: No memory for compression workspace; " 411 + "skipping compression of %s partition data\n", 412 + oops_log_partition.name); 413 + kfree(big_oops_buf); 414 + big_oops_buf = NULL; 415 + } 416 + } else { 417 + pr_err("No memory for uncompressed %s data; " 418 + "skipping compression\n", oops_log_partition.name); 419 + stream.workspace = NULL; 420 + } 421 + 425 422 rc = kmsg_dump_register(&nvram_kmsg_dumper); 426 423 if (rc != 0) { 427 424 pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); 428 425 kfree(oops_buf); 429 - return; 426 + kfree(big_oops_buf); 427 + kfree(stream.workspace); 430 428 } 431 429 } 432 430 ··· 541 473 NVRAM_RTAS_READ_TIMEOUT); 542 474 } 543 475 544 - /* our kmsg_dump callback */ 476 + /* Squeeze out each line's <n> severity prefix. */ 477 + static size_t elide_severities(char *buf, size_t len) 478 + { 479 + char *in, *out, *buf_end = buf + len; 480 + /* Assume a <n> at the very beginning marks the start of a line. */ 481 + int newline = 1; 482 + 483 + in = out = buf; 484 + while (in < buf_end) { 485 + if (newline && in+3 <= buf_end && 486 + *in == '<' && isdigit(in[1]) && in[2] == '>') { 487 + in += 3; 488 + newline = 0; 489 + } else { 490 + newline = (*in == '\n'); 491 + *out++ = *in++; 492 + } 493 + } 494 + return out - buf; 495 + } 496 + 497 + /* Derived from logfs_compress() */ 498 + static int nvram_compress(const void *in, void *out, size_t inlen, 499 + size_t outlen) 500 + { 501 + int err, ret; 502 + 503 + ret = -EIO; 504 + err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, 505 + MEM_LEVEL, Z_DEFAULT_STRATEGY); 506 + if (err != Z_OK) 507 + goto error; 508 + 509 + stream.next_in = in; 510 + stream.avail_in = inlen; 511 + stream.total_in = 0; 512 + stream.next_out = out; 513 + stream.avail_out = outlen; 514 + stream.total_out = 0; 515 + 516 + err = zlib_deflate(&stream, Z_FINISH); 517 + if (err != Z_STREAM_END) 518 + goto error; 519 + 520 + err = zlib_deflateEnd(&stream); 521 + if (err != Z_OK) 522 + goto error; 523 + 524 + if (stream.total_out >= stream.total_in) 525 + goto error; 526 + 527 + ret = stream.total_out; 528 + error: 529 + return ret; 530 + } 531 + 532 + /* Compress the text from big_oops_buf into oops_buf. */ 533 + static int zip_oops(size_t text_len) 534 + { 535 + int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len, 536 + oops_data_sz); 537 + if (zipped_len < 0) { 538 + pr_err("nvram: compression failed; returned %d\n", zipped_len); 539 + pr_err("nvram: logging uncompressed oops/panic report\n"); 540 + return -1; 541 + } 542 + *oops_len = (u16) zipped_len; 543 + return 0; 544 + } 545 + 546 + /* 547 + * This is our kmsg_dump callback, called after an oops or panic report 548 + * has been written to the printk buffer. We want to capture as much 549 + * of the printk buffer as possible. First, capture as much as we can 550 + * that we think will compress sufficiently to fit in the lnx,oops-log 551 + * partition. If that's too much, go back and capture uncompressed text. 552 + */ 545 553 static void oops_to_nvram(struct kmsg_dumper *dumper, 546 554 enum kmsg_dump_reason reason, 547 555 const char *old_msgs, unsigned long old_len, ··· 626 482 static unsigned int oops_count = 0; 627 483 static bool panicking = false; 628 484 size_t text_len; 485 + unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ; 486 + int rc = -1; 629 487 630 488 switch (reason) { 631 489 case KMSG_DUMP_RESTART: ··· 655 509 if (clobbering_unread_rtas_event()) 656 510 return; 657 511 658 - text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len, 659 - oops_buf, oops_log_partition.size); 512 + if (big_oops_buf) { 513 + text_len = capture_last_msgs(old_msgs, old_len, 514 + new_msgs, new_len, big_oops_buf, big_oops_buf_sz); 515 + text_len = elide_severities(big_oops_buf, text_len); 516 + rc = zip_oops(text_len); 517 + } 518 + if (rc != 0) { 519 + text_len = capture_last_msgs(old_msgs, old_len, 520 + new_msgs, new_len, oops_data, oops_data_sz); 521 + err_type = ERR_TYPE_KERNEL_PANIC; 522 + *oops_len = (u16) text_len; 523 + } 524 + 660 525 (void) nvram_write_os_partition(&oops_log_partition, oops_buf, 661 - (int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count); 526 + (int) (sizeof(*oops_len) + *oops_len), err_type, ++oops_count); 662 527 }