diff options
author | Tony Luck <tony.luck@intel.com> | 2011-01-03 17:22:11 -0500 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2011-01-03 17:22:11 -0500 |
commit | 0bb77c465f02e8281e24b9f02e7dc8a7e2b81ee2 (patch) | |
tree | 566ffb07506250dc91543fd99955c0028b83ac79 /drivers/acpi | |
parent | ca01d6dd2d7a2652000307520777538740efc286 (diff) |
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>
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/apei/Kconfig | 1 | ||||
-rw-r--r-- | drivers/acpi/apei/erst.c | 136 |
2 files changed, 137 insertions, 0 deletions
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index fca34ccfd294..e91680c7e047 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig | |||
@@ -1,5 +1,6 @@ | |||
1 | config ACPI_APEI | 1 | config ACPI_APEI |
2 | bool "ACPI Platform Error Interface (APEI)" | 2 | bool "ACPI Platform Error Interface (APEI)" |
3 | select PSTORE | ||
3 | depends on X86 | 4 | depends on X86 |
4 | help | 5 | help |
5 | APEI allows to report errors (for example from the chipset) | 6 | APEI allows to report errors (for example from the chipset) |
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 5850d320404c..22d70dbe75d1 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/cper.h> | 34 | #include <linux/cper.h> |
35 | #include <linux/nmi.h> | 35 | #include <linux/nmi.h> |
36 | #include <linux/hardirq.h> | 36 | #include <linux/hardirq.h> |
37 | #include <linux/pstore.h> | ||
37 | #include <acpi/apei.h> | 38 | #include <acpi/apei.h> |
38 | 39 | ||
39 | #include "apei-internal.h" | 40 | #include "apei-internal.h" |
@@ -781,6 +782,128 @@ static int erst_check_table(struct acpi_table_erst *erst_tab) | |||
781 | return 0; | 782 | return 0; |
782 | } | 783 | } |
783 | 784 | ||
785 | static size_t erst_reader(u64 *id, enum pstore_type_id *type, | ||
786 | struct timespec *time); | ||
787 | static u64 erst_writer(enum pstore_type_id type, size_t size); | ||
788 | |||
789 | static struct pstore_info erst_info = { | ||
790 | .owner = THIS_MODULE, | ||
791 | .name = "erst", | ||
792 | .read = erst_reader, | ||
793 | .write = erst_writer, | ||
794 | .erase = erst_clear | ||
795 | }; | ||
796 | |||
797 | #define CPER_CREATOR_PSTORE \ | ||
798 | UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \ | ||
799 | 0x64, 0x90, 0xb8, 0x9d) | ||
800 | #define CPER_SECTION_TYPE_DMESG \ | ||
801 | UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \ | ||
802 | 0x94, 0x19, 0xeb, 0x12) | ||
803 | #define CPER_SECTION_TYPE_MCE \ | ||
804 | UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \ | ||
805 | 0x04, 0x4a, 0x38, 0xfc) | ||
806 | |||
807 | struct cper_pstore_record { | ||
808 | struct cper_record_header hdr; | ||
809 | struct cper_section_descriptor sec_hdr; | ||
810 | char data[]; | ||
811 | } __packed; | ||
812 | |||
813 | static size_t erst_reader(u64 *id, enum pstore_type_id *type, | ||
814 | struct timespec *time) | ||
815 | { | ||
816 | int rc; | ||
817 | ssize_t len; | ||
818 | unsigned long flags; | ||
819 | u64 record_id; | ||
820 | struct cper_pstore_record *rcd = (struct cper_pstore_record *) | ||
821 | (erst_info.buf - sizeof(*rcd)); | ||
822 | |||
823 | if (erst_disable) | ||
824 | return -ENODEV; | ||
825 | |||
826 | raw_spin_lock_irqsave(&erst_lock, flags); | ||
827 | skip: | ||
828 | rc = __erst_get_next_record_id(&record_id); | ||
829 | if (rc) { | ||
830 | raw_spin_unlock_irqrestore(&erst_lock, flags); | ||
831 | return rc; | ||
832 | } | ||
833 | /* no more record */ | ||
834 | if (record_id == APEI_ERST_INVALID_RECORD_ID) { | ||
835 | raw_spin_unlock_irqrestore(&erst_lock, flags); | ||
836 | return 0; | ||
837 | } | ||
838 | |||
839 | len = __erst_read(record_id, &rcd->hdr, sizeof(*rcd) + | ||
840 | erst_erange.size); | ||
841 | if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0) | ||
842 | goto skip; | ||
843 | raw_spin_unlock_irqrestore(&erst_lock, flags); | ||
844 | |||
845 | *id = record_id; | ||
846 | if (uuid_le_cmp(rcd->sec_hdr.section_type, | ||
847 | CPER_SECTION_TYPE_DMESG) == 0) | ||
848 | *type = PSTORE_TYPE_DMESG; | ||
849 | else if (uuid_le_cmp(rcd->sec_hdr.section_type, | ||
850 | CPER_SECTION_TYPE_MCE) == 0) | ||
851 | *type = PSTORE_TYPE_MCE; | ||
852 | else | ||
853 | *type = PSTORE_TYPE_UNKNOWN; | ||
854 | |||
855 | if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP) | ||
856 | time->tv_sec = rcd->hdr.timestamp; | ||
857 | else | ||
858 | time->tv_sec = 0; | ||
859 | time->tv_nsec = 0; | ||
860 | |||
861 | return len - sizeof(*rcd); | ||
862 | } | ||
863 | |||
864 | static u64 erst_writer(enum pstore_type_id type, size_t size) | ||
865 | { | ||
866 | struct cper_pstore_record *rcd = (struct cper_pstore_record *) | ||
867 | (erst_info.buf - sizeof(*rcd)); | ||
868 | |||
869 | memset(rcd, 0, sizeof(*rcd)); | ||
870 | memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE); | ||
871 | rcd->hdr.revision = CPER_RECORD_REV; | ||
872 | rcd->hdr.signature_end = CPER_SIG_END; | ||
873 | rcd->hdr.section_count = 1; | ||
874 | rcd->hdr.error_severity = CPER_SEV_FATAL; | ||
875 | /* timestamp valid. platform_id, partition_id are invalid */ | ||
876 | rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; | ||
877 | rcd->hdr.timestamp = get_seconds(); | ||
878 | rcd->hdr.record_length = sizeof(*rcd) + size; | ||
879 | rcd->hdr.creator_id = CPER_CREATOR_PSTORE; | ||
880 | rcd->hdr.notification_type = CPER_NOTIFY_MCE; | ||
881 | rcd->hdr.record_id = cper_next_record_id(); | ||
882 | rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR; | ||
883 | |||
884 | rcd->sec_hdr.section_offset = sizeof(*rcd); | ||
885 | rcd->sec_hdr.section_length = size; | ||
886 | rcd->sec_hdr.revision = CPER_SEC_REV; | ||
887 | /* fru_id and fru_text is invalid */ | ||
888 | rcd->sec_hdr.validation_bits = 0; | ||
889 | rcd->sec_hdr.flags = CPER_SEC_PRIMARY; | ||
890 | switch (type) { | ||
891 | case PSTORE_TYPE_DMESG: | ||
892 | rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; | ||
893 | break; | ||
894 | case PSTORE_TYPE_MCE: | ||
895 | rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE; | ||
896 | break; | ||
897 | default: | ||
898 | return -EINVAL; | ||
899 | } | ||
900 | rcd->sec_hdr.section_severity = CPER_SEV_FATAL; | ||
901 | |||
902 | erst_write(&rcd->hdr); | ||
903 | |||
904 | return rcd->hdr.record_id; | ||
905 | } | ||
906 | |||
784 | static int __init erst_init(void) | 907 | static int __init erst_init(void) |
785 | { | 908 | { |
786 | int rc = 0; | 909 | int rc = 0; |
@@ -788,6 +911,7 @@ static int __init erst_init(void) | |||
788 | struct apei_exec_context ctx; | 911 | struct apei_exec_context ctx; |
789 | struct apei_resources erst_resources; | 912 | struct apei_resources erst_resources; |
790 | struct resource *r; | 913 | struct resource *r; |
914 | char *buf; | ||
791 | 915 | ||
792 | if (acpi_disabled) | 916 | if (acpi_disabled) |
793 | goto err; | 917 | goto err; |
@@ -854,6 +978,18 @@ static int __init erst_init(void) | |||
854 | if (!erst_erange.vaddr) | 978 | if (!erst_erange.vaddr) |
855 | goto err_release_erange; | 979 | goto err_release_erange; |
856 | 980 | ||
981 | buf = kmalloc(erst_erange.size, GFP_KERNEL); | ||
982 | mutex_init(&erst_info.buf_mutex); | ||
983 | if (buf) { | ||
984 | erst_info.buf = buf + sizeof(struct cper_pstore_record); | ||
985 | erst_info.bufsize = erst_erange.size - | ||
986 | sizeof(struct cper_pstore_record); | ||
987 | if (pstore_register(&erst_info)) { | ||
988 | pr_info(ERST_PFX "Could not register with persistent store\n"); | ||
989 | kfree(buf); | ||
990 | } | ||
991 | } | ||
992 | |||
857 | pr_info(ERST_PFX | 993 | pr_info(ERST_PFX |
858 | "Error Record Serialization Table (ERST) support is initialized.\n"); | 994 | "Error Record Serialization Table (ERST) support is initialized.\n"); |
859 | 995 | ||