All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] powerpc/powernv: Read opal error log and export it through sysfs interface.
@ 2013-12-16  9:58 Mahesh J Salgaonkar
  2014-02-21  0:11 ` Stewart Smith
  0 siblings, 1 reply; 11+ messages in thread
From: Mahesh J Salgaonkar @ 2013-12-16  9:58 UTC (permalink / raw)
  To: linuxppc-dev, Benjamin Herrenschmidt; +Cc: Vasant Hegde, Mamatha Inamdar

From: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>

This patch adds support to read error logs from OPAL and export them
to userspace through sysfs interface /sys/firmware/opa/opal-elog.

This patch buffers 128 error log records until it is consumed by userspace
tool. This patch provides an sysfs interface '/sys/firmware/opa/opal-elog-ack'
to user to receive an acknowledgement of successful log consumption.

This is what user space tool would do:
- Read error log from /sys/firmware/opa/opal-elog.
- Save it to the disk.
- Send an acknowledgement on successful consumption by writing error log
  id to /sys/firmware/opa/opal-elog-ack.

Signed-off-by: Mamatha Inamdar <mamatha4@linux.vnet.ibm.com>
Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/opal.h                |   11 +
 arch/powerpc/platforms/powernv/Makefile        |    2 
 arch/powerpc/platforms/powernv/opal-elog.c     |  309 ++++++++++++++++++++++++
 arch/powerpc/platforms/powernv/opal-wrappers.S |    5 
 arch/powerpc/platforms/powernv/opal.c          |    2 
 5 files changed, 328 insertions(+), 1 deletion(-)
 create mode 100644 arch/powerpc/platforms/powernv/opal-elog.c

diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 5462fa7..723a7db 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -129,6 +129,11 @@ extern int opal_enter_rtas(struct rtas_args *args,
 #define OPAL_LPC_READ				67
 #define OPAL_LPC_WRITE				68
 #define OPAL_RETURN_CPU				69
+#define OPAL_ELOG_READ				71
+#define OPAL_ELOG_WRITE				72
+#define OPAL_ELOG_ACK				73
+#define OPAL_ELOG_RESEND			74
+#define OPAL_ELOG_SIZE				75
 #define OPAL_FLASH_VALIDATE			76
 #define OPAL_FLASH_MANAGE			77
 #define OPAL_FLASH_UPDATE			78
@@ -727,6 +732,11 @@ int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type,
 		       uint32_t addr, uint32_t data, uint32_t sz);
 int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type,
 		      uint32_t addr, uint32_t *data, uint32_t sz);
+int64_t opal_read_elog(uint64_t buffer, size_t size, uint64_t log_id);
+int64_t opal_get_elog_size(uint64_t *log_id, size_t *size, uint64_t *elog_type);
+int64_t opal_write_elog(uint64_t buffer, uint64_t size, uint64_t offset);
+int64_t opal_send_ack_elog(uint64_t log_id);
+void opal_resend_pending_logs(void);
 int64_t opal_validate_flash(uint64_t buffer, uint32_t *size, uint32_t *result);
 int64_t opal_manage_flash(uint8_t op);
 int64_t opal_update_flash(uint64_t blk_list);
@@ -761,6 +771,7 @@ extern void opal_get_rtc_time(struct rtc_time *tm);
 extern unsigned long opal_get_boot_time(void);
 extern void opal_nvram_init(void);
 extern void opal_flash_init(void);
+extern int opal_elog_init(void);
 
 extern int opal_machine_check(struct pt_regs *regs);
 extern bool opal_mce_check_early_recovery(struct pt_regs *regs);
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 873fa13..0f692eb 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,6 +1,6 @@
 obj-y			+= setup.o opal-takeover.o opal-wrappers.o opal.o
 obj-y			+= opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
-obj-y			+= rng.o
+obj-y			+= rng.o opal-elog.o
 
 obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_PCI)	+= pci.o pci-p5ioc2.o pci-ioda.o
diff --git a/arch/powerpc/platforms/powernv/opal-elog.c b/arch/powerpc/platforms/powernv/opal-elog.c
new file mode 100644
index 0000000..fc891ae
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-elog.c
@@ -0,0 +1,309 @@
+/*
+ * Error log support on PowerNV.
+ *
+ * Copyright 2013 IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <asm/opal.h>
+
+/* Maximum size of a single log on FSP is 16KB */
+#define OPAL_MAX_ERRLOG_SIZE	16384
+
+/* maximu number of records powernv can hold */
+#define MAX_NUM_RECORD	128
+
+struct opal_err_log {
+	struct list_head link;
+	uint64_t opal_log_id;
+	size_t opal_log_size;
+	uint8_t data[OPAL_MAX_ERRLOG_SIZE];
+};
+
+/* Pre-allocated temp buffer to pull error log from opal. */
+static uint8_t err_log_data[OPAL_MAX_ERRLOG_SIZE];
+/* Protect err_log_data buf */
+static DEFINE_MUTEX(err_log_data_mutex);
+
+static uint64_t total_log_size;
+static bool opal_log_available;
+static LIST_HEAD(elog_list);
+static LIST_HEAD(elog_ack_list);
+
+/* lock to protect elog_list and elog-ack_list. */
+static DEFINE_SPINLOCK(opal_elog_lock);
+
+static DECLARE_WAIT_QUEUE_HEAD(opal_log_wait);
+
+/*
+ * Interface for user to acknowledge the error log.
+ *
+ * Once user acknowledge the log, we delete that record entry from the
+ * list and move it ack list.
+ */
+void opal_elog_ack(uint64_t ack_id)
+{
+	unsigned long flags;
+	struct opal_err_log *record, *next;
+	bool found = false;
+
+	printk(KERN_INFO "OPAL Log ACK=%llx", ack_id);
+
+	/* once user acknowledge a log delete record from list */
+	spin_lock_irqsave(&opal_elog_lock, flags);
+	list_for_each_entry_safe(record, next, &elog_list, link) {
+		if (ack_id == record->opal_log_id) {
+			list_del(&record->link);
+			list_add(&record->link, &elog_ack_list);
+			total_log_size -= OPAL_MAX_ERRLOG_SIZE;
+			found = true;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&opal_elog_lock, flags);
+
+	/* Send acknowledgement to FSP */
+	if (found)
+		opal_send_ack_elog(ack_id);
+	return;
+}
+
+
+static ssize_t elog_ack_store(struct kobject *kobj,
+					struct kobj_attribute *attr,
+					const char *buf, size_t count)
+{
+	uint32_t log_ack_id;
+	log_ack_id = *(uint32_t *) buf;
+
+	/* send acknowledgment to FSP */
+	opal_elog_ack(log_ack_id);
+	return 0;
+}
+
+/*
+ * Show error log records to user.
+ */
+static ssize_t opal_elog_show(struct file *filp, struct kobject *kobj,
+				struct bin_attribute *bin_attr, char *buf,
+				loff_t pos, size_t count)
+{
+	unsigned long flags;
+	struct opal_err_log *record, *next;
+	size_t size = 0;
+	size_t data_to_copy = 0;
+	int error = 0;
+
+	/* Display one log at a time. */
+	if (count > OPAL_MAX_ERRLOG_SIZE)
+		count = OPAL_MAX_ERRLOG_SIZE;
+
+	spin_lock_irqsave(&opal_elog_lock, flags);
+	/* Align the pos to point within total errlog size. */
+	if (total_log_size && pos > total_log_size)
+		pos = pos % total_log_size;
+
+	/*
+	 * if pos goes beyond total_log_size then we know we don't have any
+	 * new record to show.
+	 */
+	if (total_log_size == 0 || pos >= total_log_size) {
+		opal_log_available = 0;
+		if (filp->f_flags & O_NONBLOCK) {
+			spin_unlock_irqrestore(&opal_elog_lock, flags);
+			error = -EAGAIN;
+			goto out;
+		}
+		spin_unlock_irqrestore(&opal_elog_lock, flags);
+		pos = 0;
+
+		/* Wait until we get log from sapphire */
+		error = wait_event_interruptible(opal_log_wait,
+						 opal_log_available);
+		if (error)
+			goto out;
+		spin_lock_irqsave(&opal_elog_lock, flags);
+	}
+
+	/*
+	 * Show log record one by one through /sys/firmware/opal/opal_elog
+	 */
+	list_for_each_entry_safe(record, next, &elog_list, link) {
+		if ((pos >= size) && (pos < (size + OPAL_MAX_ERRLOG_SIZE))) {
+			data_to_copy = OPAL_MAX_ERRLOG_SIZE - (pos - size);
+			if (count > data_to_copy)
+				count = data_to_copy;
+			memcpy(buf, record->data + (pos - size), count);
+			error = count;
+			break;
+		}
+		size += OPAL_MAX_ERRLOG_SIZE;
+	}
+	spin_unlock_irqrestore(&opal_elog_lock, flags);
+out:
+	return error;
+}
+
+/* Interface to read log from OPAL */
+static void opal_elog_read(void)
+{
+	struct opal_err_log *record;
+	size_t elog_size;
+	uint64_t log_id;
+	uint64_t elog_type;
+
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&opal_elog_lock, flags);
+	if (list_empty(&elog_ack_list)) {
+		/*
+		 * We have no more room to read logs. Ignore it for now,
+		 * will read it later when we have enough space.
+		 */
+		spin_unlock_irqrestore(&opal_elog_lock, flags);
+		return;
+	}
+
+	/* Pull out the free node. */
+	record = list_entry(elog_ack_list.next, struct opal_err_log, link);
+	list_del(&record->link);
+	spin_unlock_irqrestore(&opal_elog_lock, flags);
+
+	/* read log size and log ID from OPAL */
+	rc = opal_get_elog_size(&log_id, &elog_size, &elog_type);
+	if (rc != OPAL_SUCCESS) {
+		pr_err("ELOG: Opal log read failed\n");
+		return;
+	}
+	if (elog_size >= OPAL_MAX_ERRLOG_SIZE)
+		elog_size  =  OPAL_MAX_ERRLOG_SIZE;
+
+	record->opal_log_id = log_id;
+	record->opal_log_size = elog_size;
+	memset(record->data, 0, sizeof(record->data));
+
+	mutex_lock(&err_log_data_mutex);
+	rc = opal_read_elog(__pa(err_log_data), elog_size, log_id);
+	if (rc != OPAL_SUCCESS) {
+		mutex_unlock(&err_log_data_mutex);
+		pr_err("ELOG: log read failed for log-id=%llx\n", log_id);
+		/* put back the free node. */
+		spin_lock_irqsave(&opal_elog_lock, flags);
+		list_add(&record->link, &elog_ack_list);
+		spin_unlock_irqrestore(&opal_elog_lock, flags);
+		return;
+	}
+	memcpy(record->data, err_log_data, elog_size);
+	mutex_unlock(&err_log_data_mutex);
+
+	spin_lock_irqsave(&opal_elog_lock, flags);
+	list_add_tail(&record->link, &elog_list);
+	total_log_size += OPAL_MAX_ERRLOG_SIZE;
+	spin_unlock_irqrestore(&opal_elog_lock, flags);
+
+	opal_log_available = 1;
+	wake_up_interruptible(&opal_log_wait);
+	return;
+}
+
+static void elog_work_fn(struct work_struct *work)
+{
+	opal_elog_read();
+}
+
+static DECLARE_WORK(elog_work, elog_work_fn);
+
+static int elog_event(struct notifier_block *nb,
+				unsigned long events, void *change)
+{
+	/* check for error log event */
+	if (events & OPAL_EVENT_ERROR_LOG_AVAIL)
+		schedule_work(&elog_work);
+	return 0;
+}
+
+/* Initialize sysfs file */
+static struct kobj_attribute opal_elog_ack_attr = __ATTR(opal_elog_ack,
+						0200, NULL, elog_ack_store);
+
+static struct notifier_block elog_nb = {
+	.notifier_call  = elog_event,
+	.next           = NULL,
+	.priority       = 0
+};
+
+static struct bin_attribute opal_elog_attr = {
+	.attr = {.name = "opal_elog", .mode = 0400},
+	.read = opal_elog_show,
+};
+
+/*
+ * Pre-allocate a buffer to hold handful of error logs until user space
+ * consumes it.
+ */
+static int init_err_log_buffer(void)
+{
+	int i = 0;
+	struct opal_err_log *buf_ptr;
+
+	buf_ptr = vmalloc(sizeof(struct opal_err_log) * MAX_NUM_RECORD);
+	if (!buf_ptr) {
+		printk(KERN_ERR "ELOG: failed to allocate memory.\n");
+		return -ENOMEM;
+	}
+	memset(buf_ptr, 0, sizeof(struct opal_err_log) * MAX_NUM_RECORD);
+
+	/* Initialize ack list will all free nodes. */
+	for (i = 0; i < MAX_NUM_RECORD; i++, buf_ptr++)
+		list_add(&buf_ptr->link, &elog_ack_list);
+	return 0;
+}
+
+/* Initialize error logging */
+int __init opal_elog_init(void)
+{
+	int rc = 0;
+
+	rc = init_err_log_buffer();
+	if (rc)
+		return rc;
+
+	rc = sysfs_create_bin_file(opal_kobj, &opal_elog_attr);
+	if (rc) {
+		printk(KERN_ERR "ELOG: unable to create sysfs file"
+					"opal_elog (%d)\n", rc);
+		return rc;
+	}
+
+	rc = sysfs_create_file(opal_kobj, &opal_elog_ack_attr.attr);
+	if (rc) {
+		printk(KERN_ERR "ELOG: unable to create sysfs file"
+			" opal_elog_ack (%d)\n", rc);
+		return rc;
+	}
+
+	rc = opal_notifier_register(&elog_nb);
+	if (rc) {
+		pr_err("%s: Can't register OPAL event notifier (%d)\n",
+		__func__, rc);
+		return rc;
+	}
+
+	/* We are now ready to pull error logs from opal. */
+	opal_resend_pending_logs();
+
+	return 0;
+}
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index e780650..a040b02 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -123,6 +123,11 @@ OPAL_CALL(opal_xscom_write,			OPAL_XSCOM_WRITE);
 OPAL_CALL(opal_lpc_read,			OPAL_LPC_READ);
 OPAL_CALL(opal_lpc_write,			OPAL_LPC_WRITE);
 OPAL_CALL(opal_return_cpu,			OPAL_RETURN_CPU);
+OPAL_CALL(opal_read_elog,			OPAL_ELOG_READ);
+OPAL_CALL(opal_send_ack_elog,			OPAL_ELOG_ACK);
+OPAL_CALL(opal_get_elog_size,			OPAL_ELOG_SIZE);
+OPAL_CALL(opal_resend_pending_logs,		OPAL_ELOG_RESEND);
+OPAL_CALL(opal_write_elog,			OPAL_ELOG_WRITE);
 OPAL_CALL(opal_validate_flash,			OPAL_FLASH_VALIDATE);
 OPAL_CALL(opal_manage_flash,			OPAL_FLASH_MANAGE);
 OPAL_CALL(opal_update_flash,			OPAL_FLASH_UPDATE);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 31053be..20e1834 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -475,6 +475,8 @@ static int __init opal_init(void)
 	/* Create "opal" kobject under /sys/firmware */
 	rc = opal_sysfs_init();
 	if (rc == 0) {
+		/* Setup error log interface */
+		rc = opal_elog_init();
 		/* Setup code update interface */
 		opal_flash_init();
 	}

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

* Re: [PATCH] powerpc/powernv: Read opal error log and export it through sysfs interface.
  2013-12-16  9:58 [PATCH] powerpc/powernv: Read opal error log and export it through sysfs interface Mahesh J Salgaonkar
@ 2014-02-21  0:11 ` Stewart Smith
  2014-02-25  5:02   ` Mahesh Jagannath Salgaonkar
  0 siblings, 1 reply; 11+ messages in thread
From: Stewart Smith @ 2014-02-21  0:11 UTC (permalink / raw)
  To: Mahesh J Salgaonkar, linuxppc-dev

 Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> writes:
 > This patch adds support to read error logs from OPAL and export them
 > to userspace through sysfs interface /sys/firmware/opa/opal-elog.

 I think we could provide a better interface with instead having a file
 per log message appear in sysfs. We're never going to have more than 128
 of these at any one time on the Linux side, so it's not going to bee too
 many files.

 e.g. /sys/firmware/opal/elog/<id>

 that way, any new file in /sys/firmware/opal/elog/ means there's a new
 log entry available. I believe there's 

 To ack a log, you could just echo 'ack' to the file.

 The other option woudl be to more closely follow what sysfs is meant to
 be - ascii text. This would mean having more (any) of the parser in
 kernel for the error logs - which may/may not be a bad idea.

 However, it would make the end user code for consuming them much much
 simpler, and that may be a good thing.

 Having some way of getting some information out without a userspace
 parser is probably good though, I'm pretty sure having only a binary
 interface in /sys is at least partially frowned upon.

 > This is what user space tool would do:
 > - Read error log from /sys/firmware/opa/opal-elog.
 > - Save it to the disk.
 > - Send an acknowledgement on successful consumption by writing error log
 >   id to /sys/firmware/opa/opal-elog-ack.

 A userspace tool may want to explicitly *not* ack the log too, or only
 ack some entries, so the interface sohuld be sane for this use case too.

 e.g. we could display them in petitboot.

 > diff --git a/arch/powerpc/platforms/powernv/opal-elog.c
 > b/arch/powerpc/platforms/powernv/opal-elog.c
 [ 2 more citation lines. Click/Enter to show. ]
 > new file mode 100644
 > index 0000000..fc891ae
 > --- /dev/null
 > +++ b/arch/powerpc/platforms/powernv/opal-elog.c
 > @@ -0,0 +1,309 @@
 <snip>
 > +/* Maximum size of a single log on FSP is 16KB */
 > +#define OPAL_MAX_ERRLOG_SIZE	16384

 I've seen some conflicting things on this - is it 2kb or 16kb?

 > +
 > +struct opal_err_log {
 > +	struct list_head link;
 > +	uint64_t opal_log_id;

 why is this uint64_t and not uint32_t? It appears that the log id is 32bits.

 > +	size_t opal_log_size;
 > +	uint8_t data[OPAL_MAX_ERRLOG_SIZE];
 > +};
 > +
 > +/* Pre-allocated temp buffer to pull error log from opal. */
 > +static uint8_t err_log_data[OPAL_MAX_ERRLOG_SIZE];

 Why do we need temporary space? Why not just store directly into struct
 opal_err_log?

 > +/* Protect err_log_data buf */
 > +static DEFINE_MUTEX(err_log_data_mutex);
 [ 15 more citation lines. Click/Enter to show. ]
 > +
 > +static uint64_t total_log_size;
 > +static bool opal_log_available;
 > +static LIST_HEAD(elog_list);
 > +static LIST_HEAD(elog_ack_list);
 > +
 > +/* lock to protect elog_list and elog-ack_list. */
 > +static DEFINE_SPINLOCK(opal_elog_lock);
 > +
 > +static DECLARE_WAIT_QUEUE_HEAD(opal_log_wait);
 > +
 > +/*
 > + * Interface for user to acknowledge the error log.
 > + *
 > + * Once user acknowledge the log, we delete that record entry from the
 > + * list and move it ack list.
 > + */
 > +void opal_elog_ack(uint64_t ack_id)

 s/ack_id/log_id/

 > +
 > +static ssize_t elog_ack_store(struct kobject *kobj,
 [ 7 more citation lines. Click/Enter to show. ]
 > +					struct kobj_attribute *attr,
 > +					const char *buf, size_t count)
 > +{
 > +	uint32_t log_ack_id;
 > +	log_ack_id = *(uint32_t *) buf;
 > +
 > +	/* send acknowledgment to FSP */
 > +	opal_elog_ack(log_ack_id);
 > +	return 0;
 > +}

 This function has a few problems:

 Consider the following actions:
 $ echo 1 > /sys/firmware/opal/opal-elog-ack
 $ echo 'abcde' > /sys/firmware/opal/opal-elog-ack

 The former will read undefined memory and the latter will make a kernel
 thread, rsyslogd and systemd-journal all each a CPU each.

 Basically, the problems are:
 1) not endian safe
 2) not following store API of returning nr bytes read
 3) binary interface. Use sscanf to read numbers instead.

 > +/*
 > + * Show error log records to user.
 [ 9 more citation lines. Click/Enter to show. ]
 > + */
 > +static ssize_t opal_elog_show(struct file *filp, struct kobject *kobj,
 > +				struct bin_attribute *bin_attr, char *buf,
 > +				loff_t pos, size_t count)
 > +{
 > +	unsigned long flags;
 > +	struct opal_err_log *record, *next;
 > +	size_t size = 0;
 > +	size_t data_to_copy = 0;
 > +	int error = 0;
 > +
 > +	/* Display one log@a time. */

 use words, not @.

 > +	if (count > OPAL_MAX_ERRLOG_SIZE)
 > +		count = OPAL_MAX_ERRLOG_SIZE;
 [ 23 more citation lines. Click/Enter to show. ]
 > +	spin_lock_irqsave(&opal_elog_lock, flags);
 > +	/* Align the pos to point within total errlog size. */
 > +	if (total_log_size && pos > total_log_size)
 > +		pos = pos % total_log_size;
 > +
 > +	/*
 > +	 * if pos goes beyond total_log_size then we know we don't have any
 > +	 * new record to show.
 > +	 */
 > +	if (total_log_size == 0 || pos >= total_log_size) {
 > +		opal_log_available = 0;
 > +		if (filp->f_flags & O_NONBLOCK) {
 > +			spin_unlock_irqrestore(&opal_elog_lock, flags);
 > +			error = -EAGAIN;
 > +			goto out;
 > +		}
 > +		spin_unlock_irqrestore(&opal_elog_lock, flags);
 > +		pos = 0;
 > +
 > +		/* Wait until we get log from sapphire */
 > +		error = wait_event_interruptible(opal_log_wait,
 > +						 opal_log_available);
 > +		if (error)
 > +			goto out;
 > +		spin_lock_irqsave(&opal_elog_lock, flags);
 > +	}

 Why should we wait for there to be a log message? If there's not one
 then there's not one and that's fine.

 I also wonder if we really need total_log_size and opal_log_available,
 this information seems readily available from the list of events.

 Instead, for notification (as i understand it)  we should be using
 sysfs_notify() from kernel and then in userspace we can just call
 select() to wait for something to happen.

 > +/*
 > + * Pre-allocate a buffer to hold handful of error logs until user space
 [ 5 more citation lines. Click/Enter to show. ]
 > + * consumes it.
 > + */
 > +static int init_err_log_buffer(void)
 > +{
 > +	int i = 0;
 > +	struct opal_err_log *buf_ptr;
 > +
 > +	buf_ptr = vmalloc(sizeof(struct opal_err_log) * MAX_NUM_RECORD);

 This means we constantly use 128 * sizeof(struct opal_err_log) which
 equates to somewhere north of 2MB of memory (due to list overhead).

 I don't think we need to statically allocate this, we can probably just
 allocate on-demand as in a typical system you're probably quite
 unlikely to have too many of these sitting around (besides, if for
 whatever reason we cannot allocate memory at some point, that's okay
 because we can read it again later).

 Oh, I forgot to mention, since this is sysfs there should be
 documentation in Documentation/ABI/ somewhere.

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

* Re: [PATCH] powerpc/powernv: Read opal error log and export it through sysfs interface.
  2014-02-21  0:11 ` Stewart Smith
