All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM
@ 2010-11-14  4:15 Jim Keniston
  2010-11-14  4:15 ` [PATCH 1/6] nvram: Generalize code for OS partitions " Jim Keniston
                   ` (6 more replies)
  0 siblings, 7 replies; 13+ messages in thread
From: Jim Keniston @ 2010-11-14  4:15 UTC (permalink / raw)
  To: linuxppc-dev

This patch series enables p Series systems to capture oops and panic
reports from the printk buffer into NVRAM, where they can be examined
after reboot using the nvram command.

Patches 1-2 implement the basic feature, and patch 4 adds compression,
so we can capture more of the printk buffer.  Patches 3 and 5-6 add
refinements.  Patch 5 is not powerpc-specific, and I plan to pursue that
independently on LKML.

This series applies atop the little bug-fix series I posted two days
ago --

"More ppc64 nvram code cleanup"
http://lists.ozlabs.org/pipermail/linuxppc-dev/2010-November/087009.html

-- which in turn applies atop Ben Herrenschmidt's Aug. 2 patch set --

"[RFC] Clean up ppc64 nvram code"
http://lists.ozlabs.org/pipermail/linuxppc-dev/2010-August/084601.html

My patches have been tested against 2.6.37-rc1.
---

Jim Keniston (6):
      Shrink our zlib_deflate workspace from 268K to 24K
      Slim down zlib_deflate workspace when possible
      Add compression to fit more printk output into NVRAM
      Always capture start of oops report to NVRAM
      Capture oops/panic reports in ibm,oops-log partition
      Generalize code for OS partitions in NVRAM


 arch/powerpc/include/asm/nvram.h       |    3 
 arch/powerpc/include/asm/rtas.h        |    6 
 arch/powerpc/kernel/nvram_64.c         |   31 ++
 arch/powerpc/platforms/pseries/nvram.c |  470 ++++++++++++++++++++++++++++----
 include/linux/zlib.h                   |   14 +
 lib/zlib_deflate/deflate.c             |   33 ++
 lib/zlib_deflate/deflate_syms.c        |    1 
 lib/zlib_deflate/defutil.h             |   17 +
 8 files changed, 504 insertions(+), 71 deletions(-)

