LCOV - code coverage report
Current view: top level - arch/x86/events - msr.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 15 67 22.4 %
Date: 2022-04-01 13:59:58 Functions: 6 13 46.2 %
Branches: 2 29 6.9 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : #include <linux/perf_event.h>
       3                 :            : #include <linux/sysfs.h>
       4                 :            : #include <linux/nospec.h>
       5                 :            : #include <asm/intel-family.h>
       6                 :            : #include "probe.h"
       7                 :            : 
       8                 :            : enum perf_msr_id {
       9                 :            :         PERF_MSR_TSC                    = 0,
      10                 :            :         PERF_MSR_APERF                  = 1,
      11                 :            :         PERF_MSR_MPERF                  = 2,
      12                 :            :         PERF_MSR_PPERF                  = 3,
      13                 :            :         PERF_MSR_SMI                    = 4,
      14                 :            :         PERF_MSR_PTSC                   = 5,
      15                 :            :         PERF_MSR_IRPERF                 = 6,
      16                 :            :         PERF_MSR_THERM                  = 7,
      17                 :            :         PERF_MSR_EVENT_MAX,
      18                 :            : };
      19                 :            : 
      20                 :        156 : static bool test_aperfmperf(int idx, void *data)
      21                 :            : {
      22                 :        156 :         return boot_cpu_has(X86_FEATURE_APERFMPERF);
      23                 :            : }
      24                 :            : 
      25                 :         78 : static bool test_ptsc(int idx, void *data)
      26                 :            : {
      27                 :         78 :         return boot_cpu_has(X86_FEATURE_PTSC);
      28                 :            : }
      29                 :            : 
      30                 :         78 : static bool test_irperf(int idx, void *data)
      31                 :            : {
      32                 :         78 :         return boot_cpu_has(X86_FEATURE_IRPERF);
      33                 :            : }
      34                 :            : 
      35                 :         78 : static bool test_therm_status(int idx, void *data)
      36                 :            : {
      37                 :         78 :         return boot_cpu_has(X86_FEATURE_DTHERM);
      38                 :            : }
      39                 :            : 
      40                 :        156 : static bool test_intel(int idx, void *data)
      41                 :            : {
      42         [ -  + ]:        156 :         if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
      43                 :            :             boot_cpu_data.x86 != 6)
      44                 :            :                 return false;
      45                 :            : 
      46      [ #  #  # ]:          0 :         switch (boot_cpu_data.x86_model) {
      47                 :          0 :         case INTEL_FAM6_NEHALEM:
      48                 :            :         case INTEL_FAM6_NEHALEM_G:
      49                 :            :         case INTEL_FAM6_NEHALEM_EP:
      50                 :            :         case INTEL_FAM6_NEHALEM_EX:
      51                 :            : 
      52                 :            :         case INTEL_FAM6_WESTMERE:
      53                 :            :         case INTEL_FAM6_WESTMERE_EP:
      54                 :            :         case INTEL_FAM6_WESTMERE_EX:
      55                 :            : 
      56                 :            :         case INTEL_FAM6_SANDYBRIDGE:
      57                 :            :         case INTEL_FAM6_SANDYBRIDGE_X:
      58                 :            : 
      59                 :            :         case INTEL_FAM6_IVYBRIDGE:
      60                 :            :         case INTEL_FAM6_IVYBRIDGE_X:
      61                 :            : 
      62                 :            :         case INTEL_FAM6_HASWELL:
      63                 :            :         case INTEL_FAM6_HASWELL_X:
      64                 :            :         case INTEL_FAM6_HASWELL_L:
      65                 :            :         case INTEL_FAM6_HASWELL_G:
      66                 :            : 
      67                 :            :         case INTEL_FAM6_BROADWELL:
      68                 :            :         case INTEL_FAM6_BROADWELL_D:
      69                 :            :         case INTEL_FAM6_BROADWELL_G:
      70                 :            :         case INTEL_FAM6_BROADWELL_X:
      71                 :            : 
      72                 :            :         case INTEL_FAM6_ATOM_SILVERMONT:
      73                 :            :         case INTEL_FAM6_ATOM_SILVERMONT_D:
      74                 :            :         case INTEL_FAM6_ATOM_AIRMONT:
      75                 :            : 
      76                 :            :         case INTEL_FAM6_ATOM_GOLDMONT:
      77                 :            :         case INTEL_FAM6_ATOM_GOLDMONT_D:
      78                 :            :         case INTEL_FAM6_ATOM_GOLDMONT_PLUS:
      79                 :            :         case INTEL_FAM6_ATOM_TREMONT_D:
      80                 :            :         case INTEL_FAM6_ATOM_TREMONT:
      81                 :            : 
      82                 :            :         case INTEL_FAM6_XEON_PHI_KNL:
      83                 :            :         case INTEL_FAM6_XEON_PHI_KNM:
      84         [ #  # ]:          0 :                 if (idx == PERF_MSR_SMI)
      85                 :          0 :                         return true;
      86                 :            :                 break;
      87                 :            : 
      88                 :          0 :         case INTEL_FAM6_SKYLAKE_L:
      89                 :            :         case INTEL_FAM6_SKYLAKE:
      90                 :            :         case INTEL_FAM6_SKYLAKE_X:
      91                 :            :         case INTEL_FAM6_KABYLAKE_L:
      92                 :            :         case INTEL_FAM6_KABYLAKE:
      93                 :            :         case INTEL_FAM6_COMETLAKE_L:
      94                 :            :         case INTEL_FAM6_COMETLAKE:
      95                 :            :         case INTEL_FAM6_ICELAKE_L:
      96                 :            :         case INTEL_FAM6_ICELAKE:
      97                 :            :         case INTEL_FAM6_ICELAKE_X:
      98                 :            :         case INTEL_FAM6_ICELAKE_D:
      99                 :            :         case INTEL_FAM6_TIGERLAKE_L:
     100                 :            :         case INTEL_FAM6_TIGERLAKE:
     101         [ #  # ]:          0 :                 if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF)
     102                 :          0 :                         return true;
     103                 :            :                 break;
     104                 :            :         }
     105                 :            : 
     106                 :            :         return false;
     107                 :            : }
     108                 :            : 
     109                 :            : PMU_EVENT_ATTR_STRING(tsc,                              attr_tsc,               "event=0x00"  );
     110                 :            : PMU_EVENT_ATTR_STRING(aperf,                            attr_aperf,             "event=0x01"  );
     111                 :            : PMU_EVENT_ATTR_STRING(mperf,                            attr_mperf,             "event=0x02"  );
     112                 :            : PMU_EVENT_ATTR_STRING(pperf,                            attr_pperf,             "event=0x03"  );
     113                 :            : PMU_EVENT_ATTR_STRING(smi,                              attr_smi,               "event=0x04"  );
     114                 :            : PMU_EVENT_ATTR_STRING(ptsc,                             attr_ptsc,              "event=0x05"  );
     115                 :            : PMU_EVENT_ATTR_STRING(irperf,                           attr_irperf,            "event=0x06"  );
     116                 :            : PMU_EVENT_ATTR_STRING(cpu_thermal_margin,               attr_therm,             "event=0x07"  );
     117                 :            : PMU_EVENT_ATTR_STRING(cpu_thermal_margin.snapshot,      attr_therm_snap,        "1"           );
     118                 :            : PMU_EVENT_ATTR_STRING(cpu_thermal_margin.unit,          attr_therm_unit,        "C"           );
     119                 :            : 
     120                 :            : static unsigned long msr_mask;
     121                 :            : 
     122                 :            : PMU_EVENT_GROUP(events, aperf);
     123                 :            : PMU_EVENT_GROUP(events, mperf);
     124                 :            : PMU_EVENT_GROUP(events, pperf);
     125                 :            : PMU_EVENT_GROUP(events, smi);
     126                 :            : PMU_EVENT_GROUP(events, ptsc);
     127                 :            : PMU_EVENT_GROUP(events, irperf);
     128                 :            : 
     129                 :            : static struct attribute *attrs_therm[] = {
     130                 :            :         &attr_therm.attr.attr,
     131                 :            :         &attr_therm_snap.attr.attr,
     132                 :            :         &attr_therm_unit.attr.attr,
     133                 :            :         NULL,
     134                 :            : };
     135                 :            : 
     136                 :            : static struct attribute_group group_therm = {
     137                 :            :         .name  = "events",
     138                 :            :         .attrs = attrs_therm,
     139                 :            : };
     140                 :            : 
     141                 :            : static struct perf_msr msr[] = {
     142                 :            :         [PERF_MSR_TSC]          = { .no_check = true,                                                           },
     143                 :            :         [PERF_MSR_APERF]        = { MSR_IA32_APERF,             &group_aperf,               test_aperfmperf,        },
     144                 :            :         [PERF_MSR_MPERF]        = { MSR_IA32_MPERF,             &group_mperf,               test_aperfmperf,        },
     145                 :            :         [PERF_MSR_PPERF]        = { MSR_PPERF,                  &group_pperf,               test_intel,             },
     146                 :            :         [PERF_MSR_SMI]          = { MSR_SMI_COUNT,              &group_smi,         test_intel,             },
     147                 :            :         [PERF_MSR_PTSC]         = { MSR_F15H_PTSC,              &group_ptsc,                test_ptsc,              },
     148                 :            :         [PERF_MSR_IRPERF]       = { MSR_F17H_IRPERF,            &group_irperf,              test_irperf,            },
     149                 :            :         [PERF_MSR_THERM]        = { MSR_IA32_THERM_STATUS,      &group_therm,               test_therm_status,      },
     150                 :            : };
     151                 :            : 
     152                 :            : static struct attribute *events_attrs[] = {
     153                 :            :         &attr_tsc.attr.attr,
     154                 :            :         NULL,
     155                 :            : };
     156                 :            : 
     157                 :            : static struct attribute_group events_attr_group = {
     158                 :            :         .name = "events",
     159                 :            :         .attrs = events_attrs,
     160                 :            : };
     161                 :            : 
     162                 :          0 : PMU_FORMAT_ATTR(event, "config:0-63");
     163                 :            : static struct attribute *format_attrs[] = {
     164                 :            :         &format_attr_event.attr,
     165                 :            :         NULL,
     166                 :            : };
     167                 :            : static struct attribute_group format_attr_group = {
     168                 :            :         .name = "format",
     169                 :            :         .attrs = format_attrs,
     170                 :            : };
     171                 :            : 
     172                 :            : static const struct attribute_group *attr_groups[] = {
     173                 :            :         &events_attr_group,
     174                 :            :         &format_attr_group,
     175                 :            :         NULL,
     176                 :            : };
     177                 :            : 
     178                 :            : static const struct attribute_group *attr_update[] = {
     179                 :            :         &group_aperf,
     180                 :            :         &group_mperf,
     181                 :            :         &group_pperf,
     182                 :            :         &group_smi,
     183                 :            :         &group_ptsc,
     184                 :            :         &group_irperf,
     185                 :            :         &group_therm,
     186                 :            :         NULL,
     187                 :            : };
     188                 :            : 
     189                 :          0 : static int msr_event_init(struct perf_event *event)
     190                 :            : {
     191                 :          0 :         u64 cfg = event->attr.config;
     192                 :            : 
     193         [ #  # ]:          0 :         if (event->attr.type != event->pmu->type)
     194                 :            :                 return -ENOENT;
     195                 :            : 
     196                 :            :         /* unsupported modes and filters */
     197         [ #  # ]:          0 :         if (event->attr.sample_period) /* no sampling */
     198                 :            :                 return -EINVAL;
     199                 :            : 
     200         [ #  # ]:          0 :         if (cfg >= PERF_MSR_EVENT_MAX)
     201                 :            :                 return -EINVAL;
     202                 :            : 
     203                 :          0 :         cfg = array_index_nospec((unsigned long)cfg, PERF_MSR_EVENT_MAX);
     204                 :            : 
     205         [ #  # ]:          0 :         if (!(msr_mask & (1 << cfg)))
     206                 :            :                 return -EINVAL;
     207                 :            : 
     208                 :          0 :         event->hw.idx                = -1;
     209                 :          0 :         event->hw.event_base = msr[cfg].msr;
     210                 :          0 :         event->hw.config     = cfg;
     211                 :            : 
     212                 :          0 :         return 0;
     213                 :            : }
     214                 :            : 
     215                 :            : static inline u64 msr_read_counter(struct perf_event *event)
     216                 :            : {
     217                 :            :         u64 now;
     218                 :            : 
     219                 :            :         if (event->hw.event_base)
     220                 :            :                 rdmsrl(event->hw.event_base, now);
     221                 :            :         else
     222                 :            :                 now = rdtsc_ordered();
     223                 :            : 
     224                 :            :         return now;
     225                 :            : }
     226                 :            : 
     227                 :          0 : static void msr_event_update(struct perf_event *event)
     228                 :            : {
     229                 :          0 :         u64 prev, now;
     230                 :          0 :         s64 delta;
     231                 :            : 
     232                 :            :         /* Careful, an NMI might modify the previous event value: */
     233                 :          0 : again:
     234                 :          0 :         prev = local64_read(&event->hw.prev_count);
     235                 :          0 :         now = msr_read_counter(event);
     236                 :            : 
     237         [ #  # ]:          0 :         if (local64_cmpxchg(&event->hw.prev_count, prev, now) != prev)
     238                 :          0 :                 goto again;
     239                 :            : 
     240                 :          0 :         delta = now - prev;
     241         [ #  # ]:          0 :         if (unlikely(event->hw.event_base == MSR_SMI_COUNT)) {
     242                 :          0 :                 delta = sign_extend64(delta, 31);
     243                 :          0 :                 local64_add(delta, &event->count);
     244         [ #  # ]:          0 :         } else if (unlikely(event->hw.event_base == MSR_IA32_THERM_STATUS)) {
     245                 :            :                 /* If valid, extract digital readout, otherwise set to -1: */
     246         [ #  # ]:          0 :                 now = now & (1ULL << 31) ? (now >> 16) & 0x3f :  -1;
     247                 :          0 :                 local64_set(&event->count, now);
     248                 :            :         } else {
     249                 :          0 :                 local64_add(delta, &event->count);
     250                 :            :         }
     251                 :          0 : }
     252                 :            : 
     253                 :          0 : static void msr_event_start(struct perf_event *event, int flags)
     254                 :            : {
     255                 :          0 :         u64 now = msr_read_counter(event);
     256                 :            : 
     257                 :          0 :         local64_set(&event->hw.prev_count, now);
     258                 :          0 : }
     259                 :            : 
     260                 :          0 : static void msr_event_stop(struct perf_event *event, int flags)
     261                 :            : {
     262                 :          0 :         msr_event_update(event);
     263                 :          0 : }
     264                 :            : 
     265                 :          0 : static void msr_event_del(struct perf_event *event, int flags)
     266                 :            : {
     267                 :          0 :         msr_event_stop(event, PERF_EF_UPDATE);
     268                 :          0 : }
     269                 :            : 
     270                 :          0 : static int msr_event_add(struct perf_event *event, int flags)
     271                 :            : {
     272         [ #  # ]:          0 :         if (flags & PERF_EF_START)
     273                 :          0 :                 msr_event_start(event, flags);
     274                 :            : 
     275                 :          0 :         return 0;
     276                 :            : }
     277                 :            : 
     278                 :            : static struct pmu pmu_msr = {
     279                 :            :         .task_ctx_nr    = perf_sw_context,
     280                 :            :         .attr_groups    = attr_groups,
     281                 :            :         .event_init     = msr_event_init,
     282                 :            :         .add            = msr_event_add,
     283                 :            :         .del            = msr_event_del,
     284                 :            :         .start          = msr_event_start,
     285                 :            :         .stop           = msr_event_stop,
     286                 :            :         .read           = msr_event_update,
     287                 :            :         .capabilities   = PERF_PMU_CAP_NO_INTERRUPT | PERF_PMU_CAP_NO_EXCLUDE,
     288                 :            :         .attr_update    = attr_update,
     289                 :            : };
     290                 :            : 
     291                 :         78 : static int __init msr_init(void)
     292                 :            : {
     293         [ -  + ]:         78 :         if (!boot_cpu_has(X86_FEATURE_TSC)) {
     294                 :          0 :                 pr_cont("no MSR PMU driver.\n");
     295                 :          0 :                 return 0;
     296                 :            :         }
     297                 :            : 
     298                 :         78 :         msr_mask = perf_msr_probe(msr, PERF_MSR_EVENT_MAX, true, NULL);
     299                 :            : 
     300                 :         78 :         perf_pmu_register(&pmu_msr, "msr", -1);
     301                 :            : 
     302                 :         78 :         return 0;
     303                 :            : }
     304                 :            : device_initcall(msr_init);

Generated by: LCOV version 1.14