@ 2014-02-25  5:02   ` Mahesh Jagannath Salgaonkar
  2014-02-25 23:19     ` Stewart Smith
  0 siblings, 1 reply; 11+ messages in thread
From: Mahesh Jagannath Salgaonkar @ 2014-02-25  5:02 UTC (permalink / raw)
  To: Stewart Smith, linuxppc-dev

On 02/21/2014 05:41 AM, Stewart Smith wrote:
>  Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com> writes:
>  > This patch adds support to read error logs from OPAL and export them
>  > to userspace through sysfs interface /sys/firmware/opa/opal-elog.

Hi Stewart,

Thanks for the review. This code definitely needs improvement.

> 
>  I think we could provide a better interface with instead having a file
>  per log message appear in sysfs. We're never going to have more than 128
>  of these at any one time on the Linux side, so it's not going to bee too
>  many files.

It is not just about 128 files, we may be adding/removing sysfs node for
every new log id that gets informed to kernel and ack-ed. In worst case,
when we have flood of elog errors with user daemon consuming it and
ack-ing back to get ready for next log in a tight poll, we may
continuously add/remove the sysfs node for each new <id>.

> 
>  e.g. /sys/firmware/opal/elog/<id>
> 
>  that way, any new file in /sys/firmware/opal/elog/ means there's a new
>  log entry available. I believe there's 
> 
>  To ack a log, you could just echo 'ack' to the file.
> 
>  The other option woudl be to more closely follow what sysfs is meant to
>  be - ascii text. This would mean having more (any) of the parser in
>  kernel for the error logs - which may/may not be a bad idea.
> 
>  However, it would make the end user code for consuming them much much
>  simpler, and that may be a good thing.
> 
>  Having some way of getting some information out without a userspace
>  parser is probably good though, I'm pretty sure having only a binary
>  interface in /sys is at least partially frowned upon.
> 
>  > This is what user space tool would do:
>  > - Read error log from /sys/firmware/opa/opal-elog.
>  > - Save it to the disk.
>  > - Send an acknowledgement on successful consumption by writing error log
>  >   id to /sys/firmware/opa/opal-elog-ack.
> 
>  A userspace tool may want to explicitly *not* ack the log too, or only
>  ack some entries, so the interface sohuld be sane for this use case too.
> 
>  e.g. we could display them in petitboot.
> 
>  > diff --git a/arch/powerpc/platforms/powernv/opal-elog.c
>  > b/arch/powerpc/platforms/powernv/opal-elog.c
>  [ 2 more citation lines. Click/Enter to show. ]
>  > new file mode 100644
>  > index 0000000..fc891ae
>  > --- /dev/null
>  > +++ b/arch/powerpc/platforms/powernv/opal-elog.c
>  > @@ -0,0 +1,309 @@
>  <snip>
>  > +/* Maximum size of a single log on FSP is 16KB */
>  > +#define OPAL_MAX_ERRLOG_SIZE	16384
> 
>  I've seen some conflicting things on this - is it 2kb or 16kb?

