x86: Detect and handle missing perf support
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 16 Dec 2015 22:14:36 +0000 (17:14 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 16 Dec 2015 22:52:28 +0000 (17:52 -0500)
If a machine has perf version 0, which is the case for my Qemu, we'll get a
GPF during initialization.  The per core initialization and any accesses to
the Qperf file will abort if we don't have the right version.

This assumes that if open of a Qperf fails, that there is no other way for
the user to trigger access to the perf MSRs.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/devarch.c
kern/arch/x86/init.c
kern/arch/x86/perfmon.c
kern/arch/x86/perfmon.h
kern/arch/x86/smp_boot.c

index 6549891..04dba60 100644 (file)
@@ -475,6 +475,8 @@ static struct chan *archopen(struct chan *c, int omode)
        c = devopen(c, omode, archdir, Qmax, devgen);
        switch ((uint32_t) c->qid.path) {
                case Qperf:
+                       if (!perfmon_supported())
+                               error(ENODEV, "perf is not supported");
                        assert(!c->aux);
                        c->aux = arch_create_perf_context();
                        break;
index fe35276..a208048 100644 (file)
@@ -80,6 +80,7 @@ void arch_init()
        save_fp_state(&x86_default_fpu); /* used in arch/trap.h for fpu init */
        pci_init();
        vmm_init();
+       perfmon_global_init();
        // this returns when all other cores are done and ready to receive IPIs
        #ifdef CONFIG_SINGLE_CORE
                smp_percpu_init();
@@ -88,7 +89,6 @@ void arch_init()
        #endif
        proc_init();
 
-       perfmon_init();
        cons_irq_init();
        intel_lpc_init();
 #ifdef CONFIG_ENABLE_LEGACY_USB
index 810be3c..d506b80 100644 (file)
@@ -61,7 +61,6 @@ static void perfmon_read_cpu_caps(struct perfmon_cpu_caps *pcc)
        pcc->counters_x_proc = (a >> 8) & 0xff;
        pcc->bits_x_fix_counter = (d >> 5) & 0xff;
        pcc->fix_counters_x_proc = d & 0x1f;
-       wmb_f();
        pcc->perfmon_version = a & 0xff;
 }
 
@@ -291,23 +290,25 @@ static void perfmon_arm_irq(void)
        write_mmreg32(LAPIC_LVT_PERFMON, IdtLAPIC_PCINT);
 }
 
-void perfmon_init(void)
+bool perfmon_supported(void)
+{
+       return cpu_caps.perfmon_version >= 2;
+}
+
+void perfmon_global_init(void)
+{
+       perfmon_read_cpu_caps(&cpu_caps);
+}
+
+void perfmon_pcpu_init(void)
 {
        int i;
 
+       if (!perfmon_supported())
+               return;
        /* Enable user level access to the performance counters */
        lcr4(rcr4() | CR4_PCE);
 
-       /* This will be called from every core, no need to execute more than once.
-        * All the call to perfmon_init() will be done when the core boots, so
-        * they will be no perfmon users calling it, while perfmon_read_cpu_caps()
-        * is executing.
-        * All the cores will be writing the same values, so even from that POV,
-        * no serialization is required.
-        */
-       if (cpu_caps.perfmon_version == 0)
-               perfmon_read_cpu_caps(&cpu_caps);
-
        /* Reset all the counters and selectors to zero.
         */
        write_msr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
index a655098..822b96a 100644 (file)
@@ -49,7 +49,9 @@ struct perfmon_status {
        uint64_t cores_values[0];
 };
 
-void perfmon_init(void);
+bool perfmon_supported(void);
+void perfmon_global_init(void);
+void perfmon_pcpu_init(void);
 void perfmon_interrupt(struct hw_trapframe *hw_tf, void *data);
 void perfmon_get_cpu_caps(struct perfmon_cpu_caps *pcc);
 int perfmon_open_event(const struct core_set *cset, struct perfmon_session *ps,
index 7f6f4d9..e248d9e 100644 (file)
@@ -302,7 +302,7 @@ void __arch_pcpu_init(uint32_t coreid)
        assert(read_msr(MSR_KERN_GS_BASE) == (uint64_t)pcpui);
        /* Don't try setting up til after setting GS */
        x86_sysenter_init(x86_get_stacktop_tss(pcpui->tss));
-       /* need to init perfctr before potentiall using it in timer handler */
-       perfmon_init();
+       /* need to init perfctr before potentially using it in timer handler */
+       perfmon_pcpu_init();
        vmm_pcpu_init();
 }