Merge branch 'linus' into x86/urgent
[pandora-kernel.git] / arch / x86 / kernel / cpu / mcheck / mce_64.c
index 242e866..9874107 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/smp_lock.h>
 #include <linux/string.h>
 #include <linux/rcupdate.h>
 #include <linux/kallsyms.h>
@@ -31,7 +32,7 @@
 #include <asm/idle.h>
 
 #define MISC_MCELOG_MINOR 227
-#define NR_BANKS 6
+#define NR_SYSFS_BANKS 6
 
 atomic_t mce_entry;
 
@@ -46,10 +47,10 @@ static int mce_dont_init;
  */
 static int tolerant = 1;
 static int banks;
-static unsigned long bank[NR_BANKS] = { [0 ... NR_BANKS-1] = ~0UL };
+static unsigned long bank[NR_SYSFS_BANKS] = { [0 ... NR_SYSFS_BANKS-1] = ~0UL };
 static unsigned long notify_user;
 static int rip_msr;
-static int mce_bootlog = 1;
+static int mce_bootlog = -1;
 static atomic_t mce_events;
 
 static char trigger[128];
@@ -63,7 +64,7 @@ static DECLARE_WAIT_QUEUE_HEAD(mce_wait);
  * separate MCEs from kernel messages to avoid bogus bug reports.
  */
 
-struct mce_log mcelog = {
+static struct mce_log mcelog = {
        MCE_LOG_SIGNATURE,
        MCE_LOG_LEN,
 };
@@ -80,7 +81,7 @@ void mce_log(struct mce *mce)
                        /* When the buffer fills up discard new entries. Assume
                           that the earlier errors are the more interesting. */
                        if (entry >= MCE_LOG_LEN) {
-                               set_bit(MCE_OVERFLOW, &mcelog.flags);
+                               set_bit(MCE_OVERFLOW, (unsigned long *)&mcelog.flags);
                                return;
                        }
                        /* Old left over entry. Skip. */
@@ -110,12 +111,12 @@ static void print_mce(struct mce *m)
               KERN_EMERG
               "CPU %d: Machine Check Exception: %16Lx Bank %d: %016Lx\n",
               m->cpu, m->mcgstatus, m->bank, m->status);
-       if (m->rip) {
+       if (m->ip) {
                printk(KERN_EMERG "RIP%s %02x:<%016Lx> ",
                       !(m->mcgstatus & MCG_STATUS_EIPV) ? " !INEXACT!" : "",
-                      m->cs, m->rip);
+                      m->cs, m->ip);
                if (m->cs == __KERNEL_CS)
-                       print_symbol("{%s}", m->rip);
+                       print_symbol("{%s}", m->ip);
                printk("\n");
        }
        printk(KERN_EMERG "TSC %Lx ", m->tsc);
@@ -156,16 +157,16 @@ static int mce_available(struct cpuinfo_x86 *c)
 static inline void mce_get_rip(struct mce *m, struct pt_regs *regs)
 {
        if (regs && (m->mcgstatus & MCG_STATUS_RIPV)) {
-               m->rip = regs->rip;
+               m->ip = regs->ip;
                m->cs = regs->cs;
        } else {
-               m->rip = 0;
+               m->ip = 0;
                m->cs = 0;
        }
        if (rip_msr) {
                /* Assume the RIP in the MSR is exact. Is this true? */
                m->mcgstatus |= MCG_STATUS_EIPV;
-               rdmsrl(rip_msr, m->rip);
+               rdmsrl(rip_msr, m->ip);
                m->cs = 0;
        }
 }
@@ -192,10 +193,10 @@ void do_machine_check(struct pt_regs * regs, long error_code)
 
        atomic_inc(&mce_entry);
 
-       if (regs)
-               notify_die(DIE_NMI, "machine check", regs, error_code, 18,
-                          SIGKILL);
-       if (!banks)
+       if ((regs
+            && notify_die(DIE_NMI, "machine check", regs, error_code,
+                          18, SIGKILL) == NOTIFY_STOP)
+           || !banks)
                goto out2;
 
        memset(&m, 0, sizeof(struct mce));
@@ -209,7 +210,7 @@ void do_machine_check(struct pt_regs * regs, long error_code)
        barrier();
 
        for (i = 0; i < banks; i++) {
-               if (!bank[i])
+               if (i < NR_SYSFS_BANKS && !bank[i])
                        continue;
 
                m.misc = 0;
@@ -288,7 +289,7 @@ void do_machine_check(struct pt_regs * regs, long error_code)
                 * instruction which caused the MCE.
                 */
                if (m.mcgstatus & MCG_STATUS_EIPV)
-                       user_space = panicm.rip && (panicm.cs & 3);
+                       user_space = panicm.ip && (panicm.cs & 3);
 
                /*
                 * If we know that the error was in user space, send a
@@ -444,9 +445,10 @@ static void mce_init(void *dummy)
 
        rdmsrl(MSR_IA32_MCG_CAP, cap);
        banks = cap & 0xff;
-       if (banks > NR_BANKS) {
-               printk(KERN_INFO "MCE: warning: using only %d banks\n", banks);
-               banks = NR_BANKS;
+       if (banks > MCE_EXTENDED_BANK) {
+               banks = MCE_EXTENDED_BANK;
+               printk(KERN_INFO "MCE: warning: using only %d banks\n",
+                      MCE_EXTENDED_BANK);
        }
        /* Use accurate RIP reporting if available. */
        if ((cap & (1<<9)) && ((cap >> 16) & 0xff) >= 9)
@@ -462,7 +464,11 @@ static void mce_init(void *dummy)
                wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff);
 
        for (i = 0; i < banks; i++) {
-               wrmsrl(MSR_IA32_MC0_CTL+4*i, bank[i]);
+               if (i < NR_SYSFS_BANKS)
+                       wrmsrl(MSR_IA32_MC0_CTL+4*i, bank[i]);
+               else
+                       wrmsrl(MSR_IA32_MC0_CTL+4*i, ~0UL);
+
                wrmsrl(MSR_IA32_MC0_STATUS+4*i, 0);
        }
 }