We choose 16kb because we want to pull all the log data and not partial.

> 
>  > +
>  > +struct opal_err_log {
>  > +	struct list_head link;
>  > +	uint64_t opal_log_id;
> 
>  why is this uint64_t and not uint32_t? It appears that the log id is 32bits.

Agree, This needs to be changed to uint_32.

> 
>  > +	size_t opal_log_size;
>  > +	uint8_t data[OPAL_MAX_ERRLOG_SIZE];
>  > +};
>  > +
>  > +/* Pre-allocated temp buffer to pull error log from opal. */
>  > +static uint8_t err_log_data[OPAL_MAX_ERRLOG_SIZE];
> 
>  Why do we need temporary space? Why not just store directly into struct
>  opal_err_log?
> 
>  > +/* Protect err_log_data buf */
>  > +static DEFINE_MUTEX(err_log_data_mutex);
>  [ 15 more citation lines. Click/Enter to show. ]
>  > +
>  > +static uint64_t total_log_size;
>  > +static bool opal_log_available;
>  > +static LIST_HEAD(elog_list);
>  > +static LIST_HEAD(elog_ack_list);
>  > +
>  > +/* lock to protect elog_list and elog-ack_list. */
>  > +static DEFINE_SPINLOCK(opal_elog_lock);
>  > +
>  > +static DECLARE_WAIT_QUEUE_HEAD(opal_log_wait);
>  > +
>  > +/*
>  > + * Interface for user to acknowledge the error log.
>  > + *
>  > + * Once user acknowledge the log, we delete that record entry from the
>  > + * list and move it ack list.
>  > + */
>  > +void opal_elog_ack(uint64_t ack_id)
> 
>  s/ack_id/log_id/

Yup. makes sense.

