The Intel SDM is a little unclear as to precisely how to check for the presence
of the general purpose architectural performance monitoring counters (via the 
'rdpmc' instruction). In one place in the manual, it describes capabilities
based on cpu family (using family names like "Intel Core Duo, Intel Core Solo,
Nehalem"), and in another place it advises using information gathered from
CPUID function 0xa to determine the actual level of support.

It appears that pctr(4) always assumed the presence of at least one general
purpose counter (eg, "version 1" support) based on checking CPU family and
model IDs (followning the SDM's model and CPU family descriptions). KVM, on
the other hand, can disable the feature entirely based on a decision it makes
when inspecting host platform features. Therefore, on certain host machines,
pctr(1) would panic the machine since KVM would fail the rdpmc instruction.

This diff changes the pctr(4) detection mechanism and only enables 'rdpmc'
if the CPUID function 0xa actually says version 1 or later is supported.
This should not have any impact on non-virtualized environments, and it 
resolves the panic reported earlier. Obviously, if KVM doesn't emulate the
counters on a given host, you don't get those features via pctr(1) either.
The diff retains the previous detection method for AMD cpus.

This was reported about a month ago by Hiltjo Posthuma who has also been
helping me with testing. Thanks.

PS - note that pctr(1)/pctr(4) have other known issues in MP environments,
and this diff does nothing to fix those issues.

ok?

-ml

Index: arch/amd64/amd64/pctr.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/pctr.c,v
retrieving revision 1.6
diff -u -p -a -u -r1.6 pctr.c
--- arch/amd64/amd64/pctr.c     14 Mar 2015 03:38:46 -0000      1.6
+++ arch/amd64/amd64/pctr.c     13 Apr 2016 12:45:25 -0000
@@ -38,13 +38,16 @@
 
 #define PCTR_AMD_NUM   PCTR_NUM
 #define PCTR_INTEL_NUM 2               /* Intel supports only 2 counters */
+#define PCTR_INTEL_VERSION_MASK 0xff
 
 #define usetsc         (cpu_feature & CPUID_TSC)
-#define usepctr                ((pctr_isamd || pctr_isintel) && \
-                           ((cpu_id >> 8) & 15) >= 6)
+#define usepctr                ((pctr_isamd && ((cpu_id >> 8) & 15) >= 6) || \
+                           (pctr_isintel && \
+                           (pctr_intel_cap & PCTR_INTEL_VERSION_MASK) >= 1))
 
 int                    pctr_isamd;
 int                    pctr_isintel;
+uint32_t               pctr_intel_cap;
 
 static void            pctrrd(struct pctrst *);
 static int             pctrsel(int, u_int32_t, u_int32_t);
@@ -68,13 +71,16 @@ pctrrd(struct pctrst *st)
 void
 pctrattach(int num)
 {
+       uint32_t dummy;
 
        if (num > 1)
                return;
 
        pctr_isamd = (strcmp(cpu_vendor, "AuthenticAMD") == 0);
-       if (!pctr_isamd)
+       if (!pctr_isamd) {
                pctr_isintel = (strcmp(cpu_vendor, "GenuineIntel") == 0);
+               CPUID(0xa, pctr_intel_cap, dummy, dummy, dummy);
+       }
 
        if (usepctr) {
                /* Enable RDTSC and RDPMC instructions from user-level. */
Index: arch/i386/i386/pctr.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/pctr.c,v
retrieving revision 1.27
diff -u -p -a -u -r1.27 pctr.c
--- arch/i386/i386/pctr.c       29 Mar 2014 18:09:29 -0000      1.27
+++ arch/i386/i386/pctr.c       13 Apr 2016 12:59:37 -0000
@@ -24,20 +24,22 @@
 
 #define PCTR_AMD_NUM   PCTR_NUM
 #define PCTR_INTEL_NUM 2               /* Intel supports only 2 counters */
+#define PCTR_INTEL_VERSION_MASK 0xff
 
 #define usetsc         (cpu_feature & CPUID_TSC)
 #define usep5ctr       (pctr_isintel && (((cpu_id >> 8) & 15) == 5) && \
                                (((cpu_id >> 4) & 15) > 0))
-#define usepctr                ((pctr_isamd || pctr_isintel) && \
-                           ((cpu_id >> 8) & 15) >= 6)
+#define usepctr                ((pctr_isamd && ((cpu_id >> 8) & 15) >= 6) || \
+                           (pctr_isintel && \
+                           (pctr_intel_cap & PCTR_INTEL_VERSION_MASK) >= 1))
 
 int                    pctr_isamd;
 int                    pctr_isintel;
+uint32_t               pctr_intel_cap;
 
 static int             p5ctrsel(int fflag, u_int cmd, u_int fn);
 static int             pctrsel(int fflag, u_int cmd, u_int fn);
 static void            pctrrd(struct pctrst *);
-static void            pctrrd(struct pctrst *);
 
 static void
 p5ctrrd(struct pctrst *st)
@@ -73,13 +75,16 @@ pctrrd(struct pctrst *st)
 void
 pctrattach(int num)
 {
+       uint32_t dummy;
 
        if (num > 1)
                return;
 
        pctr_isamd = (strcmp(cpu_vendor, "AuthenticAMD") == 0);
-       if (!pctr_isamd)
+       if (!pctr_isamd) {
                pctr_isintel = (strcmp(cpu_vendor, "GenuineIntel") == 0);
+               CPUID(0xa, pctr_intel_cap, dummy, dummy, dummy);
+       }
 
        if (usepctr) {
                /* Enable RDTSC and RDPMC instructions from user-level. */

Reply via email to