From: Naga Chumbalkar <nagananda.chumbalkar@hp.com>
Subject: detect oprofile counters reserved by bios
References: FATE#307426
Patch-mainline: pending
Signed-off-by: Tony Jones <tonyj@suse.de>

Currently, oprofile fails silently on platforms where a non-OS entity such as 
the system firmware "enables" and uses a performance counter. The patch below 
suggests a workaround to the user in such instances.

The patch below has been well tested on AMD and Intel based platforms. It 
improves on a previous version via better macro usage, use of rdmsrl and more
accurate/informative error messages.

Signed-off-by: Naga Chumbalkar <nagananda.chumbalkar@hp.com>
Tested-by: Shashi Belur <shashi-kiran.belur@hp.com>

---
---
 arch/x86/oprofile/nmi_int.c |   58 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

--- a/arch/x86/oprofile/nmi_int.c
+++ b/arch/x86/oprofile/nmi_int.c
@@ -27,6 +27,19 @@
 #include "op_counter.h"
 #include "op_x86_model.h"
 
+static const char RSVD_MSG[] =
+	KERN_INFO "oprofile: performance counter #%d may already be"
+	" reserved."
+	" For counter #%d, EvntSel 0x%lx has value: 0x%llx.\n";
+static const char PMC_MSG[] =
+	KERN_INFO "oprofile: if oprofile doesn't collect data, then"
+	" try using a different performance counter on your platform"
+	" to monitor the desired event."
+	" Delete counter #%d from the desired event by editing the"
+	" /usr/share/oprofile/%s/<cpu>/events file."
+	" If the event cannot be monitored by any other counter,"
+	" contact your hardware or BIOS vendor.\n";
+
 static struct op_x86_model_spec *model;
 static DEFINE_PER_CPU(struct op_msrs, cpu_msrs);
 static DEFINE_PER_CPU(unsigned long, saved_lvtpc);
@@ -466,6 +479,50 @@ static struct notifier_block oprofile_cp
 	.notifier_call = oprofile_cpu_notifier
 };
 
+#define P4_CCCR_ENABLE	(1 << 12)
+
+/* check if the counter/evtsel is already enabled, say, by firmware */
+static void nmi_is_counter_enabled(struct op_msrs * const msrs)
+{
+	__u8 vendor = boot_cpu_data.x86_vendor;
+	__u8 family = boot_cpu_data.x86;
+	u64 val;
+	unsigned int i;
+	char *arch = "arch";
+
+	/* Fill in at least the "arch" value to help the user */
+	if (vendor == X86_VENDOR_AMD) {
+		if (family == 6)
+			arch = "i386";
+		else
+			arch = "x86-64";
+	} else if (vendor == X86_VENDOR_INTEL) {
+		arch = "i386";
+	}
+
+	for (i = 0; i < model->num_controls; ++i) {
+		if (!counter_config[i].enabled)
+			continue;
+		rdmsrl(msrs->controls[i].addr, val);
+
+		/* P4 is special. Other Intel, and all AMD CPUs
+		** are consistent in using "bit 22" as "enable"
+		*/
+		if ((vendor == X86_VENDOR_INTEL) && (family == 0xf)) {
+			if (val & P4_CCCR_ENABLE)
+				goto err_rsvd;
+		} else if (val & ARCH_PERFMON_EVENTSEL_ENABLE) {
+			goto err_rsvd;
+		}
+	}
+	return;
+
+err_rsvd:
+	printk(RSVD_MSG, i, i, msrs->controls[i].addr, val);
+	printk(PMC_MSG, i, arch);
+	return;
+}
+
 static int nmi_setup(void)
 {
 	int err = 0;
@@ -483,6 +540,7 @@ static int nmi_setup(void)
 	if (err)
 		goto fail;
 
+	nmi_is_counter_enabled(&per_cpu(cpu_msrs, 0));
 	for_each_possible_cpu(cpu) {
 		if (!cpu)
 			continue;
