pstore: X86 platform interface using ACPI/APEI/ERST
authorTony Luck <tony.luck@intel.com>
Mon, 3 Jan 2011 22:22:11 +0000 (14:22 -0800)
committerTony Luck <tony.luck@intel.com>
Mon, 3 Jan 2011 22:22:11 +0000 (14:22 -0800)
The 'error record serialization table' in ACPI provides a suitable
amount of persistent storage for use by the pstore filesystem.

Signed-off-by: Tony Luck <tony.luck@intel.com>
drivers/acpi/apei/Kconfig
drivers/acpi/apei/erst.c

index fca34cc..e91680c 100644 (file)
@@ -1,5 +1,6 @@
 config ACPI_APEI
        bool "ACPI Platform Error Interface (APEI)"
 config ACPI_APEI
        bool "ACPI Platform Error Interface (APEI)"
+       select PSTORE
        depends on X86
        help
          APEI allows to report errors (for example from the chipset)
        depends on X86
        help
          APEI allows to report errors (for example from the chipset)
index 5850d32..22d70db 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/cper.h>
 #include <linux/nmi.h>
 #include <linux/hardirq.h>
 #include <linux/cper.h>
 #include <linux/nmi.h>
 #include <linux/hardirq.h>
+#include <linux/pstore.h>
 #include <acpi/apei.h>
 
 #include "apei-internal.h"
 #include <acpi/apei.h>
 
 #include "apei-internal.h"
@@ -781,6 +782,128 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
        return 0;
 }
 
        return 0;
 }
 
+static size_t erst_reader(u64 *id, enum pstore_type_id *type,
+                      struct timespec *time);
+static u64 erst_writer(enum pstore_type_id type, size_t size);
+
+static struct pstore_info erst_info = {
+       .owner          = THIS_MODULE,
+       .name           = "erst",
+       .read           = erst_reader,
+       .write          = erst_writer,
+       .erase          = erst_clear
+};
+
+#define CPER_CREATOR_PSTORE                                            \
+       UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c,     \
+               0x64, 0x90, 0xb8, 0x9d)
+#define CPER_SECTION_TYPE_DMESG                                                \
+       UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54,     \
+               0x94, 0x19, 0xeb, 0x12)
+#define CPER_SECTION_TYPE_MCE                                          \
+       UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96,     \
+               0x04, 0x4a, 0x38, 0xfc)
+
+struct cper_pstore_record {
+       struct cper_record_header hdr;
+       struct cper_section_descriptor sec_hdr;
+       char data[];
+} __packed;
+
+static size_t erst_reader(u64 *id, enum pstore_type_id *type,
+                      struct timespec *time)
+{
+       int rc;
+       ssize_t len;
+       unsigned long flags;
+       u64 record_id;
+       struct cper_pstore_record *rcd = (struct cper_pstore_record *)
+                                       (erst_info.buf - sizeof(*rcd));
+
+       if (erst_disable)
+               return -ENODEV;
+
+       raw_spin_lock_irqsave(&erst_lock, flags);
+skip:
+       rc = __erst_get_next_record_id(&record_id);
+       if (rc) {
+               raw_spin_unlock_irqrestore(&erst_lock, flags);
+               return rc;
+       }
+       /* no more record */
+       if (record_id == APEI_ERST_INVALID_RECORD_ID) {
+               raw_spin_unlock_irqrestore(&erst_lock, flags);
+               return 0;
+       }
+
+       len = __erst_read(record_id, &rcd->hdr, sizeof(*rcd) +
+                         erst_erange.size);
+       if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0)
+               goto skip;
+       raw_spin_unlock_irqrestore(&erst_lock, flags);
+
+       *id = record_id;
+       if (uuid_le_cmp(rcd->sec_hdr.section_type,
+                       CPER_SECTION_TYPE_DMESG) == 0)
+               *type = PSTORE_TYPE_DMESG;
+       else if (uuid_le_cmp(rcd->sec_hdr.section_type,
+                            CPER_SECTION_TYPE_MCE) == 0)
+               *type = PSTORE_TYPE_MCE;
+       else
+               *type = PSTORE_TYPE_UNKNOWN;
+
+       if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP)
+               time->tv_sec = rcd->hdr.timestamp;
+       else
+               time->tv_sec = 0;
+       time->tv_nsec = 0;
+
+       return len - sizeof(*rcd);
+}
+
+static u64 erst_writer(enum pstore_type_id type, size_t size)
+{
+       struct cper_pstore_record *rcd = (struct cper_pstore_record *)
+                                       (erst_info.buf - sizeof(*rcd));
+
+       memset(rcd, 0, sizeof(*rcd));
+       memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
+       rcd->hdr.revision = CPER_RECORD_REV;
+       rcd->hdr.signature_end = CPER_SIG_END;
+       rcd->hdr.section_count = 1;
+       rcd->hdr.error_severity = CPER_SEV_FATAL;
+       /* timestamp valid. platform_id, partition_id are invalid */
+       rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP;
+       rcd->hdr.timestamp = get_seconds();
+       rcd->hdr.record_length = sizeof(*rcd) + size;
+       rcd->hdr.creator_id = CPER_CREATOR_PSTORE;
+       rcd->hdr.notification_type = CPER_NOTIFY_MCE;
+       rcd->hdr.record_id = cper_next_record_id();
+       rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR;
+
+       rcd->sec_hdr.section_offset = sizeof(*rcd);
+       rcd->sec_hdr.section_length = size;
+       rcd->sec_hdr.revision = CPER_SEC_REV;
+       /* fru_id and fru_text is invalid */
+       rcd->sec_hdr.validation_bits = 0;
+       rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
+       switch (type) {
+       case PSTORE_TYPE_DMESG:
+               rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
+               break;
+       case PSTORE_TYPE_MCE:
+               rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE;
+               break;
+       default:
+               return -EINVAL;
+       }
+       rcd->sec_hdr.section_severity = CPER_SEV_FATAL;
+
+       erst_write(&rcd->hdr);
+
+       return rcd->hdr.record_id;
+}
+
 static int __init erst_init(void)
 {
        int rc = 0;
 static int __init erst_init(void)
 {
        int rc = 0;
@@ -788,6 +911,7 @@ static int __init erst_init(void)
        struct apei_exec_context ctx;
        struct apei_resources erst_resources;
        struct resource *r;
        struct apei_exec_context ctx;
        struct apei_resources erst_resources;
        struct resource *r;
+       char *buf;
 
        if (acpi_disabled)
                goto err;
 
        if (acpi_disabled)
                goto err;
@@ -854,6 +978,18 @@ static int __init erst_init(void)
        if (!erst_erange.vaddr)
                goto err_release_erange;
 
        if (!erst_erange.vaddr)
                goto err_release_erange;
 
+       buf = kmalloc(erst_erange.size, GFP_KERNEL);
+       mutex_init(&erst_info.buf_mutex);
+       if (buf) {
+               erst_info.buf = buf + sizeof(struct cper_pstore_record);
+               erst_info.bufsize = erst_erange.size -
+                                   sizeof(struct cper_pstore_record);
+               if (pstore_register(&erst_info)) {
+                       pr_info(ERST_PFX "Could not register with persistent store\n");
+                       kfree(buf);
+               }
+       }
+
        pr_info(ERST_PFX
        "Error Record Serialization Table (ERST) support is initialized.\n");
 
        pr_info(ERST_PFX
        "Error Record Serialization Table (ERST) support is initialized.\n");