--
Jim Keniston
IBM Linux Technology Center
Beaverton, OR

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/6] nvram: Generalize code for OS partitions in NVRAM
  2010-11-14  4:15 [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston
@ 2010-11-14  4:15 ` Jim Keniston
  2011-02-07  4:57   ` Benjamin Herrenschmidt
  2010-11-14  4:15 ` [PATCH 2/6] nvram: Capture oops/panic reports in ibm, oops-log partition Jim Keniston
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 13+ messages in thread
From: Jim Keniston @ 2010-11-14  4:15 UTC (permalink / raw)
  To: linuxppc-dev

Adapt the functions used to create and write to the RTAS-log partition
to work with any OS-type partition.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---

 arch/powerpc/include/asm/nvram.h       |    3 -
 arch/powerpc/kernel/nvram_64.c         |   31 ++++++-
 arch/powerpc/platforms/pseries/nvram.c |  138 +++++++++++++++++++-------------
 3 files changed, 110 insertions(+), 62 deletions(-)

diff --git a/arch/powerpc/include/asm/nvram.h b/arch/powerpc/include/asm/nvram.h
index 457a1a5..3cd90fd 100644
--- a/arch/powerpc/include/asm/nvram.h
+++ b/arch/powerpc/include/asm/nvram.h
@@ -50,7 +50,8 @@ static inline int mmio_nvram_init(void)
 
 extern loff_t nvram_create_partition(const char *name, int sig,
 				     int req_size, int min_size);
-extern int nvram_remove_partition(const char *name, int sig);
+extern int nvram_remove_partition(const char *name, int sig,
+					const char *exceptions[]);
 extern int nvram_get_partition_size(loff_t data_index);
 extern loff_t nvram_find_partition(const char *name, int sig, int *out_size);
 
diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index cda7c3f..3de46cd 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -237,22 +237,45 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
 	return c_sum;
 }
 
+/*
+ * Per the criteria passed via nvram_remove_partition(), should this
+ * partition be removed?  1=remove, 0=keep
+ */
+static int nvram_condemn_partition(struct nvram_partition *part,
+		const char *name, int sig, const char *exceptions[])
+{
+	if (part->header.signature != sig)
+		return 0;
+	if (name) {
+		if (strncmp(name, part->header.name, 12))
+			return 0;
+	} else if (exceptions) {
+		const char **except;
+		for (except = exceptions; *except; except++) {
+			if (!strncmp(*except, part->header.name, 12))
+				return 0;
+		}
+	}
+	return 1;
+}
+
 /**
  * nvram_remove_partition - Remove one or more partitions in nvram
  * @name: name of the partition to remove, or NULL for a
  *        signature only match
  * @sig: signature of the partition(s) to remove
+ * @exceptions: When removing all partitions with a matching signature,
+ *        leave these alone.
  */
 
-int __init nvram_remove_partition(const char *name, int sig)
+int __init nvram_remove_partition(const char *name, int sig,
+						const char *exceptions[])
 {
 	struct nvram_partition *part, *prev, *tmp;
 	int rc;
 
 	list_for_each_entry(part, &nvram_partitions, partition) {
-		if (part->header.signature != sig)
-			continue;
-		if (name && strncmp(name, part->header.name, 12))
+		if (!nvram_condemn_partition(part, name, sig, exceptions))
 			continue;
 
 		/* Make partition a free partition */
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 4b705dc..43d5c52 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -30,17 +30,30 @@ static int nvram_fetch, nvram_store;
 static char nvram_buf[NVRW_CNT];	/* assume this is in the first 4GB */
 static DEFINE_SPINLOCK(nvram_lock);
 
-static long nvram_error_log_index = -1;
-static long nvram_error_log_size = 0;
-
 struct err_log_info {
 	int error_type;
 	unsigned int seq_num;
 };
-#define NVRAM_MAX_REQ		2079
-#define NVRAM_MIN_REQ		1055
 
-#define NVRAM_LOG_PART_NAME	"ibm,rtas-log"
+struct os_partition {
+	const char *name;
+	int req_size;	/* desired size, in bytes */
+	int min_size;	/* minimum acceptable size (0 means req_size) */
+	long size;	/* size of data portion of partition */
+	long index;	/* offset of data portion of partition */
+};
+
+static struct os_partition rtas_log_partition = {
+	.name = "ibm,rtas-log",
+	.req_size = 2079,
+	.min_size = 1055,
+	.index = -1
+};
+
+static const char *valid_os_partitions[] = {
+	"ibm,rtas-log",
+	NULL
+};
 
 static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
 {
@@ -134,7 +147,7 @@ static ssize_t pSeries_nvram_get_size(void)
 }
 
 
-/* nvram_write_error_log
+/* nvram_write_os_partition, nvram_write_error_log
  *
  * We need to buffer the error logs into nvram to ensure that we have
  * the failure information to decode.  If we have a severe error there
@@ -156,48 +169,55 @@ static ssize_t pSeries_nvram_get_size(void)
  * The 'data' section would look like (in bytes):
  * +--------------+------------+-----------------------------------+
  * | event_logged | sequence # | error log                         |
- * |0            3|4          7|8            nvram_error_log_size-1|
+ * |0            3|4          7|8                  error_log_size-1|
  * +--------------+------------+-----------------------------------+
  *
  * event_logged: 0 if event has not been logged to syslog, 1 if it has
  * sequence #: The unique sequence # for each event. (until it wraps)
  * error log: The error log from event_scan
  */
-int nvram_write_error_log(char * buff, int length,
+int nvram_write_os_partition(struct os_partition *part, char * buff, int length,
                           unsigned int err_type, unsigned int error_log_cnt)
 {
 	int rc;
 	loff_t tmp_index;
 	struct err_log_info info;
 	
-	if (nvram_error_log_index == -1) {
+	if (part->index == -1) {
 		return -ESPIPE;
 	}
 
-	if (length > nvram_error_log_size) {
-		length = nvram_error_log_size;
+	if (length > part->size) {
+		length = part->size;
 	}
 
 	info.error_type = err_type;
 	info.seq_num = error_log_cnt;
 
-	tmp_index = nvram_error_log_index;
+	tmp_index = part->index;
 
 	rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
 	if (rc <= 0) {
-		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
+		printk(KERN_ERR "nvram_write_os_partition: Failed nvram_write (%d)\n", rc);
 		return rc;
 	}
 
 	rc = ppc_md.nvram_write(buff, length, &tmp_index);
 	if (rc <= 0) {
-		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
+		printk(KERN_ERR "nvram_write_os_partition: Failed nvram_write (%d)\n", rc);
 		return rc;
 	}
 	
 	return 0;
 }
 
+int nvram_write_error_log(char * buff, int length,
+                          unsigned int err_type, unsigned int error_log_cnt)
+{
+	return nvram_write_os_partition(&rtas_log_partition, buff, length,
+						err_type, error_log_cnt);
+}
+
 /* nvram_read_error_log
  *
  * Reads nvram for error log for at most 'length'
@@ -209,13 +229,13 @@ int nvram_read_error_log(char * buff, int length,
 	loff_t tmp_index;
 	struct err_log_info info;
 	
-	if (nvram_error_log_index == -1)
+	if (rtas_log_partition.index == -1)
 		return -1;
 
-	if (length > nvram_error_log_size)
-		length = nvram_error_log_size;
+	if (length > rtas_log_partition.size)
+		length = rtas_log_partition.size;
 
-	tmp_index = nvram_error_log_index;
+	tmp_index = rtas_log_partition.index;
 
 	rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
 	if (rc <= 0) {
@@ -244,10 +264,10 @@ int nvram_clear_error_log(void)
 	int clear_word = ERR_FLAG_ALREADY_LOGGED;
 	int rc;
 
-	if (nvram_error_log_index == -1)
+	if (rtas_log_partition.index == -1)
 		return -1;
 
-	tmp_index = nvram_error_log_index;
+	tmp_index = rtas_log_partition.index;
 	
 	rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
 	if (rc <= 0) {
@@ -258,67 +278,71 @@ int nvram_clear_error_log(void)
 	return 0;
 }
 
-/* pseries_nvram_init_log_partition
+/* pseries_nvram_init_os_partition
  *
- * This will setup the partition we need for buffering the
- * error logs and cleanup partitions if needed.
+ * This set up a partition with an "OS" signature.
  *
  * The general strategy is the following:
- * 1.) If there is log partition large enough then use it.
- * 2.) If there is none large enough, search
- * for a free partition that is large enough.
- * 3.) If there is not a free partition large enough remove 
- * _all_ OS partitions and consolidate the space.
- * 4.) Will first try getting a chunk that will satisfy the maximum
- * error log size (NVRAM_MAX_REQ).
- * 5.) If the max chunk cannot be allocated then try finding a chunk
- * that will satisfy the minum needed (NVRAM_MIN_REQ).
+ * 1.) If a partition with the indicated name already exists...
+ *	- If it's large enough, use it.
+ *	- Otherwise, recycle it and keep going.
+ * 2.) Search for a free partition that is large enough.
+ * 3.) If there's not a free partition large enough, recycle any obsolete
+ * OS partitions and try again.
+ * 4.) Will first try getting a chunk that will satisfy the requested size.
+ * 5.) If a chunk of the requested size cannot be allocated, then try finding
+ * a chunk that will satisfy the minum needed.
+ *
+ * Returns 0 on success, else -1.
  */
-static int __init pseries_nvram_init_log_partition(void)
+static int __init pseries_nvram_init_os_partition(struct os_partition *part)
 {
 	loff_t p;
 	int size;
 
-	p = nvram_find_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, &size);
+	p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size);
 
 	/* Found one but too small, remove it */
-	if (p && size < NVRAM_MIN_REQ) {
-		pr_info("nvram: Found too small "NVRAM_LOG_PART_NAME" partition"
-			",removing it...");
-		nvram_remove_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS);
+	if (p && size < part->min_size) {
+		pr_info("nvram: Found too small %s partition,"
+					" removing it...\n", part->name);
+		nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL);
 		p = 0;
 	}
 
 	/* Create one if we didn't find */
 	if (!p) {
-		p = nvram_create_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS,
-					   NVRAM_MAX_REQ, NVRAM_MIN_REQ);
-		/* No room for it, try to get rid of any OS partition
-		 * and try again
-		 */
+		p = nvram_create_partition(part->name, NVRAM_SIG_OS,
+					part->req_size, part->min_size);
 		if (p == -ENOSPC) {
-			pr_info("nvram: No room to create "NVRAM_LOG_PART_NAME
-				" partition, deleting all OS partitions...");
-			nvram_remove_partition(NULL, NVRAM_SIG_OS);
-			p = nvram_create_partition(NVRAM_LOG_PART_NAME,
-						   NVRAM_SIG_OS, NVRAM_MAX_REQ,
-						   NVRAM_MIN_REQ);
+			pr_info("nvram: No room to create %s partition, "
+				"deleting any obsolete OS partitions...\n",
+				part->name);
+			nvram_remove_partition(NULL, NVRAM_SIG_OS,
+						valid_os_partitions);
+			p = nvram_create_partition(part->name, NVRAM_SIG_OS,
+					part->req_size, part->min_size);
 		}
 	}
 
 	if (p <= 0) {
-		pr_err("nvram: Failed to find or create "NVRAM_LOG_PART_NAME
-		       " partition, err %d\n", (int)p);
-		return 0;
+		pr_err("nvram: Failed to find or create %s"
+		       " partition, err %d\n", part->name, (int)p);
+		return -1;
 	}
 
-	nvram_error_log_index = p;
-	nvram_error_log_size = nvram_get_partition_size(p) -
-		sizeof(struct err_log_info);
+	part->index = p;
+	part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info);
 	
 	return 0;
 }
-machine_late_initcall(pseries, pseries_nvram_init_log_partition);
+
+static int __init pseries_nvram_init_log_partitions(void)
+{
+	(void) pseries_nvram_init_os_partition(&rtas_log_partition);
+	return 0;
+}
+machine_late_initcall(pseries, pseries_nvram_init_log_partitions);
 
 int __init pSeries_nvram_init(void)
 {

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/6] nvram: Capture oops/panic reports in ibm, oops-log partition
  2010-11-14  4:15 [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston
  2010-11-14  4:15 ` [PATCH 1/6] nvram: Generalize code for OS partitions " Jim Keniston
@ 2010-11-14  4:15 ` Jim Keniston
  2011-02-07  5:01   ` Benjamin Herrenschmidt
  2010-11-14  4:15 ` [PATCH 3/6] nvram: Always capture start of oops report to NVRAM Jim Keniston
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 13+ messages in thread
From: Jim Keniston @ 2010-11-14  4:15 UTC (permalink / raw)
  To: linuxppc-dev

Create the ibm,oops-log NVRAM partition, and capture the end of the printk
buffer in it when there's an oops or panic.  If we can't create the
ibm,oops-log partition, capture the oops/panic report in ibm,rtas-log.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---

 arch/powerpc/platforms/pseries/nvram.c |   89 ++++++++++++++++++++++++++++++++
 1 files changed, 88 insertions(+), 1 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 43d5c52..6c88cda 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -16,6 +16,8 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/kmsg_dump.h>
 #include <asm/uaccess.h>
 #include <asm/nvram.h>
 #include <asm/rtas.h>
@@ -50,11 +52,32 @@ static struct os_partition rtas_log_partition = {
 	.index = -1
 };
 
+static struct os_partition oops_log_partition = {
+	.name = "ibm,oops-log",
+	.req_size = 4000,
+	.min_size = 2000,
+	.index = -1
+};
+
 static const char *valid_os_partitions[] = {
 	"ibm,rtas-log",
+	"ibm,oops-log",
 	NULL
 };
 
+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);
+
+static struct kmsg_dumper nvram_kmsg_dumper = {
+	.dump = oops_to_nvram
+};
+
+/* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */
+static size_t oops_buf_sz;
+static char *oops_buf;
+
 static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
 {
 	unsigned int i;
@@ -337,9 +360,36 @@ static int __init pseries_nvram_init_os_partition(struct os_partition *part)
 	return 0;
 }
 
+static void __init nvram_init_oops_partition(int rtas_partition_exists)
+{
+	int rc;
+
+	rc = pseries_nvram_init_os_partition(&oops_log_partition);
+	if (rc != 0) {
+		if (!rtas_partition_exists)
+			return;
+		pr_notice("nvram: Using %s partition to log both"
+			" RTAS errors and oops/panic reports\n",
+			rtas_log_partition.name);
+		memcpy(&oops_log_partition, &rtas_log_partition,
+						sizeof(rtas_log_partition));
+	}
+	oops_buf_sz = oops_log_partition.size - sizeof(struct err_log_info);
+	oops_buf = kmalloc(oops_buf_sz, GFP_KERNEL);
+	rc = kmsg_dump_register(&nvram_kmsg_dumper);
+	if (rc != 0) {
+		pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
+		kfree(oops_buf);
+		return;
+	}
+}
+
 static int __init pseries_nvram_init_log_partitions(void)
 {
-	(void) pseries_nvram_init_os_partition(&rtas_log_partition);
+	int rc;
+
+	rc = pseries_nvram_init_os_partition(&rtas_log_partition);
+	nvram_init_oops_partition(rc == 0);
 	return 0;
 }
 machine_late_initcall(pseries, pseries_nvram_init_log_partitions);
@@ -373,3 +423,40 @@ int __init pSeries_nvram_init(void)
 
 	return 0;
 }
+
+/*
+ * 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;
+	}
+}
+
+/* our kmsg_dump callback */
+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)
+{
+	static unsigned int oops_count = 0;
+	size_t text_len;
+
+	text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
+						oops_buf, oops_buf_sz);
+	(void) nvram_write_os_partition(&oops_log_partition, oops_buf,
+		(int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count);
+}

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/6] nvram: Always capture start of oops report to NVRAM
  2010-11-14  4:15 [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston
  2010-11-14  4:15 ` [PATCH 1/6] nvram: Generalize code for OS partitions " Jim Keniston
  2010-11-14  4:15 ` [PATCH 2/6] nvram: Capture oops/panic reports in ibm, oops-log partition Jim Keniston
@ 2010-11-14  4:15 ` Jim Keniston
  2010-11-14  4:15 ` [PATCH 4/6] nvram: Add compression to fit more printk output into NVRAM Jim Keniston
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Jim Keniston @ 2010-11-14  4:15 UTC (permalink / raw)
  To: linuxppc-dev

If we don't have room to capture the entire oops report, capture as much
as possible, starting 150 chars before the "Oops:" line.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---

 arch/powerpc/platforms/pseries/nvram.c |   91 ++++++++++++++++++++++++++++++++
 1 files changed, 91 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 6c88cda..e1bc1a4 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -446,6 +446,87 @@ static size_t capture_last_msgs(const char *old_msgs, size_t old_len,
 	}
 }
 
+/* Find the last occurrence of needle in haystack, which is haystack_len long.*/
+static const char *strnrstr(const char *haystack, const char *needle,
+							size_t haystack_len)
+{
+	size_t needle_len = strlen(needle);
+	const char *haystack_end = haystack + haystack_len;
+	const char *prev, *next = NULL;
+	do {
+		prev = next;
+		next = strnstr(haystack, needle, haystack_len);
+		if (next) {
+			haystack = next + needle_len;
+			haystack_len = haystack_end - haystack;
+		}
+	} while (next);
+	return prev;
+}
+
+/* The preamble is the last bit of messages logged before the oops. */
+#define PREAMBLE_CHARS 150
+#define OOPS_TAG "Oops: "
+
+/*
+ * Find the beginning of the most recent oops report, back up PREAMBLE_CHARS
+ * characters, and copy up to captured_len characters from there to captured[].
+ * If we can't find the oops, just capture the end of the printk buffer,
+ * if we haven't already.
+ */
+static size_t capture_oops(const char *old_msgs, size_t old_len,
+				const char *new_msgs, size_t new_len,
+				char *captured, size_t capture_len,
+				size_t already_captured)
+{
+	const char *poops;	/* Points to the 'O' in "Oops: ..." */
+	const char *preamble;
+	const char *old_end = old_msgs + old_len;
+	const char *new_end = new_msgs + new_len;
+	size_t nc1, nc2;
+
+	if ((poops = strnrstr(new_msgs, OOPS_TAG, new_len)) != NULL) {
+		/* Oops starts in new_msgs -- the most common case. */
+		preamble = poops - PREAMBLE_CHARS;
+		if (preamble >= new_msgs) {
+			/* preamble is also in new_msgs. */
+			nc1 = min(capture_len, (size_t)(new_end - preamble));
+			memcpy(captured, preamble, nc1);
+			nc2 = 0;
+		} else {
+			/* Have to get some of the preamble from old_msgs */
+			nc1 = min((size_t)(new_msgs - preamble), old_len);
+			memcpy(captured, (old_end - nc1), nc1);
+			nc2 = min(new_len, capture_len - nc1);
+			memcpy(captured + nc1, new_msgs, nc2);
+		}
+	} else if ((poops = strnrstr(old_msgs, OOPS_TAG, old_len)) != NULL) {
+		/* Oops starts in old_msgs. */
+		preamble = poops - PREAMBLE_CHARS;
+		if (preamble < old_msgs)
+			preamble = old_msgs;
+		nc1 = min(capture_len, (size_t)(old_end - preamble));
+		memcpy(captured, preamble, nc1);
+		nc2 = min((size_t)(capture_len - nc1), new_len);
+		memcpy(captured + nc1, new_msgs, nc2);
+	} else {
+		/*
+		 * Either there was a VERY long oops report that scrolled
+		 * out of the printk buffer, or the "Oops" tag is split
+		 * across old_msgs and new_msgs, or oopses don't start with
+		 * "Oops" anymore.  Just capture as much of the last messages
+		 * as we think we can squeeze into NVRAM.
+		 */
+		if (already_captured)
+			return already_captured;
+		nc1 = capture_last_msgs(old_msgs, old_len, new_msgs,
+					new_len, captured, capture_len);
+		nc2 = 0;
+	}
+
+	return nc1 + nc2;
+}
+
 /* our kmsg_dump callback */
 static void oops_to_nvram(struct kmsg_dumper *dumper,
 		enum kmsg_dump_reason reason,
@@ -457,6 +538,16 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
 
 	text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
 						oops_buf, oops_buf_sz);
+	if (reason == KMSG_DUMP_OOPS) {
+		/*
+		 * Ensure that we have the start of the oops report,
+		 * and the message(s) leading up to it.
+		 */
+		const char *poops = strnrstr(oops_buf, OOPS_TAG, oops_buf_sz);
+		if (!poops || poops < oops_buf + PREAMBLE_CHARS)
+			text_len = capture_oops(old_msgs, old_len, new_msgs,
+				new_len, oops_buf, oops_buf_sz, text_len);
+	}
 	(void) nvram_write_os_partition(&oops_log_partition, oops_buf,
 		(int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count);
 }

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/6] nvram: Add compression to fit more printk output into NVRAM
  2010-11-14  4:15 [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston
                   ` (2 preceding siblings ...)
  2010-11-14  4:15 ` [PATCH 3/6] nvram: Always capture start of oops report to NVRAM Jim Keniston
@ 2010-11-14  4:15 ` Jim Keniston
  2010-11-14  4:15 ` [PATCH 5/6] nvram: Slim down zlib_deflate workspace when possible Jim Keniston
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 13+ messages in thread
From: Jim Keniston @ 2010-11-14  4:15 UTC (permalink / raw)
  To: linuxppc-dev

Capture more than twice as much text from the printk buffer, and
compress it to fit it in the ibm,oops-log NVRAM partition.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---

 arch/powerpc/include/asm/rtas.h        |    6 +
 arch/powerpc/platforms/pseries/nvram.c |  195 ++++++++++++++++++++++++++++----
 2 files changed, 177 insertions(+), 24 deletions(-)

diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index 3d35f8a..6e0f4b5 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -205,10 +205,12 @@ extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal);
 #define ERR_FLAG_ALREADY_LOGGED	0x0
 #define ERR_FLAG_BOOT		0x1 	/* log was pulled from NVRAM on boot */
 #define ERR_TYPE_RTAS_LOG	0x2	/* from rtas event-scan */
-#define ERR_TYPE_KERNEL_PANIC	0x4	/* from panic() */
+#define ERR_TYPE_KERNEL_PANIC	0x4	/* from die()/panic() */
+#define ERR_TYPE_KERNEL_PANIC_GZ 0x8	/* ditto, compressed */
 
 /* All the types and not flags */
-#define ERR_TYPE_MASK	(ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC)
+#define ERR_TYPE_MASK \
+	(ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC | ERR_TYPE_KERNEL_PANIC_GZ)
 
 #define RTAS_DEBUG KERN_DEBUG "RTAS: "
  
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index e1bc1a4..8e5ed74 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -17,7 +17,10 @@
 #include <linux/init.h>
 #include <linux/spinlock.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/ctype.h>
 #include <linux/kmsg_dump.h>
+#include <linux/zlib.h>
 #include <asm/uaccess.h>
 #include <asm/nvram.h>
 #include <asm/rtas.h>
@@ -74,9 +77,24 @@ static struct kmsg_dumper nvram_kmsg_dumper = {
 	.dump = oops_to_nvram
 };
 
-/* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */
-static size_t oops_buf_sz;
-static char *oops_buf;
+/*
+ * big_oops_buf[] holds the uncompressed text we're capturing.  little_oops_buf
+ * holds the compressed text, plus its length.  little_oops_buf gets written
+ * to NVRAM.
+ *
+ * We preallocate these buffers during init to avoid kmalloc during oops/panic.
+ */
+static size_t big_oops_buf_sz, little_oops_buf_sz;
+static char *big_oops_buf;
+
+static struct oops_parition_data {
+#define OOPS_PTN_PREFIX_SZ sizeof(unsigned short)
+	unsigned short length;
+	char buf[0];
+} *little_oops_buf;
+
+#define COMPR_LEVEL 6
+static struct z_stream_s stream;
 
 static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
 {
@@ -374,13 +392,42 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
 		memcpy(&oops_log_partition, &rtas_log_partition,
 						sizeof(rtas_log_partition));
 	}
-	oops_buf_sz = oops_log_partition.size - sizeof(struct err_log_info);
-	oops_buf = kmalloc(oops_buf_sz, GFP_KERNEL);
+
+	little_oops_buf_sz = oops_log_partition.size - OOPS_PTN_PREFIX_SZ;
+	little_oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL);
+	if (!little_oops_buf) {
+		pr_err("nvram: No memory for %s partition\n",
+						oops_log_partition.name);
+		return;
+	}
+	/*
+	 * Figure compression (preceded by elimination of each line's <n>
+	 * severity prefix) will reduce the oops/panic report to at most
+	 * 45% of its original size.
+	 */
+	big_oops_buf_sz = (little_oops_buf_sz * 100) / 45;
+	big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
+	if (big_oops_buf) {
+		stream.workspace = vmalloc(zlib_deflate_workspacesize());
+		if (!stream.workspace) {
+			pr_err("nvram: No memory for compression workspace; "
+				"skipping compression of %s partition data\n",
+				oops_log_partition.name);
+			kfree(big_oops_buf);
+			big_oops_buf = NULL;
+		}
+	} else {
+		pr_err("No memory for uncompressed %s data; "
+			"skipping compression\n", oops_log_partition.name);
+		stream.workspace = NULL;
+	}
+
 	rc = kmsg_dump_register(&nvram_kmsg_dumper);
 	if (rc != 0) {
 		pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
-		kfree(oops_buf);
-		return;
+		kfree(little_oops_buf);
+		kfree(big_oops_buf);
+		vfree(stream.workspace);
 	}
 }
 
@@ -527,7 +574,105 @@ static size_t capture_oops(const char *old_msgs, size_t old_len,
 	return nc1 + nc2;
 }
 
-/* our kmsg_dump callback */
+/*
+ * For a panic, capture the last capture_len chars of the printk buffer.
+ * For an oops, ensure that we have the start of the oops report, and the
+ * message(s) leading up to it.
+ */
+static size_t capture_msgs(enum kmsg_dump_reason reason,
+				const char *old_msgs, size_t old_len,
+				const char *new_msgs, size_t new_len,
+				char *captured, size_t capture_len)
+{
+	size_t text_len;
+
+	text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
+						captured, capture_len);
+	if (reason == KMSG_DUMP_OOPS) {
+		const char *poops = strnrstr(captured, OOPS_TAG, capture_len);
+		if (!poops || poops < captured + PREAMBLE_CHARS)
+			text_len = capture_oops(old_msgs, old_len, new_msgs,
+				new_len, captured, capture_len, text_len);
+	}
+	return text_len;
+}
+
+/* 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)
+{
+	int err, ret;
+
+	ret = -EIO;
+	err = zlib_deflateInit(&stream, COMPR_LEVEL);
+	if (err != Z_OK)
+		goto error;
+
+	stream.next_in = in;
+	stream.avail_in = inlen;
+	stream.total_in = 0;
+	stream.next_out = out;
+	stream.avail_out = outlen;
+	stream.total_out = 0;
+
+	err = zlib_deflate(&stream, Z_FINISH);
+	if (err != Z_STREAM_END)
+		goto error;
+
+	err = zlib_deflateEnd(&stream);
+	if (err != Z_OK)
+		goto error;
+
+	if (stream.total_out >= stream.total_in)
+		goto error;
+
+	ret = stream.total_out;
+error:
+	return ret;
+}
+
+/* Compress the text from big_oops_buf into little_oops_buf. */
+static int zip_oops(size_t text_len)
+{
+	int zipped_len = nvram_compress(big_oops_buf, little_oops_buf->buf,
+					text_len, little_oops_buf_sz);
+	if (zipped_len < 0) {
+		pr_err("nvram: compression failed; returned %d\n", zipped_len);
+		pr_err("nvram: logging uncompressed oops/panic report\n");
+		return -1;
+	}
+	little_oops_buf->length = (unsigned short) zipped_len;
+	return 0;
+}
+
+/*
+ * This is our kmsg_dump callback, called after an oops or panic report
+ * has been written to the printk buffer.  We want to capture as much
+ * of the printk buffer as possible.  First, capture as much as we can
+ * that we think will compress sufficiently to fit in the ibm,oops-log
+ * 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,
@@ -535,19 +680,25 @@ static void oops_to_nvram(struct kmsg_dumper *dumper,
 {
 	static unsigned int oops_count = 0;
 	size_t text_len;
-
-	text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
-						oops_buf, oops_buf_sz);
-	if (reason == KMSG_DUMP_OOPS) {
-		/*
-		 * Ensure that we have the start of the oops report,
-		 * and the message(s) leading up to it.
-		 */
-		const char *poops = strnrstr(oops_buf, OOPS_TAG, oops_buf_sz);
-		if (!poops || poops < oops_buf + PREAMBLE_CHARS)
-			text_len = capture_oops(old_msgs, old_len, new_msgs,
-				new_len, oops_buf, oops_buf_sz, text_len);
+	unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ;
+	int rc = -1;
+
+	if (big_oops_buf) {
+		text_len = capture_msgs(reason, old_msgs, old_len,
+			new_msgs, new_len, big_oops_buf, big_oops_buf_sz);
+		text_len = elide_severities(big_oops_buf, text_len);
+		rc = zip_oops(text_len);
 	}
-	(void) nvram_write_os_partition(&oops_log_partition, oops_buf,
-		(int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count);
+	if (rc != 0) {
+		text_len = capture_msgs(reason, old_msgs, old_len,
+				new_msgs, new_len, little_oops_buf->buf,
+				little_oops_buf_sz);
+		err_type = ERR_TYPE_KERNEL_PANIC;
+		little_oops_buf->length = (unsigned short) text_len;
+	}
+
+	(void) nvram_write_os_partition(&oops_log_partition,
+		(char*) little_oops_buf,
+		(int) (OOPS_PTN_PREFIX_SZ + little_oops_buf->length),
+		err_type, ++oops_count);
 }

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 5/6] nvram: Slim down zlib_deflate workspace when possible
  2010-11-14  4:15 [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston
                   ` (3 preceding siblings ...)
  2010-11-14  4:15 ` [PATCH 4/6] nvram: Add compression to fit more printk output into NVRAM Jim Keniston
@ 2010-11-14  4:15 ` Jim Keniston
  2011-02-07  4:39   ` Benjamin Herrenschmidt
  2010-11-14  4:15 ` [PATCH 6/6] nvram: Shrink our zlib_deflate workspace from 268K to 24K Jim Keniston
  2010-11-14  4:36 ` [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston
  6 siblings, 1 reply; 13+ messages in thread
From: Jim Keniston @ 2010-11-14  4:15 UTC (permalink / raw)
  To: linuxppc-dev

Instead of always creating a huge (268K) deflate_workspace with the
maximum compression parameters (windowBits=15, memLevel=8), allow the
caller to obtain a smaller workspace (24K in our case) by specifying
smaller parameter values -- via zlib_deflate_workspacesize2(). In our
case, a small workspace is a win because our choices are to allocate
the workspace when we need it (i.e., during an oops or panic) or
allocate it at boot time.  (We do the latter.)

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---

 include/linux/zlib.h            |   14 ++++++++++++--
 lib/zlib_deflate/deflate.c      |   33 ++++++++++++++++++++++++++++++++-
 lib/zlib_deflate/deflate_syms.c |    1 +
 lib/zlib_deflate/defutil.h      |   17 +++++++++++++----
 4 files changed, 58 insertions(+), 7 deletions(-)

diff --git a/include/linux/zlib.h b/include/linux/zlib.h
index 40c49cb..3f15036 100644
--- a/include/linux/zlib.h
+++ b/include/linux/zlib.h
@@ -179,11 +179,21 @@ typedef z_stream *z_streamp;
 
                         /* basic functions */
 
+extern int zlib_deflate_workspacesize2 (int windowBits, int memLevel);
+/*
+   Returns the number of bytes that needs to be allocated for a per-
+   stream workspace with the specified parameters.  A pointer to this
+   number of bytes should be returned in stream->workspace before
+   calling zlib_deflateInit2(); and the windowBits and memLevel
+   parameters passed to zlib_deflateInit2() must not exceed those
+   passed here.
+*/
+
 extern int zlib_deflate_workspacesize (void);
 /*
    Returns the number of bytes that needs to be allocated for a per-
-   stream workspace.  A pointer to this number of bytes should be
-   returned in stream->workspace before calling zlib_deflateInit().
+   stream workspace with the default (large) windowBits and memLevel
+   parameters.
 */
 
 /* 
diff --git a/lib/zlib_deflate/deflate.c b/lib/zlib_deflate/deflate.c
index 46a31e5..cdb207a 100644
--- a/lib/zlib_deflate/deflate.c
+++ b/lib/zlib_deflate/deflate.c
@@ -176,6 +176,7 @@ int zlib_deflateInit2(
     deflate_state *s;
     int noheader = 0;
     deflate_workspace *mem;
+    char *next;
 
     ush *overlay;
     /* We overlay pending_buf and d_buf+l_buf. This works since the average
@@ -199,6 +200,21 @@ int zlib_deflateInit2(
 	strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
         return Z_STREAM_ERROR;
     }
+
+    /*
+     * Direct the workspace's pointers to the chunks that were allocated
+     * along with the deflate_workspace struct.
+     */
+    next = (char *) mem;
+    next += sizeof(*mem);
+    mem->window_memory = (Byte *) next;
+    next += zlib_deflate_window_memsize(windowBits);
+    mem->prev_memory = (Pos *) next;
+    next += zlib_deflate_prev_memsize(windowBits);
+    mem->head_memory = (Pos *) next;
+    next += zlib_deflate_head_memsize(memLevel);
+    mem->overlay_memory = next;
+
     s = (deflate_state *) &(mem->deflate_memory);
     strm->state = (struct internal_state *)s;
     s->strm = strm;
@@ -1249,5 +1265,20 @@ static block_state deflate_slow(
 
 int zlib_deflate_workspacesize(void)
 {
-    return sizeof(deflate_workspace);
+    return zlib_deflate_workspacesize2(MAX_WBITS, MAX_MEM_LEVEL);
+}
+
+int zlib_deflate_workspacesize2(int windowBits, int memLevel)
+{
+    if (windowBits < 0) /* undocumented feature: suppress zlib header */
+        windowBits = -windowBits;
+    if (memLevel < 1 || memLevel > MAX_MEM_LEVEL ||
+        windowBits < 9 || windowBits > 15)
+        return -1;
+
+    return sizeof(deflate_workspace)
+        + zlib_deflate_window_memsize(windowBits)
+        + zlib_deflate_prev_memsize(windowBits)
+        + zlib_deflate_head_memsize(memLevel)
+        + zlib_deflate_overlay_memsize(memLevel);
 }
diff --git a/lib/zlib_deflate/deflate_syms.c b/lib/zlib_deflate/deflate_syms.c
index ccfe25f..cdf1cdd 100644
--- a/lib/zlib_deflate/deflate_syms.c
+++ b/lib/zlib_deflate/deflate_syms.c
@@ -11,6 +11,7 @@
 #include <linux/zlib.h>
 
 EXPORT_SYMBOL(zlib_deflate_workspacesize);
+EXPORT_SYMBOL(zlib_deflate_workspacesize2);
 EXPORT_SYMBOL(zlib_deflate);
 EXPORT_SYMBOL(zlib_deflateInit2);
 EXPORT_SYMBOL(zlib_deflateEnd);
diff --git a/lib/zlib_deflate/defutil.h b/lib/zlib_deflate/defutil.h
index 6b15a90..b640b64 100644
--- a/lib/zlib_deflate/defutil.h
+++ b/lib/zlib_deflate/defutil.h
@@ -241,12 +241,21 @@ typedef struct deflate_state {
 typedef struct deflate_workspace {
     /* State memory for the deflator */
     deflate_state deflate_memory;
-    Byte window_memory[2 * (1 << MAX_WBITS)];
-    Pos prev_memory[1 << MAX_WBITS];
-    Pos head_memory[1 << (MAX_MEM_LEVEL + 7)];
-    char overlay_memory[(1 << (MAX_MEM_LEVEL + 6)) * (sizeof(ush)+2)];
+    Byte *window_memory;
+    Pos *prev_memory;
+    Pos *head_memory;
+    char *overlay_memory;
 } deflate_workspace;
 
+#define zlib_deflate_window_memsize(windowBits) \
+	(2 * (1 << (windowBits)) * sizeof(Byte))
+#define zlib_deflate_prev_memsize(windowBits) \
+	((1 << (windowBits)) * sizeof(Pos))
+#define zlib_deflate_head_memsize(memLevel) \
+	((1 << ((memLevel)+7)) * sizeof(Pos))
+#define zlib_deflate_overlay_memsize(memLevel) \
+	((1 << ((memLevel)+6)) * (sizeof(ush)+2))
+
 /* Output a byte on the stream.
  * IN assertion: there is enough room in pending_buf.
  */

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 6/6] nvram: Shrink our zlib_deflate workspace from 268K to 24K
  2010-11-14  4:15 [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston
                   ` (4 preceding siblings ...)
  2010-11-14  4:15 ` [PATCH 5/6] nvram: Slim down zlib_deflate workspace when possible Jim Keniston
@ 2010-11-14  4:15 ` Jim Keniston
  2010-11-14  4:36 ` [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston
  6 siblings, 0 replies; 13+ messages in thread
From: Jim Keniston @ 2010-11-14  4:15 UTC (permalink / raw)
  To: linuxppc-dev

Exploit zlib_deflate_workspacesize2() to create a much smaller
zlib_deflate workspace when capturing oops/panic reports to NVRAM.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---

 arch/powerpc/platforms/pseries/nvram.c |   11 +++++++----
 1 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 8e5ed74..6409cb6 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -17,7 +17,6 @@
 #include <linux/init.h>
 #include <linux/spinlock.h>
 #include <linux/slab.h>
-#include <linux/vmalloc.h>
 #include <linux/ctype.h>
 #include <linux/kmsg_dump.h>
 #include <linux/zlib.h>
@@ -94,6 +93,8 @@ static struct oops_parition_data {
 } *little_oops_buf;
 
 #define COMPR_LEVEL 6
+#define WINDOW_BITS 12
+#define MEM_LEVEL 4
 static struct z_stream_s stream;
 
 static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
@@ -408,7 +409,8 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
 	big_oops_buf_sz = (little_oops_buf_sz * 100) / 45;
 	big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
 	if (big_oops_buf) {
-		stream.workspace = vmalloc(zlib_deflate_workspacesize());
+		stream.workspace = kmalloc(zlib_deflate_workspacesize2(
+				WINDOW_BITS, MEM_LEVEL), GFP_KERNEL);
 		if (!stream.workspace) {
 			pr_err("nvram: No memory for compression workspace; "
 				"skipping compression of %s partition data\n",
@@ -427,7 +429,7 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
 		pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
 		kfree(little_oops_buf);
 		kfree(big_oops_buf);
-		vfree(stream.workspace);
+		kfree(stream.workspace);
 	}
 }
 
@@ -625,7 +627,8 @@ static int nvram_compress(const void *in, void *out, size_t inlen,
 	int err, ret;
 
 	ret = -EIO;
-	err = zlib_deflateInit(&stream, COMPR_LEVEL);
+	err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
+						MEM_LEVEL, Z_DEFAULT_STRATEGY);
 	if (err != Z_OK)
 		goto error;
 

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM
  2010-11-14  4:15 [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston
                   ` (5 preceding siblings ...)
  2010-11-14  4:15 ` [PATCH 6/6] nvram: Shrink our zlib_deflate workspace from 268K to 24K Jim Keniston
@ 2010-11-14  4:36 ` Jim Keniston
  6 siblings, 0 replies; 13+ messages in thread
From: Jim Keniston @ 2010-11-14  4:36 UTC (permalink / raw)
  To: linuxppc-dev

On Sat, 2010-11-13 at 20:15 -0800, Jim Keniston wrote:
> This patch series enables p Series systems to capture oops and panic
> reports from the printk buffer into NVRAM, where they can be examined
> after reboot using the nvram command.
> 

Here's a patch to the nvram command to add --unzip and --ascii options,
for examination of oops/panic reports captured in ibm,oops-log or
ibm,rtas-log.

The nvram command is part of powerpc-utils --
git://powerpc-utils.git.sourceforge.net/gitroot/powerpc-utils/powerpc-utils

You can build it using
cc nvram.c -ldl -lz -o nvram

BTW, as far as I can tell, the zlib_deflate code in the kernel can't
produce the header that the gunzip command wants -- hence the reliance
on libz in the nvram command.

Jim
---

 src/nvram.c |  136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 136 insertions(+), 0 deletions(-)

diff --git a/src/nvram.c b/src/nvram.c
index d25e073..e00ae12 100644
--- a/src/nvram.c
+++ b/src/nvram.c
@@ -43,6 +43,7 @@
 #include <glob.h>
 #include <getopt.h>
 #include <inttypes.h>
+#include <zlib.h>
 
 #include "nvram.h"
 
@@ -62,6 +63,8 @@ static struct option long_options[] = {
     {"print-event-scan", 	no_argument, 	   NULL, 'E'},
     {"partitions", 		no_argument, 	   NULL, 'P'},
     {"dump", 			required_argument, NULL, 'd'},
+    {"ascii",			required_argument, NULL, 'a'},
+    {"unzip", 			required_argument, NULL, 'z'},
     {"nvram-file", 		required_argument, NULL, 'n'},
     {"nvram-size", 		required_argument, NULL, 's'},
     {"update-config",		required_argument, NULL, 'u'},
@@ -99,6 +102,10 @@ help(void)
     "          print NVRAM paritition header info\n"
     "  --dump <name>\n"
     "          raw dump of partition (use --partitions to see names)\n"
+    "  --ascii <name>\n"
+    "          print partition contents as ASCII text\n"
+    "  --unzip <name>\n"
+    "          decompress and print compressed data from partition\n"
     "  --nvram-file <path>\n"
     "          specify alternate nvram data file (default is /dev/nvram)\n"
     "  --nvram-size\n"
@@ -1189,6 +1196,121 @@ dump_raw_partition(struct nvram *nvram, char *name)
 }
 
 /**
+ * dump_ascii_partition
+ * @brief ASCII data dump of a partition, excluding header
+ *
+ * @param nvram nvram struct containing partition
+ * @param name name of partition to dump
+ * @return 0 on success, !0 otherwise
+ *
+ * Partition subheaders, if any, are dumped along with the rest of the data.
+ * We substitute periods for unprintable characters.
+ */
+int
+dump_ascii_partition(struct nvram *nvram, char *name)
+{
+    struct partition_header *phead;
+    char *start, *end, *c;
+
+    phead = nvram_find_partition(nvram, 0, name, NULL);
+    if (!phead) {
+	err_msg("there is no %s partition!\n", name);
+	return -1;
+    }
+    
+    start = (char*) phead;
+    end = start + phead->length * NVRAM_BLOCK_SIZE;
+    start += sizeof(*phead);	/* Skip partition header. */
+    for (c = start; c < end; c++) {
+	if (isprint(*c) || isspace(*c))
+	    putchar(*c);
+	else
+	    putchar('.');
+    }
+    /* Always end with a newline.*/
+    putchar('\n');
+    return 0;
+}
+
+int
+dump_zipped_text(char *zipped_text, unsigned int zipped_length)
+{
+    z_stream strm;
+    int result;
+    char unzipped_text[4096];
+
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    strm.opaque = Z_NULL;
+    strm.avail_in = zipped_length;
+    strm.next_in = zipped_text;
+    result = inflateInit(&strm);
+    if (result != Z_OK) {
+    	err_msg("can't decompress text: inflateInit() returned %d\n", result);
+	return -1;
+    }
+
+    do {
+	strm.avail_out = 4096;
+	strm.next_out = unzipped_text;
+    	result = inflate(&strm, Z_NO_FLUSH);
+	switch (result) {
+	case Z_STREAM_ERROR:
+	case Z_NEED_DICT:
+	case Z_DATA_ERROR:
+	case Z_MEM_ERROR:
+	    err_msg("can't decompress text: inflate() returned %d\n", result);
+	    (void) inflateEnd(&strm);
+	    return -1;
+	}
+	if (fwrite(unzipped_text, 4096 - strm.avail_out, 1, stdout) != 1) {
+	    err_msg("can't decompress text: fwrite() failed\n");
+	    (void) inflateEnd(&strm);
+	    return -1;
+	}
+    } while (strm.avail_out == 0);
+
+    (void) inflateEnd(&strm);
+    return 0;
+}
+
+/**
+ * unzip_partition
+ * @brief Uncompress and print compressed data from a partition.
+ *
+ * @param nvram nvram struct containing partition
+ * @param name name of partition to dump
+ * @return 0 on success, !0 otherwise
+ */
+int
+unzip_partition(struct nvram *nvram, char *name)
+{
+    struct partition_header *phead;
+    char *start, *next;
+    unsigned short zipped_length;
+
+    phead = nvram_find_partition(nvram, 0, name, NULL);
+    if (!phead) {
+	err_msg("there is no %s partition!\n", name);
+	return -1;
+    }
+    
+    start = (char*) phead;
+    next = start + sizeof(*phead);	/* Skip partition header. */
+    next += sizeof(struct err_log_info);	/* Skip sub-header. */
+    zipped_length = *((unsigned short*) next);
+    next += sizeof(unsigned short);		/* Skip compressed length. */
+
+    if ((next-start) + zipped_length > phead->length * NVRAM_BLOCK_SIZE) {
+    	err_msg("bogus size for compressed data in partition %s: %u\n", name,
+	    zipped_length);
+	return -1;
+    }
+
+    return dump_zipped_text(next, zipped_length);
+}
+
+/**
  * print_of_config_part
  * @brief Print the name/value pairs of a partition
  *
@@ -1476,6 +1598,8 @@ main (int argc, char *argv[])
     int print_event_scan = 0;
     int	print_config_var = 0;
     char *dump_name = NULL;
+    char *ascii_name = NULL;
+    char *zip_name = NULL;
     char *update_config_var = NULL;
     char *config_pname = "common";
 
@@ -1504,6 +1628,12 @@ main (int argc, char *argv[])
 	    case 'd':	/* dump */
 		dump_name = optarg;
 		break;
+	    case 'a':	/* ASCII dump */
+	    	ascii_name = optarg;
+		break;
+	    case 'z':	/* dump compressed data */
+		zip_name = optarg;
+		break;
 	    case 'n':	/* nvram-file */
 		nvram.filename = optarg;
 		break;
@@ -1641,6 +1771,12 @@ main (int argc, char *argv[])
     if (dump_name)
 	if (dump_raw_partition(&nvram, dump_name) != 0)
 	    ret = -1;
+    if (ascii_name)
+	if (dump_ascii_partition(&nvram, ascii_name) != 0)
+	    ret = -1;
+    if (zip_name)
+	if (unzip_partition(&nvram, zip_name) != 0)
+	    ret = -1;
    
 err_exit:   
    if (nvram.data)

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 5/6] nvram: Slim down zlib_deflate workspace when possible
  2010-11-14  4:15 ` [PATCH 5/6] nvram: Slim down zlib_deflate workspace when possible Jim Keniston
@ 2011-02-07  4:39   ` Benjamin Herrenschmidt
  0 siblings, 0 replies; 13+ messages in thread
