diff options
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event.c')
| -rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 89 | 
1 files changed, 68 insertions, 21 deletions
| diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index bf0f01aea99..1025f3c99d2 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -180,8 +180,9 @@ static void release_pmc_hardware(void) {}  static bool check_hw_exists(void)  { -	u64 val, val_new = ~0; -	int i, reg, ret = 0; +	u64 val, val_fail, val_new= ~0; +	int i, reg, reg_fail, ret = 0; +	int bios_fail = 0;  	/*  	 * Check to see if the BIOS enabled any of the counters, if so @@ -192,8 +193,11 @@ static bool check_hw_exists(void)  		ret = rdmsrl_safe(reg, &val);  		if (ret)  			goto msr_fail; -		if (val & ARCH_PERFMON_EVENTSEL_ENABLE) -			goto bios_fail; +		if (val & ARCH_PERFMON_EVENTSEL_ENABLE) { +			bios_fail = 1; +			val_fail = val; +			reg_fail = reg; +		}  	}  	if (x86_pmu.num_counters_fixed) { @@ -202,8 +206,11 @@ static bool check_hw_exists(void)  		if (ret)  			goto msr_fail;  		for (i = 0; i < x86_pmu.num_counters_fixed; i++) { -			if (val & (0x03 << i*4)) -				goto bios_fail; +			if (val & (0x03 << i*4)) { +				bios_fail = 1; +				val_fail = val; +				reg_fail = reg; +			}  		}  	} @@ -221,14 +228,13 @@ static bool check_hw_exists(void)  	if (ret || val != val_new)  		goto msr_fail; -	return true; - -bios_fail:  	/*  	 * We still allow the PMU driver to operate:  	 */ -	printk(KERN_CONT "Broken BIOS detected, complain to your hardware vendor.\n"); -	printk(KERN_ERR FW_BUG "the BIOS has corrupted hw-PMU resources (MSR %x is %Lx)\n", reg, val); +	if (bios_fail) { +		printk(KERN_CONT "Broken BIOS detected, complain to your hardware vendor.\n"); +		printk(KERN_ERR FW_BUG "the BIOS has corrupted hw-PMU resources (MSR %x is %Lx)\n", reg_fail, val_fail); +	}  	return true; @@ -1316,9 +1322,16 @@ static struct attribute_group x86_pmu_format_group = {   */  static void __init filter_events(struct attribute **attrs)  { +	struct device_attribute *d; +	struct perf_pmu_events_attr *pmu_attr;  	int i, j;  	for (i = 0; attrs[i]; i++) { +		d = (struct device_attribute *)attrs[i]; +		pmu_attr = container_of(d, struct perf_pmu_events_attr, attr); +		/* str trumps id */ +		if (pmu_attr->event_str) +			continue;  		if (x86_pmu.event_map(i))  			continue; @@ -1330,22 +1343,45 @@ static void __init filter_events(struct attribute **attrs)  	}  } -static ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr, +/* Merge two pointer arrays */ +static __init struct attribute **merge_attr(struct attribute **a, struct attribute **b) +{ +	struct attribute **new; +	int j, i; + +	for (j = 0; a[j]; j++) +		; +	for (i = 0; b[i]; i++) +		j++; +	j++; + +	new = kmalloc(sizeof(struct attribute *) * j, GFP_KERNEL); +	if (!new) +		return NULL; + +	j = 0; +	for (i = 0; a[i]; i++) +		new[j++] = a[i]; +	for (i = 0; b[i]; i++) +		new[j++] = b[i]; +	new[j] = NULL; + +	return new; +} + +ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr,  			  char *page)  {  	struct perf_pmu_events_attr *pmu_attr = \  		container_of(attr, struct perf_pmu_events_attr, attr); -  	u64 config = x86_pmu.event_map(pmu_attr->id); -	return x86_pmu.events_sysfs_show(page, config); -} -#define EVENT_VAR(_id)  event_attr_##_id -#define EVENT_PTR(_id) &event_attr_##_id.attr.attr +	/* string trumps id */ +	if (pmu_attr->event_str) +		return sprintf(page, "%s", pmu_attr->event_str); -#define EVENT_ATTR(_name, _id)						\ -	PMU_EVENT_ATTR(_name, EVENT_VAR(_id), PERF_COUNT_HW_##_id,	\ -			events_sysfs_show) +	return x86_pmu.events_sysfs_show(page, config); +}  EVENT_ATTR(cpu-cycles,			CPU_CYCLES		);  EVENT_ATTR(instructions,		INSTRUCTIONS		); @@ -1459,16 +1495,27 @@ static int __init init_hw_perf_events(void)  	unconstrained = (struct event_constraint)  		__EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1, -				   0, x86_pmu.num_counters, 0); +				   0, x86_pmu.num_counters, 0, 0);  	x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */  	x86_pmu_format_group.attrs = x86_pmu.format_attrs; +	if (x86_pmu.event_attrs) +		x86_pmu_events_group.attrs = x86_pmu.event_attrs; +  	if (!x86_pmu.events_sysfs_show)  		x86_pmu_events_group.attrs = &empty_attrs;  	else  		filter_events(x86_pmu_events_group.attrs); +	if (x86_pmu.cpu_events) { +		struct attribute **tmp; + +		tmp = merge_attr(x86_pmu_events_group.attrs, x86_pmu.cpu_events); +		if (!WARN_ON(!tmp)) +			x86_pmu_events_group.attrs = tmp; +	} +  	pr_info("... version:                %d\n",     x86_pmu.version);  	pr_info("... bit width:              %d\n",     x86_pmu.cntval_bits);  	pr_info("... generic registers:      %d\n",     x86_pmu.num_counters); |