Pull thermal into release branch
[linux-drm-fsl-dcu.git] / arch / i386 / oprofile / nmi_int.c
index 5f8dc8a21bd7157e2fa9e40a6b18422fc0ed2c00..8e185208dfd438d2a30b02fe601526689fa8f1cd 100644 (file)
 #include <linux/sysdev.h>
 #include <linux/slab.h>
 #include <linux/moduleparam.h>
+#include <linux/kdebug.h>
 #include <asm/nmi.h>
 #include <asm/msr.h>
 #include <asm/apic.h>
  
 #include "op_counter.h"
 #include "op_x86_model.h"
+
 static struct op_x86_model_spec const * model;
 static struct op_msrs cpu_msrs[NR_CPUS];
 static unsigned long saved_lvtpc[NR_CPUS];
+
 static int nmi_start(void);
 static void nmi_stop(void);
 
@@ -62,7 +63,7 @@ static struct sys_device device_oprofile = {
 };
 
 
-static int __init init_driverfs(void)
+static int __init init_sysfs(void)
 {
        int error;
        if (!(error = sysdev_class_register(&oprofile_sysclass)))
@@ -71,24 +72,35 @@ static int __init init_driverfs(void)
 }
 
 
-static void exit_driverfs(void)
+static void exit_sysfs(void)
 {
        sysdev_unregister(&device_oprofile);
        sysdev_class_unregister(&oprofile_sysclass);
 }
 
 #else
-#define init_driverfs() do { } while (0)
-#define exit_driverfs() do { } while (0)
+#define init_sysfs() do { } while (0)
+#define exit_sysfs() do { } while (0)
 #endif /* CONFIG_PM */
 
-
-static int nmi_callback(struct pt_regs * regs, int cpu)
+static int profile_exceptions_notify(struct notifier_block *self,
+                                    unsigned long val, void *data)
 {
-       return model->check_ctrs(regs, &cpu_msrs[cpu]);
+       struct die_args *args = (struct die_args *)data;
+       int ret = NOTIFY_DONE;
+       int cpu = smp_processor_id();
+
+       switch(val) {
+       case DIE_NMI:
+               if (model->check_ctrs(args->regs, &cpu_msrs[cpu]))
+                       ret = NOTIFY_STOP;
+               break;
+       default:
+               break;
+       }
+       return ret;
 }
+
 static void nmi_cpu_save_registers(struct op_msrs * msrs)
 {
        unsigned int const nr_ctrs = model->num_counters;
@@ -98,15 +110,19 @@ static void nmi_cpu_save_registers(struct op_msrs * msrs)
        unsigned int i;
 
        for (i = 0; i < nr_ctrs; ++i) {
-               rdmsr(counters[i].addr,
-                       counters[i].saved.low,
-                       counters[i].saved.high);
+               if (counters[i].addr){
+                       rdmsr(counters[i].addr,
+                               counters[i].saved.low,
+                               counters[i].saved.high);
+               }
        }
  
        for (i = 0; i < nr_ctrls; ++i) {
-               rdmsr(controls[i].addr,
-                       controls[i].saved.low,
-                       controls[i].saved.high);
+               if (controls[i].addr){
+                       rdmsr(controls[i].addr,
+                               controls[i].saved.low,
+                               controls[i].saved.high);
+               }
        }
 }
 
@@ -170,27 +186,29 @@ static void nmi_cpu_setup(void * dummy)
        apic_write(APIC_LVTPC, APIC_DM_NMI);
 }
 