From: Benjamin Herrenschmidt @ 2011-02-07  4:39 UTC (permalink / raw)
  To: Jim Keniston; +Cc: linuxppc-dev

On Sat, 2010-11-13 at 20:15 -0800, Jim Keniston wrote:
> Instead of always creating a huge (268K) deflate_workspace with the
> maximum compression parameters (windowBits=15, memLevel=8), allow the
> caller to obtain a smaller workspace (24K in our case) by specifying
> smaller parameter values -- via zlib_deflate_workspacesize2(). In our
> case, a small workspace is a win because our choices are to allocate
> the workspace when we need it (i.e., during an oops or panic) or
> allocate it at boot time.  (We do the latter.)
> 
> Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
> ---

Can you submit that to lkml please ? CC Linus and Andrew (or send to
Andrew/Linus and CC lkml). This change shouldn't go via the powerpc tree
since it changes the generic zlib code.

I'm dropping 5/6 and 6/6 for now... still reviewing the rest.

Cheers,
Ben.

>  include/linux/zlib.h            |   14 ++++++++++++--
>  lib/zlib_deflate/deflate.c      |   33 ++++++++++++++++++++++++++++++++-
>  lib/zlib_deflate/deflate_syms.c |    1 +
>  lib/zlib_deflate/defutil.h      |   17 +++++++++++++----
>  4 files changed, 58 insertions(+), 7 deletions(-)
> 
> diff --git a/include/linux/zlib.h b/include/linux/zlib.h
> index 40c49cb..3f15036 100644
> --- a/include/linux/zlib.h
> +++ b/include/linux/zlib.h
> @@ -179,11 +179,21 @@ typedef z_stream *z_streamp;
>  
>                          /* basic functions */
>  
> +extern int zlib_deflate_workspacesize2 (int windowBits, int memLevel);
> +/*
> +   Returns the number of bytes that needs to be allocated for a per-
> +   stream workspace with the specified parameters.  A pointer to this
> +   number of bytes should be returned in stream->workspace before
> +   calling zlib_deflateInit2(); and the windowBits and memLevel
> +   parameters passed to zlib_deflateInit2() must not exceed those
> +   passed here.
> +*/
> +
>  extern int zlib_deflate_workspacesize (void);
>  /*
>     Returns the number of bytes that needs to be allocated for a per-
> -   stream workspace.  A pointer to this number of bytes should be
> -   returned in stream->workspace before calling zlib_deflateInit().
> +   stream workspace with the default (large) windowBits and memLevel
> +   parameters.
>  */
>  
>  /* 
> diff --git a/lib/zlib_deflate/deflate.c b/lib/zlib_deflate/deflate.c
> index 46a31e5..cdb207a 100644
> --- a/lib/zlib_deflate/deflate.c
> +++ b/lib/zlib_deflate/deflate.c
> @@ -176,6 +176,7 @@ int zlib_deflateInit2(
>      deflate_state *s;
>      int noheader = 0;
>      deflate_workspace *mem;
> +    char *next;
>  
>      ush *overlay;
>      /* We overlay pending_buf and d_buf+l_buf. This works since the average
> @@ -199,6 +200,21 @@ int zlib_deflateInit2(
>  	strategy < 0 || strategy > Z_HUFFMAN_ONLY) {
>          return Z_STREAM_ERROR;
>      }
> +
> +    /*
> +     * Direct the workspace's pointers to the chunks that were allocated
> +     * along with the deflate_workspace struct.
> +     */
> +    next = (char *) mem;
> +    next += sizeof(*mem);
> +    mem->window_memory = (Byte *) next;
> +    next += zlib_deflate_window_memsize(windowBits);
> +    mem->prev_memory = (Pos *) next;
> +    next += zlib_deflate_prev_memsize(windowBits);
> +    mem->head_memory = (Pos *) next;
> +    next += zlib_deflate_head_memsize(memLevel);
> +    mem->overlay_memory = next;
> +
>      s = (deflate_state *) &(mem->deflate_memory);
>      strm->state = (struct internal_state *)s;
>      s->strm = strm;
> @@ -1249,5 +1265,20 @@ static block_state deflate_slow(
>  
>  int zlib_deflate_workspacesize(void)
>  {
> -    return sizeof(deflate_workspace);
> +    return zlib_deflate_workspacesize2(MAX_WBITS, MAX_MEM_LEVEL);
> +}
> +
> +int zlib_deflate_workspacesize2(int windowBits, int memLevel)
> +{
> +    if (windowBits < 0) /* undocumented feature: suppress zlib header */
> +        windowBits = -windowBits;
> +    if (memLevel < 1 || memLevel > MAX_MEM_LEVEL ||
> +        windowBits < 9 || windowBits > 15)
> +        return -1;
> +
> +    return sizeof(deflate_workspace)
> +        + zlib_deflate_window_memsize(windowBits)
> +        + zlib_deflate_prev_memsize(windowBits)
> +        + zlib_deflate_head_memsize(memLevel)
> +        + zlib_deflate_overlay_memsize(memLevel);
>  }
> diff --git a/lib/zlib_deflate/deflate_syms.c b/lib/zlib_deflate/deflate_syms.c
> index ccfe25f..cdf1cdd 100644
> --- a/lib/zlib_deflate/deflate_syms.c
> +++ b/lib/zlib_deflate/deflate_syms.c
> @@ -11,6 +11,7 @@
>  #include <linux/zlib.h>
>  
>  EXPORT_SYMBOL(zlib_deflate_workspacesize);
> +EXPORT_SYMBOL(zlib_deflate_workspacesize2);
>  EXPORT_SYMBOL(zlib_deflate);
>  EXPORT_SYMBOL(zlib_deflateInit2);
>  EXPORT_SYMBOL(zlib_deflateEnd);
> diff --git a/lib/zlib_deflate/defutil.h b/lib/zlib_deflate/defutil.h
> index 6b15a90..b640b64 100644
> --- a/lib/zlib_deflate/defutil.h
> +++ b/lib/zlib_deflate/defutil.h
> @@ -241,12 +241,21 @@ typedef struct deflate_state {
>  typedef struct deflate_workspace {
>      /* State memory for the deflator */
>      deflate_state deflate_memory;
> -    Byte window_memory[2 * (1 << MAX_WBITS)];
> -    Pos prev_memory[1 << MAX_WBITS];
> -    Pos head_memory[1 << (MAX_MEM_LEVEL + 7)];
> -    char overlay_memory[(1 << (MAX_MEM_LEVEL + 6)) * (sizeof(ush)+2)];
> +    Byte *window_memory;
> +    Pos *prev_memory;
> +    Pos *head_memory;
> +    char *overlay_memory;
>  } deflate_workspace;
>  
> +#define zlib_deflate_window_memsize(windowBits) \
> +	(2 * (1 << (windowBits)) * sizeof(Byte))
> +#define zlib_deflate_prev_memsize(windowBits) \
> +	((1 << (windowBits)) * sizeof(Pos))
> +#define zlib_deflate_head_memsize(memLevel) \
> +	((1 << ((memLevel)+7)) * sizeof(Pos))
> +#define zlib_deflate_overlay_memsize(memLevel) \
> +	((1 << ((memLevel)+6)) * (sizeof(ush)+2))
> +
>  /* Output a byte on the stream.
>   * IN assertion: there is enough room in pending_buf.
>   */
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/6] nvram: Generalize code for OS partitions in NVRAM
  2010-11-14  4:15 ` [PATCH 1/6] nvram: Generalize code for OS partitions " Jim Keniston
@ 2011-02-07  4:57   ` Benjamin Herrenschmidt
  2011-02-09 22:43     ` Jim Keniston
  0 siblings, 1 reply; 13+ messages in thread
From: Benjamin Herrenschmidt @ 2011-02-07  4:57 UTC (permalink / raw)
  To: Jim Keniston; +Cc: linuxppc-dev

On Sat, 2010-11-13 at 20:15 -0800, Jim Keniston wrote:
> Adapt the functions used to create and write to the RTAS-log partition
> to work with any OS-type partition.
> 
> Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
> ---

Overall pretty good (sorry for taking that long to review, I've been
swamped down). Just a handful of minor nits:

> +/*
> + * Per the criteria passed via nvram_remove_partition(), should this
> + * partition be removed?  1=remove, 0=keep
> + */
> +static int nvram_condemn_partition(struct nvram_partition *part,
> +		const char *name, int sig, const char *exceptions[])

As "fun" as this name is, it didn't help me understand what the function
was about until I read the code for the next one :-)

What about instead something like nvram_can_remove_partition() or
something a bit more explicit like that ?

"comdemn" made me thought it was a function used to "mark" partitions
to be killed later, which is not what the function does.

 .../...

> +static const char *valid_os_partitions[] = {
> +	"ibm,rtas-log",
> +	NULL
> +};

Can you pick a name that will be less confusing in the global
symbol table ? Something like "pseries_nvram_os_partitions"
or whatever shorter you can come up with which doesn't suck ?

Also, should we move that "os partition" core out of pseries ?

Looks like it will be useful for a few other platforms, I'd like
to move that to a more generically useful location in arch/powerpc
but that's not a blocker for this series but something to do next
maybe ?

In that case "struct os_partition" should also find itself a better
name, maybe struct nvram_os_partition ?

>  static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
>  {
> @@ -134,7 +147,7 @@ static ssize_t pSeries_nvram_get_size(void)
>  }
>  
> 
> -/* nvram_write_error_log
> +/* nvram_write_os_partition, nvram_write_error_log
>   *
>   * We need to buffer the error logs into nvram to ensure that we have
>   * the failure information to decode.  If we have a severe error there
> @@ -156,48 +169,55 @@ static ssize_t pSeries_nvram_get_size(void)
>   * The 'data' section would look like (in bytes):
>   * +--------------+------------+-----------------------------------+
>   * | event_logged | sequence # | error log                         |
> - * |0            3|4          7|8            nvram_error_log_size-1|
> + * |0            3|4          7|8                  error_log_size-1|
>   * +--------------+------------+-----------------------------------+
>   *
>   * event_logged: 0 if event has not been logged to syslog, 1 if it has
>   * sequence #: The unique sequence # for each event. (until it wraps)
>   * error log: The error log from event_scan
>   */
> -int nvram_write_error_log(char * buff, int length,
> +int nvram_write_os_partition(struct os_partition *part, char * buff, int length,
>                            unsigned int err_type, unsigned int error_log_cnt)
>  {
>  	int rc;
>  	loff_t tmp_index;
>  	struct err_log_info info;
>  	
> -	if (nvram_error_log_index == -1) {
> +	if (part->index == -1) {
>  		return -ESPIPE;
>  	}
>  
> -	if (length > nvram_error_log_size) {
> -		length = nvram_error_log_size;
> +	if (length > part->size) {
> +		length = part->size;
>  	}
>  
>  	info.error_type = err_type;
>  	info.seq_num = error_log_cnt;
>  
> -	tmp_index = nvram_error_log_index;
> +	tmp_index = part->index;
>  
>  	rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
>  	if (rc <= 0) {
> -		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
> +		printk(KERN_ERR "nvram_write_os_partition: Failed nvram_write (%d)\n", rc);
>  		return rc;
>  	}
>  
>  	rc = ppc_md.nvram_write(buff, length, &tmp_index);
>  	if (rc <= 0) {
> -		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
> +		printk(KERN_ERR "nvram_write_os_partition: Failed nvram_write (%d)\n", rc);
>  		return rc;
>  	}
>  	
>  	return 0;
>  }

While at it, turn these into pr_err and use __FUNCTION__ or __func__

> +int nvram_write_error_log(char * buff, int length,
> +                          unsigned int err_type, unsigned int error_log_cnt)
> +{
> +	return nvram_write_os_partition(&rtas_log_partition, buff, length,
> +						err_type, error_log_cnt);
> +}

That's a candidate for a static inline in a .h

>  /* nvram_read_error_log
>   *
>   * Reads nvram for error log for at most 'length'
> @@ -209,13 +229,13 @@ int nvram_read_error_log(char * buff, int length,
>  	loff_t tmp_index;
>  	struct err_log_info info;
>  	
> -	if (nvram_error_log_index == -1)
> +	if (rtas_log_partition.index == -1)
>  		return -1;
>  
> -	if (length > nvram_error_log_size)
> -		length = nvram_error_log_size;
> +	if (length > rtas_log_partition.size)
> +		length = rtas_log_partition.size;
>  
> -	tmp_index = nvram_error_log_index;
> +	tmp_index = rtas_log_partition.index;
>  
>  	rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
>  	if (rc <= 0) {
> @@ -244,10 +264,10 @@ int nvram_clear_error_log(void)
>  	int clear_word = ERR_FLAG_ALREADY_LOGGED;
>  	int rc;
>  
> -	if (nvram_error_log_index == -1)
> +	if (rtas_log_partition.index == -1)
>  		return -1;
>  
> -	tmp_index = nvram_error_log_index;
> +	tmp_index = rtas_log_partition.index;
>  	
>  	rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
>  	if (rc <= 0) {
> @@ -258,67 +278,71 @@ int nvram_clear_error_log(void)
>  	return 0;
>  }
>  
> -/* pseries_nvram_init_log_partition
> +/* pseries_nvram_init_os_partition
>   *
> - * This will setup the partition we need for buffering the
> - * error logs and cleanup partitions if needed.
> + * This set up a partition with an "OS" signature.
>   *
>   * The general strategy is the following:
> - * 1.) If there is log partition large enough then use it.
> - * 2.) If there is none large enough, search
> - * for a free partition that is large enough.
> - * 3.) If there is not a free partition large enough remove 
> - * _all_ OS partitions and consolidate the space.
> - * 4.) Will first try getting a chunk that will satisfy the maximum
> - * error log size (NVRAM_MAX_REQ).
> - * 5.) If the max chunk cannot be allocated then try finding a chunk
> - * that will satisfy the minum needed (NVRAM_MIN_REQ).
> + * 1.) If a partition with the indicated name already exists...
> + *	- If it's large enough, use it.
> + *	- Otherwise, recycle it and keep going.
> + * 2.) Search for a free partition that is large enough.
> + * 3.) If there's not a free partition large enough, recycle any obsolete
> + * OS partitions and try again.
> + * 4.) Will first try getting a chunk that will satisfy the requested size.
> + * 5.) If a chunk of the requested size cannot be allocated, then try finding
> + * a chunk that will satisfy the minum needed.
> + *
> + * Returns 0 on success, else -1.
>   */
> -static int __init pseries_nvram_init_log_partition(void)
> +static int __init pseries_nvram_init_os_partition(struct os_partition *part)
>  {
>  	loff_t p;
>  	int size;
>  
> -	p = nvram_find_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, &size);
> +	p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size);
>  
>  	/* Found one but too small, remove it */
> -	if (p && size < NVRAM_MIN_REQ) {
> -		pr_info("nvram: Found too small "NVRAM_LOG_PART_NAME" partition"
> -			",removing it...");
> -		nvram_remove_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS);
> +	if (p && size < part->min_size) {
> +		pr_info("nvram: Found too small %s partition,"
> +					" removing it...\n", part->name);
> +		nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL);
>  		p = 0;
>  	}
>  
>  	/* Create one if we didn't find */
>  	if (!p) {
> -		p = nvram_create_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS,
> -					   NVRAM_MAX_REQ, NVRAM_MIN_REQ);
> -		/* No room for it, try to get rid of any OS partition
> -		 * and try again
> -		 */
> +		p = nvram_create_partition(part->name, NVRAM_SIG_OS,
> +					part->req_size, part->min_size);
>  		if (p == -ENOSPC) {
> -			pr_info("nvram: No room to create "NVRAM_LOG_PART_NAME
> -				" partition, deleting all OS partitions...");
> -			nvram_remove_partition(NULL, NVRAM_SIG_OS);
> -			p = nvram_create_partition(NVRAM_LOG_PART_NAME,
> -						   NVRAM_SIG_OS, NVRAM_MAX_REQ,
> -						   NVRAM_MIN_REQ);
> +			pr_info("nvram: No room to create %s partition, "
> +				"deleting any obsolete OS partitions...\n",
> +				part->name);
> +			nvram_remove_partition(NULL, NVRAM_SIG_OS,
> +						valid_os_partitions);
> +			p = nvram_create_partition(part->name, NVRAM_SIG_OS,
> +					part->req_size, part->min_size);
>  		}
>  	}
>  
>  	if (p <= 0) {
> -		pr_err("nvram: Failed to find or create "NVRAM_LOG_PART_NAME
> -		       " partition, err %d\n", (int)p);
> -		return 0;
> +		pr_err("nvram: Failed to find or create %s"
> +		       " partition, err %d\n", part->name, (int)p);
> +		return -1;
>  	}
>  
> -	nvram_error_log_index = p;
> -	nvram_error_log_size = nvram_get_partition_size(p) -
> -		sizeof(struct err_log_info);
> +	part->index = p;
> +	part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info);
>  	
>  	return 0;
>  }
> -machine_late_initcall(pseries, pseries_nvram_init_log_partition);
> +
> +static int __init pseries_nvram_init_log_partitions(void)
> +{
> +	(void) pseries_nvram_init_os_partition(&rtas_log_partition);
> +	return 0;
> +}
> +machine_late_initcall(pseries, pseries_nvram_init_log_partitions);
>  
>  int __init pSeries_nvram_init(void)
>  {
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/6] nvram: Capture oops/panic reports in ibm, oops-log partition
  2010-11-14  4:15 ` [PATCH 2/6] nvram: Capture oops/panic reports in ibm, oops-log partition Jim Keniston
@ 2011-02-07  5:01   ` Benjamin Herrenschmidt
  2011-02-09 23:00     ` Jim Keniston
  0 siblings, 1 reply; 13+ messages in thread
From: Benjamin Herrenschmidt @ 2011-02-07  5:01 UTC (permalink / raw)
  To: Jim Keniston; +Cc: linuxppc-dev

On Sat, 2010-11-13 at 20:15 -0800, Jim Keniston wrote:
> Create the ibm,oops-log NVRAM partition, and capture the end of the printk
> buffer in it when there's an oops or panic.  If we can't create the
> ibm,oops-log partition, capture the oops/panic report in ibm,rtas-log.

Won't the parsing tools choke on the oops log in the RTAS log
partition ?

Also, maybe remove the "ibm," prefix on the nvram partition, make it
"lnx," instead.

Then, if you move the rest of the code out of pseries, you can move that
out too, just make pseries platform code call the init code for it, that
way other platforms can re-use that as-is.

But that later part can wait a separate patch series, just make sure you
change the partition name now.

Cheers,
Ben.

> Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
> ---
> 
>  arch/powerpc/platforms/pseries/nvram.c |   89 ++++++++++++++++++++++++++++++++
>  1 files changed, 88 insertions(+), 1 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
> index 43d5c52..6c88cda 100644
> --- a/arch/powerpc/platforms/pseries/nvram.c
> +++ b/arch/powerpc/platforms/pseries/nvram.c
> @@ -16,6 +16,8 @@
>  #include <linux/errno.h>
>  #include <linux/init.h>
>  #include <linux/spinlock.h>
> +#include <linux/slab.h>
> +#include <linux/kmsg_dump.h>
>  #include <asm/uaccess.h>
>  #include <asm/nvram.h>
>  #include <asm/rtas.h>
> @@ -50,11 +52,32 @@ static struct os_partition rtas_log_partition = {
>  	.index = -1
>  };
>  
> +static struct os_partition oops_log_partition = {
> +	.name = "ibm,oops-log",
> +	.req_size = 4000,
> +	.min_size = 2000,
> +	.index = -1
> +};
> +
>  static const char *valid_os_partitions[] = {
>  	"ibm,rtas-log",
> +	"ibm,oops-log",
>  	NULL
>  };
>  
> +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);
> +
> +static struct kmsg_dumper nvram_kmsg_dumper = {
> +	.dump = oops_to_nvram
> +};
> +
> +/* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */
> +static size_t oops_buf_sz;
> +static char *oops_buf;
> +
>  static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
>  {
>  	unsigned int i;
> @@ -337,9 +360,36 @@ static int __init pseries_nvram_init_os_partition(struct os_partition *part)
>  	return 0;
>  }
>  
> +static void __init nvram_init_oops_partition(int rtas_partition_exists)
> +{
> +	int rc;
> +
> +	rc = pseries_nvram_init_os_partition(&oops_log_partition);
> +	if (rc != 0) {
> +		if (!rtas_partition_exists)
> +			return;
> +		pr_notice("nvram: Using %s partition to log both"
> +			" RTAS errors and oops/panic reports\n",
> +			rtas_log_partition.name);
> +		memcpy(&oops_log_partition, &rtas_log_partition,
> +						sizeof(rtas_log_partition));
> +	}
> +	oops_buf_sz = oops_log_partition.size - sizeof(struct err_log_info);
> +	oops_buf = kmalloc(oops_buf_sz, GFP_KERNEL);
> +	rc = kmsg_dump_register(&nvram_kmsg_dumper);
> +	if (rc != 0) {
> +		pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
> +		kfree(oops_buf);
> +		return;
> +	}
> +}
> +
>  static int __init pseries_nvram_init_log_partitions(void)
>  {
> -	(void) pseries_nvram_init_os_partition(&rtas_log_partition);
> +	int rc;
> +
> +	rc = pseries_nvram_init_os_partition(&rtas_log_partition);
> +	nvram_init_oops_partition(rc == 0);
>  	return 0;
>  }
>  machine_late_initcall(pseries, pseries_nvram_init_log_partitions);
> @@ -373,3 +423,40 @@ int __init pSeries_nvram_init(void)
>  
>  	return 0;
>  }
> +
> +/*
> + * 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;
> +	}
> +}
> +
> +/* our kmsg_dump callback */
> +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)
> +{
> +	static unsigned int oops_count = 0;
> +	size_t text_len;
> +
> +	text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
> +						oops_buf, oops_buf_sz);
> +	(void) nvram_write_os_partition(&oops_log_partition, oops_buf,
> +		(int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count);
> +}
> 
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/6] nvram: Generalize code for OS partitions in NVRAM
  2011-02-07  4:57   ` Benjamin Herrenschmidt
@ 2011-02-09 22:43     ` Jim Keniston
  0 siblings, 0 replies; 13+ messages in thread
