From: John L. <mov...@us...> - 2002-09-22 17:46:05
|
Update of /cvsroot/oprofile/oprofile/module/x86 In directory usw-pr-cvs1:/tmp/cvs-serv10833/module/x86 Modified Files: Makefile.in op_nmi.c Added Files: op_model_athlon.c op_model_ppro.c op_x86_model.h Log Message: The model specific patch. --- NEW FILE: op_model_athlon.c --- /** * @file op_model_athlon.h * athlon / K7 model-specific MSR operations * * @remark Copyright 2002 OProfile authors * @remark Read the file COPYING * * @author John Levon * @author Philippe Elie * @author Graydon Hoare */ #include "op_x86_model.h" #include "op_msr.h" #define NUM_COUNTERS 4 #define NUM_CONTROLS 4 #define CTR_READ(l,h,msrs,c) do {rdmsr(msrs->counters.addrs[(c)], (l), (h));} while (0) #define CTR_WRITE(l,msrs,c) do {wrmsr(msrs->counters.addrs[(c)], -(u32)(l), -1);} while (0) #define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) #define CTRL_READ(l,h,msrs,c) do {rdmsr(msrs->controls.addrs[(c)], (l), (h));} while (0) #define CTRL_WRITE(l,h,msrs,c) do {wrmsr(msrs->controls.addrs[(c)], (l), (h));} while (0) #define CTRL_SET_ACTIVE(n) (n |= (1<<22)) #define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) #define CTRL_CLEAR(x) (x &= (1<<21)) #define CTRL_SET_ENABLE(val) (val |= 1<<20) #define CTRL_SET_USR(val,u) (val |= ((u & 1) << 16)) #define CTRL_SET_KERN(val,k) (val |= ((k & 1) << 17)) #define CTRL_SET_UM(val, m) (val |= (m << 8)) #define CTRL_SET_EVENT(val, e) (val |= e) static void athlon_fill_in_addresses(struct op_msrs * const msrs) { msrs->counters.addrs[0] = MSR_K7_PERFCTR0; msrs->counters.addrs[1] = MSR_K7_PERFCTR1; msrs->counters.addrs[2] = MSR_K7_PERFCTR2; msrs->counters.addrs[3] = MSR_K7_PERFCTR3; msrs->controls.addrs[0] = MSR_K7_PERFCTL0; msrs->controls.addrs[1] = MSR_K7_PERFCTL1; msrs->controls.addrs[2] = MSR_K7_PERFCTL2; msrs->controls.addrs[3] = MSR_K7_PERFCTL3; } static void athlon_setup_ctrs(struct op_msrs const * const msrs) { uint low, high; int i; /* clear all counters */ for (i = 0 ; i < NUM_CONTROLS; ++i) { CTRL_READ(low, high, msrs, i); CTRL_CLEAR(low); CTRL_WRITE(low, high, msrs, i); } /* avoid a false detection of ctr overflows in NMI handler */ for (i = 0; i < NUM_COUNTERS; ++i) { CTR_WRITE(1, msrs, i); } /* enable active counters */ for (i = 0; i < NUM_COUNTERS; ++i) { if (sysctl.ctr[i].event) { CTR_WRITE(sysctl.ctr[i].count, msrs, i); CTRL_READ(low, high, msrs, i); CTRL_CLEAR(low); CTRL_SET_ENABLE(low); CTRL_SET_USR(low, sysctl.ctr[i].user); CTRL_SET_KERN(low, sysctl.ctr[i].kernel); CTRL_SET_UM(low, sysctl.ctr[i].unit_mask); CTRL_SET_EVENT(low, sysctl.ctr[i].event); CTRL_WRITE(low, high, msrs, i); } } } static void athlon_check_ctrs(uint const cpu, struct op_msrs const * const msrs, struct pt_regs * const regs) { ulong low, high; int i; for (i = 0 ; i < NUM_COUNTERS; ++i) { CTR_READ(low, high, msrs, i); if (CTR_OVERFLOWED(low)) { op_do_profile(cpu, regs, i); CTR_WRITE(oprof_data[cpu].ctr_count[i], msrs, i); } } } static void athlon_start(struct op_msrs const * const msrs) { uint low, high; int i; for (i = 0 ; i < NUM_COUNTERS ; ++i) { if (sysctl.ctr[i].count) { CTRL_READ(low, high, msrs, i); CTRL_SET_ACTIVE(low); CTRL_WRITE(low, high, msrs, i); } } } static void athlon_stop(struct op_msrs const * const msrs) { uint low,high; int i; for (i = 0 ; i < NUM_COUNTERS ; ++i) { if (sysctl.ctr[i].count) { CTRL_READ(low, high, msrs, i); CTRL_SET_INACTIVE(low); CTRL_WRITE(low, high, msrs, i); } } } struct op_x86_model_spec const op_athlon_spec = { .num_counters = NUM_COUNTERS, .num_controls = NUM_CONTROLS, .fill_in_addresses = &athlon_fill_in_addresses, .setup_ctrs = &athlon_setup_ctrs, .check_ctrs = &athlon_check_ctrs, .start = &athlon_start, .stop = &athlon_stop }; --- NEW FILE: op_model_ppro.c --- /** * @file op_model_ppro.h * pentium pro / P6 model-specific MSR operations * * @remark Copyright 2002 OProfile authors * @remark Read the file COPYING * * @author John Levon * @author Philippe Elie * @author Graydon Hoare */ #include "op_x86_model.h" #include "op_msr.h" #define NUM_COUNTERS 2 #define NUM_CONTROLS 2 #define CTR_READ(l,h,msrs,c) do {rdmsr(msrs->counters.addrs[(c)], (l), (h));} while (0) #define CTR_WRITE(l,msrs,c) do {wrmsr(msrs->counters.addrs[(c)], -(u32)(l), -1);} while (0) #define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) #define CTRL_READ(l,h,msrs,c) do {rdmsr((msrs->controls.addrs[(c)]), (l), (h));} while (0) #define CTRL_WRITE(l,h,msrs,c) do {wrmsr((msrs->controls.addrs[(c)]), (l), (h));} while (0) #define CTRL_SET_ACTIVE(n) (n |= (1<<22)) #define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) #define CTRL_CLEAR(x) (x &= (1<<21)) #define CTRL_SET_ENABLE(val) (val |= 1<<20) #define CTRL_SET_USR(val,u) (val |= ((u & 1) << 16)) #define CTRL_SET_KERN(val,k) (val |= ((k & 1) << 17)) #define CTRL_SET_UM(val, m) (val |= (m << 8)) #define CTRL_SET_EVENT(val, e) (val |= e) static void ppro_fill_in_addresses(struct op_msrs * const msrs) { msrs->counters.addrs[0] = MSR_P6_PERFCTR0; msrs->counters.addrs[1] = MSR_P6_PERFCTR1; msrs->controls.addrs[0] = MSR_P6_EVNTSEL0; msrs->controls.addrs[1] = MSR_P6_EVNTSEL1; } static void ppro_setup_ctrs(struct op_msrs const * const msrs) { uint low, high; int i; /* clear all counters */ for (i = 0 ; i < NUM_CONTROLS; ++i) { CTRL_READ(low, high, msrs, i); CTRL_CLEAR(low); CTRL_WRITE(low, high, msrs, i); } /* avoid a false detection of ctr overflows in NMI handler */ for (i = 0; i < NUM_COUNTERS; ++i) { CTR_WRITE(1, msrs, i); } /* enable active counters */ for (i = 0; i < NUM_COUNTERS; ++i) { if (sysctl.ctr[i].event) { CTR_WRITE(sysctl.ctr[i].count, msrs, i); CTRL_READ(low, high, msrs, i); CTRL_CLEAR(low); CTRL_SET_ENABLE(low); CTRL_SET_USR(low, sysctl.ctr[i].user); CTRL_SET_KERN(low, sysctl.ctr[i].kernel); CTRL_SET_UM(low, sysctl.ctr[i].unit_mask); CTRL_SET_EVENT(low, sysctl.ctr[i].event); CTRL_WRITE(low, high, msrs, i); } } } static void ppro_check_ctrs(uint const cpu, struct op_msrs const * const msrs, struct pt_regs * const regs) { ulong low, high; int i; for (i = 0 ; i < NUM_COUNTERS; ++i) { CTR_READ(low, high, msrs, i); if (CTR_OVERFLOWED(low)) { op_do_profile(cpu, regs, i); CTR_WRITE(oprof_data[cpu].ctr_count[i], msrs, i); } } } static void ppro_start(struct op_msrs const * const msrs) { uint low,high; CTRL_READ(low, high, msrs, 0); CTRL_SET_ACTIVE(low); CTRL_WRITE(low, high, msrs, 0); } static void ppro_stop(struct op_msrs const * const msrs) { uint low,high; CTRL_READ(low, high, msrs, 0); CTRL_SET_INACTIVE(low); CTRL_WRITE(low, high, msrs, 0); } struct op_x86_model_spec const op_ppro_spec = { .num_counters = NUM_COUNTERS, .num_controls = NUM_CONTROLS, .fill_in_addresses = &ppro_fill_in_addresses, .setup_ctrs = &ppro_setup_ctrs, .check_ctrs = &ppro_check_ctrs, .start = &ppro_start, .stop = &ppro_stop }; --- NEW FILE: op_x86_model.h --- /** * @file op_x86_model.h * interface to x86 model-specific MSR operations * * @remark Copyright 2002 OProfile authors * @remark Read the file COPYING * * @author Graydon Hoare */ #ifndef OP_X86_MODEL_H #define OP_X86_MODEL_H #include "oprofile.h" struct op_saved_msr { uint high; uint low; }; struct op_msr_group { uint * addrs; struct op_saved_msr * saved; }; struct op_msrs { struct op_msr_group counters; struct op_msr_group controls; }; struct pt_regs; struct op_x86_model_spec { uint const num_counters; uint const num_controls; void (*fill_in_addresses)(struct op_msrs * const msrs); void (*setup_ctrs)(struct op_msrs const * const msrs); void (*check_ctrs)(uint const cpu, struct op_msrs const * const msrs, struct pt_regs * const regs); void (*start)(struct op_msrs const * const msrs); void (*stop)(struct op_msrs const * const msrs); }; extern struct op_x86_model_spec const op_ppro_spec; extern struct op_x86_model_spec const op_athlon_spec; #endif /* OP_X86_MODEL_H */ Index: Makefile.in =================================================================== RCS file: /cvsroot/oprofile/oprofile/module/x86/Makefile.in,v retrieving revision 1.10 retrieving revision 1.11 diff -u -d -r1.10 -r1.11 --- Makefile.in 4 Sep 2002 14:19:37 -0000 1.10 +++ Makefile.in 22 Sep 2002 17:46:00 -0000 1.11 @@ -18,7 +18,8 @@ O_TARGET := arch.o obj-y := cpu_type.o op_apic.o op_fixmap.o op_rtc.o op_nmi.o \ - op_syscalls.o oprofile_nmi.o + op_syscalls.o op_model_ppro.o op_model_athlon.o \ + oprofile_nmi.o obj-m := $(O_TARGET) O_OBJS := $(obj-y) M_OBJS := $(O_TARGET) Index: op_nmi.c =================================================================== RCS file: /cvsroot/oprofile/oprofile/module/x86/op_nmi.c,v retrieving revision 1.13 retrieving revision 1.14 diff -u -d -r1.13 -r1.14 --- op_nmi.c 7 Sep 2002 18:19:39 -0000 1.13 +++ op_nmi.c 22 Sep 2002 17:46:00 -0000 1.14 @@ -14,175 +14,84 @@ #include "op_apic.h" #include "op_events.h" #include "op_util.h" +#include "op_x86_model.h" -/* the MSRs we need */ -static uint perfctr_msr[OP_MAX_COUNTERS]; -static uint eventsel_msr[OP_MAX_COUNTERS]; - -/* number of counters physically present */ -static uint op_nr_counters = 2; - -/* whether we enable for each counter (athlon) or globally (intel) */ -static int separate_running_bit; - -/* ---------------- NMI handler ------------------ */ -/* preempt: all things inside the interrupt handler are preempt safe : we - * never reenable interrupt */ +static struct op_msrs cpu_msrs[OP_MAX_CPUS]; +static struct op_x86_model_spec const * model = NULL; -static void op_check_ctr(uint cpu, struct pt_regs *regs, int ctr) +static struct op_x86_model_spec const * get_model(void) { - ulong l,h; - get_perfctr(l, h, ctr); - if (ctr_overflowed(l)) { - op_do_profile(cpu, regs, ctr); - set_perfctr(oprof_data[cpu].ctr_count[ctr], ctr); + if (!model) { + /* pick out our per-model function table */ + switch (sysctl.cpu_type) { + case CPU_ATHLON: + model = &op_athlon_spec; + break; + + default: + model = &op_ppro_spec; + break; + } } + return model; } + +/* preempt: all things inside the interrupt handler are preempt safe : we + * never reenable interrupt */ asmlinkage void op_do_nmi(struct pt_regs * regs) { - uint cpu = op_cpu_id(); - int i; + uint const cpu = op_cpu_id(); + struct op_msrs const * const msrs = &cpu_msrs[cpu]; - for (i = 0 ; i < op_nr_counters ; ++i) - op_check_ctr(cpu, regs, i); + model->check_ctrs(cpu, msrs, regs); } /* ---------------- PMC setup ------------------ */ -static void pmc_fill_in(uint *val, u8 kernel, u8 user, u8 event, u8 um) +static int pmc_setup_ctr(void * dummy) { - /* enable interrupt generation */ - *val |= (1<<20); - /* enable/disable chosen OS and USR counting */ - (user) ? (*val |= (1<<16)) - : (*val &= ~(1<<16)); - - (kernel) ? (*val |= (1<<17)) - : (*val &= ~(1<<17)); + uint const cpu = op_cpu_id(); + struct op_msrs const * const msrs = &cpu_msrs[cpu]; - /* what are we counting ? */ - *val |= event; - *val |= (um<<8); + get_model()->setup_ctrs(msrs); + return 0; } -static void pmc_setup(void *dummy) -{ - uint low, high; - int i; - - /* IA Vol. 3 Figure 15-3 */ - - /* Stop and clear all counter: IA32 use bit 22 of eventsel_msr0 to - * enable/disable all counter, AMD use separate bit 22 in each msr, - * all bits are cleared except the reserved bits 21 */ - for (i = 0 ; i < op_nr_counters ; ++i) { - rdmsr(eventsel_msr[i], low, high); - wrmsr(eventsel_msr[i], low & (1 << 21), high); - - /* avoid a false detection of ctr overflow in NMI handler */ - wrmsr(perfctr_msr[i], -1, -1); - } - - /* setup each counter */ - for (i = 0 ; i < op_nr_counters ; ++i) { - if (sysctl.ctr[i].event) { - rdmsr(eventsel_msr[i], low, high); - - low &= 1 << 21; /* do not touch the reserved bit */ - set_perfctr(sysctl.ctr[i].count, i); - - pmc_fill_in(&low, sysctl.ctr[i].kernel, sysctl.ctr[i].user, - sysctl.ctr[i].event, sysctl.ctr[i].unit_mask); - - wrmsr(eventsel_msr[i], low, high); - } - } - - /* Here all setup is made except the start/stop bit 22, counter - * disabled contains zeros in the eventsel msr except the reserved bit - * 21 */ -} static int pmc_setup_all(void) { - if ((smp_call_function(pmc_setup, NULL, 0, 1))) + if (smp_call_function(pmc_setup_ctr, NULL, 0, 1)) return -EFAULT; - - pmc_setup(NULL); + pmc_setup_ctr(NULL); return 0; } -inline static void pmc_start_P6(void) -{ - uint low,high; - - rdmsr(eventsel_msr[0], low, high); - wrmsr(eventsel_msr[0], low | (1 << 22), high); -} -inline static void pmc_start_Athlon(void) +static void pmc_start(void * info) { - uint low,high; - int i; - - for (i = 0 ; i < op_nr_counters ; ++i) { - if (sysctl.ctr[i].count) { - rdmsr(eventsel_msr[i], low, high); - wrmsr(eventsel_msr[i], low | (1 << 22), high); - } - } -} + uint const cpu = op_cpu_id(); + struct op_msrs const * const msrs = &cpu_msrs[cpu]; -static void pmc_start(void *info) -{ - if (info && (*((uint *)info) != op_cpu_id())) + if (info && (*((uint *)info) != cpu)) return; - /* assert: all enable counter are setup except the bit start/stop, - * all counter disable contains zeroes (except perhaps the reserved - * bit 21), counter disable contains -1 sign extended in msr count */ - - /* enable all needed counter */ - if (separate_running_bit == 0) - pmc_start_P6(); - else - pmc_start_Athlon(); -} - -inline static void pmc_stop_P6(void) -{ - uint low,high; - - rdmsr(eventsel_msr[0], low, high); - wrmsr(eventsel_msr[0], low & ~(1 << 22), high); + get_model()->start(msrs); } -inline static void pmc_stop_Athlon(void) -{ - uint low,high; - int i; - - for (i = 0 ; i < op_nr_counters ; ++i) { - if (sysctl.ctr[i].count) { - rdmsr(eventsel_msr[i], low, high); - wrmsr(eventsel_msr[i], low & ~(1 << 22), high); - } - } -} static void pmc_stop(void *info) { - if (info && (*((uint *)info) != op_cpu_id())) + uint const cpu = op_cpu_id(); + struct op_msrs const * const msrs = &cpu_msrs[cpu]; + + if (info && (*((uint *)info) != cpu)) return; - /* disable counters */ - if (separate_running_bit == 0) - pmc_stop_P6(); - else - pmc_stop_Athlon(); + get_model()->stop(msrs); } + static void pmc_select_start(uint cpu) { if (cpu == op_cpu_id()) @@ -191,6 +100,7 @@ smp_call_function(pmc_start, &cpu, 0, 1); } + static void pmc_select_stop(uint cpu) { if (cpu == op_cpu_id()) @@ -199,6 +109,7 @@ smp_call_function(pmc_stop, &cpu, 0, 1); } + static void pmc_start_all(void) { int cpu, i; @@ -206,7 +117,7 @@ for (cpu = 0 ; cpu < OP_MAX_CPUS; cpu++) { struct _oprof_data * data = &oprof_data[cpu]; - for (i = 0 ; i < op_nr_counters ; ++i) { + for (i = 0 ; i < get_model()->num_counters ; ++i) { if (sysctl.ctr[i].enabled) data->ctr_count[i] = sysctl.ctr[i].count; else @@ -219,6 +130,7 @@ pmc_start(NULL); } + static void pmc_stop_all(void) { smp_call_function(pmc_stop, NULL, 0, 1); @@ -226,16 +138,17 @@ restore_nmi(); } + static int pmc_check_params(void) { int i; int enabled = 0; int ok = 0; - for (i = 0; i < op_nr_counters ; i++) { + for (i = 0; i < get_model()->num_counters; i++) { int min_count; int ret; - + if (!sysctl.ctr[i].enabled) continue; @@ -280,72 +193,138 @@ return ok; } -static uint saved_perfctr_low[OP_MAX_COUNTERS]; -static uint saved_perfctr_high[OP_MAX_COUNTERS]; -static uint saved_eventsel_low[OP_MAX_COUNTERS]; -static uint saved_eventsel_high[OP_MAX_COUNTERS]; -static int pmc_init(void) +static void free_msr_group(struct op_msr_group * group) { - int i; - int err = 0; + if (group->addrs) + kfree(group->addrs); + if (group->saved) + kfree(group->saved); + group->addrs = NULL; + group->saved = NULL; +} + - if (sysctl.cpu_type == CPU_ATHLON) { - op_nr_counters = 4; - separate_running_bit = 1; +static void pmc_save_registers(void * dummy) +{ + uint i; + uint const cpu = op_cpu_id(); + uint const nr_ctrs = get_model()->num_counters; + uint const nr_ctrls = get_model()->num_controls; + struct op_msr_group * counters = &cpu_msrs[cpu].counters; + struct op_msr_group * controls = &cpu_msrs[cpu].controls; + + counters->addrs = NULL; + counters->saved = NULL; + controls->addrs = NULL; + controls->saved = NULL; + + counters->addrs = kmalloc(nr_ctrs * sizeof(uint), GFP_KERNEL); + if (!counters->addrs) + goto fault; + + counters->saved = kmalloc( + nr_ctrs * sizeof(struct op_saved_msr), GFP_KERNEL); + if (!counters->saved) + goto fault; + + controls->addrs = kmalloc(nr_ctrls * sizeof(uint), GFP_KERNEL); + if (!controls->addrs) + goto fault; + + controls->saved = kmalloc( + nr_ctrls * sizeof(struct op_saved_msr), GFP_KERNEL); + if (!controls->saved) + goto fault; + + model->fill_in_addresses(&cpu_msrs[cpu]); + + for (i = 0; i < nr_ctrs; ++i) { + rdmsr(counters->addrs[i], + counters->saved[i].low, + counters->saved[i].high); } - /* let's use the right MSRs */ - switch (sysctl.cpu_type) { - case CPU_ATHLON: - eventsel_msr[0] = MSR_K7_PERFCTL0; - eventsel_msr[1] = MSR_K7_PERFCTL1; - eventsel_msr[2] = MSR_K7_PERFCTL2; - eventsel_msr[3] = MSR_K7_PERFCTL3; - perfctr_msr[0] = MSR_K7_PERFCTR0; - perfctr_msr[1] = MSR_K7_PERFCTR1; - perfctr_msr[2] = MSR_K7_PERFCTR2; - perfctr_msr[3] = MSR_K7_PERFCTR3; - break; - default: - eventsel_msr[0] = MSR_P6_EVNTSEL0; - eventsel_msr[1] = MSR_P6_EVNTSEL1; - perfctr_msr[0] = MSR_P6_PERFCTR0; - perfctr_msr[1] = MSR_P6_PERFCTR1; - break; + for (i = 0; i < nr_ctrls; ++i) { + rdmsr(controls->addrs[i], + controls->saved[i].low, + controls->saved[i].high); } + return; - for (i = 0 ; i < op_nr_counters ; ++i) { - rdmsr(eventsel_msr[i], saved_eventsel_low[i], saved_eventsel_high[i]); - rdmsr(perfctr_msr[i], saved_perfctr_low[i], saved_perfctr_high[i]); +fault: + free_msr_group(counters); + free_msr_group(controls); +} + + +static void pmc_restore_registers(void * dummy) +{ + uint i; + uint const cpu = op_cpu_id(); + uint const nr_ctrs = get_model()->num_counters; + uint const nr_ctrls = get_model()->num_controls; + struct op_msr_group * counters = &cpu_msrs[cpu].counters; + struct op_msr_group * controls = &cpu_msrs[cpu].controls; + + if (controls->addrs) { + for (i = 0; i < nr_ctrls; ++i) { + wrmsr(controls->addrs[i], + controls->saved[i].low, + controls->saved[i].high); + } } - /* setup each counter */ - if ((err = apic_setup())) + if (counters->addrs) { + for (i = 0; i < nr_ctrs; ++i) { + wrmsr(counters->addrs[i], + counters->saved[i].low, + counters->saved[i].high); + } + } + + free_msr_group(counters); + free_msr_group(controls); +} + + +static int pmc_init(void) +{ + int err = 0; + + if ((err = smp_call_function(pmc_save_registers, NULL, 0, 1))) { goto out; + } + pmc_save_registers(NULL); + + if ((err = apic_setup())) + goto out_restore; if ((err = smp_call_function(lvtpc_apic_setup, NULL, 0, 1))) { lvtpc_apic_restore(NULL); + goto out_restore; } out: return err; +out_restore: + smp_call_function(pmc_restore_registers, NULL, 0, 1); + pmc_restore_registers(NULL); + goto out; } + static void pmc_deinit(void) { - int i; - smp_call_function(lvtpc_apic_restore, NULL, 0, 1); lvtpc_apic_restore(NULL); - for (i = 0 ; i < op_nr_counters ; ++i) { - wrmsr(eventsel_msr[i], saved_eventsel_low[i], saved_eventsel_high[i]); - wrmsr(perfctr_msr[i], saved_perfctr_low[i], saved_perfctr_high[i]); - } - apic_restore(); + + smp_call_function(pmc_restore_registers, NULL, 0, 1); + pmc_restore_registers(NULL); } + static char *names[] = { "0", "1", "2", "3", "4", }; @@ -355,7 +334,8 @@ ctl_table * tab; int i, j; - for (i=0; i < op_nr_counters; i++) { + /* now init the sysctls */ + for (i=0; i < get_model()->num_counters; i++) { next->ctl_name = 1; next->procname = names[i]; next->mode = 0755; @@ -386,16 +366,17 @@ return -EFAULT; } + static void pmc_remove_sysctls(ctl_table * next) { int i; - - for (i=0; i < op_nr_counters; i++) { + for (i=0; i < get_model()->num_counters; i++) { kfree(next->child); next++; } } + static struct op_int_operations op_nmi_ops = { init: pmc_init, deinit: pmc_deinit, @@ -408,6 +389,7 @@ start_cpu: pmc_select_start, stop_cpu: pmc_select_stop, }; + struct op_int_operations const * op_int_interface() { |