> 
>  > +
>  > +static ssize_t elog_ack_store(struct kobject *kobj,
>  [ 7 more citation lines. Click/Enter to show. ]
>  > +					struct kobj_attribute *attr,
>  > +					const char *buf, size_t count)
>  > +{
>  > +	uint32_t log_ack_id;
>  > +	log_ack_id = *(uint32_t *) buf;
>  > +
>  > +	/* send acknowledgment to FSP */
>  > +	opal_elog_ack(log_ack_id);
>  > +	return 0;
>  > +}
> 
>  This function has a few problems:
> 
>  Consider the following actions:
>  $ echo 1 > /sys/firmware/opal/opal-elog-ack
>  $ echo 'abcde' > /sys/firmware/opal/opal-elog-ack
> 
>  The former will read undefined memory and the latter will make a kernel
>  thread, rsyslogd and systemd-journal all each a CPU each.
> 
>  Basically, the problems are:
>  1) not endian safe
>  2) not following store API of returning nr bytes read
>  3) binary interface. Use sscanf to read numbers instead.
> 
>  > +/*
>  > + * Show error log records to user.
>  [ 9 more citation lines. Click/Enter to show. ]
>  > + */
>  > +static ssize_t opal_elog_show(struct file *filp, struct kobject *kobj,
>  > +				struct bin_attribute *bin_attr, char *buf,
>  > +				loff_t pos, size_t count)
>  > +{
>  > +	unsigned long flags;
>  > +	struct opal_err_log *record, *next;
>  > +	size_t size = 0;
>  > +	size_t data_to_copy = 0;
>  > +	int error = 0;
>  > +
>  > +	/* Display one log@a time. */
> 
>  use words, not @.
> 
>  > +	if (count > OPAL_MAX_ERRLOG_SIZE)
>  > +		count = OPAL_MAX_ERRLOG_SIZE;
>  [ 23 more citation lines. Click/Enter to show. ]
>  > +	spin_lock_irqsave(&opal_elog_lock, flags);
>  > +	/* Align the pos to point within total errlog size. */
>  > +	if (total_log_size && pos > total_log_size)
>  > +		pos = pos % total_log_size;
>  > +
>  > +	/*
>  > +	 * if pos goes beyond total_log_size then we know we don't have any
>  > +	 * new record to show.
>  > +	 */
>  > +	if (total_log_size == 0 || pos >= total_log_size) {
>  > +		opal_log_available = 0;
>  > +		if (filp->f_flags & O_NONBLOCK) {
>  > +			spin_unlock_irqrestore(&opal_elog_lock, flags);
>  > +			error = -EAGAIN;
>  > +			goto out;
>  > +		}
>  > +		spin_unlock_irqrestore(&opal_elog_lock, flags);
>  > +		pos = 0;
>  > +
>  > +		/* Wait until we get log from sapphire */
>  > +		error = wait_event_interruptible(opal_log_wait,
>  > +						 opal_log_available);
>  > +		if (error)
>  > +			goto out;
>  > +		spin_lock_irqsave(&opal_elog_lock, flags);
>  > +	}
> 
>  Why should we wait for there to be a log message? If there's not one
>  then there's not one and that's fine.
> 
>  I also wonder if we really need total_log_size and opal_log_available,
>  this information seems readily available from the list of events.
> 
>  Instead, for notification (as i understand it)  we should be using
>  sysfs_notify() from kernel and then in userspace we can just call
>  select() to wait for something to happen.
> 
>  > +/*
>  > + * Pre-allocate a buffer to hold handful of error logs until user space
>  [ 5 more citation lines. Click/Enter to show. ]
>  > + * consumes it.
>  > + */
>  > +static int init_err_log_buffer(void)
>  > +{
>  > +	int i = 0;
>  > +	struct opal_err_log *buf_ptr;
>  > +
>  > +	buf_ptr = vmalloc(sizeof(struct opal_err_log) * MAX_NUM_RECORD);
> 
>  This means we constantly use 128 * sizeof(struct opal_err_log) which
>  equates to somewhere north of 2MB of memory (due to list overhead).
> 
>  I don't think we need to statically allocate this, we can probably just
>  allocate on-demand as in a typical system you're probably quite
>  unlikely to have too many of these sitting around (besides, if for
>  whatever reason we cannot allocate memory at some point, that's okay
>  because we can read it again later).

The reason we choose to go for static allocation is, we can not afford
to drop or delay a critical error log due to memory allocation failure.
OR we can keep static allocations for critical errors and follow dynamic
allocation for informative error logs.  What do you say?

Thanks,
-Mahesh.

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

* Re: [PATCH] powerpc/powernv: Read opal error log and export it through sysfs interface.
  2014-02-25  5:02   ` Mahesh Jagannath Salgaonkar
@ 2014-02-25 23:19     ` Stewart Smith
  2014-02-28  0:58       ` [PATCH] powerpc/powernv: Read OPAL error log and export it through sysfs Stewart Smith
  0 siblings, 1 reply; 11+ messages in thread
From: Stewart Smith @ 2014-02-25 23:19 UTC (permalink / raw)
  To: Mahesh Jagannath Salgaonkar, linuxppc-dev

Mahesh Jagannath Salgaonkar <mahesh@linux.vnet.ibm.com> writes:
>>  I think we could provide a better interface with instead having a file
>>  per log message appear in sysfs. We're never going to have more than 128
>>  of these at any one time on the Linux side, so it's not going to bee too
>>  many files.
>
> It is not just about 128 files, we may be adding/removing sysfs node for
> every new log id that gets informed to kernel and ack-ed. In worst case,
> when we have flood of elog errors with user daemon consuming it and
> ack-ing back to get ready for next log in a tight poll, we may
> continuously add/remove the sysfs node for each new <id>.

Do we ever get a storm of hundreds/thousands of them though? If many
come it at once userspace may just be woken up one or two times, as it
would just select() and wait for events.

>>  I've seen some conflicting things on this - is it 2kb or 16kb?
>
> We choose 16kb because we want to pull all the log data and not
> partial.

So the max log size for any one entry is in fact 16kb?

>>  This means we constantly use 128 * sizeof(struct opal_err_log) which
>>  equates to somewhere north of 2MB of memory (due to list overhead).
>> 
>>  I don't think we need to statically allocate this, we can probably just
>>  allocate on-demand as in a typical system you're probably quite
>>  unlikely to have too many of these sitting around (besides, if for
>>  whatever reason we cannot allocate memory at some point, that's okay
>>  because we can read it again later).
>
> The reason we choose to go for static allocation is, we can not afford
> to drop or delay a critical error log due to memory allocation failure.
> OR we can keep static allocations for critical errors and follow dynamic
> allocation for informative error logs.  What do you say?

Userspace is probably going to have to do IO to get the log and ack it,
so it's probably not a huge problem - if we can't allocate a few kb in a
couple of attempts then we likely have bigger problems.

If we were going to have a sustained amount of hundreds/thousands of
these per second then perhaps we'd have other issues, but from what I
understand we're probably only going to have a handful per year on a
typical system? (I am, of course, not talking about our dev systems,
which are rather atypical :)

I'll likely have a patch today that shows kind of what I mean.

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

* [PATCH] powerpc/powernv: Read OPAL error log and export it through sysfs
  2014-02-25 23:19     ` Stewart Smith
@ 2014-02-28  0:58       ` Stewart Smith
  2014-03-04 12:31         ` Vasant Hegde
  0 siblings, 1 reply; 11+ messages in thread
From: Stewart Smith @ 2014-02-28  0:58 UTC (permalink / raw)
  To: Mahesh J Salgaonkar, benh, linuxppc-dev; +Cc: Stewart Smith

Based on a patch by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>

This patch adds support to read error logs from OPAL and export
them to userspace through a sysfs interface.

We export each log entry as a directory in /sys/firmware/opal/elog/

Currently, OPAL will buffer up to 128 error log records, we don't
need to have any knowledge of this limit on the Linux side as that
is actually largely transparent to us.

Each error log entry has the following files: id, type, acknowledge, raw.
Currently we just export the raw binary error log in the 'raw' attribute.
In a future patch, we may parse more of the error log to make it a bit
easier for userspace (e.g. to be able to display a brief summary in
petitboot without having to have a full parser).

If we have >128 logs from OPAL, we'll only be notified of 128 until
userspace starts acknowledging them. This limitation may be lifted in
the future and with this patch, that should "just work" from the linux side.

A userspace daemon should:
- wait for error log entries using normal mechanisms (we announce creation)
- read error log entry
- save error log entry safely to disk
- acknowledge the error log entry
- rinse, repeat.

On the Linux side, we read the error log when we're notified of it. This
possibly isn't ideal as it would be better to only read them on-demand.
However, this doesn't really work with current OPAL interface, so we
read the error log immediately when notified at the moment.

I've tested this pretty extensively and am rather confident that the
linux side of things works rather well. There is currently an issue with
the service processor side of things for >128 error logs though.

Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
---
 Documentation/ABI/stable/sysfs-firmware-opal-elog |   60 ++++
 arch/powerpc/include/asm/opal.h                   |   13 +
 arch/powerpc/platforms/powernv/Makefile           |    2 +-
 arch/powerpc/platforms/powernv/opal-elog.c        |  312 +++++++++++++++++++++
 arch/powerpc/platforms/powernv/opal-wrappers.S    |    5 +
 arch/powerpc/platforms/powernv/opal.c             |    2 +
 6 files changed, 393 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/ABI/stable/sysfs-firmware-opal-elog
 create mode 100644 arch/powerpc/platforms/powernv/opal-elog.c

diff --git a/Documentation/ABI/stable/sysfs-firmware-opal-elog b/Documentation/ABI/stable/sysfs-firmware-opal-elog
new file mode 100644
index 0000000..e1f3058
--- /dev/null
+++ b/Documentation/ABI/stable/sysfs-firmware-opal-elog
@@ -0,0 +1,60 @@
+What:		/sys/firmware/opal/elog
+Date:		Feb 2014
+Contact:	Stewart Smith <stewart@linux.vnet.ibm.com>
+Description:
+		This directory exposes error log entries retrieved
+		through the OPAL firmware interface.
+
+		Each error log is identified by a unique ID and will
+		exist until explicitly acknowledged to firmware.
+
+		Each log entry has a directory in /sys/firmware/opal/elog.
+
+		Log entries may be purged by the service processor
+		before retrieved by firmware or retrieved/acknowledged by
+		Linux if there is no room for more log entries.
+
+		In the event that Linux has retrieved the log entries
+		but not explicitly acknowledged them to firmware and
+		the service processor needs more room for log entries,
+		the only remaining copy of a log message may be in
+		Linux.
+
+		Typically, a user space daemon will monitor for new
+		entries, read them out and acknowledge them.
+
+		The service processor may be able to store more log
+		entries than firmware can, so after you acknowledge
+		an event from Linux you may instantly get another one
+		from the queue that was generated some time in the past.
+
+		The raw log format is a binary format. We currently
+		do not parse this at all in kernel, leaving it up to
+		user space to solve the problem. In future, we may
+		do more parsing in kernel and add more files to make
+		it easier for simple user space processes to extract
+		more information.
+
+		For each log entry (directory), there are the following
+		files:
+
+		id:		An ASCII representation of the ID of the
+				error log, in hex - e.g. "0x01".
+
+		type:		An ASCII representation of the type id and
+				description of the type of error log.
+				Currently just "0x00 PEL" - platform error log.
+				In the future there may be additional types.
+
+		raw:		A read-only binary file that can be read
+				to get the raw log entry. These are
+				<16kb, often just hundreds of bytes and
+				"average" 2kb.
+
+		acknowledge:	Writing 'ack' to this file will acknowledge
+				the error log to firmware (and in turn
+				the service processor, if applicable).
+				Shortly after acknowledging it, the log
+				entry will be removed from sysfs.
+				Reading this file will list the supported
+				operations (curently just acknowledge).
\ No newline at end of file
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 40157e2..b404545 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -151,6 +151,11 @@ extern int opal_enter_rtas(struct rtas_args *args,
 #define OPAL_LPC_READ				67
 #define OPAL_LPC_WRITE				68
 #define OPAL_RETURN_CPU				69
+#define OPAL_ELOG_READ				71
+#define OPAL_ELOG_WRITE				72
+#define OPAL_ELOG_ACK				73
+#define OPAL_ELOG_RESEND			74
+#define OPAL_ELOG_SIZE				75
 #define OPAL_FLASH_VALIDATE			76
 #define OPAL_FLASH_MANAGE			77
 #define OPAL_FLASH_UPDATE			78
@@ -823,6 +828,13 @@ int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type,
 		       uint32_t addr, uint32_t data, uint32_t sz);
 int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type,
 		      uint32_t addr, __be32 *data, uint32_t sz);
+
+int64_t opal_read_elog(uint64_t buffer, size_t size, uint64_t log_id);
+int64_t opal_get_elog_size(uint64_t *log_id, size_t *size, uint64_t *elog_type);
+int64_t opal_write_elog(uint64_t buffer, uint64_t size, uint64_t offset);
+int64_t opal_send_ack_elog(uint64_t log_id);
+void opal_resend_pending_logs(void);
+
 int64_t opal_validate_flash(uint64_t buffer, uint32_t *size, uint32_t *result);
 int64_t opal_manage_flash(uint8_t op);
 int64_t opal_update_flash(uint64_t blk_list);
@@ -861,6 +873,7 @@ extern void opal_get_rtc_time(struct rtc_time *tm);
 extern unsigned long opal_get_boot_time(void);
 extern void opal_nvram_init(void);
 extern void opal_flash_init(void);
+extern int opal_elog_init(void);
 
 extern int opal_machine_check(struct pt_regs *regs);
 
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 8d767fd..189fd45 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,6 +1,6 @@
 obj-y			+= setup.o opal-takeover.o opal-wrappers.o opal.o
 obj-y			+= opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
-obj-y			+= rng.o
+obj-y			+= rng.o opal-elog.o
 
 obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_PCI)	+= pci.o pci-p5ioc2.o pci-ioda.o