From: Jim Keniston @ 2011-02-09 22:43 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev

On Mon, 2011-02-07 at 15:57 +1100, Benjamin Herrenschmidt wrote:
> On Sat, 2010-11-13 at 20:15 -0800, Jim Keniston wrote:
> > Adapt the functions used to create and write to the RTAS-log partition
> > to work with any OS-type partition.
> > 
> > Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
> > ---
> 
> Overall pretty good (sorry for taking that long to review, I've been
> swamped down). Just a handful of minor nits:

I've appended an updated patch, which addresses your comments and also
applies to 2.6.38-rc4, rather than to your kernel as it stood on Nov.
13.  See below for responses to comments.

> 
> > +/*
> > + * Per the criteria passed via nvram_remove_partition(), should this
> > + * partition be removed?  1=remove, 0=keep
> > + */
> > +static int nvram_condemn_partition(struct nvram_partition *part,
> > +		const char *name, int sig, const char *exceptions[])
> 
> As "fun" as this name is, it didn't help me understand what the function
> was about until I read the code for the next one :-)
> 
> What about instead something like nvram_can_remove_partition() or
> something a bit more explicit like that ?

Done.

...
>  .../...
> 
> > +static const char *valid_os_partitions[] = {
> > +	"ibm,rtas-log",
> > +	NULL
> > +};
> 
> Can you pick a name that will be less confusing in the global
> symbol table ? Something like "pseries_nvram_os_partitions"