+static struct notifier_block profile_exceptions_nb = {
+       .notifier_call = profile_exceptions_notify,
+       .next = NULL,
+       .priority = 0
+};
 
 static int nmi_setup(void)
 {
+       int err=0;
+
        if (!allocate_msrs())
                return -ENOMEM;
 
-       /* We walk a thin line between law and rape here.
-        * We need to be careful to install our NMI handler
-        * without actually triggering any NMIs as this will
-        * break the core code horrifically.
-        */
-       if (reserve_lapic_nmi() < 0) {
+       if ((err = register_die_notifier(&profile_exceptions_nb))){
                free_msrs();
-               return -EBUSY;
+               return err;
        }
+
        /* We need to serialize save and setup for HT because the subset
         * of msrs are distinct for save and setup operations
         */
        on_each_cpu(nmi_save_registers, NULL, 0, 1);
        on_each_cpu(nmi_cpu_setup, NULL, 0, 1);
-       set_nmi_callback(nmi_callback);
        nmi_enabled = 1;
        return 0;
 }
@@ -205,15 +223,19 @@ static void nmi_restore_registers(struct op_msrs * msrs)
        unsigned int i;
 
        for (i = 0; i < nr_ctrls; ++i) {
-               wrmsr(controls[i].addr,
-                       controls[i].saved.low,
-                       controls[i].saved.high);
+               if (controls[i].addr){
+                       wrmsr(controls[i].addr,
+                               controls[i].saved.low,
+                               controls[i].saved.high);
+               }
        }
  
        for (i = 0; i < nr_ctrs; ++i) {
-               wrmsr(counters[i].addr,
-                       counters[i].saved.low,
-                       counters[i].saved.high);
+               if (counters[i].addr){
+                       wrmsr(counters[i].addr,
+                               counters[i].saved.low,
+                               counters[i].saved.high);
+               }
        }
 }
  
@@ -234,6 +256,7 @@ static void nmi_cpu_shutdown(void * dummy)
        apic_write(APIC_LVTPC, saved_lvtpc[cpu]);
        apic_write(APIC_LVTERR, v);
        nmi_restore_registers(msrs);
+       model->shutdown(msrs);
 }
 
  
@@ -241,8 +264,7 @@ static void nmi_shutdown(void)
 {
        nmi_enabled = 0;
        on_each_cpu(nmi_cpu_shutdown, NULL, 0, 1);
-       unset_nmi_callback();
-       release_lapic_nmi();
+       unregister_die_notifier(&profile_exceptions_nb);
        free_msrs();
 }
 
@@ -284,6 +306,14 @@ static int nmi_create_files(struct super_block * sb, struct dentry * root)
                struct dentry * dir;
                char buf[4];
  
+               /* quick little hack to _not_ expose a counter if it is not
+                * available for use.  This should protect userspace app.
+                * NOTE:  assumes 1:1 mapping here (that counters are organized
+                *        sequentially in their struct assignment).
+                */
+               if (unlikely(!avail_to_resrv_perfctr_nmi_bit(i)))
+                       continue;
+
                snprintf(buf,  sizeof(buf), "%d", i);
                dir = oprofilefs_mkdir(sb, root, buf);
                oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled); 
@@ -355,7 +385,7 @@ static int __init ppro_init(char ** cpu_type)
        return 1;
 }
 
-/* in order to get driverfs right */
+/* in order to get sysfs right */
 static int using_nmi;
 
 int __init op_nmi_init(struct oprofile_operations *ops)
@@ -384,6 +414,10 @@ int __init op_nmi_init(struct oprofile_operations *ops)
                                   user space an consistent name. */
                                cpu_type = "x86-64/hammer";
                                break;
+                       case 0x10:
+                               model = &op_athlon_spec;
+                               cpu_type = "x86-64/family10";
+                               break;
                        }
                        break;
  
@@ -410,7 +444,7 @@ int __init op_nmi_init(struct oprofile_operations *ops)
                        return -ENODEV;
        }
 
-       init_driverfs();
+       init_sysfs();
        using_nmi = 1;
        ops->create_files = nmi_create_files;
        ops->setup = nmi_setup;
@@ -426,5 +460,5 @@ int __init op_nmi_init(struct oprofile_operations *ops)
 void op_nmi_exit(void)
 {
        if (using_nmi)
-               exit_driverfs();
+               exit_sysfs();
 }