diff --git a/arch/powerpc/platforms/powernv/opal-elog.c b/arch/powerpc/platforms/powernv/opal-elog.c
new file mode 100644
index 0000000..61e2ef3
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-elog.c
@@ -0,0 +1,312 @@
+/*
+ * Error log support on PowerNV.
+ *
+ * Copyright 2013,2014 IBM Corp.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+#include <asm/opal.h>
+
+struct elog_obj {
+	struct kobject kobj;
+	struct bin_attribute raw_attr;
+	uint64_t id;
+	uint64_t type;
+	size_t size;
+	char *buffer;
+};
+#define to_elog_obj(x) container_of(x, struct elog_obj, kobj)
+
+struct elog_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct elog_obj *elog, struct elog_attribute *attr,
+			char *buf);
+	ssize_t (*store)(struct elog_obj *elog, struct elog_attribute *attr,
+			 const char *buf, size_t count);
+};
+#define to_elog_attr(x) container_of(x, struct elog_attribute, attr)
+
+static ssize_t elog_id_show(struct elog_obj *elog_obj,
+			    struct elog_attribute *attr,
+			    char *buf)
+{
+	return sprintf(buf, "0x%llx\n", elog_obj->id);
+}
+
+static const char *elog_type_to_string(uint64_t type)
+{
+	switch (type) {
+	case 0: return "PEL";
+	default: return "unknown";
+	}
+}
+
+static ssize_t elog_type_show(struct elog_obj *elog_obj,
+			      struct elog_attribute *attr,
+			      char *buf)
+{
+	return sprintf(buf, "0x%llx %s\n",
+		       elog_obj->type,
+		       elog_type_to_string(elog_obj->type));
+}
+
+static ssize_t elog_ack_show(struct elog_obj *elog_obj,
+			     struct elog_attribute *attr,
+			     char *buf)
+{
+	return sprintf(buf, "ack - acknowledge log message\n");
+}
+
+static void delay_release_kobj(void *kobj)
+{
+	kobject_put((struct kobject *)kobj);
+}
+
+static ssize_t elog_ack_store(struct elog_obj *elog_obj,
+			      struct elog_attribute *attr,
+			      const char *buf,
+			      size_t count)
+{
+	opal_send_ack_elog(elog_obj->id);
+	sysfs_schedule_callback(&elog_obj->kobj, delay_release_kobj,
+				&elog_obj->kobj, THIS_MODULE);
+	return count;
+}
+
+static struct elog_attribute id_attribute =
+	__ATTR(id, 0666, elog_id_show, NULL);
+static struct elog_attribute type_attribute =
+	__ATTR(type, 0666, elog_type_show, NULL);
+static struct elog_attribute ack_attribute =
+	__ATTR(acknowledge, 0660, elog_ack_show, elog_ack_store);
+
+static struct kset *elog_kset;
+
+static ssize_t elog_attr_show(struct kobject *kobj,
+			      struct attribute *attr,
+			      char *buf)
+{
+	struct elog_attribute *attribute;
+	struct elog_obj *elog;
+
+	attribute = to_elog_attr(attr);
+	elog = to_elog_obj(kobj);
+
+	if (!attribute->show)
+		return -EIO;
+
+	return attribute->show(elog, attribute, buf);
+}
+
+static ssize_t elog_attr_store(struct kobject *kobj,
+			       struct attribute *attr,
+			       const char *buf, size_t len)
+{
+	struct elog_attribute *attribute;
+	struct elog_obj *elog;
+
+	attribute = to_elog_attr(attr);
+	elog = to_elog_obj(kobj);
+
+	if (!attribute->store)
+		return -EIO;
+
+	return attribute->store(elog, attribute, buf, len);
+}
+
+static const struct sysfs_ops elog_sysfs_ops = {
+	.show = elog_attr_show,
+	.store = elog_attr_store,
+};
+
+static void elog_release(struct kobject *kobj)
+{
+	struct elog_obj *elog;
+
+	elog = to_elog_obj(kobj);
+	kfree(elog->buffer);
+	kfree(elog);
+}
+
+static struct attribute *elog_default_attrs[] = {
+	&id_attribute.attr,
+	&type_attribute.attr,
+	&ack_attribute.attr,
+	NULL,
+};
+
+static struct kobj_type elog_ktype = {
+	.sysfs_ops = &elog_sysfs_ops,
+	.release = &elog_release,
+	.default_attrs = elog_default_attrs,
+};
+
+/* Maximum size of a single log on FSP is 16KB */
+#define OPAL_MAX_ERRLOG_SIZE	16384
+
+static ssize_t raw_attr_read(struct file *filep, struct kobject *kobj,
+			     struct bin_attribute *bin_attr,
+			     char *buffer, loff_t pos, size_t count)
+{
+	int opal_rc;
+
+	struct elog_obj *elog = to_elog_obj(kobj);
+
+	/* We may have had an error reading before, so let's retry */
+	if (!elog->buffer) {
+		elog->buffer = kzalloc(elog->size, GFP_KERNEL);
+		if (!elog->buffer)
+			return -EIO;
+
+		opal_rc = opal_read_elog(__pa(elog->buffer),
+					 elog->size, elog->id);
+		if (opal_rc != OPAL_SUCCESS) {
+			pr_err("ELOG: log read failed for log-id=%llx\n",
+			       elog->id);
+			kfree(elog->buffer);
+			elog->buffer = NULL;
+			return -EIO;
+		}
+	}
+
+	memcpy(buffer, elog->buffer + pos, count);
+
+	return count;
+}
+
+static struct elog_obj *create_elog_obj(uint64_t id, size_t size, uint64_t type)
+{
+	struct elog_obj *elog;
+	int rc;
+
+	elog = kzalloc(sizeof(*elog), GFP_KERNEL);
+	if (!elog)
+		return NULL;
+
+	elog->kobj.kset = elog_kset;
+
+	kobject_init(&elog->kobj, &elog_ktype);
+
+	sysfs_bin_attr_init(&elog->raw_attr);
+
+	elog->raw_attr.attr.name = "raw";
+	elog->raw_attr.attr.mode = 0400;
+	elog->raw_attr.size = size;
+	elog->raw_attr.read = raw_attr_read;
+
+	elog->id = id;
+	elog->size = size;
+	elog->type = type;
+
+	elog->buffer = kzalloc(elog->size, GFP_KERNEL);
+
+	if (elog->buffer) {
+		rc = opal_read_elog(__pa(elog->buffer),
+					 elog->size, elog->id);
+		if (rc != OPAL_SUCCESS) {
+			pr_err("ELOG: log read failed for log-id=%llx\n",
+			       elog->id);
+			kfree(elog->buffer);
+			elog->buffer = NULL;
+		}
+	}
+
+	rc = kobject_add(&elog->kobj, NULL, "0x%llx", id);
+	if (rc) {
+		kobject_put(&elog->kobj);
+		return NULL;
+	}
+
+	rc = sysfs_create_bin_file(&elog->kobj, &elog->raw_attr);
+	if (rc) {
+		kobject_put(&elog->kobj);
+		return NULL;
+	}
+
+	kobject_uevent(&elog->kobj, KOBJ_ADD);
+
+	return elog;
+}
+
+static void elog_work_fn(struct work_struct *work)
+{
+	size_t elog_size;
+	uint64_t log_id;
+	uint64_t elog_type;
+	int rc;
+	char name[2+16+1];
+
+	rc = opal_get_elog_size(&log_id, &elog_size, &elog_type);
+	if (rc != OPAL_SUCCESS) {
+		pr_err("ELOG: Opal log read failed\n");
+		return;
+	}
+
+	BUG_ON(elog_size > OPAL_MAX_ERRLOG_SIZE);
+
+	if (elog_size >= OPAL_MAX_ERRLOG_SIZE)
+		elog_size  =  OPAL_MAX_ERRLOG_SIZE;
+
+	sprintf(name, "0x%llx", log_id);
+
+	/* we may get notified twice, let's handle
+	 * that gracefully and not create two conflicting
+	 * entries.
+	 */
+	if (kset_find_obj(elog_kset, name))
+		return;
+
+	create_elog_obj(log_id, elog_size, elog_type);
+}
+
+static DECLARE_WORK(elog_work, elog_work_fn);
+
+static int elog_event(struct notifier_block *nb,
+				unsigned long events, void *change)
+{
+	/* check for error log event */
+	if (events & OPAL_EVENT_ERROR_LOG_AVAIL)
+		schedule_work(&elog_work);
+	return 0;
+}
+
+static struct notifier_block elog_nb = {
+	.notifier_call  = elog_event,
+	.next           = NULL,
+	.priority       = 0
+};
+
+int __init opal_elog_init(void)
+{
+	int rc = 0;
+
+	elog_kset = kset_create_and_add("elog", NULL, opal_kobj);
+	if (!elog_kset) {
+		pr_warn("%s: failed to create elog kset\n", __func__);
+		return -1;
+	}
+
+	rc = opal_notifier_register(&elog_nb);
+	if (rc) {
+		pr_err("%s: Can't register OPAL event notifier (%d)\n",
+		__func__, rc);
+		return rc;
+	}
+
+	/* We are now ready to pull error logs from opal. */
+	opal_resend_pending_logs();
+
+	return 0;
+}
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 3e8829c..5fcbf25 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -123,6 +123,11 @@ OPAL_CALL(opal_xscom_write,			OPAL_XSCOM_WRITE);
 OPAL_CALL(opal_lpc_read,			OPAL_LPC_READ);
 OPAL_CALL(opal_lpc_write,			OPAL_LPC_WRITE);
 OPAL_CALL(opal_return_cpu,			OPAL_RETURN_CPU);