Done.

> or whatever shorter you can come up with which doesn't suck ?
> 
> Also, should we move that "os partition" core out of pseries ?
> 
> Looks like it will be useful for a few other platforms, I'd like
> to move that to a more generically useful location in arch/powerpc
> but that's not a blocker for this series but something to do next
> maybe ?

I haven't tried to do that reorganization.

> 
> In that case "struct os_partition" should also find itself a better
> name, maybe struct nvram_os_partition ?

Done.

...
> >  	if (rc <= 0) {
> > -		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
> > +		printk(KERN_ERR "nvram_write_os_partition: Failed nvram_write (%d)\n", rc);
> >  		return rc;
> >  	}
> >  	
> >  	return 0;
> >  }
> 
> While at it, turn these into pr_err and use __FUNCTION__ or __func__

Done.  However, I didn't convert any printks in functions that I wasn't
otherwise changing.

> 
> > +int nvram_write_error_log(char * buff, int length,
> > +                          unsigned int err_type, unsigned int error_log_cnt)
> > +{
> > +	return nvram_write_os_partition(&rtas_log_partition, buff, length,
> > +						err_type, error_log_cnt);
> > +}
> 
> That's a candidate for a static inline in a .h

Maybe, but in my next patch this function adjusts a static variable, so
I figure that putting it in a header would be messy.

