diff options
| -rw-r--r-- | arch/x86/Kconfig | 1 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/mcheck/Makefile | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce-genpool.c | 99 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce-internal.h | 12 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce.c | 8 |
5 files changed, 120 insertions, 2 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index b3a1a5d77d92..06dbb5da90c6 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
| @@ -955,6 +955,7 @@ config X86_REROUTE_FOR_BROKEN_BOOT_IRQS | |||
| 955 | 955 | ||
| 956 | config X86_MCE | 956 | config X86_MCE |
| 957 | bool "Machine Check / overheating reporting" | 957 | bool "Machine Check / overheating reporting" |
| 958 | select GENERIC_ALLOCATOR | ||
| 958 | default y | 959 | default y |
| 959 | ---help--- | 960 | ---help--- |
| 960 | Machine Check support allows the processor to notify the | 961 | Machine Check support allows the processor to notify the |
diff --git a/arch/x86/kernel/cpu/mcheck/Makefile b/arch/x86/kernel/cpu/mcheck/Makefile index bb34b03af252..a3311c886194 100644 --- a/arch/x86/kernel/cpu/mcheck/Makefile +++ b/arch/x86/kernel/cpu/mcheck/Makefile | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | obj-y = mce.o mce-severity.o | 1 | obj-y = mce.o mce-severity.o mce-genpool.o |
| 2 | 2 | ||
| 3 | obj-$(CONFIG_X86_ANCIENT_MCE) += winchip.o p5.o | 3 | obj-$(CONFIG_X86_ANCIENT_MCE) += winchip.o p5.o |
| 4 | obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o | 4 | obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o |
diff --git a/arch/x86/kernel/cpu/mcheck/mce-genpool.c b/arch/x86/kernel/cpu/mcheck/mce-genpool.c new file mode 100644 index 000000000000..0a850100c594 --- /dev/null +++ b/arch/x86/kernel/cpu/mcheck/mce-genpool.c | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | /* | ||
| 2 | * MCE event pool management in MCE context | ||
| 3 | * | ||
| 4 | * Copyright (C) 2015 Intel Corp. | ||
| 5 | * Author: Chen, Gong <gong.chen@linux.intel.com> | ||
| 6 | * | ||
| 7 | * This file is licensed under GPLv2. | ||
| 8 | */ | ||
| 9 | #include <linux/smp.h> | ||
| 10 | #include <linux/mm.h> | ||
| 11 | #include <linux/genalloc.h> | ||
| 12 | #include <linux/llist.h> | ||
| 13 | #include "mce-internal.h" | ||
| 14 | |||
| 15 | /* | ||
| 16 | * printk() is not safe in MCE context. This is a lock-less memory allocator | ||
| 17 | * used to save error information organized in a lock-less list. | ||
| 18 | * | ||
| 19 | * This memory pool is only to be used to save MCE records in MCE context. | ||
| 20 | * MCE events are rare, so a fixed size memory pool should be enough. Use | ||
| 21 | * 2 pages to save MCE events for now (~80 MCE records at most). | ||
| 22 | */ | ||
| 23 | #define MCE_POOLSZ (2 * PAGE_SIZE) | ||
| 24 | |||
| 25 | static struct gen_pool *mce_evt_pool; | ||
| 26 | static LLIST_HEAD(mce_event_llist); | ||
| 27 | static char gen_pool_buf[MCE_POOLSZ]; | ||
| 28 | |||
| 29 | void mce_gen_pool_process(void) | ||
| 30 | { | ||
| 31 | struct llist_node *head; | ||
| 32 | struct mce_evt_llist *node; | ||
| 33 | struct mce *mce; | ||
| 34 | |||
| 35 | head = llist_del_all(&mce_event_llist); | ||
| 36 | if (!head) | ||
| 37 | return; | ||
| 38 | |||
| 39 | head = llist_reverse_order(head); | ||
| 40 | llist_for_each_entry(node, head, llnode) { | ||
| 41 | mce = &node->mce; | ||
| 42 | atomic_notifier_call_chain(&x86_mce_decoder_chain, 0, mce); | ||
| 43 | gen_pool_free(mce_evt_pool, (unsigned long)node, sizeof(*node)); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | bool mce_gen_pool_empty(void) | ||
| 48 | { | ||
| 49 | return llist_empty(&mce_event_llist); | ||
| 50 | } | ||
| 51 | |||
| 52 | int mce_gen_pool_add(struct mce *mce) | ||
| 53 | { | ||
| 54 | struct mce_evt_llist *node; | ||
| 55 | |||
| 56 | if (!mce_evt_pool) | ||
| 57 | return -EINVAL; | ||
| 58 | |||
| 59 | node = (void *)gen_pool_alloc(mce_evt_pool, sizeof(*node)); | ||
| 60 | if (!node) { | ||
| 61 | pr_warn_ratelimited("MCE records pool full!\n"); | ||
| 62 | return -ENOMEM; | ||
| 63 | } | ||
| 64 | |||
| 65 | memcpy(&node->mce, mce, sizeof(*mce)); | ||
| 66 | llist_add(&node->llnode, &mce_event_llist); | ||
| 67 | |||
| 68 | return 0; | ||
| 69 | } | ||
| 70 | |||
| 71 | static int mce_gen_pool_create(void) | ||
| 72 | { | ||
| 73 | struct gen_pool *tmpp; | ||
| 74 | int ret = -ENOMEM; | ||
| 75 | |||
| 76 | tmpp = gen_pool_create(ilog2(sizeof(struct mce_evt_llist)), -1); | ||
| 77 | if (!tmpp) | ||
| 78 | goto out; | ||
| 79 | |||
| 80 | ret = gen_pool_add(tmpp, (unsigned long)gen_pool_buf, MCE_POOLSZ, -1); | ||
| 81 | if (ret) { | ||
| 82 | gen_pool_destroy(tmpp); | ||
| 83 | goto out; | ||
| 84 | } | ||
| 85 | |||
| 86 | mce_evt_pool = tmpp; | ||
| 87 | |||
| 88 | out: | ||
| 89 | return ret; | ||
| 90 | } | ||
| 91 | |||
| 92 | int mce_gen_pool_init(void) | ||
| 93 | { | ||
| 94 | /* Just init mce_gen_pool once. */ | ||
| 95 | if (mce_evt_pool) | ||
| 96 | return 0; | ||
| 97 | |||
| 98 | return mce_gen_pool_create(); | ||
| 99 | } | ||
diff --git a/arch/x86/kernel/cpu/mcheck/mce-internal.h b/arch/x86/kernel/cpu/mcheck/mce-internal.h index fe32074b865b..ea8b62264c14 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-internal.h +++ b/arch/x86/kernel/cpu/mcheck/mce-internal.h | |||
| @@ -13,6 +13,8 @@ enum severity_level { | |||
| 13 | MCE_PANIC_SEVERITY, | 13 | MCE_PANIC_SEVERITY, |
| 14 | }; | 14 | }; |
| 15 | 15 | ||
| 16 | extern struct atomic_notifier_head x86_mce_decoder_chain; | ||
| 17 | |||
| 16 | #define ATTR_LEN 16 | 18 | #define ATTR_LEN 16 |
| 17 | #define INITIAL_CHECK_INTERVAL 5 * 60 /* 5 minutes */ | 19 | #define INITIAL_CHECK_INTERVAL 5 * 60 /* 5 minutes */ |
| 18 | 20 | ||
| @@ -24,6 +26,16 @@ struct mce_bank { | |||
| 24 | char attrname[ATTR_LEN]; /* attribute name */ | 26 | char attrname[ATTR_LEN]; /* attribute name */ |
| 25 | }; | 27 | }; |
| 26 | 28 | ||
| 29 | struct mce_evt_llist { | ||
| 30 | struct llist_node llnode; | ||
| 31 | struct mce mce; | ||
| 32 | }; | ||
| 33 | |||
| 34 | void mce_gen_pool_process(void); | ||
| 35 | bool mce_gen_pool_empty(void); | ||
| 36 | int mce_gen_pool_add(struct mce *mce); | ||
| 37 | int mce_gen_pool_init(void); | ||
| 38 | |||
| 27 | extern int (*mce_severity)(struct mce *a, int tolerant, char **msg, bool is_excp); | 39 | extern int (*mce_severity)(struct mce *a, int tolerant, char **msg, bool is_excp); |
| 28 | struct dentry *mce_get_debugfs_dir(void); | 40 | struct dentry *mce_get_debugfs_dir(void); |
| 29 | 41 | ||
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index df919ff103c3..a41c014e5cde 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c | |||
| @@ -118,7 +118,7 @@ static void (*quirk_no_way_out)(int bank, struct mce *m, struct pt_regs *regs); | |||
| 118 | * CPU/chipset specific EDAC code can register a notifier call here to print | 118 | * CPU/chipset specific EDAC code can register a notifier call here to print |
| 119 | * MCE errors in a human-readable form. | 119 | * MCE errors in a human-readable form. |
| 120 | */ | 120 | */ |
| 121 | static ATOMIC_NOTIFIER_HEAD(x86_mce_decoder_chain); | 121 | ATOMIC_NOTIFIER_HEAD(x86_mce_decoder_chain); |
| 122 | 122 | ||
| 123 | /* Do initial initialization of a struct mce */ | 123 | /* Do initial initialization of a struct mce */ |
| 124 | void mce_setup(struct mce *m) | 124 | void mce_setup(struct mce *m) |
| @@ -1731,6 +1731,12 @@ void mcheck_cpu_init(struct cpuinfo_x86 *c) | |||
| 1731 | return; | 1731 | return; |
| 1732 | } | 1732 | } |
| 1733 | 1733 | ||
| 1734 | if (mce_gen_pool_init()) { | ||
| 1735 | mca_cfg.disabled = true; | ||
| 1736 | pr_emerg("Couldn't allocate MCE records pool!\n"); | ||
| 1737 | return; | ||
| 1738 | } | ||
| 1739 | |||
| 1734 | machine_check_vector = do_machine_check; | 1740 | machine_check_vector = do_machine_check; |
| 1735 | 1741 | ||
| 1736 | __mcheck_cpu_init_generic(); | 1742 | __mcheck_cpu_init_generic(); |