+OPAL_CALL(opal_read_elog,			OPAL_ELOG_READ);
+OPAL_CALL(opal_send_ack_elog,			OPAL_ELOG_ACK);
+OPAL_CALL(opal_get_elog_size,			OPAL_ELOG_SIZE);
+OPAL_CALL(opal_resend_pending_logs,		OPAL_ELOG_RESEND);
+OPAL_CALL(opal_write_elog,			OPAL_ELOG_WRITE);
 OPAL_CALL(opal_validate_flash,			OPAL_FLASH_VALIDATE);
 OPAL_CALL(opal_manage_flash,			OPAL_FLASH_MANAGE);
 OPAL_CALL(opal_update_flash,			OPAL_FLASH_UPDATE);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 65499ad..fb77302 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -472,6 +472,8 @@ static int __init opal_init(void)
 	/* Create "opal" kobject under /sys/firmware */
 	rc = opal_sysfs_init();
 	if (rc == 0) {
+		/* Setup error log interface */
+		rc = opal_elog_init();
 		/* Setup code update interface */
 		opal_flash_init();
 	}
-- 
1.7.10.4

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

* Re: [PATCH] powerpc/powernv: Read OPAL error log and export it through sysfs
  2014-02-28  0:58       ` [PATCH] powerpc/powernv: Read OPAL error log and export it through sysfs Stewart Smith
@ 2014-03-04 12:31         ` Vasant Hegde
  2014-03-05  1:56           ` Stewart Smith
  0 siblings, 1 reply; 11+ messages in thread
From: Vasant Hegde @ 2014-03-04 12:31 UTC (permalink / raw)
  To: Stewart Smith, Mahesh J Salgaonkar, benh, linuxppc-dev

On 02/28/2014 06:28 AM, Stewart Smith wrote:
> Based on a patch by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
>
> This patch adds support to read error logs from OPAL and export
> them to userspace through a sysfs interface.
>
> We export each log entry as a directory in /sys/firmware/opal/elog/
>
> Currently, OPAL will buffer up to 128 error log records, we don't
> need to have any knowledge of this limit on the Linux side as that
> is actually largely transparent to us.
>
> Each error log entry has the following files: id, type, acknowledge, raw.
> Currently we just export the raw binary error log in the 'raw' attribute.
> In a future patch, we may parse more of the error log to make it a bit
> easier for userspace (e.g. to be able to display a brief summary in
> petitboot without having to have a full parser).
>
> If we have >128 logs from OPAL, we'll only be notified of 128 until
> userspace starts acknowledging them. This limitation may be lifted in
> the future and with this patch, that should "just work" from the linux side.
>
> A userspace daemon should:
> - wait for error log entries using normal mechanisms (we announce creation)
> - read error log entry
> - save error log entry safely to disk
> - acknowledge the error log entry
> - rinse, repeat.
>
> On the Linux side, we read the error log when we're notified of it. This
> possibly isn't ideal as it would be better to only read them on-demand.
> However, this doesn't really work with current OPAL interface, so we
> read the error log immediately when notified at the moment.
>
> I've tested this pretty extensively and am rather confident that the
> linux side of things works rather well. There is currently an issue with
> the service processor side of things for >128 error logs though.
>
> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
> ---
>   Documentation/ABI/stable/sysfs-firmware-opal-elog |   60 ++++
>   arch/powerpc/include/asm/opal.h                   |   13 +
>   arch/powerpc/platforms/powernv/Makefile           |    2 +-
>   arch/powerpc/platforms/powernv/opal-elog.c        |  312 +++++++++++++++++++++
>   arch/powerpc/platforms/powernv/opal-wrappers.S    |    5 +
>   arch/powerpc/platforms/powernv/opal.c             |    2 +
>   6 files changed, 393 insertions(+), 1 deletion(-)
>   create mode 100644 Documentation/ABI/stable/sysfs-firmware-opal-elog
>   create mode 100644 arch/powerpc/platforms/powernv/opal-elog.c
>
> diff --git a/Documentation/ABI/stable/sysfs-firmware-opal-elog b/Documentation/ABI/stable/sysfs-firmware-opal-elog
> new file mode 100644
> index 0000000..e1f3058
> --- /dev/null
> +++ b/Documentation/ABI/stable/sysfs-firmware-opal-elog
> @@ -0,0 +1,60 @@
> +What:		/sys/firmware/opal/elog
> +Date:		Feb 2014
> +Contact:	Stewart Smith <stewart@linux.vnet.ibm.com>
> +Description:
> +		This directory exposes error log entries retrieved
> +		through the OPAL firmware interface.
> +
> +		Each error log is identified by a unique ID and will
> +		exist until explicitly acknowledged to firmware.
> +
> +		Each log entry has a directory in /sys/firmware/opal/elog.
> +
> +		Log entries may be purged by the service processor
> +		before retrieved by firmware or retrieved/acknowledged by
> +		Linux if there is no room for more log entries.
> +
> +		In the event that Linux has retrieved the log entries
> +		but not explicitly acknowledged them to firmware and
> +		the service processor needs more room for log entries,
> +		the only remaining copy of a log message may be in
> +		Linux.
> +
> +		Typically, a user space daemon will monitor for new
> +		entries, read them out and acknowledge them.
> +
> +		The service processor may be able to store more log
> +		entries than firmware can, so after you acknowledge
> +		an event from Linux you may instantly get another one
> +		from the queue that was generated some time in the past.
> +
> +		The raw log format is a binary format. We currently
> +		do not parse this at all in kernel, leaving it up to
> +		user space to solve the problem. In future, we may
> +		do more parsing in kernel and add more files to make
> +		it easier for simple user space processes to extract
> +		more information.
> +
> +		For each log entry (directory), there are the following
> +		files:
> +
> +		id:		An ASCII representation of the ID of the
> +				error log, in hex - e.g. "0x01".
> +
> +		type:		An ASCII representation of the type id and
> +				description of the type of error log.
> +				Currently just "0x00 PEL" - platform error log.
> +				In the future there may be additional types.
> +
> +		raw:		A read-only binary file that can be read
> +				to get the raw log entry. These are
> +				<16kb, often just hundreds of bytes and
> +				"average" 2kb.
> +
> +		acknowledge:	Writing 'ack' to this file will acknowledge
> +				the error log to firmware (and in turn
> +				the service processor, if applicable).
> +				Shortly after acknowledging it, the log
> +				entry will be removed from sysfs.
> +				Reading this file will list the supported
> +				operations (curently just acknowledge).
> \ No newline at end of file
> diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
> index 40157e2..b404545 100644
> --- a/arch/powerpc/include/asm/opal.h
> +++ b/arch/powerpc/include/asm/opal.h
> @@ -151,6 +151,11 @@ extern int opal_enter_rtas(struct rtas_args *args,
>   #define OPAL_LPC_READ				67
>   #define OPAL_LPC_WRITE				68
>   #define OPAL_RETURN_CPU				69
> +#define OPAL_ELOG_READ				71
> +#define OPAL_ELOG_WRITE				72
> +#define OPAL_ELOG_ACK				73
> +#define OPAL_ELOG_RESEND			74
> +#define OPAL_ELOG_SIZE				75
>   #define OPAL_FLASH_VALIDATE			76
>   #define OPAL_FLASH_MANAGE			77
>   #define OPAL_FLASH_UPDATE			78
> @@ -823,6 +828,13 @@ int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type,
>   		       uint32_t addr, uint32_t data, uint32_t sz);
>   int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type,
>   		      uint32_t addr, __be32 *data, uint32_t sz);
> +
> +int64_t opal_read_elog(uint64_t buffer, size_t size, uint64_t log_id);
> +int64_t opal_get_elog_size(uint64_t *log_id, size_t *size, uint64_t *elog_type);
> +int64_t opal_write_elog(uint64_t buffer, uint64_t size, uint64_t offset);
> +int64_t opal_send_ack_elog(uint64_t log_id);

Stewart,

Why are you creating 64bit log ID  when actual ID is 32bit ?

Rest looks good.

-Vasant


> +void opal_resend_pending_logs(void);
> +
>   int64_t opal_validate_flash(uint64_t buffer, uint32_t *size, uint32_t *result);
>   int64_t opal_manage_flash(uint8_t op);
>   int64_t opal_update_flash(uint64_t blk_list);
> @@ -861,6 +873,7 @@ extern void opal_get_rtc_time(struct rtc_time *tm);
>   extern unsigned long opal_get_boot_time(void);
>   extern void opal_nvram_init(void);
>   extern void opal_flash_init(void);
> +extern int opal_elog_init(void);
>
>   extern int opal_machine_check(struct pt_regs *regs);
>
> diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
> index 8d767fd..189fd45 100644
> --- a/arch/powerpc/platforms/powernv/Makefile
> +++ b/arch/powerpc/platforms/powernv/Makefile
> @@ -1,6 +1,6 @@
>   obj-y			+= setup.o opal-takeover.o opal-wrappers.o opal.o
>   obj-y			+= opal-rtc.o opal-nvram.o opal-lpc.o opal-flash.o
> -obj-y			+= rng.o
> +obj-y			+= rng.o opal-elog.o
>
>   obj-$(CONFIG_SMP)	+= smp.o
>   obj-$(CONFIG_PCI)	+= pci.o pci-p5ioc2.o pci-ioda.o
> diff --git a/arch/powerpc/platforms/powernv/opal-elog.c b/arch/powerpc/platforms/powernv/opal-elog.c
> new file mode 100644
> index 0000000..61e2ef3
> --- /dev/null
> +++ b/arch/powerpc/platforms/powernv/opal-elog.c
> @@ -0,0 +1,312 @@
> +/*
> + * Error log support on PowerNV.
> + *
> + * Copyright 2013,2014 IBM Corp.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/sysfs.h>
> +#include <linux/fs.h>
> +#include <linux/vmalloc.h>
> +#include <linux/fcntl.h>
> +#include <asm/uaccess.h>
> +#include <asm/opal.h>
> +
> +struct elog_obj {
> +	struct kobject kobj;
> +	struct bin_attribute raw_attr;
> +	uint64_t id;
> +	uint64_t type;
> +	size_t size;
> +	char *buffer;
> +};
> +#define to_elog_obj(x) container_of(x, struct elog_obj, kobj)
> +
> +struct elog_attribute {
> +	struct attribute attr;
> +	ssize_t (*show)(struct elog_obj *elog, struct elog_attribute *attr,
> +			char *buf);
> +	ssize_t (*store)(struct elog_obj *elog, struct elog_attribute *attr,
> +			 const char *buf, size_t count);
> +};
> +#define to_elog_attr(x) container_of(x, struct elog_attribute, attr)
> +
> +static ssize_t elog_id_show(struct elog_obj *elog_obj,
> +			    struct elog_attribute *attr,
> +			    char *buf)
> +{
> +	return sprintf(buf, "0x%llx\n", elog_obj->id);
> +}
> +
> +static const char *elog_type_to_string(uint64_t type)
> +{
> +	switch (type) {
> +	case 0: return "PEL";
> +	default: return "unknown";
> +	}
> +}
> +
> +static ssize_t elog_type_show(struct elog_obj *elog_obj,
> +			      struct elog_attribute *attr,
> +			      char *buf)
> +{
> +	return sprintf(buf, "0x%llx %s\n",
> +		       elog_obj->type,
> +		       elog_type_to_string(elog_obj->type));
> +}
> +
> +static ssize_t elog_ack_show(struct elog_obj *elog_obj,
> +			     struct elog_attribute *attr,
> +			     char *buf)
> +{
> +	return sprintf(buf, "ack - acknowledge log message\n");
> +}
> +
> +static void delay_release_kobj(void *kobj)
> +{
> +	kobject_put((struct kobject *)kobj);
> +}
> +
> +static ssize_t elog_ack_store(struct elog_obj *elog_obj,
> +			      struct elog_attribute *attr,
> +			      const char *buf,
> +			      size_t count)
> +{
> +	opal_send_ack_elog(elog_obj->id);
> +	sysfs_schedule_callback(&elog_obj->kobj, delay_release_kobj,
> +				&elog_obj->kobj, THIS_MODULE);
> +	return count;
> +}
> +
> +static struct elog_attribute id_attribute =
> +	__ATTR(id, 0666, elog_id_show, NULL);
> +static struct elog_attribute type_attribute =
> +	__ATTR(type, 0666, elog_type_show, NULL);
> +static struct elog_attribute ack_attribute =
> +	__ATTR(acknowledge, 0660, elog_ack_show, elog_ack_store);
> +
> +static struct kset *elog_kset;
> +
> +static ssize_t elog_attr_show(struct kobject *kobj,
> +			      struct attribute *attr,
> +			      char *buf)
> +{
> +	struct elog_attribute *attribute;
> +	struct elog_obj *elog;
> +
> +	attribute = to_elog_attr(attr);
> +	elog = to_elog_obj(kobj);
> +
> +	if (!attribute->show)
> +		return -EIO;
> +
> +	return attribute->show(elog, attribute, buf);
> +}
> +
> +static ssize_t elog_attr_store(struct kobject *kobj,
> +			       struct attribute *attr,
> +			       const char *buf, size_t len)
> +{
> +	struct elog_attribute *attribute;
> +	struct elog_obj *elog;
> +
> +	attribute = to_elog_attr(attr);
> +	elog = to_elog_obj(kobj);
> +
> +	if (!attribute->store)
> +		return -EIO;
> +
> +	return attribute->store(elog, attribute, buf, len);
> +}
> +
> +static const struct sysfs_ops elog_sysfs_ops = {
> +	.show = elog_attr_show,
> +	.store = elog_attr_store,
> +};
> +
> +static void elog_release(struct kobject *kobj)
> +{
> +	struct elog_obj *elog;
> +
> +	elog = to_elog_obj(kobj);
> +	kfree(elog->buffer);
> +	kfree(elog);
> +}
> +
> +static struct attribute *elog_default_attrs[] = {
> +	&id_attribute.attr,
> +	&type_attribute.attr,
> +	&ack_attribute.attr,
> +	NULL,
> +};
> +
> +static struct kobj_type elog_ktype = {
> +	.sysfs_ops = &elog_sysfs_ops,
> +	.release = &elog_release,
> +	.default_attrs = elog_default_attrs,
> +};
> +
> +/* Maximum size of a single log on FSP is 16KB */
> +#define OPAL_MAX_ERRLOG_SIZE	16384
> +
> +static ssize_t raw_attr_read(struct file *filep, struct kobject *kobj,
> +			     struct bin_attribute *bin_attr,
> +			     char *buffer, loff_t pos, size_t count)
> +{
> +	int opal_rc;
> +
> +	struct elog_obj *elog = to_elog_obj(kobj);
> +
> +	/* We may have had an error reading before, so let's retry */
> +	if (!elog->buffer) {
> +		elog->buffer = kzalloc(elog->size, GFP_KERNEL);
> +		if (!elog->buffer)
> +			return -EIO;
> +
> +		opal_rc = opal_read_elog(__pa(elog->buffer),
> +					 elog->size, elog->id);
> +		if (opal_rc != OPAL_SUCCESS) {
> +			pr_err("ELOG: log read failed for log-id=%llx\n",
> +			       elog->id);
> +			kfree(elog->buffer);
> +			elog->buffer = NULL;
> +			return -EIO;
> +		}
> +	}
> +
> +	memcpy(buffer, elog->buffer + pos, count);
> +
> +	return count;
> +}
> +
> +static struct elog_obj *create_elog_obj(uint64_t id, size_t size, uint64_t type)
> +{
> +	struct elog_obj *elog;
> +	int rc;
> +
> +	elog = kzalloc(sizeof(*elog), GFP_KERNEL);
> +	if (!elog)
> +		return NULL;
> +
> +	elog->kobj.kset = elog_kset;
> +
> +	kobject_init(&elog->kobj, &elog_ktype);
> +
> +	sysfs_bin_attr_init(&elog->raw_attr);
> +
> +	elog->raw_attr.attr.name = "raw";
> +	elog->raw_attr.attr.mode = 0400;
> +	elog->raw_attr.size = size;
> +	elog->raw_attr.read = raw_attr_read;
> +
> +	elog->id = id;
> +	elog->size = size;
> +	elog->type = type;
> +
> +	elog->buffer = kzalloc(elog->size, GFP_KERNEL);
> +
> +	if (elog->buffer) {
> +		rc = opal_read_elog(__pa(elog->buffer),
> +					 elog->size, elog->id);
> +		if (rc != OPAL_SUCCESS) {
> +			pr_err("ELOG: log read failed for log-id=%llx\n",
> +			       elog->id);
> +			kfree(elog->buffer);
> +			elog->buffer = NULL;
> +		}
> +	}
> +
> +	rc = kobject_add(&elog->kobj, NULL, "0x%llx", id);
> +	if (rc) {
> +		kobject_put(&elog->kobj);
> +		return NULL;
> +	}
> +
> +	rc = sysfs_create_bin_file(&elog->kobj, &elog->raw_attr);
> +	if (rc) {
> +		kobject_put(&elog->kobj);
> +		return NULL;
> +	}
> +
> +	kobject_uevent(&elog->kobj, KOBJ_ADD);
> +
> +	return elog;
> +}
> +
> +static void elog_work_fn(struct work_struct *work)
> +{
> +	size_t elog_size;
> +	uint64_t log_id;
> +	uint64_t elog_type;
> +	int rc;
> +	char name[2+16+1];
> +
> +	rc = opal_get_elog_size(&log_id, &elog_size, &elog_type);
> +	if (rc != OPAL_SUCCESS) {
> +		pr_err("ELOG: Opal log read failed\n");
> +		return;
> +	}
> +
> +	BUG_ON(elog_size > OPAL_MAX_ERRLOG_SIZE);
> +
> +	if (elog_size >= OPAL_MAX_ERRLOG_SIZE)
> +		elog_size  =  OPAL_MAX_ERRLOG_SIZE;
> +
> +	sprintf(name, "0x%llx", log_id);
> +
> +	/* we may get notified twice, let's handle
> +	 * that gracefully and not create two conflicting
> +	 * entries.
> +	 */
> +	if (kset_find_obj(elog_kset, name))
> +		return;
> +
> +	create_elog_obj(log_id, elog_size, elog_type);
> +}
> +
> +static DECLARE_WORK(elog_work, elog_work_fn);
> +
> +static int elog_event(struct notifier_block *nb,
> +				unsigned long events, void *change)
> +{
> +	/* check for error log event */
> +	if (events & OPAL_EVENT_ERROR_LOG_AVAIL)
> +		schedule_work(&elog_work);
> +	return 0;
> +}
> +
> +static struct notifier_block elog_nb = {
> +	.notifier_call  = elog_event,
> +	.next           = NULL,
> +	.priority       = 0
> +};
> +
> +int __init opal_elog_init(void)
> +{
> +	int rc = 0;
> +
> +	elog_kset = kset_create_and_add("elog", NULL, opal_kobj);
> +	if (!elog_kset) {
> +		pr_warn("%s: failed to create elog kset\n", __func__);
> +		return -1;
> +	}
> +
> +	rc = opal_notifier_register(&elog_nb);
> +	if (rc) {
> +		pr_err("%s: Can't register OPAL event notifier (%d)\n",
> +		__func__, rc);
> +		return rc;
> +	}
> +
> +	/* We are now ready to pull error logs from opal. */
> +	opal_resend_pending_logs();
> +
> +	return 0;
> +}
> diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
> index 3e8829c..5fcbf25 100644
> --- a/arch/powerpc/platforms/powernv/opal-wrappers.S
> +++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
> @@ -123,6 +123,11 @@ OPAL_CALL(opal_xscom_write,			OPAL_XSCOM_WRITE);
>   OPAL_CALL(opal_lpc_read,			OPAL_LPC_READ);
>   OPAL_CALL(opal_lpc_write,			OPAL_LPC_WRITE);
>   OPAL_CALL(opal_return_cpu,			OPAL_RETURN_CPU);
> +OPAL_CALL(opal_read_elog,			OPAL_ELOG_READ);
> +OPAL_CALL(opal_send_ack_elog,			OPAL_ELOG_ACK);
> +OPAL_CALL(opal_get_elog_size,			OPAL_ELOG_SIZE);
> +OPAL_CALL(opal_resend_pending_logs,		OPAL_ELOG_RESEND);
> +OPAL_CALL(opal_write_elog,			OPAL_ELOG_WRITE);
>   OPAL_CALL(opal_validate_flash,			OPAL_FLASH_VALIDATE);
>   OPAL_CALL(opal_manage_flash,			OPAL_FLASH_MANAGE);
>   OPAL_CALL(opal_update_flash,			OPAL_FLASH_UPDATE);
> diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
> index 65499ad..fb77302 100644
> --- a/arch/powerpc/platforms/powernv/opal.c
> +++ b/arch/powerpc/platforms/powernv/opal.c
> @@ -472,6 +472,8 @@ static int __init opal_init(void)
>   	/* Create "opal" kobject under /sys/firmware */
>   	rc = opal_sysfs_init();
>   	if (rc == 0) {
> +		/* Setup error log interface */
> +		rc = opal_elog_init();
>   		/* Setup code update interface */
>   		opal_flash_init();
>   	}
>

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

* Re: [PATCH] powerpc/powernv: Read OPAL error log and export it through sysfs
  2014-03-04 12:31         ` Vasant Hegde