@@ -471,13 +477,15 @@ static void mce_init(void *dummy)
 static void __cpuinit mce_cpu_quirks(struct cpuinfo_x86 *c)
 {
        /* This should be disabled by the BIOS, but isn't always */
-       if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 15) {
-               /* disable GART TBL walk error reporting, which trips off
-                  incorrectly with the IOMMU & 3ware & Cerberus. */
-               clear_bit(10, &bank[4]);
-               /* Lots of broken BIOS around that don't clear them
-                  by default and leave crap in there. Don't log. */
-               mce_bootlog = 0;
+       if (c->x86_vendor == X86_VENDOR_AMD) {
+               if(c->x86 == 15)
+                       /* disable GART TBL walk error reporting, which trips off
+                          incorrectly with the IOMMU & 3ware & Cerberus. */
+                       clear_bit(10, &bank[4]);
+               if(c->x86 <= 17 && mce_bootlog < 0)
+                       /* Lots of broken BIOS around that don't clear them
+                          by default and leave crap in there. Don't log. */
+                       mce_bootlog = 0;
        }
 
 }
@@ -525,10 +533,12 @@ static int open_exclu;    /* already open exclusive? */
 
 static int mce_open(struct inode *inode, struct file *file)
 {
+       lock_kernel();
        spin_lock(&mce_state_lock);
 
        if (open_exclu || (open_count && (file->f_flags & O_EXCL))) {
                spin_unlock(&mce_state_lock);
+               unlock_kernel();
                return -EBUSY;
        }
 
@@ -537,6 +547,7 @@ static int mce_open(struct inode *inode, struct file *file)
        open_count++;
 
        spin_unlock(&mce_state_lock);
+       unlock_kernel();
 
        return nonseekable_open(inode, file);
 }
@@ -564,7 +575,7 @@ static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize,
                        loff_t *off)
 {
        unsigned long *cpu_tsc;
-       static DECLARE_MUTEX(mce_read_sem);
+       static DEFINE_MUTEX(mce_read_mutex);
        unsigned next;
        char __user *buf = ubuf;
        int i, err;
@@ -573,12 +584,12 @@ static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize,
        if (!cpu_tsc)
                return -ENOMEM;
 
-       down(&mce_read_sem);
+       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)) {
-               up(&mce_read_sem);
+               mutex_unlock(&mce_read_mutex);
                kfree(cpu_tsc);
                return -EINVAL;
        }
@@ -621,7 +632,7 @@ static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize,
                        memset(&mcelog.entry[i], 0, sizeof(struct mce));
                }
        }
-       up(&mce_read_sem);
+       mutex_unlock(&mce_read_mutex);
        kfree(cpu_tsc);
        return err ? -EFAULT : buf - ubuf;
 }
@@ -634,8 +645,7 @@ static unsigned int mce_poll(struct file *file, poll_table *wait)
        return 0;
 }
 
-static int mce_ioctl(struct inode *i, struct file *f,unsigned int cmd,
-                    unsigned long arg)
+static long mce_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
 {
        int __user *p = (int __user *)arg;
 
@@ -664,7 +674,7 @@ static const struct file_operations mce_chrdev_ops = {
        .release = mce_release,
        .read = mce_read,
        .poll = mce_poll,
-       .ioctl = mce_ioctl,
+       .unlocked_ioctl = mce_ioctl,
 };
 
 static struct miscdevice mce_log_device = {
@@ -765,7 +775,10 @@ DEFINE_PER_CPU(struct sys_device, device_mce);
        }                                                               \
        static SYSDEV_ATTR(name, 0644, show_ ## name, set_ ## name);
 
-/* TBD should generate these dynamically based on number of available banks */
+/*
+ * TBD should generate these dynamically based on number of available banks.
+ * Have only 6 contol banks in /sysfs until then.
+ */
 ACCESSOR(bank0ctl,bank[0],mce_restart())
 ACCESSOR(bank1ctl,bank[1],mce_restart())
 ACCESSOR(bank2ctl,bank[2],mce_restart())
@@ -855,8 +868,8 @@ static void mce_remove_device(unsigned int cpu)
 }
 
 /* Get notified when a cpu comes on/off. Be hotplug friendly. */
-static int
-mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
+static int __cpuinit mce_cpu_callback(struct notifier_block *nfb,
+                                     unsigned long action, void *hcpu)
 {
        unsigned int cpu = (unsigned long)hcpu;
 
@@ -873,7 +886,7 @@ mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
        return NOTIFY_OK;
 }
 
-static struct notifier_block mce_cpu_notifier = {
+static struct notifier_block mce_cpu_notifier __cpuinitdata = {
        .notifier_call = mce_cpu_callback,
 };