The branch main has been updated by imp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=00c0a1f0bf6c07e63384a389060dfc10924c0ed6

commit 00c0a1f0bf6c07e63384a389060dfc10924c0ed6
Author:     Ali Mashtizadeh <[email protected]>
AuthorDate: 2026-02-21 19:07:26 +0000
Commit:     Warner Losh <[email protected]>
CommitDate: 2026-02-27 21:19:40 +0000

    hwpmc: Fix PMC flags for AMD Zen cores
    
    The PMC flags available for DF and L3 counters were not all implemented.
    More importantly, the field encodings for the L3 counters changed in an
    incompatible way between Family 17h and Family 19h.  Similarly, the
    field encodings for the DF coutners changed between Family 19h and 1Ah.
    I also added the precise retire flag for the 3rd core counter.
    
    Lastly, I added a warning in the jevent parser because ignoring the
    unknown fields results in counters incorrectly programmed.  We should
    not just ignore that.
    
    Sponsored by: Netflix
    
    Reviewed by: imp
    Pull Request: https://github.com/freebsd/freebsd-src/pull/2040
---
 lib/libpmc/libpmc_pmu_util.c    | 71 ++++++++++++++++++++++++++++---
 lib/libpmc/pmu-events/jevents.c | 40 +++++++++++++++++-
 lib/libpmc/pmu-events/json.c    | 14 +++++++
 lib/libpmc/pmu-events/json.h    |  1 +
 sys/dev/hwpmc/hwpmc_amd.c       | 19 ++++++++-
 sys/dev/hwpmc/hwpmc_amd.h       | 93 +++++++++++++++++++++++++++++------------
 6 files changed, 204 insertions(+), 34 deletions(-)

diff --git a/lib/libpmc/libpmc_pmu_util.c b/lib/libpmc/libpmc_pmu_util.c
index de642aa71a18..0832ab32e2f1 100644
--- a/lib/libpmc/libpmc_pmu_util.c
+++ b/lib/libpmc/libpmc_pmu_util.c
@@ -121,6 +121,24 @@ pmu_events_mfr(void)
        return (mfr);
 }
 
+static int
+pmu_events_x86_family(void)
+{
+       char buf[PMC_CPUID_LEN];
+       size_t s = sizeof(buf);
+       char *cpuid, *family;
+
+       if (sysctlbyname("kern.hwpmc.cpuid", buf, &s,
+           (void *)NULL, 0) == -1)
+               return (-1);
+       cpuid = &buf[0];
+
+       strsep(&cpuid, "-");
+       family = strsep(&cpuid, "-");
+
+       return (strtol(family, NULL, 10));
+}
+
 /*
  *  The Intel fixed mode counters are:
  *     "inst_retired.any",
@@ -208,6 +226,10 @@ struct pmu_event_desc {
        uint64_t ped_offcore_rsp;
        uint64_t ped_l3_thread;
        uint64_t ped_l3_slice;
+       uint32_t ped_sourceid;
+       uint32_t ped_coreid;
+       uint32_t ped_allsources;
+       uint32_t ped_allcores;
        uint32_t ped_event;
        uint32_t ped_frontend;
        uint32_t ped_ldlat;
@@ -347,6 +369,14 @@ pmu_parse_event(struct pmu_event_desc *ped, const char 
*eventin)
                        ped->ped_l3_thread = strtol(value, NULL, 16);
                else if (strcmp(key, "l3_slice_mask") == 0)
                        ped->ped_l3_slice = strtol(value, NULL, 16);
+               else if (strcmp(key, "sourceid") == 0)
+                       ped->ped_sourceid = strtol(value, NULL, 16);
+               else if (strcmp(key, "coreid") == 0)
+                       ped->ped_coreid = strtol(value, NULL, 16);
+               else if (strcmp(key, "allcores") == 0)
+                       ped->ped_allcores = strtol(value, NULL, 10);
+               else if (strcmp(key, "allsources") == 0)
+                       ped->ped_allsources = strtol(value, NULL, 10);
                else {
                        debug = getenv("PMUDEBUG");
                        if (debug != NULL && strcmp(debug, "true") == 0 && 
value != NULL)
@@ -486,20 +516,23 @@ static int
 pmc_pmu_amd_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm,
        struct pmu_event_desc *ped)
 {
+       int cpu_family;
        struct pmc_md_amd_op_pmcallocate *amd;
        const struct pmu_event *pe;
        int idx = -1;
 
+       cpu_family = pmu_events_x86_family();
+
        amd = &pm->pm_md.pm_amd;
        if (ped->ped_umask > 0) {
                pm->pm_caps |= PMC_CAP_QUALIFIER;
-               amd->pm_amd_config |= AMD_PMC_TO_UNITMASK(ped->ped_umask);
        }
        pm->pm_class = PMC_CLASS_K8;
        pe = pmu_event_get(NULL, event_name, &idx);
 
        if (pe->pmu == NULL) {
                amd->pm_amd_config |= AMD_PMC_TO_EVENTMASK(ped->ped_event);
+               amd->pm_amd_config |= AMD_PMC_TO_UNITMASK(ped->ped_umask);
                amd->pm_amd_sub_class = PMC_AMD_SUB_CLASS_CORE;
                if ((pm->pm_caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 0 ||
                        (pm->pm_caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) ==
@@ -515,14 +548,42 @@ pmc_pmu_amd_pmcallocate(const char *event_name, struct 
pmc_op_pmcallocate *pm,
                        amd->pm_amd_config |= AMD_PMC_INVERT;
                if (pm->pm_caps & PMC_CAP_INTERRUPT)
                        amd->pm_amd_config |= AMD_PMC_INT;
+               if (pm->pm_caps & PMC_CAP_PRECISE)
+                       amd->pm_amd_config |= AMD_PMC_PRECISERETIRE;
        } else if (strcmp("amd_l3", pe->pmu) == 0) {
-               amd->pm_amd_config |= AMD_PMC_TO_EVENTMASK(ped->ped_event);
+               amd->pm_amd_config |= AMD_PMC_L3_TO_EVENTMASK(ped->ped_event);
+               amd->pm_amd_config |= AMD_PMC_L3_TO_UNITMASK(ped->ped_umask);
                amd->pm_amd_sub_class = PMC_AMD_SUB_CLASS_L3_CACHE;
-               amd->pm_amd_config |= AMD_PMC_TO_L3SLICE(ped->ped_l3_slice);
-               amd->pm_amd_config |= AMD_PMC_TO_L3CORE(ped->ped_l3_thread);
+               if (cpu_family <= 0x17) {
+                       amd->pm_amd_config |=
+                           AMD_PMC_L31_TO_SLICE(ped->ped_l3_slice);
+                       amd->pm_amd_config |=
+                           AMD_PMC_L31_TO_CORE(ped->ped_l3_thread);
+               } else {
+                       amd->pm_amd_config |=
+                           AMD_PMC_L32_TO_THREAD(ped->ped_l3_thread);
+                       amd->pm_amd_config |=
+                           AMD_PMC_L32_TO_SOURCEID(ped->ped_sourceid);
+                       amd->pm_amd_config |=
+                           AMD_PMC_L32_TO_COREID(ped->ped_coreid);
+                       if (ped->ped_allcores)
+                               amd->pm_amd_config |= AMD_PMC_L32_ALLCORES;
+                       if (ped->ped_allsources)
+                               amd->pm_amd_config |= AMD_PMC_L32_ALLSOURCES;
+               }
        } else if (strcmp("amd_df", pe->pmu) == 0) {
-               amd->pm_amd_config |= AMD_PMC_TO_EVENTMASK_DF(ped->ped_event);
                amd->pm_amd_sub_class = PMC_AMD_SUB_CLASS_DATA_FABRIC;
+               if (cpu_family <= 19) {
+                       amd->pm_amd_config |=
+                           AMD_PMC_DF1_TO_EVENTMASK(ped->ped_event);
+                       amd->pm_amd_config |=
+                           AMD_PMC_DF1_TO_UNITMASK(ped->ped_umask);
+               } else {
+                       amd->pm_amd_config |=
+                           AMD_PMC_DF2_TO_EVENTMASK(ped->ped_event);
+                       amd->pm_amd_config |=
+                           AMD_PMC_DF2_TO_UNITMASK(ped->ped_umask);
+               }
        } else {
                printf("PMC pmu '%s' is not supported!\n", pe->pmu);
                return (EOPNOTSUPP);
diff --git a/lib/libpmc/pmu-events/jevents.c b/lib/libpmc/pmu-events/jevents.c
index 628ed26c6f9d..4e4f53a0c0d0 100644
--- a/lib/libpmc/pmu-events/jevents.c
+++ b/lib/libpmc/pmu-events/jevents.c
@@ -560,6 +560,10 @@ static int json_events(const char *fn,
                jsmntok_t *obj = tok++;
                bool configcode_present = false;
                char *umask = NULL;
+               char *allcores = NULL;
+               char *allslices = NULL;
+               char *sliceid = NULL;
+               char *threadmask = NULL;
                char *cmask = NULL;
                char *inv = NULL;
                char *any = NULL;
@@ -585,6 +589,22 @@ static int json_events(const char *fn,
                        /* match_field */
                        if (json_streq(map, field, "UMask") && nz) {
                                addfield(map, &umask, "", "umask=", val);
+                       } else if (json_streq(map, field, "EnAllCores") && nz) {
+                               addfield(map, &allcores, "", "allcores=", val);
+                       } else if (json_streq(map, field, "EnAllSlices") && nz) 
{
+                               addfield(map, &allslices, "", "allslices=", 
val);
+                       } else if (json_streq(map, field, "SliceId") && nz) {
+                               /*
+                                * We use sourceid because there's a
+                                * descripency where the JSON from linux calls
+                                * it a SliceId, which is not the name used by
+                                * AMD in the PPRs.  The field name from Family
+                                * 19h and below that calls it slicemask see
+                                * the references in hwpmc_amd.h.
+                                */
+                               addfield(map, &sliceid, "", "sourceid=", val);
+                       } else if (json_streq(map, field, "ThreadMask") && nz) {
+                               addfield(map, &threadmask, "", 
"l3_thread_mask=", val);
                        } else if (json_streq(map, field, "CounterMask") && nz) 
{
                                addfield(map, &cmask, "", "cmask=", val);
                        } else if (json_streq(map, field, "Invert") && nz) {
@@ -675,8 +695,14 @@ static int json_events(const char *fn,
                                addfield(map, &arch_std, "", "", val);
                                for (s = arch_std; *s; s++)
                                        *s = tolower(*s);
+                       } else {
+                               /*
+                                * We shouldn't ignore unknown fields that
+                                * makes the counter invalid!
+                                */
+                               json_copystr(map, field, buf, sizeof(buf));
+                               fprintf(stderr, "Unknown field '%s'!\n", buf);
                        }
-                       /* ignore unknown fields */
                }
                if (precise && je.desc && !strstr(je.desc, "(Precise Event)")) {
                        if (json_streq(map, precise, "2"))
@@ -707,6 +733,14 @@ static int json_events(const char *fn,
                        addfield(map, &event, ",", period, NULL);
                if (umask)
                        addfield(map, &event, ",", umask, NULL);
+               if (allcores)
+                       addfield(map, &event, ",", allcores, NULL);
+               if (allslices)
+                       addfield(map, &event, ",", allslices, NULL);
+               if (sliceid)
+                       addfield(map, &event, ",", sliceid, NULL);
+               if (threadmask)
+                       addfield(map, &event, ",", threadmask, NULL);
 
                if (je.desc && extra_desc)
                        addfield(map, &je.desc, " ", extra_desc, NULL);
@@ -737,6 +771,10 @@ static int json_events(const char *fn,
                err = func(data, &je);
 free_strings:
                free(umask);
+               free(allcores);
+               free(allslices);
+               free(sliceid);
+               free(threadmask);
                free(cmask);
                free(inv);
                free(any);
diff --git a/lib/libpmc/pmu-events/json.c b/lib/libpmc/pmu-events/json.c
index 89cafbc04fb6..66eaf0a74ba6 100644
--- a/lib/libpmc/pmu-events/json.c
+++ b/lib/libpmc/pmu-events/json.c
@@ -160,3 +160,17 @@ int json_streq(char *map, jsmntok_t *t, const char *s)
        unsigned len = json_len(t);
        return len == strlen(s) && !strncasecmp(map + t->start, s, len);
 }
+
+int json_copystr(char *map, jsmntok_t *t, char *s, int len)
+{
+       int jlen;
+
+       jlen = json_len(t);
+       if (jlen > len)
+               jlen = len - 1;
+
+       memcpy(s, map + t->start, jlen);
+       s[jlen] = '\0';
+
+       return (jlen);
+}
diff --git a/lib/libpmc/pmu-events/json.h b/lib/libpmc/pmu-events/json.h
index 278ebd32cfb6..89b9c2fba617 100644
--- a/lib/libpmc/pmu-events/json.h
+++ b/lib/libpmc/pmu-events/json.h
@@ -9,6 +9,7 @@ int json_line(char *map, jsmntok_t *t);
 const char *json_name(jsmntok_t *t);
 int json_streq(char *map, jsmntok_t *t, const char *s);
 int json_len(jsmntok_t *t);
+int json_copystr(char *map, jsmntok_t *t, char *s, int len);
 
 extern int verbose;
 
diff --git a/sys/dev/hwpmc/hwpmc_amd.c b/sys/dev/hwpmc/hwpmc_amd.c
index 801b75b39595..b34cbffcffa8 100644
--- a/sys/dev/hwpmc/hwpmc_amd.c
+++ b/sys/dev/hwpmc/hwpmc_amd.c
@@ -347,6 +347,10 @@ amd_allocate_pmc(int cpu __unused, int ri, struct pmc *pm,
 
        caps = pm->pm_caps;
 
+       if (((caps & PMC_CAP_PRECISE) != 0) &&
+           ((pd->pd_caps & PMC_CAP_PRECISE) == 0))
+               return (EINVAL);
+
        PMCDBG2(MDP, ALL, 1,"amd-allocate ri=%d caps=0x%x", ri, caps);
 
        /* Validate sub-class. */
@@ -360,6 +364,9 @@ amd_allocate_pmc(int cpu __unused, int ri, struct pmc *pm,
                return (0);
        }
 
+       /*
+        * Everything below this is for supporting older processors.
+        */
        pe = a->pm_ev;
 
        /* map ev to the correct event mask code */
@@ -817,6 +824,14 @@ pmc_amd_initialize(void)
                    "K8-%d", i);
                d->pm_descr.pd_class = PMC_CLASS_K8;
                d->pm_descr.pd_caps = AMD_PMC_CAPS;
+               /*
+                * Zen 5 can precisely count retire events.
+                *
+                * Refer to PPR Vol 1 for AMD Family 1Ah Model 02h C1 57238
+                * Rev. 0.24 September 29, 2024.
+                */
+               if ((family >= 0x1a) && (i == 2))
+                       d->pm_descr.pd_caps |= PMC_CAP_PRECISE;
                d->pm_descr.pd_width = 48;
                if ((amd_feature2 & AMDID2_PCXC) != 0) {
                        d->pm_evsel = AMD_PMC_CORE_BASE + 2 * i;
@@ -836,7 +851,7 @@ pmc_amd_initialize(void)
                        snprintf(d->pm_descr.pd_name, PMC_NAME_MAX,
                            "K8-L3-%d", i);
                        d->pm_descr.pd_class = PMC_CLASS_K8;
-                       d->pm_descr.pd_caps = AMD_PMC_CAPS;
+                       d->pm_descr.pd_caps = AMD_PMC_L3_CAPS;
                        d->pm_descr.pd_width = 48;
                        d->pm_evsel = AMD_PMC_L3_BASE + 2 * i;
                        d->pm_perfctr = AMD_PMC_L3_BASE + 2 * i + 1;
@@ -852,7 +867,7 @@ pmc_amd_initialize(void)
                        snprintf(d->pm_descr.pd_name, PMC_NAME_MAX,
                            "K8-DF-%d", i);
                        d->pm_descr.pd_class = PMC_CLASS_K8;
-                       d->pm_descr.pd_caps = AMD_PMC_CAPS;
+                       d->pm_descr.pd_caps = AMD_PMC_DF_CAPS;
                        d->pm_descr.pd_width = 48;
                        d->pm_evsel = AMD_PMC_DF_BASE + 2 * i;
                        d->pm_perfctr = AMD_PMC_DF_BASE + 2 * i + 1;
diff --git a/sys/dev/hwpmc/hwpmc_amd.h b/sys/dev/hwpmc/hwpmc_amd.h
index be484a1111a2..6d8ab8203942 100644
--- a/sys/dev/hwpmc/hwpmc_amd.h
+++ b/sys/dev/hwpmc/hwpmc_amd.h
@@ -62,21 +62,10 @@
 #define        AMD_PMC_CORE_DEFAULT    6
 #define        AMD_PMC_CORE_MAX        16
 
-/* L3 */
-#define        AMD_PMC_L3_BASE         0xC0010230
-#define        AMD_PMC_L3_DEFAULT      6
-#define        AMD_PMC_L3_MAX          6
-
-/* DF */
-#define        AMD_PMC_DF_BASE         0xC0010240
-#define        AMD_PMC_DF_DEFAULT      4
-#define        AMD_PMC_DF_MAX          64
-
-#define        AMD_NPMCS_K8            4
-#define AMD_NPMCS_MAX          (AMD_PMC_CORE_MAX + AMD_PMC_L3_MAX + \
-                                AMD_PMC_DF_MAX)
-
 #define        AMD_PMC_COUNTERMASK     0xFF000000
+#define AMD_PMC_PRECISERETIRE  (1ULL << 43) /* Only valid for PERF_CTL2 */
+#define        AMD_PMC_HOST            (1ULL << 41)
+#define        AMD_PMC_GUEST           (1ULL << 40)
 #define        AMD_PMC_TO_COUNTER(x)   (((x) << 24) & AMD_PMC_COUNTERMASK)
 #define        AMD_PMC_INVERT          (1 << 23)
 #define        AMD_PMC_ENABLE          (1 << 22)
@@ -85,24 +74,13 @@
 #define        AMD_PMC_EDGE            (1 << 18)
 #define        AMD_PMC_OS              (1 << 17)
 #define        AMD_PMC_USR             (1 << 16)
-#define        AMD_PMC_L3SLICEMASK     (0x000F000000000000)
-#define        AMD_PMC_L3COREMASK      (0xFF00000000000000)
-#define        AMD_PMC_TO_L3SLICE(x)   (((x) << 48) & AMD_PMC_L3SLICEMASK)
-#define        AMD_PMC_TO_L3CORE(x)    (((x) << 56) & AMD_PMC_L3COREMASK)
-
-#define        AMD_PMC_UNITMASK_M      0x10
-#define        AMD_PMC_UNITMASK_O      0x08
-#define        AMD_PMC_UNITMASK_E      0x04
-#define        AMD_PMC_UNITMASK_S      0x02
-#define        AMD_PMC_UNITMASK_I      0x01
-#define        AMD_PMC_UNITMASK_MOESI  0x1F
 
 #define        AMD_PMC_UNITMASK        0xFF00
 #define        AMD_PMC_EVENTMASK       0xF000000FF
 
 #define        AMD_PMC_TO_UNITMASK(x)  (((x) << 8) & AMD_PMC_UNITMASK)
 #define        AMD_PMC_TO_EVENTMASK(x) (((x) & 0xFF) | (((uint64_t)(x) & 
0xF00) << 24))
-#define        AMD_PMC_TO_EVENTMASK_DF(x)      (((x) & 0xFF) | (((uint64_t)(x) 
& 0x0F00) << 24)) | (((uint64_t)(x) & 0x3000) << 47)
+
 #define        AMD_VALID_BITS          (AMD_PMC_COUNTERMASK | AMD_PMC_INVERT | 
\
        AMD_PMC_ENABLE | AMD_PMC_INT | AMD_PMC_PC | AMD_PMC_EDGE |      \
        AMD_PMC_OS | AMD_PMC_USR | AMD_PMC_UNITMASK | AMD_PMC_EVENTMASK)
@@ -111,6 +89,69 @@
        PMC_CAP_SYSTEM | PMC_CAP_EDGE | PMC_CAP_THRESHOLD |             \
        PMC_CAP_READ | PMC_CAP_WRITE | PMC_CAP_INVERT | PMC_CAP_QUALIFIER)
 
+/* L3 */
+#define        AMD_PMC_L3_BASE         0xC0010230
+#define        AMD_PMC_L3_DEFAULT      6
+#define        AMD_PMC_L3_MAX          6
+
+/*
+ * L3 counters change their encoding slightly between Family 17h and Family 19h
+ * processors.
+ *
+ * Refer to the following documents for the L3 fields:
+ * PPR for AMD Family 17h Model 20h A1 55772-A1 Rev. 3.08 April 14, 2021
+ * PPR for AMD Family 19h Model 51h A1 56569-A1 Rev. 3.03 September 21, 2021
+ * PPR for AMD Family 1Ah Model 02h C1 57238 Rev. 0.24 September 29, 2024
+ */
+#define        AMD_PMC_L31_SLICEMASK   (0x000F000000000000ULL)
+#define        AMD_PMC_L31_COREMASK    (0xFF00000000000000ULL)
+
+#define        AMD_PMC_L31_TO_SLICE(x) (((uint64_t)(x) << 48) & 
AMD_PMC_L31_SLICEMASK)
+#define        AMD_PMC_L31_TO_CORE(x)  (((uint64_t)(x) << 56) & 
AMD_PMC_L31_COREMASK)
+
+#define        AMD_PMC_L32_THREADMASK  (0x0F00000000000000ULL)
+#define        AMD_PMC_L32_SOURCEMASK  (0x0007000000000000ULL)
+#define        AMD_PMC_L32_ALLCORES    (1ULL << 47)
+#define        AMD_PMC_L32_ALLSOURCES  (1ULL << 46)
+#define        AMD_PMC_L32_COREMASK    (0x00001C0000000000ULL)
+
+#define        AMD_PMC_L32_TO_THREAD(x) (((uint64_t)(x) << 56) & 
AMD_PMC_L32_THREADMASK)
+#define        AMD_PMC_L32_TO_SOURCEID(x) (((uint64_t)(x) << 48) & 
AMD_PMC_L32_SOURCEMASK)
+#define        AMD_PMC_L32_TO_COREID(x) (((uint64_t)(x) << 42) & 
AMD_PMC_L32_COREMASK)
+
+#define        AMD_PMC_L3_TO_UNITMASK(x)       (((x) << 8) & AMD_PMC_UNITMASK)
+#define        AMD_PMC_L3_TO_EVENTMASK(x)      ((x) & 0xFF)
+
+#define AMD_PMC_L3_CAPS                (PMC_CAP_READ | PMC_CAP_WRITE | \
+       PMC_CAP_QUALIFIER | PMC_CAP_DOMWIDE)
+
+/* DF */
+#define        AMD_PMC_DF_BASE         0xC0010240
+#define        AMD_PMC_DF_DEFAULT      4
+#define        AMD_PMC_DF_MAX          64
+
+#define AMD_PMC_DF_CAPS                (PMC_CAP_READ | PMC_CAP_WRITE | \
+       PMC_CAP_QUALIFIER | PMC_CAP_DOMWIDE)
+
+/*
+ * DF counters change their encoding between Family 19h and Family 1Ah
+ * processors.
+ *
+ * Refer to the same documents as the L3 counters.
+ */
+#define        AMD_PMC_DF1_TO_EVENTMASK(x)     (((x) & 0xFF) | \
+       (((uint64_t)(x) & 0x0F00) << 24) | (((uint64_t)(x) & 0x3000) << 47))
+#define AMD_PMC_DF1_TO_UNITMASK(x)     (((x) & 0xFF) << 8)
+
+#define        AMD_PMC_DF2_TO_EVENTMASK(x)     (((x) & 0xFF) | \
+       (((uint64_t)(x) & 0x7F00) << 24))
+#define AMD_PMC_DF2_TO_UNITMASK(x)     ((((x) & 0xFF) << 8) | \
+       (((uint64_t)(x) & 0x0F00) << 16))
+
+#define        AMD_NPMCS_K8            4
+#define AMD_NPMCS_MAX          (AMD_PMC_CORE_MAX + AMD_PMC_L3_MAX + \
+                                AMD_PMC_DF_MAX)
+
 #define AMD_PMC_IS_STOPPED(evsel) ((rdmsr((evsel)) & AMD_PMC_ENABLE) == 0)
 #define AMD_PMC_HAS_OVERFLOWED(pmc) ((rdpmc(pmc) & (1ULL << 47)) == 0)
 

Reply via email to