diff options
author | Keir Fraser <keir.fraser@citrix.com> | 2009-09-15 10:03:16 +0100 |
---|---|---|
committer | Keir Fraser <keir.fraser@citrix.com> | 2009-09-15 10:03:16 +0100 |
commit | 3aa88456d12d6180300b923880c68234b95ec82c (patch) | |
tree | a333b931b7c246185ad247f84fd080e9872f69c7 /xen/arch/x86/oprofile | |
parent | ec5446eae22d8ace3b67d0752d3620098d68fe7f (diff) | |
download | xen-3aa88456d12d6180300b923880c68234b95ec82c.tar.gz xen-3aa88456d12d6180300b923880c68234b95ec82c.tar.bz2 xen-3aa88456d12d6180300b923880c68234b95ec82c.zip |
xenoprof: support Intel's architectural perfmon registers.
One benefit is that more perfmon counters can be used on Nehalem.
Signed-off-by: Yang Zhang <yang.zhang@intel.com>
Signed-off-by: Yang Xiaowei <xiaowei.yang@intel.com>
Diffstat (limited to 'xen/arch/x86/oprofile')
-rw-r--r-- | xen/arch/x86/oprofile/nmi_int.c | 31 | ||||
-rw-r--r-- | xen/arch/x86/oprofile/op_model_ppro.c | 143 | ||||
-rw-r--r-- | xen/arch/x86/oprofile/op_x86_model.h | 8 |
3 files changed, 134 insertions, 48 deletions
diff --git a/xen/arch/x86/oprofile/nmi_int.c b/xen/arch/x86/oprofile/nmi_int.c index 7ab1573364..d05d7a360c 100644 --- a/xen/arch/x86/oprofile/nmi_int.c +++ b/xen/arch/x86/oprofile/nmi_int.c @@ -374,18 +374,15 @@ static int __init ppro_init(char ** cpu_type) ppro_has_global_ctrl = 1; break; case 26: + arch_perfmon_setup_counters(); *cpu_type = "i386/core_i7"; ppro_has_global_ctrl = 1; break; case 28: *cpu_type = "i386/atom"; - ppro_has_global_ctrl = 1; break; default: /* Unknown */ - printk("xenoprof: Initialization failed. " - "Intel processor model %d for P6 class family is not " - "supported\n", cpu_model); return 0; } @@ -393,10 +390,21 @@ static int __init ppro_init(char ** cpu_type) return 1; } +static int __init arch_perfmon_init(char **cpu_type) +{ + if (!cpu_has_arch_perfmon) + return 0; + *cpu_type = "i386/arch_perfmon"; + model = &op_arch_perfmon_spec; + arch_perfmon_setup_counters(); + return 1; +} + static int __init nmi_init(void) { __u8 vendor = current_cpu_data.x86_vendor; __u8 family = current_cpu_data.x86; + __u8 _model = current_cpu_data.x86_model; if (!cpu_has_apic) { printk("xenoprof: Initialization failed. No APIC\n"); @@ -438,21 +446,22 @@ static int __init nmi_init(void) switch (family) { /* Pentium IV */ case 0xf: - if (!p4_init(&cpu_type)) - return -ENODEV; + p4_init(&cpu_type); break; /* A P6-class processor */ case 6: - if (!ppro_init(&cpu_type)) - return -ENODEV; + ppro_init(&cpu_type); break; default: + break; + } + if (!cpu_type && !arch_perfmon_init(&cpu_type)) { printk("xenoprof: Initialization failed. " - "Intel processor family %d is not " - "supported\n", family); - return -ENODEV; + "Intel processor family %d model %d" + "is not supported\n", family, _model); + return -ENODEV; } break; diff --git a/xen/arch/x86/oprofile/op_model_ppro.c b/xen/arch/x86/oprofile/op_model_ppro.c index 0dc962dfe6..0bd90ec2ff 100644 --- a/xen/arch/x86/oprofile/op_model_ppro.c +++ b/xen/arch/x86/oprofile/op_model_ppro.c @@ -24,12 +24,24 @@ #include "op_x86_model.h" #include "op_counter.h" -#define NUM_COUNTERS 2 -#define NUM_CONTROLS 2 +/* + * Intel "Architectural Performance Monitoring" CPUID + * detection/enumeration details: + */ +union cpuid10_eax { + struct { + unsigned int version_id:8; + unsigned int num_counters:8; + unsigned int bit_width:8; + unsigned int mask_length:8; + } split; + unsigned int full; +}; + +static int num_counters = 2; +static int counter_width = 32; -#define CTR_READ(l,h,msrs,c) do {rdmsr(msrs->counters[(c)].addr, (l), (h));} while (0) -#define CTR_WRITE(l,msrs,c) do {wrmsr(msrs->counters[(c)].addr, -(u32)(l), -1);} while (0) -#define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) +#define CTR_OVERFLOWED(n) (!((n) & (1ULL<<(counter_width-1)))) #define CTRL_READ(l,h,msrs,c) do {rdmsr((msrs->controls[(c)].addr), (l), (h));} while (0) #define CTRL_WRITE(l,h,msrs,c) do {wrmsr((msrs->controls[(c)].addr), (l), (h));} while (0) @@ -43,17 +55,18 @@ #define CTRL_SET_EVENT(val, e) (val |= e) #define IS_ACTIVE(val) (val & (1 << 22) ) #define IS_ENABLE(val) (val & (1 << 20) ) -static unsigned long reset_value[NUM_COUNTERS]; +static unsigned long reset_value[OP_MAX_COUNTER]; int ppro_has_global_ctrl = 0; extern int is_passive(struct domain *d); static void ppro_fill_in_addresses(struct op_msrs * const msrs) { - msrs->counters[0].addr = MSR_P6_PERFCTR0; - msrs->counters[1].addr = MSR_P6_PERFCTR1; - - msrs->controls[0].addr = MSR_P6_EVNTSEL0; - msrs->controls[1].addr = MSR_P6_EVNTSEL1; + int i; + + for (i = 0; i < num_counters; i++) + msrs->counters[i].addr = MSR_P6_PERFCTR0 + i; + for (i = 0; i < num_counters; i++) + msrs->controls[i].addr = MSR_P6_EVNTSEL0 + i; } @@ -61,25 +74,41 @@ static void ppro_setup_ctrs(struct op_msrs const * const msrs) { unsigned int low, high; int i; + + if (cpu_has_arch_perfmon) { + union cpuid10_eax eax; + eax.full = cpuid_eax(0xa); + + /* + * For Core2 (family 6, model 15), don't reset the + * counter width: + */ + if (!(eax.split.version_id == 0 && + current_cpu_data.x86 == 6 && + current_cpu_data.x86_model == 15)) { + + if (counter_width < eax.split.bit_width) + counter_width = eax.split.bit_width; + } + } /* clear all counters */ - for (i = 0 ; i < NUM_CONTROLS; ++i) { + for (i = 0 ; i < num_counters; ++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); - } + for (i = 0; i < num_counters; ++i) + wrmsrl(msrs->counters[i].addr, -1LL); /* enable active counters */ - for (i = 0; i < NUM_COUNTERS; ++i) { + for (i = 0; i < num_counters; ++i) { if (counter_config[i].enabled) { reset_value[i] = counter_config[i].count; - CTR_WRITE(counter_config[i].count, msrs, i); + wrmsrl(msrs->counters[i].addr, -reset_value[i]); CTRL_READ(low, high, msrs, i); CTRL_CLEAR(low); @@ -89,6 +118,8 @@ static void ppro_setup_ctrs(struct op_msrs const * const msrs) CTRL_SET_UM(low, counter_config[i].unit_mask); CTRL_SET_EVENT(low, counter_config[i].event); CTRL_WRITE(low, high, msrs, i); + } else { + reset_value[i] = 0; } } } @@ -102,26 +133,26 @@ static int ppro_check_ctrs(unsigned int const cpu, struct op_msrs const * const msrs, struct cpu_user_regs * const regs) { - unsigned int low, high; + u64 val; int i; int ovf = 0; unsigned long eip = regs->eip; int mode = xenoprofile_get_mode(current, regs); struct arch_msr_pair *msrs_content = vcpu_vpmu(current)->context; - for (i = 0 ; i < NUM_COUNTERS; ++i) { + for (i = 0 ; i < num_counters; ++i) { if (!reset_value[i]) continue; - CTR_READ(low, high, msrs, i); - if (CTR_OVERFLOWED(low)) { + rdmsrl(msrs->counters[i].addr, val); + if (CTR_OVERFLOWED(val)) { xenoprof_log_event(current, regs, eip, mode, i); - CTR_WRITE(reset_value[i], msrs, i); + wrmsrl(msrs->counters[i].addr, -reset_value[i]); if ( is_passive(current->domain) && (mode != 2) && (vcpu_vpmu(current)->flags & PASSIVE_DOMAIN_ALLOCATED) ) { if ( IS_ACTIVE(msrs_content[i].control) ) { - msrs_content[i].counter = (low | (u64)high << 32); + msrs_content[i].counter = val; if ( IS_ENABLE(msrs_content[i].control) ) ovf = 2; } @@ -144,7 +175,7 @@ static void ppro_start(struct op_msrs const * const msrs) unsigned int low,high; int i; - for (i = 0; i < NUM_COUNTERS; ++i) { + for (i = 0; i < num_counters; ++i) { if (reset_value[i]) { CTRL_READ(low, high, msrs, i); CTRL_SET_ACTIVE(low); @@ -155,7 +186,7 @@ static void ppro_start(struct op_msrs const * const msrs) * However, this may not hold true when xenoprof starts to run. */ if ( ppro_has_global_ctrl ) - wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, (1<<NUM_COUNTERS) - 1); + wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, (1<<num_counters) - 1); } @@ -164,7 +195,7 @@ static void ppro_stop(struct op_msrs const * const msrs) unsigned int low,high; int i; - for (i = 0; i < NUM_COUNTERS; ++i) { + for (i = 0; i < num_counters; ++i) { if (!reset_value[i]) continue; CTRL_READ(low, high, msrs, i); @@ -178,14 +209,14 @@ static void ppro_stop(struct op_msrs const * const msrs) static int ppro_is_arch_pmu_msr(u64 msr_index, int *type, int *index) { if ( (msr_index >= MSR_IA32_PERFCTR0) && - (msr_index < (MSR_IA32_PERFCTR0 + NUM_COUNTERS)) ) + (msr_index < (MSR_IA32_PERFCTR0 + num_counters)) ) { *type = MSR_TYPE_ARCH_COUNTER; *index = msr_index - MSR_IA32_PERFCTR0; return 1; } if ( (msr_index >= MSR_P6_EVNTSEL0) && - (msr_index < (MSR_P6_EVNTSEL0 + NUM_CONTROLS)) ) + (msr_index < (MSR_P6_EVNTSEL0 + num_counters)) ) { *type = MSR_TYPE_ARCH_CTRL; *index = msr_index - MSR_P6_EVNTSEL0; @@ -199,11 +230,11 @@ static int ppro_allocate_msr(struct vcpu *v) { struct vpmu_struct *vpmu = vcpu_vpmu(v); struct arch_msr_pair *msr_content; - - msr_content = xmalloc_bytes( sizeof(struct arch_msr_pair) * NUM_COUNTERS ); + + msr_content = xmalloc_bytes( sizeof(struct arch_msr_pair) * num_counters ); if ( !msr_content ) goto out; - memset(msr_content, 0, sizeof(struct arch_msr_pair) * NUM_COUNTERS); + memset(msr_content, 0, sizeof(struct arch_msr_pair) * num_counters); vpmu->context = (void *)msr_content; vpmu->flags = 0; vpmu->flags |= PASSIVE_DOMAIN_ALLOCATED; @@ -254,9 +285,53 @@ static void ppro_save_msr(struct vcpu *v, int type, int index, u64 msr_content) } } -struct op_x86_model_spec const op_ppro_spec = { - .num_counters = NUM_COUNTERS, - .num_controls = NUM_CONTROLS, +/* + * Architectural performance monitoring. + * + * Newer Intel CPUs (Core1+) have support for architectural + * events described in CPUID 0xA. See the IA32 SDM Vol3b.18 for details. + * The advantage of this is that it can be done without knowing about + * the specific CPU. + */ +void arch_perfmon_setup_counters(void) +{ + union cpuid10_eax eax; + + eax.full = cpuid_eax(0xa); + + /* Workaround for BIOS bugs in 6/15. Taken from perfmon2 */ + if (eax.split.version_id == 0 && current_cpu_data.x86 == 6 && + current_cpu_data.x86_model == 15) { + eax.split.version_id = 2; + eax.split.num_counters = 2; + eax.split.bit_width = 40; + } + + num_counters = min_t(u8, eax.split.num_counters, OP_MAX_COUNTER); + + op_arch_perfmon_spec.num_counters = num_counters; + op_arch_perfmon_spec.num_controls = num_counters; + op_ppro_spec.num_counters = num_counters; + op_ppro_spec.num_controls = num_counters; +} + +struct op_x86_model_spec op_ppro_spec = { + .num_counters = 2, + .num_controls = 2, + .fill_in_addresses = &ppro_fill_in_addresses, + .setup_ctrs = &ppro_setup_ctrs, + .check_ctrs = &ppro_check_ctrs, + .start = &ppro_start, + .stop = &ppro_stop, + .is_arch_pmu_msr = &ppro_is_arch_pmu_msr, + .allocated_msr = &ppro_allocate_msr, + .free_msr = &ppro_free_msr, + .load_msr = &ppro_load_msr, + .save_msr = &ppro_save_msr +}; + +struct op_x86_model_spec op_arch_perfmon_spec = { + /* num_counters/num_controls filled in at runtime */ .fill_in_addresses = &ppro_fill_in_addresses, .setup_ctrs = &ppro_setup_ctrs, .check_ctrs = &ppro_check_ctrs, diff --git a/xen/arch/x86/oprofile/op_x86_model.h b/xen/arch/x86/oprofile/op_x86_model.h index 8568bd7f25..7ac709cb6b 100644 --- a/xen/arch/x86/oprofile/op_x86_model.h +++ b/xen/arch/x86/oprofile/op_x86_model.h @@ -32,8 +32,8 @@ struct pt_regs; * various x86 CPU model's perfctr support. */ struct op_x86_model_spec { - unsigned int const num_counters; - unsigned int const num_controls; + unsigned int num_counters; + unsigned int num_controls; void (*fill_in_addresses)(struct op_msrs * const msrs); void (*setup_ctrs)(struct op_msrs const * const msrs); int (*check_ctrs)(unsigned int const cpu, @@ -48,9 +48,11 @@ struct op_x86_model_spec { void (*save_msr)(struct vcpu * const v, int type, int index, u64 msr_content); }; -extern struct op_x86_model_spec const op_ppro_spec; +extern struct op_x86_model_spec op_ppro_spec; +extern struct op_x86_model_spec op_arch_perfmon_spec; extern struct op_x86_model_spec const op_p4_spec; extern struct op_x86_model_spec const op_p4_ht2_spec; extern struct op_x86_model_spec const op_athlon_spec; +void arch_perfmon_setup_counters(void); #endif /* OP_X86_MODEL_H */ |