diff options
author | Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com> | 2013-06-05 14:51:32 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-06-20 03:04:49 -0400 |
commit | d7563c94f728d8c9228cfddbb044c843b2e243e8 (patch) | |
tree | a1aa32537e0a3946f36cc9b5e3f2e05ac2a1c3b2 | |
parent | 126746101e89991f179209ef58ab5937bc5ea4a3 (diff) |
powerpc/pseries: Read/Write oops nvram partition via pstore
IBM's p series machines provide persistent storage for LPARs through NVRAM.
NVRAM's lnx,oops-log partition is used to log oops messages.
Currently the kernel provides the contents of p-series NVRAM only as a
simple stream of bytes via /dev/nvram, which must be interpreted in user
space by the nvram command in the powerpc-utils package.
This patch set exploits the pstore subsystem to expose oops partition in
NVRAM as a separate file in /dev/pstore. For instance, Oops messages will be
stored in a file named [dmesg-nvram-2]. In case pstore registration fails it
will fall back to kmsg_dump mechanism.
This patch will read/write the oops messages from/to this partition via pstore.
Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r-- | arch/powerpc/platforms/pseries/nvram.c | 172 |
1 files changed, 157 insertions, 15 deletions
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 088f023d8f25..9edec8ee02ef 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c | |||
@@ -18,6 +18,7 @@ | |||
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/pstore.h> | ||
21 | #include <linux/ctype.h> | 22 | #include <linux/ctype.h> |
22 | #include <linux/zlib.h> | 23 | #include <linux/zlib.h> |
23 | #include <asm/uaccess.h> | 24 | #include <asm/uaccess.h> |
@@ -127,6 +128,14 @@ static size_t oops_data_sz; | |||
127 | #define MEM_LEVEL 4 | 128 | #define MEM_LEVEL 4 |
128 | static struct z_stream_s stream; | 129 | static struct z_stream_s stream; |
129 | 130 | ||
131 | #ifdef CONFIG_PSTORE | ||
132 | static enum pstore_type_id nvram_type_ids[] = { | ||
133 | PSTORE_TYPE_DMESG, | ||
134 | -1 | ||
135 | }; | ||
136 | static int read_type; | ||
137 | #endif | ||
138 | |||
130 | static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) | 139 | static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) |
131 | { | 140 | { |
132 | unsigned int i; | 141 | unsigned int i; |
@@ -430,6 +439,149 @@ static int __init pseries_nvram_init_os_partition(struct nvram_os_partition | |||
430 | return 0; | 439 | return 0; |
431 | } | 440 | } |
432 | 441 | ||
442 | /* | ||
443 | * Are we using the ibm,rtas-log for oops/panic reports? And if so, | ||
444 | * would logging this oops/panic overwrite an RTAS event that rtas_errd | ||
445 | * hasn't had a chance to read and process? Return 1 if so, else 0. | ||
446 | * | ||
447 | * We assume that if rtas_errd hasn't read the RTAS event in | ||
448 | * NVRAM_RTAS_READ_TIMEOUT seconds, it's probably not going to. | ||
449 | */ | ||
450 | static int clobbering_unread_rtas_event(void) | ||
451 | { | ||
452 | return (oops_log_partition.index == rtas_log_partition.index | ||
453 | && last_unread_rtas_event | ||
454 | && get_seconds() - last_unread_rtas_event <= | ||
455 | NVRAM_RTAS_READ_TIMEOUT); | ||
456 | } | ||
457 | |||
458 | #ifdef CONFIG_PSTORE | ||
459 | static int nvram_pstore_open(struct pstore_info *psi) | ||
460 | { | ||
461 | /* Reset the iterator to start reading partitions again */ | ||
462 | read_type = -1; | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | /** | ||
467 | * nvram_pstore_write - pstore write callback for nvram | ||
468 | * @type: Type of message logged | ||
469 | * @reason: reason behind dump (oops/panic) | ||
470 | * @id: identifier to indicate the write performed | ||
471 | * @part: pstore writes data to registered buffer in parts, | ||
472 | * part number will indicate the same. | ||
473 | * @count: Indicates oops count | ||
474 | * @size: number of bytes written to the registered buffer | ||
475 | * @psi: registered pstore_info structure | ||
476 | * | ||
477 | * Called by pstore_dump() when an oops or panic report is logged in the | ||
478 | * printk buffer. | ||
479 | * Returns 0 on successful write. | ||
480 | */ | ||
481 | static int nvram_pstore_write(enum pstore_type_id type, | ||
482 | enum kmsg_dump_reason reason, | ||
483 | u64 *id, unsigned int part, int count, | ||
484 | size_t size, struct pstore_info *psi) | ||
485 | { | ||
486 | int rc; | ||
487 | struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; | ||
488 | |||
489 | /* part 1 has the recent messages from printk buffer */ | ||
490 | if (part > 1 || type != PSTORE_TYPE_DMESG || | ||
491 | clobbering_unread_rtas_event()) | ||
492 | return -1; | ||
493 | |||
494 | oops_hdr->version = OOPS_HDR_VERSION; | ||
495 | oops_hdr->report_length = (u16) size; | ||
496 | oops_hdr->timestamp = get_seconds(); | ||
497 | rc = nvram_write_os_partition(&oops_log_partition, oops_buf, | ||
498 | (int) (sizeof(*oops_hdr) + size), ERR_TYPE_KERNEL_PANIC, | ||
499 | count); | ||
500 | |||
501 | if (rc != 0) | ||
502 | return rc; | ||
503 | |||
504 | *id = part; | ||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | /* | ||
509 | * Reads the oops/panic report. | ||
510 | * Returns the length of the data we read from each partition. | ||
511 | * Returns 0 if we've been called before. | ||
512 | */ | ||
513 | static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, | ||
514 | int *count, struct timespec *time, char **buf, | ||
515 | struct pstore_info *psi) | ||
516 | { | ||
517 | struct oops_log_info *oops_hdr; | ||
518 | unsigned int err_type, id_no; | ||
519 | struct nvram_os_partition *part = NULL; | ||
520 | char *buff = NULL; | ||
521 | |||
522 | read_type++; | ||
523 | |||
524 | switch (nvram_type_ids[read_type]) { | ||
525 | case PSTORE_TYPE_DMESG: | ||
526 | part = &oops_log_partition; | ||
527 | *type = PSTORE_TYPE_DMESG; | ||
528 | break; | ||
529 | default: | ||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | buff = kmalloc(part->size, GFP_KERNEL); | ||
534 | |||
535 | if (!buff) | ||
536 | return -ENOMEM; | ||
537 | |||
538 | if (nvram_read_partition(part, buff, part->size, &err_type, &id_no)) { | ||
539 | kfree(buff); | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | *count = 0; | ||
544 | *id = id_no; | ||
545 | oops_hdr = (struct oops_log_info *)buff; | ||
546 | *buf = buff + sizeof(*oops_hdr); | ||
547 | time->tv_sec = oops_hdr->timestamp; | ||
548 | time->tv_nsec = 0; | ||
549 | return oops_hdr->report_length; | ||
550 | } | ||
551 | |||
552 | static struct pstore_info nvram_pstore_info = { | ||
553 | .owner = THIS_MODULE, | ||
554 | .name = "nvram", | ||
555 | .open = nvram_pstore_open, | ||
556 | .read = nvram_pstore_read, | ||
557 | .write = nvram_pstore_write, | ||
558 | }; | ||
559 | |||
560 | static int nvram_pstore_init(void) | ||
561 | { | ||
562 | int rc = 0; | ||
563 | |||
564 | nvram_pstore_info.buf = oops_data; | ||
565 | nvram_pstore_info.bufsize = oops_data_sz; | ||
566 | |||
567 | rc = pstore_register(&nvram_pstore_info); | ||
568 | if (rc != 0) | ||
569 | pr_err("nvram: pstore_register() failed, defaults to " | ||
570 | "kmsg_dump; returned %d\n", rc); | ||
571 | else | ||
572 | /*TODO: Support compression when pstore is configured */ | ||
573 | pr_info("nvram: Compression of oops text supported only when " | ||
574 | "pstore is not configured"); | ||
575 | |||
576 | return rc; | ||
577 | } | ||
578 | #else | ||
579 | static int nvram_pstore_init(void) | ||
580 | { | ||
581 | return -1; | ||
582 | } | ||
583 | #endif | ||
584 | |||
433 | static void __init nvram_init_oops_partition(int rtas_partition_exists) | 585 | static void __init nvram_init_oops_partition(int rtas_partition_exists) |
434 | { | 586 | { |
435 | int rc; | 587 | int rc; |
@@ -453,6 +605,11 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) | |||
453 | oops_data = oops_buf + sizeof(struct oops_log_info); | 605 | oops_data = oops_buf + sizeof(struct oops_log_info); |
454 | oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info); | 606 | oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info); |
455 | 607 | ||
608 | rc = nvram_pstore_init(); | ||
609 | |||
610 | if (!rc) | ||
611 | return; | ||
612 | |||
456 | /* | 613 | /* |
457 | * Figure compression (preceded by elimination of each line's <n> | 614 | * Figure compression (preceded by elimination of each line's <n> |
458 | * severity prefix) will reduce the oops/panic report to at most | 615 | * severity prefix) will reduce the oops/panic report to at most |
@@ -525,21 +682,6 @@ int __init pSeries_nvram_init(void) | |||
525 | return 0; | 682 | return 0; |
526 | } | 683 | } |
527 | 684 | ||
528 | /* | ||
529 | * Are we using the ibm,rtas-log for oops/panic reports? And if so, | ||
530 | * would logging this oops/panic overwrite an RTAS event that rtas_errd | ||
531 | * hasn't had a chance to read and process? Return 1 if so, else 0. | ||
532 | * | ||
533 | * We assume that if rtas_errd hasn't read the RTAS event in | ||
534 | * NVRAM_RTAS_READ_TIMEOUT seconds, it's probably not going to. | ||
535 | */ | ||
536 | static int clobbering_unread_rtas_event(void) | ||
537 | { | ||
538 | return (oops_log_partition.index == rtas_log_partition.index | ||
539 | && last_unread_rtas_event | ||
540 | && get_seconds() - last_unread_rtas_event <= | ||
541 | NVRAM_RTAS_READ_TIMEOUT); | ||
542 | } | ||
543 | 685 | ||
544 | /* Derived from logfs_compress() */ | 686 | /* Derived from logfs_compress() */ |
545 | static int nvram_compress(const void *in, void *out, size_t inlen, | 687 | static int nvram_compress(const void *in, void *out, size_t inlen, |