pstore: X86 platform interface using ACPI/APEI/ERST

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>
This commit is contained in:
Tony Luck 2011-01-03 14:22:11 -08:00
parent ca01d6dd2d
commit 0bb77c465f
2 changed files with 137 additions and 0 deletions

View File

@ -1,5 +1,6 @@
config ACPI_APEI config ACPI_APEI
bool "ACPI Platform Error Interface (APEI)" bool "ACPI Platform Error Interface (APEI)"
select PSTORE
depends on X86 depends on X86
help help
APEI allows to report errors (for example from the chipset) APEI allows to report errors (for example from the chipset)

View File

@ -34,6 +34,7 @@
#include <linux/cper.h> #include <linux/cper.h>
#include <linux/nmi.h> #include <linux/nmi.h>
#include <linux/hardirq.h> #include <linux/hardirq.h>
#include <linux/pstore.h>
#include <acpi/apei.h> #include <acpi/apei.h>
#include "apei-internal.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) static int __init erst_init(void)
{ {
int rc = 0; int rc = 0;
@ -788,6 +911,7 @@ static int __init erst_init(void)
struct apei_exec_context ctx; struct apei_exec_context ctx;
struct apei_resources erst_resources; struct apei_resources erst_resources;
struct resource *r; struct resource *r;
char *buf;
if (acpi_disabled) if (acpi_disabled)
goto err; goto err;
@ -854,6 +978,18 @@ static int __init erst_init(void)
if (!erst_erange.vaddr) if (!erst_erange.vaddr)
goto err_release_erange; 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 pr_info(ERST_PFX
"Error Record Serialization Table (ERST) support is initialized.\n"); "Error Record Serialization Table (ERST) support is initialized.\n");