Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux...
[safe/jmp/linux-2.6] / arch / x86 / kernel / cpu / mcheck / mce.c
index a8aacd4..707165d 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/sched.h>
 #include <linux/sysfs.h>
 #include <linux/types.h>
+#include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/kmod.h>
 #include <linux/poll.h>
 
 #include "mce-internal.h"
 
+static DEFINE_MUTEX(mce_read_mutex);
+
+#define rcu_dereference_check_mce(p) \
+       rcu_dereference_check((p), \
+                             rcu_read_lock_sched_held() || \
+                             lockdep_is_held(&mce_read_mutex))
+
 #define CREATE_TRACE_POINTS
 #include <trace/events/mce.h>
 
@@ -158,7 +166,7 @@ void mce_log(struct mce *mce)
        mce->finished = 0;
        wmb();
        for (;;) {
-               entry = rcu_dereference(mcelog.next);
+               entry = rcu_dereference_check_mce(mcelog.next);
                for (;;) {
                        /*
                         * When the buffer fills up discard new entries.
@@ -256,7 +264,7 @@ static void wait_for_panic(void)
 
 static void mce_panic(char *msg, struct mce *final, char *exp)
 {
-       int i;
+       int i, apei_err = 0;
 
        if (!fake_panic) {
                /*
@@ -279,8 +287,11 @@ static void mce_panic(char *msg, struct mce *final, char *exp)
                struct mce *m = &mcelog.entry[i];
                if (!(m->status & MCI_STATUS_VAL))
                        continue;
-               if (!(m->status & MCI_STATUS_UC))
+               if (!(m->status & MCI_STATUS_UC)) {
                        print_mce(m);
+                       if (!apei_err)
+                               apei_err = apei_write_mce(m);
+               }
        }
        /* Now print uncorrected but with the final one last */
        for (i = 0; i < MCE_LOG_LEN; i++) {
@@ -289,11 +300,17 @@ static void mce_panic(char *msg, struct mce *final, char *exp)
                        continue;
                if (!(m->status & MCI_STATUS_UC))
                        continue;
-               if (!final || memcmp(m, final, sizeof(struct mce)))
+               if (!final || memcmp(m, final, sizeof(struct mce))) {
                        print_mce(m);
+                       if (!apei_err)
+                               apei_err = apei_write_mce(m);
+               }
        }
-       if (final)
+       if (final) {
                print_mce(final);
+               if (!apei_err)
+                       apei_err = apei_write_mce(final);
+       }
        if (cpu_missing)
                printk(KERN_EMERG "Some CPUs didn't answer in synchronization\n");
        print_mce_tail();
@@ -531,7 +548,7 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b)
        struct mce m;
        int i;
 
-       __get_cpu_var(mce_poll_count)++;
+       percpu_inc(mce_poll_count);
 
        mce_setup(&m);
 
@@ -926,7 +943,7 @@ void do_machine_check(struct pt_regs *regs, long error_code)
 
        atomic_inc(&mce_entry);
 
-       __get_cpu_var(mce_exception_count)++;
+       percpu_inc(mce_exception_count);
 
        if (notify_die(DIE_NMI, "machine check", regs, error_code,
                           18, SIGKILL) == NOTIFY_STOP)
@@ -1485,7 +1502,42 @@ static void collect_tscs(void *data)
        rdtscll(cpu_tsc[smp_processor_id()]);
 }
 
-static DEFINE_MUTEX(mce_read_mutex);
+static int mce_apei_read_done;
+
+/* Collect MCE record of previous boot in persistent storage via APEI ERST. */
+static int __mce_read_apei(char __user **ubuf, size_t usize)
+{
+       int rc;
+       u64 record_id;
+       struct mce m;
+
+       if (usize < sizeof(struct mce))
+               return -EINVAL;
+
+       rc = apei_read_mce(&m, &record_id);
+       /* Error or no more MCE record */
+       if (rc <= 0) {
+               mce_apei_read_done = 1;
+               return rc;
+       }
+       rc = -EFAULT;
+       if (copy_to_user(*ubuf, &m, sizeof(struct mce)))
+               return rc;
+       /*
+        * In fact, we should have cleared the record after that has
+        * been flushed to the disk or sent to network in
+        * /sbin/mcelog, but we have no interface to support that now,
+        * so just clear it to avoid duplication.
+        */
+       rc = apei_clear_mce(record_id);
+       if (rc) {
+               mce_apei_read_done = 1;
+               return rc;
+       }
+       *ubuf += sizeof(struct mce);
+
+       return 0;
+}
 
 static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize,
                        loff_t *off)
@@ -1500,16 +1552,20 @@ static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize,
                return -ENOMEM;
 
        mutex_lock(&mce_read_mutex);
-       next = rcu_dereference(mcelog.next);
 
-       /* Only supports full reads right now */
-       if (*off != 0 || usize < MCE_LOG_LEN*sizeof(struct mce)) {
-               mutex_unlock(&mce_read_mutex);
-               kfree(cpu_tsc);
-
-               return -EINVAL;
+       if (!mce_apei_read_done) {
+               err = __mce_read_apei(&buf, usize);
+               if (err || buf != ubuf)
+                       goto out;
        }
 
+       next = rcu_dereference_check_mce(mcelog.next);
+
+       /* Only supports full reads right now */
+       err = -EINVAL;
+       if (*off != 0 || usize < MCE_LOG_LEN*sizeof(struct mce))
+               goto out;
+
        err = 0;
        prev = 0;
        do {
@@ -1556,16 +1612,23 @@ timeout:
                        memset(&mcelog.entry[i], 0, sizeof(struct mce));
                }
        }
+
+       if (err)
+               err = -EFAULT;
+
+out:
        mutex_unlock(&mce_read_mutex);
        kfree(cpu_tsc);
 
-       return err ? -EFAULT : buf - ubuf;
+       return err ? err : buf - ubuf;
 }
 
 static unsigned int mce_poll(struct file *file, poll_table *wait)
 {
        poll_wait(file, &mce_wait, wait);
-       if (rcu_dereference(mcelog.next))
+       if (rcu_dereference_check_mce(mcelog.next))
+               return POLLIN | POLLRDNORM;
+       if (!mce_apei_read_done && apei_check_mce())
                return POLLIN | POLLRDNORM;
        return 0;
 }
@@ -2044,6 +2107,7 @@ static __init void mce_init_banks(void)
                struct mce_bank *b = &mce_banks[i];
                struct sysdev_attribute *a = &b->attr;
 
+               sysfs_attr_init(&a->attr);
                a->attr.name    = b->attrname;
                snprintf(b->attrname, ATTR_LEN, "bank%d", i);