xref: /freebsd/sys/dev/hwpmc/hwpmc_rapl.c (revision a99d04f39dab0eac88eb4f5af425aceaf9238207)
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