@ 2014-03-05  1:56           ` Stewart Smith
  2014-03-05  3:26             ` Vasant Hegde
  2014-03-05  3:26             ` Vasant Hegde
  0 siblings, 2 replies; 11+ messages in thread
From: Stewart Smith @ 2014-03-05  1:56 UTC (permalink / raw)
  To: Vasant Hegde, Mahesh J Salgaonkar, benh, linuxppc-dev

Vasant Hegde <hegdevasant@linux.vnet.ibm.com> writes:
>> +int64_t opal_send_ack_elog(uint64_t log_id);
>
> Stewart,
>
> Why are you creating 64bit log ID  when actual ID is 32bit ?

IIRC it's what OPAL gives us, even though FSP MBOX spec says 32bit.

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

* Re: [PATCH] powerpc/powernv: Read OPAL error log and export it through sysfs
  2014-03-05  1:56           ` Stewart Smith
@ 2014-03-05  3:26             ` Vasant Hegde
  2014-03-05  3:26             ` Vasant Hegde
  1 sibling, 0 replies; 11+ messages in thread
From: Vasant Hegde @ 2014-03-05  3:26 UTC (permalink / raw)
  To: Stewart Smith, Mahesh J Salgaonkar, benh, linuxppc-dev

On 03/05/2014 07:26 AM, Stewart Smith wrote:
> Vasant Hegde <hegdevasant@linux.vnet.ibm.com> writes:
>>> +int64_t opal_send_ack_elog(uint64_t log_id);
>>
>> Stewart,
>>
>> Why are you creating 64bit log ID  when actual ID is 32bit ?
>
> IIRC it's what OPAL gives us, even though FSP MBOX spec says 32bit.
>

