#include <linux/oprofile.h>
#include <linux/sysdev.h>
#include <linux/slab.h>
+#include <linux/moduleparam.h>
#include <asm/nmi.h>
#include <asm/msr.h>
#include <asm/apic.h>
+#include <asm/kdebug.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);
#ifdef CONFIG_PM
-static int nmi_suspend(struct sys_device *dev, u32 state)
+static int nmi_suspend(struct sys_device *dev, pm_message_t state)
{
if (nmi_enabled == 1)
nmi_stop();
}
-static void __exit exit_driverfs(void)
+static void exit_driverfs(void)
{
sysdev_unregister(&device_oprofile);
sysdev_class_unregister(&oprofile_sysclass);
#define exit_driverfs() 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(cpu, &cpu_msrs[cpu], regs);
+ 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;
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);
+ }
}
}
static void free_msrs(void)
{
int i;
- for (i = 0; i < NR_CPUS; ++i) {
+ for_each_possible_cpu(i) {
kfree(cpu_msrs[i].counters);
cpu_msrs[i].counters = NULL;
kfree(cpu_msrs[i].controls);
size_t counters_size = sizeof(struct op_msr) * model->num_counters;
int i;
- for (i = 0; i < NR_CPUS; ++i) {
- if (!cpu_online(i))
- continue;
-
+ for_each_online_cpu(i) {
cpu_msrs[i].counters = kmalloc(counters_size, GFP_KERNEL);
if (!cpu_msrs[i].counters) {
success = 0;
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;
}
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);
+ }
}
}
apic_write(APIC_LVTPC, saved_lvtpc[cpu]);
apic_write(APIC_LVTERR, v);
nmi_restore_registers(msrs);
+ model->shutdown(msrs);
}
{
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();
}
for (i = 0; i < model->num_counters; ++i) {
struct dentry * dir;
- char buf[2];
+ char buf[4];
- snprintf(buf, 2, "%d", i);
+ /* 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);
oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event);
return 0;
}
+static int p4force;
+module_param(p4force, int, 0);
-struct oprofile_operations nmi_ops = {
- .create_files = nmi_create_files,
- .setup = nmi_setup,
- .shutdown = nmi_shutdown,
- .start = nmi_start,
- .stop = nmi_stop
-};
-
-
-static int __init p4_init(void)
+static int __init p4_init(char ** cpu_type)
{
- __u8 cpu_model = current_cpu_data.x86_model;
+ __u8 cpu_model = boot_cpu_data.x86_model;
- if (cpu_model > 3)
+ if (!p4force && (cpu_model > 6 || cpu_model == 5))
return 0;
#ifndef CONFIG_SMP
- nmi_ops.cpu_type = "i386/p4";
+ *cpu_type = "i386/p4";
model = &op_p4_spec;
return 1;
#else
switch (smp_num_siblings) {
case 1:
- nmi_ops.cpu_type = "i386/p4";
+ *cpu_type = "i386/p4";
model = &op_p4_spec;
return 1;
case 2:
- nmi_ops.cpu_type = "i386/p4-ht";
+ *cpu_type = "i386/p4-ht";
model = &op_p4_ht2_spec;
return 1;
}
}
-static int __init ppro_init(void)
+static int __init ppro_init(char ** cpu_type)
{
- __u8 cpu_model = current_cpu_data.x86_model;
+ __u8 cpu_model = boot_cpu_data.x86_model;
- if (cpu_model > 0xd)
+ if (cpu_model == 14)
+ *cpu_type = "i386/core";
+ else if (cpu_model == 15)
+ *cpu_type = "i386/core_2";
+ else if (cpu_model > 0xd)
return 0;
-
- if (cpu_model == 9) {
- nmi_ops.cpu_type = "i386/p6_mobile";
+ else if (cpu_model == 9) {
+ *cpu_type = "i386/p6_mobile";
} else if (cpu_model > 5) {
- nmi_ops.cpu_type = "i386/piii";
+ *cpu_type = "i386/piii";
} else if (cpu_model > 2) {
- nmi_ops.cpu_type = "i386/pii";
+ *cpu_type = "i386/pii";
} else {
- nmi_ops.cpu_type = "i386/ppro";
+ *cpu_type = "i386/ppro";
}
model = &op_ppro_spec;
/* in order to get driverfs right */
static int using_nmi;
-int __init nmi_init(struct oprofile_operations ** ops)
+int __init op_nmi_init(struct oprofile_operations *ops)
{
- __u8 vendor = current_cpu_data.x86_vendor;
- __u8 family = current_cpu_data.x86;
-
+ __u8 vendor = boot_cpu_data.x86_vendor;
+ __u8 family = boot_cpu_data.x86;
+ char *cpu_type;
+
if (!cpu_has_apic)
return -ENODEV;
return -ENODEV;
case 6:
model = &op_athlon_spec;
- nmi_ops.cpu_type = "i386/athlon";
+ cpu_type = "i386/athlon";
break;
case 0xf:
model = &op_athlon_spec;
/* Actually it could be i386/hammer too, but give
user space an consistent name. */
- nmi_ops.cpu_type = "x86-64/hammer";
+ cpu_type = "x86-64/hammer";
break;
}
break;
switch (family) {
/* Pentium IV */
case 0xf:
- if (!p4_init())
+ if (!p4_init(&cpu_type))
return -ENODEV;
break;
/* A P6-class processor */
case 6:
- if (!ppro_init())
+ if (!ppro_init(&cpu_type))
return -ENODEV;
break;
init_driverfs();
using_nmi = 1;
- *ops = &nmi_ops;
+ ops->create_files = nmi_create_files;
+ ops->setup = nmi_setup;
+ ops->shutdown = nmi_shutdown;
+ ops->start = nmi_start;
+ ops->stop = nmi_stop;
+ ops->cpu_type = cpu_type;
printk(KERN_INFO "oprofile: using NMI interrupt.\n");
return 0;
}
-void __exit nmi_exit(void)
+void op_nmi_exit(void)
{
if (using_nmi)
exit_driverfs();