root/arch/x86/events/amd/power.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. event_update
  2. __pmu_event_start
  3. pmu_event_start
  4. pmu_event_stop
  5. pmu_event_add
  6. pmu_event_del
  7. pmu_event_init
  8. pmu_event_read
  9. get_attr_cpumask
  10. power_cpu_exit
  11. power_cpu_init
  12. amd_power_pmu_init
  13. amd_power_pmu_exit

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Performance events - AMD Processor Power Reporting Mechanism
   4  *
   5  * Copyright (C) 2016 Advanced Micro Devices, Inc.
   6  *
   7  * Author: Huang Rui <ray.huang@amd.com>
   8  */
   9 
  10 #include <linux/module.h>
  11 #include <linux/slab.h>
  12 #include <linux/perf_event.h>
  13 #include <asm/cpu_device_id.h>
  14 #include "../perf_event.h"
  15 
  16 #define MSR_F15H_CU_PWR_ACCUMULATOR     0xc001007a
  17 #define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b
  18 #define MSR_F15H_PTSC                   0xc0010280
  19 
  20 /* Event code: LSB 8 bits, passed in attr->config any other bit is reserved. */
  21 #define AMD_POWER_EVENT_MASK            0xFFULL
  22 
  23 /*
  24  * Accumulated power status counters.
  25  */
  26 #define AMD_POWER_EVENTSEL_PKG          1
  27 
  28 /*
  29  * The ratio of compute unit power accumulator sample period to the
  30  * PTSC period.
  31  */
  32 static unsigned int cpu_pwr_sample_ratio;
  33 
  34 /* Maximum accumulated power of a compute unit. */
  35 static u64 max_cu_acc_power;
  36 
  37 static struct pmu pmu_class;
  38 
  39 /*
  40  * Accumulated power represents the sum of each compute unit's (CU) power
  41  * consumption. On any core of each CU we read the total accumulated power from
  42  * MSR_F15H_CU_PWR_ACCUMULATOR. cpu_mask represents CPU bit map of all cores
  43  * which are picked to measure the power for the CUs they belong to.
  44  */
  45 static cpumask_t cpu_mask;
  46 
  47 static void event_update(struct perf_event *event)
  48 {
  49         struct hw_perf_event *hwc = &event->hw;
  50         u64 prev_pwr_acc, new_pwr_acc, prev_ptsc, new_ptsc;
  51         u64 delta, tdelta;
  52 
  53         prev_pwr_acc = hwc->pwr_acc;
  54         prev_ptsc = hwc->ptsc;
  55         rdmsrl(MSR_F15H_CU_PWR_ACCUMULATOR, new_pwr_acc);
  56         rdmsrl(MSR_F15H_PTSC, new_ptsc);
  57 
  58         /*
  59          * Calculate the CU power consumption over a time period, the unit of
  60          * final value (delta) is micro-Watts. Then add it to the event count.
  61          */
  62         if (new_pwr_acc < prev_pwr_acc) {
  63                 delta = max_cu_acc_power + new_pwr_acc;
  64                 delta -= prev_pwr_acc;
  65         } else
  66                 delta = new_pwr_acc - prev_pwr_acc;
  67 
  68         delta *= cpu_pwr_sample_ratio * 1000;
  69         tdelta = new_ptsc - prev_ptsc;
  70 
  71         do_div(delta, tdelta);
  72         local64_add(delta, &event->count);
  73 }
  74 
  75 static void __pmu_event_start(struct perf_event *event)
  76 {
  77         if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED)))
  78                 return;
  79 
  80         event->hw.state = 0;
  81 
  82         rdmsrl(MSR_F15H_PTSC, event->hw.ptsc);
  83         rdmsrl(MSR_F15H_CU_PWR_ACCUMULATOR, event->hw.pwr_acc);
  84 }
  85 
  86 static void pmu_event_start(struct perf_event *event, int mode)
  87 {
  88         __pmu_event_start(event);
  89 }
  90 
  91 static void pmu_event_stop(struct perf_event *event, int mode)
  92 {
  93         struct hw_perf_event *hwc = &event->hw;
  94 
  95         /* Mark event as deactivated and stopped. */
  96         if (!(hwc->state & PERF_HES_STOPPED))
  97                 hwc->state |= PERF_HES_STOPPED;
  98 
  99         /* Check if software counter update is necessary. */
 100         if ((mode & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
 101                 /*
 102                  * Drain the remaining delta count out of an event
 103                  * that we are disabling:
 104                  */
 105                 event_update(event);
 106                 hwc->state |= PERF_HES_UPTODATE;
 107         }
 108 }
 109 
 110 static int pmu_event_add(struct perf_event *event, int mode)
 111 {
 112         struct hw_perf_event *hwc = &event->hw;
 113 
 114         hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
 115 
 116         if (mode & PERF_EF_START)
 117                 __pmu_event_start(event);
 118 
 119         return 0;
 120 }
 121 
 122 static void pmu_event_del(struct perf_event *event, int flags)
 123 {
 124         pmu_event_stop(event, PERF_EF_UPDATE);
 125 }
 126 
 127 static int pmu_event_init(struct perf_event *event)
 128 {
 129         u64 cfg = event->attr.config & AMD_POWER_EVENT_MASK;
 130 
 131         /* Only look at AMD power events. */
 132         if (event->attr.type != pmu_class.type)
 133                 return -ENOENT;
 134 
 135         /* Unsupported modes and filters. */
 136         if (event->attr.sample_period)
 137                 return -EINVAL;
 138 
 139         if (cfg != AMD_POWER_EVENTSEL_PKG)
 140                 return -EINVAL;
 141 
 142         return 0;
 143 }
 144 
 145 static void pmu_event_read(struct perf_event *event)
 146 {
 147         event_update(event);
 148 }
 149 
 150 static ssize_t
 151 get_attr_cpumask(struct device *dev, struct device_attribute *attr, char *buf)
 152 {
 153         return cpumap_print_to_pagebuf(true, buf, &cpu_mask);
 154 }
 155 
 156 static DEVICE_ATTR(cpumask, S_IRUGO, get_attr_cpumask, NULL);
 157 
 158 static struct attribute *pmu_attrs[] = {
 159         &dev_attr_cpumask.attr,
 160         NULL,
 161 };
 162 
 163 static struct attribute_group pmu_attr_group = {
 164         .attrs = pmu_attrs,
 165 };
 166 
 167 /*
 168  * Currently it only supports to report the power of each
 169  * processor/package.
 170  */
 171 EVENT_ATTR_STR(power-pkg, power_pkg, "event=0x01");
 172 
 173 EVENT_ATTR_STR(power-pkg.unit, power_pkg_unit, "mWatts");
 174 
 175 /* Convert the count from micro-Watts to milli-Watts. */
 176 EVENT_ATTR_STR(power-pkg.scale, power_pkg_scale, "1.000000e-3");
 177 
 178 static struct attribute *events_attr[] = {
 179         EVENT_PTR(power_pkg),
 180         EVENT_PTR(power_pkg_unit),
 181         EVENT_PTR(power_pkg_scale),
 182         NULL,
 183 };
 184 
 185 static struct attribute_group pmu_events_group = {
 186         .name   = "events",
 187         .attrs  = events_attr,
 188 };
 189 
 190 PMU_FORMAT_ATTR(event, "config:0-7");
 191 
 192 static struct attribute *formats_attr[] = {
 193         &format_attr_event.attr,
 194         NULL,
 195 };
 196 
 197 static struct attribute_group pmu_format_group = {
 198         .name   = "format",
 199         .attrs  = formats_attr,
 200 };
 201 
 202 static const struct attribute_group *attr_groups[] = {
 203         &pmu_attr_group,
 204         &pmu_format_group,
 205         &pmu_events_group,
 206         NULL,
 207 };
 208 
 209 static struct pmu pmu_class = {
 210         .attr_groups    = attr_groups,
 211         /* system-wide only */
 212         .task_ctx_nr    = perf_invalid_context,
 213         .event_init     = pmu_event_init,
 214         .add            = pmu_event_add,
 215         .del            = pmu_event_del,
 216         .start          = pmu_event_start,
 217         .stop           = pmu_event_stop,
 218         .read           = pmu_event_read,
 219         .capabilities   = PERF_PMU_CAP_NO_EXCLUDE,
 220 };
 221 
 222 static int power_cpu_exit(unsigned int cpu)
 223 {
 224         int target;
 225 
 226         if (!cpumask_test_and_clear_cpu(cpu, &cpu_mask))
 227                 return 0;
 228 
 229         /*
 230          * Find a new CPU on the same compute unit, if was set in cpumask
 231          * and still some CPUs on compute unit. Then migrate event and
 232          * context to new CPU.
 233          */
 234         target = cpumask_any_but(topology_sibling_cpumask(cpu), cpu);
 235         if (target < nr_cpumask_bits) {
 236                 cpumask_set_cpu(target, &cpu_mask);
 237                 perf_pmu_migrate_context(&pmu_class, cpu, target);
 238         }
 239         return 0;
 240 }
 241 
 242 static int power_cpu_init(unsigned int cpu)
 243 {
 244         int target;
 245 
 246         /*
 247          * 1) If any CPU is set at cpu_mask in the same compute unit, do
 248          * nothing.
 249          * 2) If no CPU is set at cpu_mask in the same compute unit,
 250          * set current ONLINE CPU.
 251          *
 252          * Note: if there is a CPU aside of the new one already in the
 253          * sibling mask, then it is also in cpu_mask.
 254          */
 255         target = cpumask_any_but(topology_sibling_cpumask(cpu), cpu);
 256         if (target >= nr_cpumask_bits)
 257                 cpumask_set_cpu(cpu, &cpu_mask);
 258         return 0;
 259 }
 260 
 261 static const struct x86_cpu_id cpu_match[] = {
 262         { .vendor = X86_VENDOR_AMD, .family = 0x15 },
 263         {},
 264 };
 265 
 266 static int __init amd_power_pmu_init(void)
 267 {
 268         int ret;
 269 
 270         if (!x86_match_cpu(cpu_match))
 271                 return -ENODEV;
 272 
 273         if (!boot_cpu_has(X86_FEATURE_ACC_POWER))
 274                 return -ENODEV;
 275 
 276         cpu_pwr_sample_ratio = cpuid_ecx(0x80000007);
 277 
 278         if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &max_cu_acc_power)) {
 279                 pr_err("Failed to read max compute unit power accumulator MSR\n");
 280                 return -ENODEV;
 281         }
 282 
 283 
 284         cpuhp_setup_state(CPUHP_AP_PERF_X86_AMD_POWER_ONLINE,
 285                           "perf/x86/amd/power:online",
 286                           power_cpu_init, power_cpu_exit);
 287 
 288         ret = perf_pmu_register(&pmu_class, "power", -1);
 289         if (WARN_ON(ret)) {
 290                 pr_warn("AMD Power PMU registration failed\n");
 291                 return ret;
 292         }
 293 
 294         pr_info("AMD Power PMU detected\n");
 295         return ret;
 296 }
 297 module_init(amd_power_pmu_init);
 298 
 299 static void __exit amd_power_pmu_exit(void)
 300 {
 301         cpuhp_remove_state_nocalls(CPUHP_AP_PERF_X86_AMD_POWER_ONLINE);
 302         perf_pmu_unregister(&pmu_class);
 303 }
 304 module_exit(amd_power_pmu_exit);
 305 
 306 MODULE_AUTHOR("Huang Rui <ray.huang@amd.com>");
 307 MODULE_DESCRIPTION("AMD Processor Power Reporting Mechanism");
 308 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */