linux/arch/x86/events/msr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/perf_event.h>
   3#include <asm/intel-family.h>
   4
   5enum perf_msr_id {
   6        PERF_MSR_TSC                    = 0,
   7        PERF_MSR_APERF                  = 1,
   8        PERF_MSR_MPERF                  = 2,
   9        PERF_MSR_PPERF                  = 3,
  10        PERF_MSR_SMI                    = 4,
  11        PERF_MSR_PTSC                   = 5,
  12        PERF_MSR_IRPERF                 = 6,
  13        PERF_MSR_THERM                  = 7,
  14        PERF_MSR_THERM_SNAP             = 8,
  15        PERF_MSR_THERM_UNIT             = 9,
  16        PERF_MSR_EVENT_MAX,
  17};
  18
  19static bool test_aperfmperf(int idx)
  20{
  21        return boot_cpu_has(X86_FEATURE_APERFMPERF);
  22}
  23
  24static bool test_ptsc(int idx)
  25{
  26        return boot_cpu_has(X86_FEATURE_PTSC);
  27}
  28
  29static bool test_irperf(int idx)
  30{
  31        return boot_cpu_has(X86_FEATURE_IRPERF);
  32}
  33
  34static bool test_therm_status(int idx)
  35{
  36        return boot_cpu_has(X86_FEATURE_DTHERM);
  37}
  38
  39static bool test_intel(int idx)
  40{
  41        if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
  42            boot_cpu_data.x86 != 6)
  43                return false;
  44
  45        switch (boot_cpu_data.x86_model) {
  46        case INTEL_FAM6_NEHALEM:
  47        case INTEL_FAM6_NEHALEM_G:
  48        case INTEL_FAM6_NEHALEM_EP:
  49        case INTEL_FAM6_NEHALEM_EX:
  50
  51        case INTEL_FAM6_WESTMERE:
  52        case INTEL_FAM6_WESTMERE_EP:
  53        case INTEL_FAM6_WESTMERE_EX:
  54
  55        case INTEL_FAM6_SANDYBRIDGE:
  56        case INTEL_FAM6_SANDYBRIDGE_X:
  57
  58        case INTEL_FAM6_IVYBRIDGE:
  59        case INTEL_FAM6_IVYBRIDGE_X:
  60
  61        case INTEL_FAM6_HASWELL_CORE:
  62        case INTEL_FAM6_HASWELL_X:
  63        case INTEL_FAM6_HASWELL_ULT:
  64        case INTEL_FAM6_HASWELL_GT3E:
  65
  66        case INTEL_FAM6_BROADWELL_CORE:
  67        case INTEL_FAM6_BROADWELL_XEON_D:
  68        case INTEL_FAM6_BROADWELL_GT3E:
  69        case INTEL_FAM6_BROADWELL_X:
  70
  71        case INTEL_FAM6_ATOM_SILVERMONT1:
  72        case INTEL_FAM6_ATOM_SILVERMONT2:
  73        case INTEL_FAM6_ATOM_AIRMONT:
  74
  75        case INTEL_FAM6_ATOM_GOLDMONT:
  76        case INTEL_FAM6_ATOM_DENVERTON:
  77
  78        case INTEL_FAM6_ATOM_GEMINI_LAKE:
  79
  80        case INTEL_FAM6_XEON_PHI_KNL:
  81        case INTEL_FAM6_XEON_PHI_KNM:
  82                if (idx == PERF_MSR_SMI)
  83                        return true;
  84                break;
  85
  86        case INTEL_FAM6_SKYLAKE_MOBILE:
  87        case INTEL_FAM6_SKYLAKE_DESKTOP:
  88        case INTEL_FAM6_SKYLAKE_X:
  89        case INTEL_FAM6_KABYLAKE_MOBILE:
  90        case INTEL_FAM6_KABYLAKE_DESKTOP:
  91                if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF)
  92                        return true;
  93                break;
  94        }
  95
  96        return false;
  97}
  98
  99struct perf_msr {
 100        u64     msr;
 101        struct  perf_pmu_events_attr *attr;
 102        bool    (*test)(int idx);
 103};
 104
 105PMU_EVENT_ATTR_STRING(tsc,                              evattr_tsc,             "event=0x00"    );
 106PMU_EVENT_ATTR_STRING(aperf,                            evattr_aperf,           "event=0x01"    );
 107PMU_EVENT_ATTR_STRING(mperf,                            evattr_mperf,           "event=0x02"    );
 108PMU_EVENT_ATTR_STRING(pperf,                            evattr_pperf,           "event=0x03"    );
 109PMU_EVENT_ATTR_STRING(smi,                              evattr_smi,             "event=0x04"    );
 110PMU_EVENT_ATTR_STRING(ptsc,                             evattr_ptsc,            "event=0x05"    );
 111PMU_EVENT_ATTR_STRING(irperf,                           evattr_irperf,          "event=0x06"    );
 112PMU_EVENT_ATTR_STRING(cpu_thermal_margin,               evattr_therm,           "event=0x07"    );
 113PMU_EVENT_ATTR_STRING(cpu_thermal_margin.snapshot,      evattr_therm_snap,      "1"             );
 114PMU_EVENT_ATTR_STRING(cpu_thermal_margin.unit,          evattr_therm_unit,      "C"             );
 115
 116static struct perf_msr msr[] = {
 117        [PERF_MSR_TSC]          = { 0,                          &evattr_tsc,            NULL,                   },
 118        [PERF_MSR_APERF]        = { MSR_IA32_APERF,             &evattr_aperf,          test_aperfmperf,        },
 119        [PERF_MSR_MPERF]        = { MSR_IA32_MPERF,             &evattr_mperf,          test_aperfmperf,        },
 120        [PERF_MSR_PPERF]        = { MSR_PPERF,                  &evattr_pperf,          test_intel,             },
 121        [PERF_MSR_SMI]          = { MSR_SMI_COUNT,              &evattr_smi,            test_intel,             },
 122        [PERF_MSR_PTSC]         = { MSR_F15H_PTSC,              &evattr_ptsc,           test_ptsc,              },
 123        [PERF_MSR_IRPERF]       = { MSR_F17H_IRPERF,            &evattr_irperf,         test_irperf,            },
 124        [PERF_MSR_THERM]        = { MSR_IA32_THERM_STATUS,      &evattr_therm,          test_therm_status,      },
 125        [PERF_MSR_THERM_SNAP]   = { MSR_IA32_THERM_STATUS,      &evattr_therm_snap,     test_therm_status,      },
 126        [PERF_MSR_THERM_UNIT]   = { MSR_IA32_THERM_STATUS,      &evattr_therm_unit,     test_therm_status,      },
 127};
 128
 129static struct attribute *events_attrs[PERF_MSR_EVENT_MAX + 1] = {
 130        NULL,
 131};
 132
 133static struct attribute_group events_attr_group = {
 134        .name = "events",
 135        .attrs = events_attrs,
 136};
 137
 138PMU_FORMAT_ATTR(event, "config:0-63");
 139static struct attribute *format_attrs[] = {
 140        &format_attr_event.attr,
 141        NULL,
 142};
 143static struct attribute_group format_attr_group = {
 144        .name = "format",
 145        .attrs = format_attrs,
 146};
 147
 148static const struct attribute_group *attr_groups[] = {
 149        &events_attr_group,
 150        &format_attr_group,
 151        NULL,
 152};
 153
 154static int msr_event_init(struct perf_event *event)
 155{
 156        u64 cfg = event->attr.config;
 157
 158        if (event->attr.type != event->pmu->type)
 159                return -ENOENT;
 160
 161        if (cfg >= PERF_MSR_EVENT_MAX)
 162                return -EINVAL;
 163
 164        /* unsupported modes and filters */
 165        if (event->attr.exclude_user   ||
 166            event->attr.exclude_kernel ||
 167            event->attr.exclude_hv     ||
 168            event->attr.exclude_idle   ||
 169            event->attr.exclude_host   ||
 170            event->attr.exclude_guest  ||
 171            event->attr.sample_period) /* no sampling */
 172                return -EINVAL;
 173
 174        if (!msr[cfg].attr)
 175                return -EINVAL;
 176
 177        event->hw.idx           = -1;
 178        event->hw.event_base    = msr[cfg].msr;
 179        event->hw.config        = cfg;
 180
 181        return 0;
 182}
 183
 184static inline u64 msr_read_counter(struct perf_event *event)
 185{
 186        u64 now;
 187
 188        if (event->hw.event_base)
 189                rdmsrl(event->hw.event_base, now);
 190        else
 191                rdtscll(now);
 192
 193        return now;
 194}
 195static void msr_event_update(struct perf_event *event)
 196{
 197        u64 prev, now;
 198        s64 delta;
 199
 200        /* Careful, an NMI might modify the previous event value: */
 201again:
 202        prev = local64_read(&event->hw.prev_count);
 203        now = msr_read_counter(event);
 204
 205        if (local64_cmpxchg(&event->hw.prev_count, prev, now) != prev)
 206                goto again;
 207
 208        delta = now - prev;
 209        if (unlikely(event->hw.event_base == MSR_SMI_COUNT)) {
 210                delta = sign_extend64(delta, 31);
 211                local64_add(delta, &event->count);
 212        } else if (unlikely(event->hw.event_base == MSR_IA32_THERM_STATUS)) {
 213                /* If valid, extract digital readout, otherwise set to -1: */
 214                now = now & (1ULL << 31) ? (now >> 16) & 0x3f :  -1;
 215                local64_set(&event->count, now);
 216        } else {
 217                local64_add(delta, &event->count);
 218        }
 219}
 220
 221static void msr_event_start(struct perf_event *event, int flags)
 222{
 223        u64 now = msr_read_counter(event);
 224
 225        local64_set(&event->hw.prev_count, now);
 226}
 227
 228static void msr_event_stop(struct perf_event *event, int flags)
 229{
 230        msr_event_update(event);
 231}
 232
 233static void msr_event_del(struct perf_event *event, int flags)
 234{
 235        msr_event_stop(event, PERF_EF_UPDATE);
 236}
 237
 238static int msr_event_add(struct perf_event *event, int flags)
 239{
 240        if (flags & PERF_EF_START)
 241                msr_event_start(event, flags);
 242
 243        return 0;
 244}
 245
 246static struct pmu pmu_msr = {
 247        .task_ctx_nr    = perf_sw_context,
 248        .attr_groups    = attr_groups,
 249        .event_init     = msr_event_init,
 250        .add            = msr_event_add,
 251        .del            = msr_event_del,
 252        .start          = msr_event_start,
 253        .stop           = msr_event_stop,
 254        .read           = msr_event_update,
 255        .capabilities   = PERF_PMU_CAP_NO_INTERRUPT,
 256};
 257
 258static int __init msr_init(void)
 259{
 260        int i, j = 0;
 261
 262        if (!boot_cpu_has(X86_FEATURE_TSC)) {
 263                pr_cont("no MSR PMU driver.\n");
 264                return 0;
 265        }
 266
 267        /* Probe the MSRs. */
 268        for (i = PERF_MSR_TSC + 1; i < PERF_MSR_EVENT_MAX; i++) {
 269                u64 val;
 270
 271                /* Virt sucks; you cannot tell if a R/O MSR is present :/ */
 272                if (!msr[i].test(i) || rdmsrl_safe(msr[i].msr, &val))
 273                        msr[i].attr = NULL;
 274        }
 275
 276        /* List remaining MSRs in the sysfs attrs. */
 277        for (i = 0; i < PERF_MSR_EVENT_MAX; i++) {
 278                if (msr[i].attr)
 279                        events_attrs[j++] = &msr[i].attr->attr.attr;
 280        }
 281        events_attrs[j] = NULL;
 282
 283        perf_pmu_register(&pmu_msr, "msr", -1);
 284
 285        return 0;
 286}
 287device_initcall(msr_init);
 288