Here's my revised patch.
Jim
=====
Generalize code for OS partitions in NVRAM

Adapt the functions used to create and write to the RTAS-log partition
to work with any OS-type partition.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---

 arch/powerpc/include/asm/nvram.h       |    3 -
 arch/powerpc/kernel/nvram_64.c         |   31 ++++++-
 arch/powerpc/platforms/pseries/nvram.c |  143 +++++++++++++++++++-------------
 3 files changed, 113 insertions(+), 64 deletions(-)

diff --git a/arch/powerpc/include/asm/nvram.h b/arch/powerpc/include/asm/nvram.h
index 92efe67..9d1aafe 100644
--- a/arch/powerpc/include/asm/nvram.h
+++ b/arch/powerpc/include/asm/nvram.h
@@ -51,7 +51,8 @@ static inline int mmio_nvram_init(void)
 extern int __init nvram_scan_partitions(void);
 extern loff_t nvram_create_partition(const char *name, int sig,
 				     int req_size, int min_size);
-extern int nvram_remove_partition(const char *name, int sig);
+extern int nvram_remove_partition(const char *name, int sig,
+					const char *exceptions[]);
 extern int nvram_get_partition_size(loff_t data_index);
 extern loff_t nvram_find_partition(const char *name, int sig, int *out_size);
 
diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c
index bb12b32..bec1e93 100644
--- a/arch/powerpc/kernel/nvram_64.c
+++ b/arch/powerpc/kernel/nvram_64.c
@@ -237,22 +237,45 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
 	return c_sum;
 }
 
+/*
+ * Per the criteria passed via nvram_remove_partition(), should this
+ * partition be removed?  1=remove, 0=keep
+ */
+static int nvram_can_remove_partition(struct nvram_partition *part,
+		const char *name, int sig, const char *exceptions[])
+{
+	if (part->header.signature != sig)
+		return 0;
+	if (name) {
+		if (strncmp(name, part->header.name, 12))
+			return 0;
+	} else if (exceptions) {
+		const char **except;
+		for (except = exceptions; *except; except++) {
+			if (!strncmp(*except, part->header.name, 12))
+				return 0;
+		}
+	}
+	return 1;
+}
+
 /**
  * nvram_remove_partition - Remove one or more partitions in nvram
  * @name: name of the partition to remove, or NULL for a
  *        signature only match
  * @sig: signature of the partition(s) to remove
+ * @exceptions: When removing all partitions with a matching signature,
+ *        leave these alone.
  */
 
-int __init nvram_remove_partition(const char *name, int sig)
+int __init nvram_remove_partition(const char *name, int sig,
+						const char *exceptions[])
 {
 	struct nvram_partition *part, *prev, *tmp;
 	int rc;
 
 	list_for_each_entry(part, &nvram_partitions, partition) {
-		if (part->header.signature != sig)
-			continue;
-		if (name && strncmp(name, part->header.name, 12))
+		if (!nvram_can_remove_partition(part, name, sig, exceptions))
 			continue;
 
 		/* Make partition a free partition */
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 7e828ba..befb41b 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -30,17 +30,30 @@ static int nvram_fetch, nvram_store;
 static char nvram_buf[NVRW_CNT];	/* assume this is in the first 4GB */
 static DEFINE_SPINLOCK(nvram_lock);
 
-static long nvram_error_log_index = -1;
-static long nvram_error_log_size = 0;
-
 struct err_log_info {
 	int error_type;
 	unsigned int seq_num;
 };
-#define NVRAM_MAX_REQ		2079
-#define NVRAM_MIN_REQ		1055
 
-#define NVRAM_LOG_PART_NAME	"ibm,rtas-log"
+struct nvram_os_partition {
+	const char *name;
+	int req_size;	/* desired size, in bytes */
+	int min_size;	/* minimum acceptable size (0 means req_size) */
+	long size;	/* size of data portion of partition */
+	long index;	/* offset of data portion of partition */
+};
+
+static struct nvram_os_partition rtas_log_partition = {
+	.name = "ibm,rtas-log",
+	.req_size = 2079,
+	.min_size = 1055,
+	.index = -1
+};
+
+static const char *pseries_nvram_os_partitions[] = {
+	"ibm,rtas-log",
+	NULL
+};
 
 static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
 {
@@ -134,7 +147,7 @@ static ssize_t pSeries_nvram_get_size(void)
 }
 
 
-/* nvram_write_error_log
+/* nvram_write_os_partition, nvram_write_error_log
  *
  * We need to buffer the error logs into nvram to ensure that we have
  * the failure information to decode.  If we have a severe error there
@@ -156,48 +169,55 @@ static ssize_t pSeries_nvram_get_size(void)
  * The 'data' section would look like (in bytes):
  * +--------------+------------+-----------------------------------+
  * | event_logged | sequence # | error log                         |
- * |0            3|4          7|8            nvram_error_log_size-1|
+ * |0            3|4          7|8                  error_log_size-1|
  * +--------------+------------+-----------------------------------+
  *
  * event_logged: 0 if event has not been logged to syslog, 1 if it has
  * sequence #: The unique sequence # for each event. (until it wraps)
  * error log: The error log from event_scan
  */
-int nvram_write_error_log(char * buff, int length,
-                          unsigned int err_type, unsigned int error_log_cnt)
+int nvram_write_os_partition(struct nvram_os_partition *part, char * buff,
+		int length, unsigned int err_type, unsigned int error_log_cnt)
 {
 	int rc;
 	loff_t tmp_index;
 	struct err_log_info info;
 	
-	if (nvram_error_log_index == -1) {
+	if (part->index == -1) {
 		return -ESPIPE;
 	}
 
-	if (length > nvram_error_log_size) {
-		length = nvram_error_log_size;
+	if (length > part->size) {
+		length = part->size;
 	}
 
 	info.error_type = err_type;
 	info.seq_num = error_log_cnt;
 
-	tmp_index = nvram_error_log_index;
+	tmp_index = part->index;
 
 	rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
 	if (rc <= 0) {
-		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
+		pr_err("%s: Failed nvram_write (%d)\n", __FUNCTION__, rc);
 		return rc;
 	}
 
 	rc = ppc_md.nvram_write(buff, length, &tmp_index);
 	if (rc <= 0) {
-		printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc);
+		pr_err("%s: Failed nvram_write (%d)\n", __FUNCTION__, rc);
 		return rc;
 	}
 	
 	return 0;
 }
 
+int nvram_write_error_log(char * buff, int length,
+                          unsigned int err_type, unsigned int error_log_cnt)
+{
+	return nvram_write_os_partition(&rtas_log_partition, buff, length,
+						err_type, error_log_cnt);
+}
+
 /* nvram_read_error_log
  *
  * Reads nvram for error log for at most 'length'
@@ -209,13 +229,13 @@ int nvram_read_error_log(char * buff, int length,
 	loff_t tmp_index;
 	struct err_log_info info;
 	
-	if (nvram_error_log_index == -1)
+	if (rtas_log_partition.index == -1)
 		return -1;
 
-	if (length > nvram_error_log_size)
-		length = nvram_error_log_size;
+	if (length > rtas_log_partition.size)
+		length = rtas_log_partition.size;
 
-	tmp_index = nvram_error_log_index;
+	tmp_index = rtas_log_partition.index;
 
 	rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
 	if (rc <= 0) {
@@ -244,10 +264,10 @@ int nvram_clear_error_log(void)
 	int clear_word = ERR_FLAG_ALREADY_LOGGED;
 	int rc;
 
-	if (nvram_error_log_index == -1)
+	if (rtas_log_partition.index == -1)
 		return -1;
 
-	tmp_index = nvram_error_log_index;
+	tmp_index = rtas_log_partition.index;
 	
 	rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
 	if (rc <= 0) {
@@ -258,23 +278,25 @@ int nvram_clear_error_log(void)
 	return 0;
 }
 
-/* pseries_nvram_init_log_partition
+/* pseries_nvram_init_os_partition
  *
- * This will setup the partition we need for buffering the
- * error logs and cleanup partitions if needed.
+ * This sets up a partition with an "OS" signature.
  *
  * The general strategy is the following:
- * 1.) If there is log partition large enough then use it.
- * 2.) If there is none large enough, search
- * for a free partition that is large enough.
- * 3.) If there is not a free partition large enough remove 
- * _all_ OS partitions and consolidate the space.
- * 4.) Will first try getting a chunk that will satisfy the maximum
- * error log size (NVRAM_MAX_REQ).
- * 5.) If the max chunk cannot be allocated then try finding a chunk
- * that will satisfy the minum needed (NVRAM_MIN_REQ).
+ * 1.) If a partition with the indicated name already exists...
+ *	- If it's large enough, use it.
+ *	- Otherwise, recycle it and keep going.
+ * 2.) Search for a free partition that is large enough.
+ * 3.) If there's not a free partition large enough, recycle any obsolete
+ * OS partitions and try again.
+ * 4.) Will first try getting a chunk that will satisfy the requested size.
+ * 5.) If a chunk of the requested size cannot be allocated, then try finding
+ * a chunk that will satisfy the minum needed.
+ *
+ * Returns 0 on success, else -1.
  */
-static int __init pseries_nvram_init_log_partition(void)
+static int __init pseries_nvram_init_os_partition(struct nvram_os_partition
+									*part)
 {
 	loff_t p;
 	int size;
@@ -282,47 +304,50 @@ static int __init pseries_nvram_init_log_partition(void)
 	/* Scan nvram for partitions */
 	nvram_scan_partitions();
 
-	/* Lookg for ours */
-	p = nvram_find_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, &size);
+	/* Look for ours */
+	p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size);
 
 	/* Found one but too small, remove it */
-	if (p && size < NVRAM_MIN_REQ) {
-		pr_info("nvram: Found too small "NVRAM_LOG_PART_NAME" partition"
-			",removing it...");
-		nvram_remove_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS);
+	if (p && size < part->min_size) {
+		pr_info("nvram: Found too small %s partition,"
+					" removing it...\n", part->name);
+		nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL);
 		p = 0;
 	}
 
 	/* Create one if we didn't find */
 	if (!p) {
-		p = nvram_create_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS,
-					   NVRAM_MAX_REQ, NVRAM_MIN_REQ);
-		/* No room for it, try to get rid of any OS partition
-		 * and try again
-		 */
+		p = nvram_create_partition(part->name, NVRAM_SIG_OS,
+					part->req_size, part->min_size);
 		if (p == -ENOSPC) {
-			pr_info("nvram: No room to create "NVRAM_LOG_PART_NAME
-				" partition, deleting all OS partitions...");
-			nvram_remove_partition(NULL, NVRAM_SIG_OS);
-			p = nvram_create_partition(NVRAM_LOG_PART_NAME,
-						   NVRAM_SIG_OS, NVRAM_MAX_REQ,
-						   NVRAM_MIN_REQ);
+			pr_info("nvram: No room to create %s partition, "
+				"deleting any obsolete OS partitions...\n",
+				part->name);
+			nvram_remove_partition(NULL, NVRAM_SIG_OS,
+						pseries_nvram_os_partitions);
+			p = nvram_create_partition(part->name, NVRAM_SIG_OS,
+					part->req_size, part->min_size);
 		}
 	}
 
 	if (p <= 0) {
-		pr_err("nvram: Failed to find or create "NVRAM_LOG_PART_NAME
-		       " partition, err %d\n", (int)p);
-		return 0;
+		pr_err("nvram: Failed to find or create %s"
+		       " partition, err %d\n", part->name, (int)p);
+		return -1;
 	}
 
