1 /*
2 * Copyright (c) 2026 Advanced Micro Devices, Inc.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7 /*
8 * AMD/Intel RAPL energy counters exposed as an hwpmc(4) PMC class.
9 *
10 * Read-only, system-scope (PMC_MODE_SC), 64-bit counters reporting
11 * microjoules.
12 */
13
14 #include <sys/param.h>
15 #include <sys/bus.h>
16 #include <sys/callout.h>
17 #include <sys/malloc.h>
18 #include <sys/mutex.h>
19 #include <sys/pmc.h>
20 #include <sys/pmckern.h>
21 #include <sys/priv.h>
22 #include <sys/proc.h>
23 #include <sys/smp.h>
24 #include <sys/systm.h>
25
26 #include <machine/cpu.h>
27 #include <machine/cpufunc.h>
28 #include <machine/cputypes.h>
29 #include <machine/specialreg.h>
30
31 #include <x86/x86_var.h>
32
33 #include <dev/hwpmc/hwpmc_rapl.h>
34
35 /* Energy counters are per-package/core domains, not per CPU: DOMWIDE. */
36 #define RAPL_CAPS (PMC_CAP_READ | PMC_CAP_DOMWIDE)
37
38 /* Worst-case package watts for sizing the guard timer. */
39 #define RAPL_GUARD_WATT 1000
40
41 /* Guard interval clamp band (ms). */
42 #define RAPL_GUARD_MIN_MS 10
43 #define RAPL_GUARD_MAX_MS 60000
44
45 struct rapl_event {
46 enum pmc_event re_ev;
47 uint32_t re_msr;
48 uint32_t re_unit; /* unit shift: 1 tick = 1/2^unit J */
49 };
50
51 struct rapl_value {
52 uint64_t rv_prev; /* last raw 32-bit MSR value */
53 uint64_t rv_accum;
54 sbintime_t rv_prev_time;
55 bool rv_primed;
56 };
57
58 struct rapl_cpu {
59 struct pmc_hw rc_hw[RAPL_MAX_NPMCS];
60 struct rapl_value rc_value[RAPL_MAX_NPMCS];
61 struct mtx rc_mtx;
62 int rc_nalloc; /* allocated RAPL PMCs on this CPU */
63 };
64
65 static struct rapl_cpu **rapl_pcpu;
66
67 static struct rapl_event rapl_events[RAPL_MAX_NPMCS];
68 static struct pmc_descr rapl_pmcdesc[RAPL_MAX_NPMCS];
69 static int rapl_npmcs;
70 static int rapl_ri;
71
72 static struct callout rapl_guard_callout;
73 static sbintime_t rapl_guard_sbt;
74 static int rapl_nalloc;
75 static cpuset_t rapl_cpus; /* CPUs with an allocated RAPL PMC */
76
77 static struct mtx rapl_alloc_mtx;
78
79 /* Convert energy ticks to microjoules without overflowing uint64_t. */
80 static uint64_t
rapl_raw_to_uj(uint64_t raw,uint32_t shift)81 rapl_raw_to_uj(uint64_t raw, uint32_t shift)
82 {
83 uint64_t unit, whole, frac;
84
85 unit = 1ULL << shift;
86 whole = raw / unit;
87 frac = raw % unit;
88 return (whole * 1000000ULL + (frac * 1000000ULL) / unit);
89 }
90
91 /* Fold a 32-bit MSR reading into the 64-bit accumulator, can recover one wrap. */
92 static void
rapl_update_delta(struct rapl_value * val,uint64_t cur)93 rapl_update_delta(struct rapl_value *val, uint64_t cur)
94 {
95 sbintime_t now = sbinuptime();
96 uint64_t diff;
97
98 cur &= UINT32_MAX;
99 if (!val->rv_primed) {
100 val->rv_prev = cur;
101 val->rv_prev_time = now;
102 val->rv_primed = true;
103 return;
104 }
105 /* Skip sub-ms re-samples; the next sample folds the full interval. */
106 if (now - val->rv_prev_time < SBT_1MS)
107 return;
108 if (cur >= val->rv_prev)
109 diff = cur - val->rv_prev;
110 else
111 diff = (UINT32_MAX - val->rv_prev) + cur + 1;
112 val->rv_accum += diff;
113 val->rv_prev = cur;
114 val->rv_prev_time = now;
115 }
116
117 /* Sample one row's MSR on the current CPU and return the folded accumulator. */
118 static uint64_t
rapl_sample_row(int cpu,int ri)119 rapl_sample_row(int cpu, int ri)
120 {
121 struct rapl_cpu *rc;
122 uint64_t accum, cur;
123
124 rc = rapl_pcpu[cpu];
125 KASSERT(rc != NULL, ("[rapl,%d] null pcpu state cpu %d", __LINE__,
126 cpu));
127 mtx_lock_spin(&rc->rc_mtx);
128 if (rdmsr_safe(rapl_events[ri].re_msr, &cur) == 0)
129 rapl_update_delta(&rc->rc_value[ri], cur);
130 accum = rc->rc_value[ri].rv_accum;
131 mtx_unlock_spin(&rc->rc_mtx);
132 return (accum);
133 }
134
135 /* Guard rendezvous handler: sample every row on this CPU. */
136 static void
rapl_guard_handler(void * arg __unused)137 rapl_guard_handler(void *arg __unused)
138 {
139 int cpu = curcpu;
140 int ri;
141
142 for (ri = 0; ri < rapl_npmcs; ri++)
143 (void)rapl_sample_row(cpu, ri);
144 }
145
146 static void rapl_guard_tick(void *arg);
147
148 /* (Re)arm the guard callout. Caller holds rapl_alloc_mtx. */
149 static void
rapl_guard_schedule(void)150 rapl_guard_schedule(void)
151 {
152 mtx_assert(&rapl_alloc_mtx, MA_OWNED);
153 callout_reset_sbt(&rapl_guard_callout, rapl_guard_sbt,
154 rapl_guard_sbt / 10, rapl_guard_tick, NULL, 0);
155 }
156
157 /* Periodic overflow guard. */
158 static void
rapl_guard_tick(void * arg __unused)159 rapl_guard_tick(void *arg __unused)
160 {
161 cpuset_t cpus;
162
163 mtx_lock(&rapl_alloc_mtx);
164 cpus = rapl_cpus;
165 mtx_unlock(&rapl_alloc_mtx);
166
167 if (!CPU_EMPTY(&cpus))
168 smp_rendezvous_cpus(cpus, smp_no_rendezvous_barrier,
169 rapl_guard_handler, smp_no_rendezvous_barrier, NULL);
170
171 /* Keep firing while any RAPL PMC remains allocated. */
172 mtx_lock(&rapl_alloc_mtx);
173 if (rapl_nalloc > 0)
174 rapl_guard_schedule();
175 mtx_unlock(&rapl_alloc_mtx);
176 }
177
178 static int
rapl_allocate_pmc(int cpu,int ri,struct pmc * pm __unused,const struct pmc_op_pmcallocate * a)179 rapl_allocate_pmc(int cpu, int ri, struct pmc *pm __unused,
180 const struct pmc_op_pmcallocate *a)
181 {
182
183 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
184 ("[rapl,%d] illegal CPU value %d", __LINE__, cpu));
185 KASSERT(ri >= 0 && ri < rapl_npmcs,
186 ("[rapl,%d] illegal row index %d", __LINE__, ri));
187
188 if (a->pm_class != PMC_CLASS_RAPL)
189 return (EINVAL);
190
191 if (a->pm_mode != PMC_MODE_SC)
192 return (EINVAL);
193
194 /* Power side channel (PLATYPUS): require privilege even if syspmcs bypass is set. */
195 if (priv_check(curthread, PRIV_PMC_SYSTEM) != 0)
196 return (EPERM);
197
198 /* Reject events this vendor does not expose (e.g. DRAM on AMD). */
199 if (a->pm_ev != rapl_events[ri].re_ev)
200 return (EINVAL);
201
202 /* Arm the guard on the first allocation (per-CPU and global). */
203 mtx_lock(&rapl_alloc_mtx);
204 if (rapl_pcpu[cpu]->rc_nalloc++ == 0)
205 CPU_SET(cpu, &rapl_cpus);
206 if (rapl_nalloc++ == 0)
207 rapl_guard_schedule();
208 mtx_unlock(&rapl_alloc_mtx);
209
210 return (0);
211 }
212
213 static int
rapl_config_pmc(int cpu,int ri,struct pmc * pm)214 rapl_config_pmc(int cpu, int ri, struct pmc *pm)
215 {
216 struct pmc_hw *phw;
217
218 PMCDBG3(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
219
220 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
221 ("[rapl,%d] illegal CPU value %d", __LINE__, cpu));
222 KASSERT(ri >= 0 && ri < rapl_npmcs,
223 ("[rapl,%d] illegal row-index %d", __LINE__, ri));
224
225 phw = &rapl_pcpu[cpu]->rc_hw[ri];
226
227 KASSERT(pm == NULL || phw->phw_pmc == NULL,
228 ("[rapl,%d] pm=%p phw->pm=%p hwpmc not unconfigured", __LINE__,
229 pm, phw->phw_pmc));
230
231 phw->phw_pmc = pm;
232
233 return (0);
234 }
235
236 static int
rapl_describe(int cpu,int ri,struct pmc_info * pi,struct pmc ** ppmc)237 rapl_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
238 {
239 const struct pmc_descr *pd;
240 struct pmc_hw *phw;
241
242 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
243 ("[rapl,%d] illegal CPU %d", __LINE__, cpu));
244 KASSERT(ri >= 0 && ri < rapl_npmcs,
245 ("[rapl,%d] illegal row-index %d", __LINE__, ri));
246
247 phw = &rapl_pcpu[cpu]->rc_hw[ri];
248 pd = &rapl_pmcdesc[ri];
249
250 strlcpy(pi->pm_name, pd->pd_name, sizeof(pi->pm_name));
251 pi->pm_class = pd->pd_class;
252
253 if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
254 pi->pm_enabled = true;
255 *ppmc = phw->phw_pmc;
256 } else {
257 pi->pm_enabled = false;
258 *ppmc = NULL;
259 }
260
261 return (0);
262 }
263
264 static int
rapl_get_config(int cpu,int ri,struct pmc ** ppm)265 rapl_get_config(int cpu, int ri, struct pmc **ppm)
266 {
267
268 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
269 ("[rapl,%d] illegal CPU %d", __LINE__, cpu));
270 KASSERT(ri >= 0 && ri < rapl_npmcs,
271 ("[rapl,%d] illegal row-index %d", __LINE__, ri));
272
273 *ppm = rapl_pcpu[cpu]->rc_hw[ri].phw_pmc;
274
275 return (0);
276 }
277
278 static int
rapl_pcpu_init(struct pmc_mdep * md __unused,int cpu)279 rapl_pcpu_init(struct pmc_mdep *md __unused, int cpu)
280 {
281 struct pmc_cpu *pc;
282 struct rapl_cpu *rapl_pc;
283 int ri, n;
284
285 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
286 ("[rapl,%d] illegal cpu %d", __LINE__, cpu));
287 KASSERT(rapl_pcpu, ("[rapl,%d] null pcpu", __LINE__));
288 KASSERT(rapl_pcpu[cpu] == NULL, ("[rapl,%d] non-null per-cpu",
289 __LINE__));
290
291 rapl_pc = malloc(sizeof(struct rapl_cpu), M_PMC, M_WAITOK | M_ZERO);
292 mtx_init(&rapl_pc->rc_mtx, "rapl-cpu", NULL, MTX_SPIN);
293
294 for (n = 0; n < rapl_npmcs; n++)
295 rapl_pc->rc_hw[n].phw_state = PMC_PHW_FLAG_IS_ENABLED |
296 PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n) |
297 PMC_PHW_FLAG_IS_SHAREABLE;
298
299 rapl_pcpu[cpu] = rapl_pc;
300
301 KASSERT(pmc_pcpu, ("[rapl,%d] null generic pcpu", __LINE__));
302
303 pc = pmc_pcpu[cpu];
304
305 KASSERT(pc, ("[rapl,%d] null generic per-cpu", __LINE__));
306
307 for (n = 0; n < rapl_npmcs; n++) {
308 ri = rapl_ri + n;
309 pc->pc_hwpmcs[ri] = &rapl_pc->rc_hw[n];
310 }
311
312 return (0);
313 }
314
315 static int
rapl_pcpu_fini(struct pmc_mdep * md __unused,int cpu)316 rapl_pcpu_fini(struct pmc_mdep *md __unused, int cpu)
317 {
318 struct pmc_cpu *pc;
319 int ri, n;
320
321 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
322 ("[rapl,%d] illegal cpu %d", __LINE__, cpu));
323 KASSERT(rapl_pcpu[cpu] != NULL, ("[rapl,%d] null pcpu", __LINE__));
324 KASSERT(rapl_pcpu[cpu]->rc_nalloc == 0,
325 ("[rapl,%d] %d PMCs still allocated on cpu %d", __LINE__,
326 rapl_pcpu[cpu]->rc_nalloc, cpu));
327
328 /* Last release already drained the guard, so no handler can race here. */
329 mtx_destroy(&rapl_pcpu[cpu]->rc_mtx);
330 free(rapl_pcpu[cpu], M_PMC);
331 rapl_pcpu[cpu] = NULL;
332
333 pc = pmc_pcpu[cpu];
334 for (n = 0; n < rapl_npmcs; n++) {
335 ri = rapl_ri + n;
336 pc->pc_hwpmcs[ri] = NULL;
337 }
338
339 return (0);
340 }
341
342 static int
rapl_read_pmc(int cpu,int ri,struct pmc * pm,pmc_value_t * v)343 rapl_read_pmc(int cpu, int ri, struct pmc *pm, pmc_value_t *v)
344 {
345 enum pmc_mode mode __diagused;
346
347 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
348 ("[rapl,%d] illegal CPU value %d", __LINE__, cpu));
349 KASSERT(ri >= 0 && ri < rapl_npmcs,
350 ("[rapl,%d] illegal ri %d", __LINE__, ri));
351
352 mode = PMC_TO_MODE(pm);
353
354 KASSERT(mode == PMC_MODE_SC,
355 ("[rapl,%d] illegal pmc mode %d", __LINE__, mode));
356
357 PMCDBG1(MDP,REA,1, "rapl-read id=%d", ri);
358
359 /* Bound to cpu by hwpmc, so rdmsr reads this CPU's domain (see DOMWIDE). */
360 *v = rapl_raw_to_uj(rapl_sample_row(cpu, ri), rapl_events[ri].re_unit);
361
362 return (0);
363 }
364
365 static int
rapl_release_pmc(int cpu,int ri,struct pmc * pmc __unused)366 rapl_release_pmc(int cpu, int ri, struct pmc *pmc __unused)
367 {
368 struct pmc_hw *phw __diagused;
369 bool last;
370
371 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
372 ("[rapl,%d] illegal CPU value %d", __LINE__, cpu));
373 KASSERT(ri >= 0 && ri < rapl_npmcs,
374 ("[rapl,%d] illegal row-index %d", __LINE__, ri));
375
376 phw = &rapl_pcpu[cpu]->rc_hw[ri];
377
378 KASSERT(phw->phw_pmc == NULL,
379 ("[rapl,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
380
381 mtx_lock(&rapl_alloc_mtx);
382 KASSERT(rapl_pcpu[cpu]->rc_nalloc > 0 && rapl_nalloc > 0,
383 ("[rapl,%d] release underflow", __LINE__));
384 if (--rapl_pcpu[cpu]->rc_nalloc == 0)
385 CPU_CLR(cpu, &rapl_cpus);
386 last = (--rapl_nalloc == 0);
387 mtx_unlock(&rapl_alloc_mtx);
388
389 /* Last release: drain the guard (sleepable here, mutex already dropped). */
390 if (last)
391 callout_drain(&rapl_guard_callout);
392
393 return (0);
394 }
395
396 static int
rapl_start_pmc(int cpu __diagused,int ri __diagused,struct pmc * pm __unused)397 rapl_start_pmc(int cpu __diagused, int ri __diagused, struct pmc *pm __unused)
398 {
399
400 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
401 ("[rapl,%d] illegal CPU value %d", __LINE__, cpu));
402 KASSERT(ri >= 0 && ri < rapl_npmcs,
403 ("[rapl,%d] illegal row-index %d", __LINE__, ri));
404
405 return (0); /* RAPL counters are always running. */
406 }
407
408 static int
rapl_stop_pmc(int cpu __diagused,int ri __diagused,struct pmc * pm __unused)409 rapl_stop_pmc(int cpu __diagused, int ri __diagused, struct pmc *pm __unused)
410 {
411
412 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
413 ("[rapl,%d] illegal CPU value %d", __LINE__, cpu));
414 KASSERT(ri >= 0 && ri < rapl_npmcs,
415 ("[rapl,%d] illegal row-index %d", __LINE__, ri));
416
417 return (0); /* RAPL counters cannot be stopped. */
418 }
419
420 static int
rapl_write_pmc(int cpu __diagused,int ri __diagused,struct pmc * pm __unused,pmc_value_t v __unused)421 rapl_write_pmc(int cpu __diagused, int ri __diagused, struct pmc *pm __unused,
422 pmc_value_t v __unused)
423 {
424
425 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
426 ("[rapl,%d] illegal CPU value %d", __LINE__, cpu));
427 KASSERT(ri >= 0 && ri < rapl_npmcs,
428 ("[rapl,%d] illegal row-index %d", __LINE__, ri));
429
430 /* Energy counters are not writable; refuse silently like TSC. */
431 return (0);
432 }
433
434 /* Fault-safe RAPL MSR presence probe on the current CPU. */
435 static bool
rapl_msr_present(uint32_t msr)436 rapl_msr_present(uint32_t msr)
437 {
438 uint64_t v;
439
440 return (rdmsr_safe(msr, &v) == 0);
441 }
442
443 /* Append an event row to the table if its MSR responds on this hardware. */
444 static void
rapl_add_event(enum pmc_event ev,uint32_t msr,uint32_t unit,const char * name)445 rapl_add_event(enum pmc_event ev, uint32_t msr, uint32_t unit,
446 const char *name)
447 {
448
449 if (!rapl_msr_present(msr))
450 return;
451
452 rapl_events[rapl_npmcs].re_ev = ev;
453 rapl_events[rapl_npmcs].re_msr = msr;
454 rapl_events[rapl_npmcs].re_unit = unit;
455 rapl_pmcdesc[rapl_npmcs].pd_class = PMC_CLASS_RAPL;
456 rapl_pmcdesc[rapl_npmcs].pd_caps = RAPL_CAPS;
457 rapl_pmcdesc[rapl_npmcs].pd_width = 64;
458 strlcpy(rapl_pmcdesc[rapl_npmcs].pd_name, name,
459 sizeof(rapl_pmcdesc[rapl_npmcs].pd_name));
460 rapl_npmcs++;
461 }
462
463 /* Guard interval: half the worst-case wrap period at RAPL_GUARD_WATT. */
464 static sbintime_t
rapl_compute_guard_sbt(uint32_t shift)465 rapl_compute_guard_sbt(uint32_t shift)
466 {
467 uint64_t max_energy_uj, guard_ms;
468
469 max_energy_uj = rapl_raw_to_uj(UINT32_MAX, shift);
470 guard_ms = max_energy_uj / (2000ULL * RAPL_GUARD_WATT);
471 if (guard_ms < RAPL_GUARD_MIN_MS)
472 guard_ms = RAPL_GUARD_MIN_MS;
473 else if (guard_ms > RAPL_GUARD_MAX_MS)
474 guard_ms = RAPL_GUARD_MAX_MS;
475
476 return (guard_ms * SBT_1MS);
477 }
478
479 /* Fixed 2^-16 J DRAM unit, not the ESU (HSX/KNL only -- not SPR/EMR/GNR). */
480 static bool
rapl_intel_fixed_dram_unit(void)481 rapl_intel_fixed_dram_unit(void)
482 {
483
484 if (CPUID_TO_FAMILY(cpu_id) != 0x6)
485 return (false);
486
487 switch (CPUID_TO_MODEL(cpu_id)) {
488 case 0x3f: /* Haswell-EP */
489 case 0x4f: /* Broadwell-EP */
490 case 0x55: /* Skylake/Cascade Lake/Cooper Lake-SP */
491 case 0x56: /* Broadwell-DE */
492 case 0x57: /* Xeon Phi KNL */
493 case 0x6a: /* Ice Lake-SP */
494 case 0x6c: /* Ice Lake-D */
495 case 0x85: /* Xeon Phi KNM */
496 return (true);
497 default:
498 return (false);
499 }
500 }
501
502 int
pmc_rapl_initialize(struct pmc_mdep * md,int maxcpu,int classindex)503 pmc_rapl_initialize(struct pmc_mdep *md, int maxcpu, int classindex)
504 {
505 struct pmc_classdep *pcd;
506 uint32_t unit_msr, pkg_msr, cores_msr, dram_msr;
507 uint32_t esu, dram_unit, max_unit;
508 uint64_t unit_val;
509 int i;
510
511 KASSERT(md != NULL, ("[rapl,%d] md is NULL", __LINE__));
512 KASSERT(md->pmd_nclass >= 1, ("[rapl,%d] dubious md->nclass %d",
513 __LINE__, md->pmd_nclass));
514
515 /* Select the per-vendor MSR set. */
516 switch (cpu_vendor_id) {
517 case CPU_VENDOR_AMD:
518 case CPU_VENDOR_HYGON:
519 unit_msr = MSR_AMD_RAPL_POWER_UNIT;
520 pkg_msr = MSR_AMD_PKG_ENERGY_STATUS;
521 cores_msr = MSR_AMD_CORE_ENERGY_STATUS;
522 dram_msr = 0; /* AMD has no DRAM domain */
523 break;
524 case CPU_VENDOR_INTEL:
525 unit_msr = MSR_RAPL_POWER_UNIT;
526 pkg_msr = MSR_PKG_ENERGY_STATUS;
527 cores_msr = MSR_PP0_ENERGY_STATUS;
528 dram_msr = MSR_DRAM_ENERGY_STATUS;
529 break;
530 default:
531 return (ENXIO);
532 }
533
534 /* Decode the energy unit. */
535 unit_val = rdmsr(unit_msr);
536 esu = (unit_val >> 8) & 0x1f;
537 dram_unit = rapl_intel_fixed_dram_unit() ? 16 : esu;
538
539 /* Build the event table from the MSRs that actually respond. */
540 rapl_npmcs = 0;
541 rapl_add_event(PMC_EV_RAPL_ENERGY_PKG, pkg_msr, esu,
542 "RAPL_ENERGY_PKG");
543 rapl_add_event(PMC_EV_RAPL_ENERGY_CORES, cores_msr, esu,
544 "RAPL_ENERGY_CORES");
545 if (dram_msr != 0)
546 rapl_add_event(PMC_EV_RAPL_ENERGY_DRAM, dram_msr, dram_unit,
547 "RAPL_ENERGY_DRAM");
548
549 /* No RAPL energy MSR responded. */
550 if (rapl_npmcs == 0)
551 return (ENXIO);
552
553 /* Size the guard for the fastest-wrapping row (largest unit shift). */
554 max_unit = 0;
555 for (i = 0; i < rapl_npmcs; i++)
556 max_unit = MAX(max_unit, rapl_events[i].re_unit);
557 rapl_guard_sbt = rapl_compute_guard_sbt(max_unit);
558 rapl_nalloc = 0;
559 CPU_ZERO(&rapl_cpus);
560
561 mtx_init(&rapl_alloc_mtx, "rapl-alloc", NULL, MTX_DEF);
562 /* It does not need associated mutex, the handler locks itself. */
563 callout_init(&rapl_guard_callout, 1);
564
565 rapl_pcpu = malloc(sizeof(struct rapl_cpu *) * maxcpu, M_PMC,
566 M_ZERO | M_WAITOK);
567
568 pcd = &md->pmd_classdep[classindex];
569
570 pcd->pcd_caps = RAPL_CAPS;
571 pcd->pcd_class = PMC_CLASS_RAPL;
572 pcd->pcd_num = rapl_npmcs;
573 pcd->pcd_ri = md->pmd_npmc;
574 pcd->pcd_width = 64;
575
576 pcd->pcd_allocate_pmc = rapl_allocate_pmc;
577 pcd->pcd_config_pmc = rapl_config_pmc;
578 pcd->pcd_describe = rapl_describe;
579 pcd->pcd_get_config = rapl_get_config;
580 pcd->pcd_pcpu_init = rapl_pcpu_init;
581 pcd->pcd_pcpu_fini = rapl_pcpu_fini;
582 pcd->pcd_read_pmc = rapl_read_pmc;
583 pcd->pcd_release_pmc = rapl_release_pmc;
584 pcd->pcd_start_pmc = rapl_start_pmc;
585 pcd->pcd_stop_pmc = rapl_stop_pmc;
586 pcd->pcd_write_pmc = rapl_write_pmc;
587
588 rapl_ri = md->pmd_npmc;
589 md->pmd_npmc += rapl_npmcs;
590
591 return (0);
592 }
593
594 void
pmc_rapl_finalize(struct pmc_mdep * md __unused)595 pmc_rapl_finalize(struct pmc_mdep *md __unused)
596 {
597 PMCDBG0(MDP, INI, 1, "rapl-finalize");
598
599 if (rapl_pcpu == NULL)
600 return;
601
602 KASSERT(rapl_nalloc == 0, ("[rapl,%d] %d PMCs still allocated",
603 __LINE__, rapl_nalloc));
604 for (int i = 0; i < pmc_cpu_max(); i++)
605 KASSERT(rapl_pcpu[i] == NULL, ("[rapl,%d] non-null pcpu cpu %d",
606 __LINE__, i));
607
608 mtx_destroy(&rapl_alloc_mtx);
609
610 free(rapl_pcpu, M_PMC);
611 rapl_pcpu = NULL;
612 }
613