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. */