-	nvram_error_log_index = p;
-	nvram_error_log_size = nvram_get_partition_size(p) -
-		sizeof(struct err_log_info);
+	part->index = p;
+	part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info);
 	
 	return 0;
 }
-machine_arch_initcall(pseries, pseries_nvram_init_log_partition);
+
+static int __init pseries_nvram_init_log_partitions(void)
+{
+	(void) pseries_nvram_init_os_partition(&rtas_log_partition);
+	return 0;
+}
+machine_arch_initcall(pseries, pseries_nvram_init_log_partitions);
 
 int __init pSeries_nvram_init(void)
 {

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/6] nvram: Capture oops/panic reports in ibm, oops-log partition
  2011-02-07  5:01   ` Benjamin Herrenschmidt
@ 2011-02-09 23:00     ` Jim Keniston
  0 siblings, 0 replies; 13+ messages in thread
From: Jim Keniston @ 2011-02-09 23:00 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev

On Mon, 2011-02-07 at 16:01 +1100, Benjamin Herrenschmidt wrote:
> On Sat, 2010-11-13 at 20:15 -0800, Jim Keniston wrote:
> > Create the ibm,oops-log NVRAM partition, and capture the end of the printk
> > buffer in it when there's an oops or panic.  If we can't create the
> > ibm,oops-log partition, capture the oops/panic report in ibm,rtas-log.

I've appended an updated patch that addresses your comments and applies
to 2.6.38-rc4.

> 
> Won't the parsing tools choke on the oops log in the RTAS log
> partition ?

I don't think that that will be a problem, because pSeries_log_error()
doesn't log errors that aren't of type ERR_TYPE_RTAS_LOG.

However, I did add code to ensure that even if ibm,rtas-log is used for
both RTAS errors and oops reports, an oops report won't overwrite a
recently captured RTAS event that hasn't yet been read into the
filesystem (e.g., by rtas_errd).

> 
> Also, maybe remove the "ibm," prefix on the nvram partition, make it
> "lnx," instead.

Done.

> 
> Then, if you move the rest of the code out of pseries, you can move that
> out too, just make pseries platform code call the init code for it, that
> way other platforms can re-use that as-is.
> 
> But that later part can wait a separate patch series,

Agreed.

> just make sure you
> change the partition name now.

Still done.

> 
> Cheers,
> Ben.

Here's my revised patch.
Jim
=====
Capture oops/panic reports in lnx,oops-log partition

Create the lnx,oops-log NVRAM partition, and capture the end of the printk
buffer in it when there's an oops or panic.  If we can't create the
lnx,oops-log partition, capture the oops/panic report in ibm,rtas-log.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---

 arch/powerpc/platforms/pseries/nvram.c |  118 +++++++++++++++++++++++++++++++-
 1 files changed, 115 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index befb41b..419707b 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -16,6 +16,8 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/kmsg_dump.h>
 #include <asm/uaccess.h>
 #include <asm/nvram.h>
 #include <asm/rtas.h>
@@ -39,7 +41,7 @@ struct nvram_os_partition {
 	const char *name;
 	int req_size;	/* desired size, in bytes */
 	int min_size;	/* minimum acceptable size (0 means req_size) */
-	long size;	/* size of data portion of partition */
+	long size;	/* size of data portion (excluding err_log_info) */
 	long index;	/* offset of data portion of partition */
 };
 
@@ -50,11 +52,35 @@ static struct nvram_os_partition rtas_log_partition = {
 	.index = -1
 };
 
+static struct nvram_os_partition oops_log_partition = {
+	.name = "lnx,oops-log",
+	.req_size = 4000,
+	.min_size = 2000,
+	.index = -1
+};
+
 static const char *pseries_nvram_os_partitions[] = {
 	"ibm,rtas-log",
+	"lnx,oops-log",
 	NULL
 };
 
+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);
+
+static struct kmsg_dumper nvram_kmsg_dumper = {
+	.dump = oops_to_nvram
+};
+
+/* See clobbering_unread_rtas_event() */
+#define NVRAM_RTAS_READ_TIMEOUT 5		/* seconds */
+static unsigned long last_unread_rtas_event;	/* timestamp */
+
+/* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */
+static char *oops_buf;
+
 static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
 {
 	unsigned int i;
@@ -214,8 +240,11 @@ int nvram_write_os_partition(struct nvram_os_partition *part, char * buff,
 int nvram_write_error_log(char * buff, int length,
                           unsigned int err_type, unsigned int error_log_cnt)
 {
-	return nvram_write_os_partition(&rtas_log_partition, buff, length,
+	int rc = nvram_write_os_partition(&rtas_log_partition, buff, length,
 						err_type, error_log_cnt);
+	if (!rc)
+		last_unread_rtas_event = get_seconds();
+	return rc;
 }
 
 /* nvram_read_error_log
@@ -274,6 +303,7 @@ int nvram_clear_error_log(void)
 		printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc);
 		return rc;
 	}
+	last_unread_rtas_event = 0;
 
 	return 0;
 }
@@ -342,9 +372,35 @@ static int __init pseries_nvram_init_os_partition(struct nvram_os_partition
 	return 0;
 }
 
+static void __init nvram_init_oops_partition(int rtas_partition_exists)
+{
+	int rc;
+
+	rc = pseries_nvram_init_os_partition(&oops_log_partition);
+	if (rc != 0) {
+		if (!rtas_partition_exists)
+			return;
+		pr_notice("nvram: Using %s partition to log both"
+			" RTAS errors and oops/panic reports\n",
+			rtas_log_partition.name);
+		memcpy(&oops_log_partition, &rtas_log_partition,
+						sizeof(rtas_log_partition));
+	}
+	oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL);
+	rc = kmsg_dump_register(&nvram_kmsg_dumper);
+	if (rc != 0) {
+		pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);
+		kfree(oops_buf);
+		return;
+	}
+}
+
 static int __init pseries_nvram_init_log_partitions(void)
 {
-	(void) pseries_nvram_init_os_partition(&rtas_log_partition);
+	int rc;
+
+	rc = pseries_nvram_init_os_partition(&rtas_log_partition);
+	nvram_init_oops_partition(rc == 0);
 	return 0;
 }
 machine_arch_initcall(pseries, pseries_nvram_init_log_partitions);
@@ -378,3 +434,59 @@ int __init pSeries_nvram_init(void)
 
 	return 0;
 }
+
+/*
+ * 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.
+ *
+ * We assume that if rtas_errd hasn't read the RTAS event in
+ * NVRAM_RTAS_READ_TIMEOUT seconds, it's probably not going to.
+ */
+static int clobbering_unread_rtas_event(void)
+{
+	return (oops_log_partition.index == rtas_log_partition.index
+		&& last_unread_rtas_event
+		&& get_seconds() - last_unread_rtas_event <=
+						NVRAM_RTAS_READ_TIMEOUT);
+}
+
+/* our kmsg_dump callback */
+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)
+{
+	static unsigned int oops_count = 0;
+	size_t text_len;
+
+	if (clobbering_unread_rtas_event())
+		return;
+
+	text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len,
+					oops_buf, oops_log_partition.size);
+	(void) nvram_write_os_partition(&oops_log_partition, oops_buf,
+		(int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count);
+}

^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2011-02-09 23:00 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-11-14  4:15 [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston
2010-11-14  4:15 ` [PATCH 1/6] nvram: Generalize code for OS partitions " Jim Keniston
2011-02-07  4:57   ` Benjamin Herrenschmidt
2011-02-09 22:43     ` Jim Keniston
2010-11-14  4:15 ` [PATCH 2/6] nvram: Capture oops/panic reports in ibm, oops-log partition Jim Keniston
2011-02-07  5:01   ` Benjamin Herrenschmidt
2011-02-09 23:00     ` Jim Keniston
2010-11-14  4:15 ` [PATCH 3/6] nvram: Always capture start of oops report to NVRAM Jim Keniston
2010-11-14  4:15 ` [PATCH 4/6] nvram: Add compression to fit more printk output into NVRAM Jim Keniston
2010-11-14  4:15 ` [PATCH 5/6] nvram: Slim down zlib_deflate workspace when possible Jim Keniston
2011-02-07  4:39   ` Benjamin Herrenschmidt
2010-11-14  4:15 ` [PATCH 6/6] nvram: Shrink our zlib_deflate workspace from 268K to 24K Jim Keniston
2010-11-14  4:36 ` [RFC PATCH 0/6] nvram: Capture oops/panic reports in NVRAM Jim Keniston

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.