Stewart,

Better ask OPAL folks to fix in firmware ?

-Vasant

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

* Re: [PATCH] powerpc/powernv: Read OPAL error log and export it through sysfs
  2014-03-05  1:56           ` Stewart Smith
  2014-03-05  3:26             ` Vasant Hegde
@ 2014-03-05  3:26             ` Vasant Hegde
  2014-03-05  4:00               ` Benjamin Herrenschmidt
  1 sibling, 1 reply; 11+ messages in thread
From: Vasant Hegde @ 2014-03-05  3:26 UTC (permalink / raw)
  To: Stewart Smith, Mahesh J Salgaonkar, benh, linuxppc-dev

On 03/05/2014 07:26 AM, Stewart Smith wrote:
> Vasant Hegde <hegdevasant@linux.vnet.ibm.com> writes:
>>> +int64_t opal_send_ack_elog(uint64_t log_id);
>>
>> Stewart,
>>
>> Why are you creating 64bit log ID  when actual ID is 32bit ?
>
> IIRC it's what OPAL gives us, even though FSP MBOX spec says 32bit.
>

Stewart,

Better ask OPAL folks to fix in firmware ?

-Vasant

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

* Re: [PATCH] powerpc/powernv: Read OPAL error log and export it through sysfs
  2014-03-05  3:26             ` Vasant Hegde
@ 2014-03-05  4:00               ` Benjamin Herrenschmidt
  2014-03-05  4:05                 ` Stewart Smith
  0 siblings, 1 reply; 11+ messages in thread
From: Benjamin Herrenschmidt @ 2014-03-05  4:00 UTC (permalink / raw)
  To: Vasant Hegde; +Cc: Stewart Smith, Mahesh J Salgaonkar, linuxppc-dev

On Wed, 2014-03-05 at 08:56 +0530, Vasant Hegde wrote:
> On 03/05/2014 07:26 AM, Stewart Smith wrote:
> > Vasant Hegde <hegdevasant@linux.vnet.ibm.com> writes:
> >>> +int64_t opal_send_ack_elog(uint64_t log_id);
> >>
> >> Stewart,
> >>
> >> Why are you creating 64bit log ID  when actual ID is 32bit ?
> >
> > IIRC it's what OPAL gives us, even though FSP MBOX spec says 32bit.
> >
> 
> Stewart,
> 
> Better ask OPAL folks to fix in firmware ?

Why bother ? That way if future versions of OPAL want to use 64-bit IDs,
they can.

Cheers,
Ben.

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

* Re: [PATCH] powerpc/powernv: Read OPAL error log and export it through sysfs
  2014-03-05  4:00               ` Benjamin Herrenschmidt
@ 2014-03-05  4:05                 ` Stewart Smith
  0 siblings, 0 replies; 11+ messages in thread
From: Stewart Smith @ 2014-03-05  4:05 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, Vasant Hegde; +Cc: Mahesh J Salgaonkar, linuxppc-dev

Benjamin Herrenschmidt <benh@kernel.crashing.org> writes:
> On Wed, 2014-03-05 at 08:56 +0530, Vasant Hegde wrote:
>> On 03/05/2014 07:26 AM, Stewart Smith wrote:
>> > Vasant Hegde <hegdevasant@linux.vnet.ibm.com> writes:
>> >>> +int64_t opal_send_ack_elog(uint64_t log_id);
>> >>
>> >> Stewart,
>> >>
>> >> Why are you creating 64bit log ID  when actual ID is 32bit ?
>> >
>> > IIRC it's what OPAL gives us, even though FSP MBOX spec says 32bit.
>> >
>> 
>> Stewart,
>> 
>> Better ask OPAL folks to fix in firmware ?
>
> Why bother ? That way if future versions of OPAL want to use 64-bit IDs,
> they can.

Yeah, that was my thought. The number of error logs on some POWER8
systems almost needs 64bit already :)

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

end of thread, other threads:[~2014-03-05  4:05 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-12-16  9:58 [PATCH] powerpc/powernv: Read opal error log and export it through sysfs interface Mahesh J Salgaonkar
2014-02-21  0:11 ` Stewart Smith
2014-02-25  5:02   ` Mahesh Jagannath Salgaonkar
2014-02-25 23:19     ` Stewart Smith
2014-02-28  0:58       ` [PATCH] powerpc/powernv: Read OPAL error log and export it through sysfs Stewart Smith
2014-03-04 12:31         ` Vasant Hegde
2014-03-05  1:56           ` Stewart Smith
2014-03-05  3:26             ` Vasant Hegde
2014-03-05  3:26             ` Vasant Hegde
2014-03-05  4:00               ` Benjamin Herrenschmidt
2014-03-05  4:05                 ` Stewart Smith

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.