Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1416421 > unrolled thread
| Started by | Borislav Petkov <bp@alien8.de> |
|---|---|
| First post | 2016-06-07 19:00 +0200 |
| Last post | 2016-06-07 23:10 +0200 |
| Articles | 5 — 2 participants |
Back to article view | Back to linux.kernel
[R-F-C PATCH 0/4] RFC: x86/mce: Deprecate mcelog and other funsies Borislav Petkov <bp@alien8.de> - 2016-06-07 19:00 +0200
[RFC PATCH 4/4] x86/mce-inject: Use debugfs_remove_recursive() Borislav Petkov <bp@alien8.de> - 2016-06-07 19:00 +0200
[RFC PATCH 1/4] RAS: Add a Corrected Errors Collector Borislav Petkov <bp@alien8.de> - 2016-06-07 19:00 +0200
Re: [RFC PATCH 1/4] RAS: Add a Corrected Errors Collector "Luck, Tony" <tony.luck@intel.com> - 2016-06-07 20:20 +0200
Re: [RFC PATCH 1/4] RAS: Add a Corrected Errors Collector Borislav Petkov <bp@alien8.de> - 2016-06-07 23:10 +0200
| From | Borislav Petkov <bp@alien8.de> |
|---|---|
| Date | 2016-06-07 19:00 +0200 |
| Subject | [R-F-C PATCH 0/4] RFC: x86/mce: Deprecate mcelog and other funsies |
| Message-ID | <rHsw9-5a4-3@gated-at.bofh.it> |
From: Borislav Petkov <bp@suse.de>
So this set cannot have a bigger "RFC" tag:
It attempts to revive the correctable errors collector. In this version,
the thing gets counters in debugfs:
(debugfs)/ras/cec/
|-- array
`-- pfn
which give some stats on the CEC like how many CEs it caught and the
number of pages it soft-offlined due to excessive amount of CEs.
$ cat ras/cec/array
{ n: 2
000: [0000000000000012|001]
001: [0000003735928559|e01]
}
Stats:
CEs: 514
offlined pages: 0
Flags: 0x0
Then, we stuck all the /dev/mcelog functionality behind a
CONFIG_X86_MCELOG which people can disable if they don't need mcelog and
are going to use the rasdaemon. This is probably going to happen sooner
or later with the distros anyway.
And then, as a result of that move, mce-inject is being made independent
from mcelog and gets the error injection debugfs nodes of mce_amd_inj
which you can echo into and don't need special tools:
$ tree --charset=ascii /sys/kernel/debug/mce-inject/
/sys/kernel/debug/mce-inject/
|-- addr
|-- bank
|-- cpu
|-- flags
|-- misc
|-- README
`-- status
0 directories, 7 files
Comments are more than welcome, Tony and will be polishing on those but
thought it would be a nice point in time to send them out to get other
people's opinions.
Thanks.
Borislav Petkov (3):
RAS: Add a Corrected Errors Collector
x86/mce: Merge mce_amd_inj into mce-inject
x86/mce-inject: Use debugfs_remove_recursive()
Tony Luck (1):
x86/mce: Deprecate /dev/mcelog
arch/x86/Kconfig | 8 +
arch/x86/kernel/cpu/mcheck/Makefile | 2 +
arch/x86/kernel/cpu/mcheck/dev-mcelog.c | 397 +++++++++++++++++++++++
arch/x86/kernel/cpu/mcheck/mce-inject.c | 519 +++++++++++++++++++++++++++++-
arch/x86/kernel/cpu/mcheck/mce-internal.h | 8 +
arch/x86/kernel/cpu/mcheck/mce.c | 513 +++++------------------------
arch/x86/ras/Kconfig | 11 +
arch/x86/ras/Makefile | 2 -
arch/x86/ras/mce_amd_inj.c | 484 ----------------------------
drivers/ras/Makefile | 3 +-
drivers/ras/cec.c | 425 ++++++++++++++++++++++++
drivers/ras/debugfs.c | 2 +-
drivers/ras/debugfs.h | 8 +
include/linux/ras.h | 12 +-
14 files changed, 1467 insertions(+), 927 deletions(-)
create mode 100644 arch/x86/kernel/cpu/mcheck/dev-mcelog.c
delete mode 100644 arch/x86/ras/mce_amd_inj.c
create mode 100644 drivers/ras/cec.c
create mode 100644 drivers/ras/debugfs.h
--
2.7.3
[toc] | [next] | [standalone]
| From | Borislav Petkov <bp@alien8.de> |
|---|---|
| Date | 2016-06-07 19:00 +0200 |
| Subject | [RFC PATCH 4/4] x86/mce-inject: Use debugfs_remove_recursive() |
| Message-ID | <rHswa-5a4-25@gated-at.bofh.it> |
| In reply to | #1416421 |
From: Borislav Petkov <bp@suse.de>
... and simplify a bit.
No functionality change.
Signed-off-by: Borislav Petkov <bp@suse.de>
---
arch/x86/kernel/cpu/mcheck/mce-inject.c | 30 ++++++++++++------------------
1 file changed, 12 insertions(+), 18 deletions(-)
diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c
index f1d8d3f9ec1d..675d2da3d31f 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-inject.c
+++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c
@@ -676,8 +676,8 @@ static struct dfs_node {
static int __init debugfs_init(void)
{
- int i;
u64 cap;
+ int i;
rdmsrl(MSR_IA32_MCG_CAP, cap);
n_banks = cap & MCG_BANKCNT_MASK;
@@ -700,28 +700,12 @@ static int __init debugfs_init(void)
return 0;
err_dfs_add:
- while (--i >= 0)
- debugfs_remove(dfs_fls[i].d);
-
- debugfs_remove(dfs_inj);
+ debugfs_remove_recursive(dfs_inj);
dfs_inj = NULL;
return -ENOMEM;
}
-static void __exit debugfs_exit(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
- debugfs_remove(dfs_fls[i].d);
-
- memset(&dfs_fls, 0, sizeof(dfs_fls));
-
- debugfs_remove(dfs_inj);
- dfs_inj = NULL;
-}
-
static int inject_init(void)
{
int err;
@@ -752,6 +736,16 @@ module_init(inject_init);
* guarantee all openers of mce_chrdev will get a reference to us.
*/
#ifndef CONFIG_X86_MCELOG
+static void __exit debugfs_exit(void)
+{
+ debugfs_remove_recursive(dfs_inj);
+ dfs_inj = NULL;
+
+ unregister_nmi_handler(NMI_LOCAL, "mce_notify");
+
+ free_cpumask_var(mce_inject_cpumask);
+}
+
module_exit(debugfs_exit);
#endif
--
2.7.3
[toc] | [prev] | [next] | [standalone]
| From | Borislav Petkov <bp@alien8.de> |
|---|---|
| Date | 2016-06-07 19:00 +0200 |
| Subject | [RFC PATCH 1/4] RAS: Add a Corrected Errors Collector |
| Message-ID | <rHswa-5a4-27@gated-at.bofh.it> |
| In reply to | #1416421 |
From: Borislav Petkov <bp@suse.de>
A simple data structure for collecting correctable errors along with
accessors. More detailed description in the code itself.
Signed-off-by: Borislav Petkov <bp@suse.de>
---
arch/x86/kernel/cpu/mcheck/mce.c | 120 +++++------
arch/x86/ras/Kconfig | 11 +
drivers/ras/Makefile | 3 +-
drivers/ras/cec.c | 425 +++++++++++++++++++++++++++++++++++++++
drivers/ras/debugfs.c | 2 +-
drivers/ras/debugfs.h | 8 +
include/linux/ras.h | 12 +-
7 files changed, 521 insertions(+), 60 deletions(-)
create mode 100644 drivers/ras/cec.c
create mode 100644 drivers/ras/debugfs.h
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 92e5e37d97bf..a6cc08a10cc3 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -35,6 +35,7 @@
#include <linux/poll.h>
#include <linux/nmi.h>
#include <linux/cpu.h>
+#include <linux/ras.h>
#include <linux/smp.h>
#include <linux/fs.h>
#include <linux/mm.h>
@@ -151,14 +152,71 @@ static struct mce_log mcelog = {
.recordlen = sizeof(struct mce),
};
-void mce_log(struct mce *mce)
+static bool memory_error(struct mce *m)
+{
+ struct cpuinfo_x86 *c = &boot_cpu_data;
+
+ if (c->x86_vendor == X86_VENDOR_AMD) {
+ /* ErrCodeExt[20:16] */
+ u8 xec = (m->status >> 16) & 0x1f;
+
+ return (xec == 0x0 || xec == 0x8);
+ } else if (c->x86_vendor == X86_VENDOR_INTEL) {
+ /*
+ * Intel SDM Volume 3B - 15.9.2 Compound Error Codes
+ *
+ * Bit 7 of the MCACOD field of IA32_MCi_STATUS is used for
+ * indicating a memory error. Bit 8 is used for indicating a
+ * cache hierarchy error. The combination of bit 2 and bit 3
+ * is used for indicating a `generic' cache hierarchy error
+ * But we can't just blindly check the above bits, because if
+ * bit 11 is set, then it is a bus/interconnect error - and
+ * either way the above bits just gives more detail on what
+ * bus/interconnect error happened. Note that bit 12 can be
+ * ignored, as it's the "filter" bit.
+ */
+ return (m->status & 0xef80) == BIT(7) ||
+ (m->status & 0xef00) == BIT(8) ||
+ (m->status & 0xeffc) == 0xc;
+ }
+
+ return false;
+}
+
+/*
+ * Check if the address reported by the CPU is in a format we can parse.
+ * It would be possible to add code for most other cases, but all would
+ * be somewhat complicated (e.g. segment offset would require an instruction
+ * parser). So only support physical addresses up to page granuality for now.
+ */
+static int mce_usable_address(struct mce *m)
+{
+ if (!(m->status & MCI_STATUS_MISCV) || !(m->status & MCI_STATUS_ADDRV))
+ return 0;
+
+ /* Checks after this one are Intel-specific: */
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
+ return 1;
+
+ if (MCI_MISC_ADDR_LSB(m->misc) > PAGE_SHIFT)
+ return 0;
+ if (MCI_MISC_ADDR_MODE(m->misc) != MCI_MISC_ADDR_PHYS)
+ return 0;
+ return 1;
+}
+
+void mce_log(struct mce *m)
{
unsigned next, entry;
+ if (!in_atomic() && memory_error(m) && mce_usable_address(m))
+ if (!ce_add_elem(m->addr >> PAGE_SHIFT))
+ return;
+
/* Emit the trace record: */
- trace_mce_record(mce);
+ trace_mce_record(m);
- if (!mce_gen_pool_add(mce))
+ if (!mce_gen_pool_add(m))
irq_work_queue(&mce_irq_work);
wmb();
@@ -188,7 +246,7 @@ void mce_log(struct mce *mce)
if (cmpxchg(&mcelog.next, entry, next) == entry)
break;
}
- memcpy(mcelog.entry + entry, mce, sizeof(struct mce));
+ memcpy(mcelog.entry + entry, m, sizeof(struct mce));
wmb();
mcelog.entry[entry].finished = 1;
wmb();
@@ -518,28 +576,6 @@ static void mce_report_event(struct pt_regs *regs)
irq_work_queue(&mce_irq_work);
}
-/*
- * Check if the address reported by the CPU is in a format we can parse.
- * It would be possible to add code for most other cases, but all would
- * be somewhat complicated (e.g. segment offset would require an instruction
- * parser). So only support physical addresses up to page granuality for now.
- */
-static int mce_usable_address(struct mce *m)
-{
- if (!(m->status & MCI_STATUS_MISCV) || !(m->status & MCI_STATUS_ADDRV))
- return 0;
-
- /* Checks after this one are Intel-specific: */
- if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
- return 1;
-
- if (MCI_MISC_ADDR_LSB(m->misc) > PAGE_SHIFT)
- return 0;
- if (MCI_MISC_ADDR_MODE(m->misc) != MCI_MISC_ADDR_PHYS)
- return 0;
- return 1;
-}
-
static int srao_decode_notifier(struct notifier_block *nb, unsigned long val,
void *data)
{
@@ -582,37 +618,6 @@ static void mce_read_aux(struct mce *m, int i)
}
}
-static bool memory_error(struct mce *m)
-{
- struct cpuinfo_x86 *c = &boot_cpu_data;
-
- if (c->x86_vendor == X86_VENDOR_AMD) {
- /* ErrCodeExt[20:16] */
- u8 xec = (m->status >> 16) & 0x1f;
-
- return (xec == 0x0 || xec == 0x8);
- } else if (c->x86_vendor == X86_VENDOR_INTEL) {
- /*
- * Intel SDM Volume 3B - 15.9.2 Compound Error Codes
- *
- * Bit 7 of the MCACOD field of IA32_MCi_STATUS is used for
- * indicating a memory error. Bit 8 is used for indicating a
- * cache hierarchy error. The combination of bit 2 and bit 3
- * is used for indicating a `generic' cache hierarchy error
- * But we can't just blindly check the above bits, because if
- * bit 11 is set, then it is a bus/interconnect error - and
- * either way the above bits just gives more detail on what
- * bus/interconnect error happened. Note that bit 12 can be
- * ignored, as it's the "filter" bit.
- */
- return (m->status & 0xef80) == BIT(7) ||
- (m->status & 0xef00) == BIT(8) ||
- (m->status & 0xeffc) == 0xc;
- }
-
- return false;
-}
-
DEFINE_PER_CPU(unsigned, mce_poll_count);
/*
@@ -2679,6 +2684,7 @@ static int __init mcheck_debugfs_init(void) { return -EINVAL; }
static int __init mcheck_late_init(void)
{
mcheck_debugfs_init();
+ ce_init();
/*
* Flush out everything that has been logged during early boot, now that
diff --git a/arch/x86/ras/Kconfig b/arch/x86/ras/Kconfig
index d957d5f21a86..513e8ffc9e08 100644
--- a/arch/x86/ras/Kconfig
+++ b/arch/x86/ras/Kconfig
@@ -7,3 +7,14 @@ config MCE_AMD_INJ
aspects of the MCE handling code.
WARNING: Do not even assume this interface is staying stable!
+
+config RAS_CEC
+ bool "Correctable Errors Collector"
+ depends on X86_MCE && MEMORY_FAILURE && DEBUG_FS
+ ---help---
+ This is a small cache which collects correctable memory errors per 4K
+ page PFN and counts their repeated occurrence. Once the counter for a
+ PFN overflows, we try to soft-offline that page as we take it to mean
+ that it has reached a relatively high error count and would probably
+ be best if we don't use it anymore.
+
diff --git a/drivers/ras/Makefile b/drivers/ras/Makefile
index d7f73341ced3..7b26dd3aa5d0 100644
--- a/drivers/ras/Makefile
+++ b/drivers/ras/Makefile
@@ -1 +1,2 @@
-obj-$(CONFIG_RAS) += ras.o debugfs.o
+obj-$(CONFIG_RAS) += ras.o debugfs.o
+obj-$(CONFIG_RAS_CEC) += cec.o
diff --git a/drivers/ras/cec.c b/drivers/ras/cec.c
new file mode 100644
index 000000000000..846155124598
--- /dev/null
+++ b/drivers/ras/cec.c
@@ -0,0 +1,425 @@
+#include <linux/mm.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+
+#include <asm/bug.h>
+
+#include "debugfs.h"
+
+/*
+ * RAS Correctable Errors Collector
+ *
+ * This is a simple gadget which collects correctable errors and counts their
+ * occurrence per physical page address.
+ *
+ * We've opted for possibly the simplest data structure to collect those - an
+ * array of the size of a memory page. It stores 512 u64's with the following
+ * structure:
+ *
+ * [63 ... PFN ... 12 | 11 ... generation ... 10 | 9 ... count ... 0]
+ *
+ * The generation in the two highest order bits is two bits which are set to 11b
+ * on every insertion. During the course of each entry's existence, the
+ * generation field gets decremented during spring cleaning to 10b, then 01b and
+ * then 00b.
+ *
+ * This way we're employing the natural numeric ordering to make sure that newly
+ * inserted/touched elements have higher 12-bit counts (which we've manufactured)
+ * and thus iterating over the array initially won't kick out those elements
+ * which were inserted last.
+ *
+ * Spring cleaning is what we do when we reach a certain number CLEAN_ELEMS of
+ * elements entered into the array, during which, we're decaying all elements.
+ * If, after decay, an element gets inserted again, its generation is set to 11b
+ * to make sure it has higher numerical count than other, older elements and
+ * thus emulate an an LRU-like behavior when deleting elements to free up space
+ * in the page.
+ *
+ * When an element reaches it's max count of COUNT_MASK, we try to poison it by
+ * assuming that errors triggered COUNT_MASK times in a single page are
+ * excessive and that page shouldn't be used anymore.
+ *
+ * To the question why we've chosen a page and moving elements around with
+ * memmove(), it is because it is a very simple structure to handle and max data
+ * movement is 4K which on highly optimized modern CPUs is almost unnoticeable.
+ * We wanted to avoid the pointer traversal of more complex structures like a
+ * linked list or some sort of a balancing search tree.
+ *
+ * Deleting an element takes O(n) but since it is only a single page, it should
+ * be fast enough and it shouldn't happen all too often depending on error
+ * patterns.
+ */
+
+#undef pr_fmt
+#define pr_fmt(fmt) "RAS: " fmt
+
+/*
+ * We use DECAY_BITS bits of PAGE_SHIFT bits for counting decay, i.e., how long
+ * elements have stayed in the array without having been accessed again.
+ */
+#define DECAY_BITS 2
+#define DECAY_MASK ((1ULL << DECAY_BITS) - 1)
+#define MAX_ELEMS (PAGE_SIZE / sizeof(u64))
+
+/*
+ * Threshold amount of inserted elements after which we start spring
+ * cleaning.
+ */
+#define CLEAN_ELEMS (MAX_ELEMS >> DECAY_BITS)
+
+/* Bits which count the number of errors happened in this 4K page. */
+#define COUNT_BITS (PAGE_SHIFT - DECAY_BITS)
+#define COUNT_MASK ((1ULL << COUNT_BITS) - 1)
+#define FULL_COUNT_MASK (PAGE_SIZE - 1)
+
+/*
+ * u64: [ 63 ... 12 | DECAY_BITS | COUNT_BITS ]
+ */
+
+#define PFN(e) ((e) >> PAGE_SHIFT)
+#define DECAY(e) (((e) >> COUNT_BITS) & DECAY_MASK)
+#define COUNT(e) ((unsigned int)(e) & COUNT_MASK)
+#define FULL_COUNT(e) ((e) & (PAGE_SIZE - 1))
+
+static struct ce_array {
+ u64 *array; /* container page */
+ unsigned n; /* number of elements in the array */
+
+ unsigned int decay_count; /*
+ * number of element insertions/increments
+ * since the last spring cleaning.
+ */
+
+ u64 pfns_poisoned; /*
+ * number of PFNs which got poisoned.
+ */
+
+ u64 ces_entered; /*
+ * The number of correctable errors
+ * entered into the collector.
+ */
+ union {
+ struct {
+ __u32 disabled : 1, /* cmdline disabled */
+ __resv : 31;
+ };
+ __u32 flags;
+ };
+} ce_arr;
+
+static DEFINE_MUTEX(ce_mutex);
+
+static u64 dfs_pfn;
+
+/*
+ * Decrement decay value. We're using DECAY_BITS bits to denote decay of an
+ * element in the array. On insertion and any access, it gets reset to max.
+ */
+static void do_spring_cleaning(struct ce_array *ca)
+{
+ int i;
+
+ for (i = 0; i < ca->n; i++) {
+ u8 decay = DECAY(ca->array[i]);
+
+ if (!decay)
+ continue;
+
+ decay--;
+
+ ca->array[i] &= ~(DECAY_MASK << COUNT_BITS);
+ ca->array[i] |= (decay << COUNT_BITS);
+ }
+ ca->decay_count = 0;
+}
+
+/*
+ * @to: index of the smallest element which is >= then @pfn.
+ *
+ * Return the index of the pfn if found, otherwise negative value.
+ */
+static int __find_elem(struct ce_array *ca, u64 pfn, unsigned *to)
+{
+ u64 this_pfn;
+ int min = 0, max = ca->n;
+
+ while (min < max) {
+ int tmp = (max + min) >> 1;
+
+ this_pfn = PFN(ca->array[tmp]);
+
+ if (this_pfn < pfn)
+ min = tmp + 1;
+ else if (this_pfn > pfn)
+ max = tmp;
+ else {
+ min = tmp;
+ break;
+ }
+ }
+
+ if (to)
+ *to = min;
+
+ this_pfn = PFN(ca->array[min]);
+
+ if (this_pfn == pfn)
+ return min;
+
+ return -ENOKEY;
+}
+
+static int find_elem(struct ce_array *ca, u64 pfn, unsigned *to)
+{
+ WARN_ON(!to);
+
+ if (!ca->n) {
+ *to = 0;
+ return -ENOKEY;
+ }
+ return __find_elem(ca, pfn, to);
+}
+
+static void del_elem(struct ce_array *ca, int idx)
+{
+ /* Save us a function call when deleting the last element. */
+ if (ca->n - (idx + 1))
+ memmove((void *)&ca->array[idx],
+ (void *)&ca->array[idx + 1],
+ (ca->n - (idx + 1)) * sizeof(u64));
+
+ ca->n--;
+}
+
+static u64 del_lru_elem_unlocked(struct ce_array *ca)
+{
+ unsigned int min = FULL_COUNT_MASK;
+ int i, min_idx = 0;
+
+ for (i = 0; i < ca->n; i++) {
+ unsigned int this = FULL_COUNT(ca->array[i]);
+ if (min > this) {
+ min = this;
+ min_idx = i;
+ }
+ }
+
+ del_elem(ca, min_idx);
+
+ return PFN(ca->array[min_idx]);
+}
+
+/*
+ * We return the 0th pfn in the error case under the assumption that it cannot
+ * be poisoned and excessive CEs in there are a serious deal anyway.
+ */
+static u64 __maybe_unused del_lru_elem(void)
+{
+ struct ce_array *ca = &ce_arr;
+ u64 pfn;
+
+ if (!ca->n)
+ return 0;
+
+ mutex_lock(&ce_mutex);
+ pfn = del_lru_elem_unlocked(ca);
+ mutex_unlock(&ce_mutex);
+
+ return pfn;
+}
+
+
+int ce_add_elem(u64 pfn)
+{
+ struct ce_array *ca = &ce_arr;
+ unsigned to;
+ int count, ret;
+
+ /*
+ * We can be called very early on the identify_cpu() path where we are
+ * not initialized yet. We ignore the error for simplicity.
+ */
+ if (!ce_arr.array)
+ return -ENODEV;
+
+ ca->ces_entered++;
+
+ mutex_lock(&ce_mutex);
+
+ if (ca->n == MAX_ELEMS)
+ WARN_ON(!del_lru_elem_unlocked(ca));
+
+ ret = find_elem(ca, pfn, &to);
+ if (ret < 0) {
+ /*
+ * Shift range [to-end] to make room for one more element.
+ */
+ memmove((void *)&ca->array[to + 1],
+ (void *)&ca->array[to],
+ (ca->n - to) * sizeof(u64));
+
+ ca->array[to] = (pfn << PAGE_SHIFT) |
+ (DECAY_MASK << COUNT_BITS) | 1;
+
+ ca->n++;
+
+ goto decay;
+ }
+
+ count = COUNT(ca->array[to]);
+
+ if (count < COUNT_MASK) {
+ ca->array[to] |= (DECAY_MASK << COUNT_BITS);
+ ca->array[to]++;
+ } else {
+ u64 pfn = ca->array[to] >> PAGE_SHIFT;
+
+ /*
+ * We have reached max count for this page, soft-offline it.
+ */
+ pr_err("Soft-offlining pfn: 0x%llx\n", pfn);
+ memory_failure_queue(pfn, 0, MF_SOFT_OFFLINE);
+ ca->pfns_poisoned++;
+ del_elem(ca, to);
+
+ ret = 0;
+
+ goto unlock;
+ }
+
+decay:
+ ca->decay_count++;
+
+ if (ca->decay_count >= CLEAN_ELEMS)
+ do_spring_cleaning(ca);
+
+unlock:
+ mutex_unlock(&ce_mutex);
+
+ return 0;
+}
+
+static int pfn_get(void *data, u64 *val)
+{
+ *val = *(u64 *)data;
+
+ return 0;
+}
+
+static int pfn_set(void *data, u64 val)
+{
+ *(u64 *)data = val;
+
+ return ce_add_elem(val);
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(pfn_ops, pfn_get, pfn_set, "0x%llx\n");
+
+static int array_dump(struct seq_file *m, void *v)
+{
+ struct ce_array *ca = &ce_arr;
+ u64 prev = 0;
+ int i;
+
+ mutex_lock(&ce_mutex);
+
+ seq_printf(m, "{ n: %d\n", ca->n);
+ for (i = 0; i < ca->n; i++) {
+ u64 this = PFN(ca->array[i]);
+
+ seq_printf(m, " %03d: [%016llu|%03llx]\n", i, this, FULL_COUNT(ca->array[i]));
+
+ WARN_ON(prev > this);
+
+ prev = this;
+ }
+
+ seq_printf(m, "}\n");
+
+ seq_printf(m, "Stats:\n CEs: %llu\n offlined pages: %llu\n",
+ ca->ces_entered, ca->pfns_poisoned);
+
+ seq_printf(m, "Flags: 0x%x\n", ca->flags);
+
+ mutex_unlock(&ce_mutex);
+
+ return 0;
+}
+
+static int array_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, array_dump, NULL);
+}
+
+static const struct file_operations array_ops = {
+ .owner = THIS_MODULE,
+ .open = array_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init create_debugfs_nodes(void)
+{
+ struct dentry *d, *pfn, *array;
+
+ d = debugfs_create_dir("cec", ras_debugfs_dir);
+ if (!d) {
+ pr_warn("Error creating cec debugfs node!\n");
+ return -1;
+ }
+
+ pfn = debugfs_create_file("pfn", S_IRUSR | S_IWUSR, d, &dfs_pfn, &pfn_ops);
+ if (!pfn) {
+ pr_warn("Error creating pfn debugfs node!\n");
+ goto err;
+ }
+
+ array = debugfs_create_file("array", S_IRUSR, d, NULL, &array_ops);
+ if (!array) {
+ pr_warn("Error creating array debugfs node!\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ debugfs_remove_recursive(d);
+
+ return 1;
+}
+
+void __init ce_init(void)
+{
+ if (ce_arr.disabled)
+ return;
+
+ ce_arr.array = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!ce_arr.array) {
+ pr_err("Error allocating CE array page!\n");
+ return;
+ }
+
+ if (create_debugfs_nodes())
+ return;
+
+ pr_info("Correctable Errors collector initialized.\n");
+}
+
+static int __init cec_param(char *str)
+{
+ if (!str)
+ return 0;
+
+ if (*str == '=')
+ str++;
+
+ if (!strncmp(str, "disable", 7))
+ ce_arr.disabled = 1;
+ else
+ return 0;
+
+ return 1;
+}
+
+__setup("cec", cec_param);
diff --git a/drivers/ras/debugfs.c b/drivers/ras/debugfs.c
index 0322acf67ea5..501603057dff 100644
--- a/drivers/ras/debugfs.c
+++ b/drivers/ras/debugfs.c
@@ -1,6 +1,6 @@
#include <linux/debugfs.h>
-static struct dentry *ras_debugfs_dir;
+struct dentry *ras_debugfs_dir;
static atomic_t trace_count = ATOMIC_INIT(0);
diff --git a/drivers/ras/debugfs.h b/drivers/ras/debugfs.h
new file mode 100644
index 000000000000..db72e4513191
--- /dev/null
+++ b/drivers/ras/debugfs.h
@@ -0,0 +1,8 @@
+#ifndef __RAS_DEBUGFS_H__
+#define __RAS_DEBUGFS_H__
+
+#include <linux/debugfs.h>
+
+extern struct dentry *ras_debugfs_dir;
+
+#endif /* __RAS_DEBUGFS_H__ */
diff --git a/include/linux/ras.h b/include/linux/ras.h
index 2aceeafd6fe5..a96762a10100 100644
--- a/include/linux/ras.h
+++ b/include/linux/ras.h
@@ -1,14 +1,24 @@
#ifndef __RAS_H__
#define __RAS_H__
+#include <asm/errno.h>
+
#ifdef CONFIG_DEBUG_FS
int ras_userspace_consumers(void);
void ras_debugfs_init(void);
int ras_add_daemon_trace(void);
#else
static inline int ras_userspace_consumers(void) { return 0; }
-static inline void ras_debugfs_init(void) { return; }
+static inline void ras_debugfs_init(void) { }
static inline int ras_add_daemon_trace(void) { return 0; }
#endif
+#ifdef CONFIG_RAS_CEC
+void __init ce_init(void);
+int ce_add_elem(u64 pfn);
+#else
+static inline void __init ce_init(void) { }
+static inline int ce_add_elem(u64 pfn) { return -ENODEV; }
#endif
+
+#endif /* __RAS_H__ */
--
2.7.3
[toc] | [prev] | [next] | [standalone]
| From | "Luck, Tony" <tony.luck@intel.com> |
|---|---|
| Date | 2016-06-07 20:20 +0200 |
| Subject | Re: [RFC PATCH 1/4] RAS: Add a Corrected Errors Collector |
| Message-ID | <rHtLA-64y-13@gated-at.bofh.it> |
| In reply to | #1416424 |
On Tue, Jun 07, 2016 at 06:52:22PM +0200, Borislav Petkov wrote:
> +void mce_log(struct mce *m)
> {
> unsigned next, entry;
>
> + if (!in_atomic() && memory_error(m) && mce_usable_address(m))
> + if (!ce_add_elem(m->addr >> PAGE_SHIFT))
> + return;
> +
> /* Emit the trace record: */
> - trace_mce_record(mce);
> + trace_mce_record(m);
>
> - if (!mce_gen_pool_add(mce))
> + if (!mce_gen_pool_add(m))
> irq_work_queue(&mce_irq_work);
Is there a reason that we need to call the ce_add_elem() inline
here instead of having it just register on the mce_notifier chain?
This series just cleaned out all the /dev/mcelog special code from
here, and you are adding something back before the ink is dry on
that change.
I'm also strongly divided about whether this corrected error
handler should be allowed to preempt anything else even seeing
the error.
Argument for:
Lonely corrected errors are "No Big Deal"(TM). Just counting them
and moving on is a good thing.
Arguments against:
1) We may miss out on a one-time opportunity to get extra information
(from acpi_extlog.c).
2) I think this subverts our CMCI storm detection and mitigation code?
We could make the chain more caller friendly by adding a filter
argument so users could say "just tell me about memory errors"
(currently each of the EDAC drivers has inline code to do the same
as "memory_error(m) && mce_usable_address(m)")
-Tony
[toc] | [prev] | [next] | [standalone]
| From | Borislav Petkov <bp@alien8.de> |
|---|---|
| Date | 2016-06-07 23:10 +0200 |
| Subject | Re: [RFC PATCH 1/4] RAS: Add a Corrected Errors Collector |
| Message-ID | <rHwq6-7Ly-7@gated-at.bofh.it> |
| In reply to | #1416493 |
On Tue, Jun 07, 2016 at 11:11:09AM -0700, Luck, Tony wrote:
> Is there a reason that we need to call the ce_add_elem() inline
> here instead of having it just register on the mce_notifier chain?
> This series just cleaned out all the /dev/mcelog special code from
> here, and you are adding something back before the ink is dry on
> that change.
>
> I'm also strongly divided about whether this corrected error
> handler should be allowed to preempt anything else even seeing
> the error.
Well, so this is the main reason for adding the CEC: not to disturb
users with random CECC errors which might happen a couple of times
due to alpha particles and then never again. I.e., address all those
sporadic bursts of correctable errors which don't mean that the hw is
going faulty.
If the CEC consumes the error and does the leaky bucket of "forgetting"
about it after a while and after no more of that same PFN triggers
errors, then we do that silently and do not scare users. Yeah, they
think their hw is broken and whether they should start swapping things.
And then there's the aspect of soft-offlining PFNs when the error
threshold has been reached. I don't think we have had any automatic
recovery actions wrt errors so far without external agents.
> Argument for:
> Lonely corrected errors are "No Big Deal"(TM). Just counting them
> and moving on is a good thing.
Yap, exactly.
> Arguments against:
> 1) We may miss out on a one-time opportunity to get extra information
> (from acpi_extlog.c).
> 2) I think this subverts our CMCI storm detection and mitigation code?
...and we can address that by adding "ras=cec_doesnt_consume_errors" or
somesuch so that the rest of the chain sees them too.
I think we can be pretty flexible about it. And again, my main angle is
the "do not disturb users unnecessarily".
> We could make the chain more caller friendly by adding a filter
> argument so users could say "just tell me about memory errors"
> (currently each of the EDAC drivers has inline code to do the same
> as "memory_error(m) && mce_usable_address(m)")
Sure, that too.
And it can work on any system without the need for an EDAC driver.
Thanks.
--
Regards/Gruss,
Boris.
ECO tip #101: Trim your mails when you reply.
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web