]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - arch/x86/kernel/cpu/mcheck/mce.c
Merge branch 'x86-mce-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[karo-tx-linux.git] / arch / x86 / kernel / cpu / mcheck / mce.c
index e9c9d0aab36ab55ae508a9432510387a4c7a735d..cbe82b5918cea2b37cce33fe77991a141474344a 100644 (file)
@@ -95,13 +95,6 @@ static DECLARE_WAIT_QUEUE_HEAD(mce_chrdev_wait);
 static DEFINE_PER_CPU(struct mce, mces_seen);
 static int                     cpu_missing;
 
-/*
- * CPU/chipset specific EDAC code can register a notifier call here to print
- * MCE errors in a human-readable form.
- */
-ATOMIC_NOTIFIER_HEAD(x86_mce_decoder_chain);
-EXPORT_SYMBOL_GPL(x86_mce_decoder_chain);
-
 /* MCA banks polled by the period polling timer for corrected events */
 DEFINE_PER_CPU(mce_banks_t, mce_poll_banks) = {
        [0 ... BITS_TO_LONGS(MAX_NR_BANKS)-1] = ~0UL
@@ -109,6 +102,12 @@ DEFINE_PER_CPU(mce_banks_t, mce_poll_banks) = {
 
 static DEFINE_PER_CPU(struct work_struct, mce_work);
 
+/*
+ * CPU/chipset specific EDAC code can register a notifier call here to print
+ * MCE errors in a human-readable form.
+ */
+ATOMIC_NOTIFIER_HEAD(x86_mce_decoder_chain);
+
 /* Do initial initialization of a struct mce */
 void mce_setup(struct mce *m)
 {
@@ -188,6 +187,57 @@ void mce_log(struct mce *mce)
        set_bit(0, &mce_need_notify);
 }
 
+static void drain_mcelog_buffer(void)
+{
+       unsigned int next, i, prev = 0;
+
+       next = rcu_dereference_check_mce(mcelog.next);
+
+       do {
+               struct mce *m;
+
+               /* drain what was logged during boot */
+               for (i = prev; i < next; i++) {
+                       unsigned long start = jiffies;
+                       unsigned retries = 1;
+
+                       m = &mcelog.entry[i];
+
+                       while (!m->finished) {
+                               if (time_after_eq(jiffies, start + 2*retries))
+                                       retries++;
+
+                               cpu_relax();
+
+                               if (!m->finished && retries >= 4) {
+                                       pr_err("MCE: skipping error being logged currently!\n");
+                                       break;
+                               }
+                       }
+                       smp_rmb();
+                       atomic_notifier_call_chain(&x86_mce_decoder_chain, 0, m);
+               }
+
+               memset(mcelog.entry + prev, 0, (next - prev) * sizeof(*m));
+               prev = next;
+               next = cmpxchg(&mcelog.next, prev, 0);
+       } while (next != prev);
+}
+
+
+void mce_register_decode_chain(struct notifier_block *nb)
+{
+       atomic_notifier_chain_register(&x86_mce_decoder_chain, nb);
+       drain_mcelog_buffer();
+}
+EXPORT_SYMBOL_GPL(mce_register_decode_chain);
+
+void mce_unregister_decode_chain(struct notifier_block *nb)
+{
+       atomic_notifier_chain_unregister(&x86_mce_decoder_chain, nb);
+}
+EXPORT_SYMBOL_GPL(mce_unregister_decode_chain);
+
 static void print_mce(struct mce *m)
 {
        int ret = 0;