All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mahesh J Salgaonkar <mahesh@linux.vnet.ibm.com>
To: linuxppc-dev <linuxppc-dev@ozlabs.org>,
	Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>,
	Mamatha Inamdar <mamatha4@linux.vnet.ibm.com>
Subject: [PATCH] powerpc/powernv: Read opal error log and export it through sysfs interface.
Date: Mon, 16 Dec 2013 15:28:01 +0530	[thread overview]
Message-ID: <20131216095746.14595.64602.stgit@mars.in.ibm.com> (raw)

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();
 	}

             reply	other threads:[~2013-12-16  9:58 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-12-16  9:58 Mahesh J Salgaonkar [this message]
2014-02-21  0:11 ` [PATCH] powerpc/powernv: Read opal error log and export it through sysfs interface 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20131216095746.14595.64602.stgit@mars.in.ibm.com \
    --to=mahesh@linux.vnet.ibm.com \
    --cc=benh@kernel.crashing.org \
    --cc=hegdevasant@linux.vnet.ibm.com \
    --cc=linuxppc-dev@ozlabs.org \
    --cc=mamatha4@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.