Page MenuHomeFreeBSD

D58029.id181268.diff
No OneTemporary

D58029.id181268.diff

diff --git a/lib/libpmc/Makefile b/lib/libpmc/Makefile
--- a/lib/libpmc/Makefile
+++ b/lib/libpmc/Makefile
@@ -77,6 +77,7 @@
MAN+= pmc.ibs.3
MAN+= pmc.ivybridge.3
MAN+= pmc.ivybridgexeon.3
+MAN+= pmc.rapl.3
MAN+= pmc.sandybridge.3
MAN+= pmc.sandybridgeuc.3
MAN+= pmc.sandybridgexeon.3
diff --git a/lib/libpmc/libpmc.c b/lib/libpmc/libpmc.c
--- a/lib/libpmc/libpmc.c
+++ b/lib/libpmc/libpmc.c
@@ -58,6 +58,8 @@
struct pmc_op_pmcallocate *_pmc_config);
static int tsc_allocate_pmc(enum pmc_event _pe, char *_ctrspec,
struct pmc_op_pmcallocate *_pmc_config);
+static int rapl_allocate_pmc(enum pmc_event _pe, char *_ctrspec,
+ struct pmc_op_pmcallocate *_pmc_config);
#endif
#if defined(__arm__)
static int armv7_allocate_pmc(enum pmc_event _pe, char *_ctrspec,
@@ -191,6 +193,11 @@
__PMC_EV_ALIAS_TSC()
};
+static const struct pmc_event_descr rapl_event_table[] =
+{
+ __PMC_EV_RAPL()
+};
+
#undef PMC_CLASS_TABLE_DESC
#define PMC_CLASS_TABLE_DESC(NAME, CLASS, EVENTS, ALLOCATOR) \
static const struct pmc_class_descr NAME##_class_table_descr = \
@@ -208,6 +215,7 @@
PMC_CLASS_TABLE_DESC(k8, K8, k8, k8);
PMC_CLASS_TABLE_DESC(ibs, IBS, ibs, ibs);
PMC_CLASS_TABLE_DESC(tsc, TSC, tsc, tsc);
+PMC_CLASS_TABLE_DESC(rapl, RAPL, rapl, rapl);
#endif
#if defined(__arm__)
PMC_CLASS_TABLE_DESC(cortex_a8, ARMV7, cortex_a8, armv7);
@@ -879,6 +887,22 @@
return (0);
}
+
+static int
+rapl_allocate_pmc(enum pmc_event pe, char *ctrspec,
+ struct pmc_op_pmcallocate *pmc_config)
+{
+ if ((int)pe < PMC_EV_RAPL_FIRST || (int)pe > PMC_EV_RAPL_LAST)
+ return (-1);
+
+ /* RAPL events must be unqualified. */
+ if (ctrspec && *ctrspec != '\0')
+ return (-1);
+
+ pmc_config->pm_caps |= PMC_CAP_READ;
+
+ return (0);
+}
#endif
static struct pmc_event_alias generic_aliases[] = {
@@ -1435,6 +1459,10 @@
ev = tsc_event_table;
count = PMC_EVENT_TABLE_SIZE(tsc);
break;
+ case PMC_CLASS_RAPL:
+ ev = rapl_event_table;
+ count = PMC_EVENT_TABLE_SIZE(rapl);
+ break;
case PMC_CLASS_K8:
ev = k8_event_table;
count = PMC_EVENT_TABLE_SIZE(k8);
@@ -1643,6 +1671,10 @@
pmc_class_table[n++] = &tsc_class_table_descr;
break;
+ case PMC_CLASS_RAPL:
+ pmc_class_table[n++] = &rapl_class_table_descr;
+ break;
+
case PMC_CLASS_K8:
pmc_class_table[n++] = &k8_class_table_descr;
break;
@@ -1915,6 +1947,9 @@
} else if (pe == PMC_EV_TSC_TSC) {
ev = tsc_event_table;
evfence = tsc_event_table + PMC_EVENT_TABLE_SIZE(tsc);
+ } else if (pe >= PMC_EV_RAPL_FIRST && pe <= PMC_EV_RAPL_LAST) {
+ ev = rapl_event_table;
+ evfence = rapl_event_table + PMC_EVENT_TABLE_SIZE(rapl);
} else if ((int)pe >= PMC_EV_SOFT_FIRST && (int)pe <= PMC_EV_SOFT_LAST) {
ev = soft_event_table;
evfence = soft_event_table + soft_event_info.pm_nevent;
diff --git a/lib/libpmc/pmc.3 b/lib/libpmc/pmc.3
--- a/lib/libpmc/pmc.3
+++ b/lib/libpmc/pmc.3
@@ -233,6 +233,12 @@
Programmable hardware counters present in
.Tn "AMD Athlon64"
CPUs.
+.It Li PMC_CLASS_RAPL
+RAPL energy counters present in
+.Tn AMD
+and
+.Tn Intel
+CPUs.
.It Li PMC_CLASS_TSC
The timestamp counter on i386 and amd64 architecture CPUs.
.It Li PMC_CLASS_ARMV7
@@ -498,6 +504,7 @@
.It Li PMC_CLASS_IAP Ta Xr pmc.atom 3 , Xr pmc.core 3 , Xr pmc.core2 3
.It Li PMC_CLASS_IBS Ta Xr pmc.ibs 3
.It Li PMC_CLASS_K8 Ta Xr pmc.amd 3
+.It Li PMC_CLASS_RAPL Ta Xr pmc.rapl 3
.It Li PMC_CLASS_TSC Ta Xr pmc.tsc 3
.El
.Ss Event Name Aliases
@@ -551,6 +558,7 @@
.Xr pmc.ibs 3 ,
.Xr pmc.ivybridge 3 ,
.Xr pmc.ivybridgexeon 3 ,
+.Xr pmc.rapl 3 ,
.Xr pmc.sandybridge 3 ,
.Xr pmc.sandybridgeuc 3 ,
.Xr pmc.sandybridgexeon 3 ,
diff --git a/lib/libpmc/pmc.rapl.3 b/lib/libpmc/pmc.rapl.3
new file mode 100644
--- /dev/null
+++ b/lib/libpmc/pmc.rapl.3
@@ -0,0 +1,138 @@
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" Copyright (c) 2026 Advanced Micro Devices, Inc.
+.\"
+.Dd July 2, 2026
+.Dt PMC.RAPL 3
+.Os
+.Sh NAME
+.Nm pmc.rapl
+.Nd measurements using RAPL energy counters
+.Sh LIBRARY
+.Lb libpmc
+.Sh SYNOPSIS
+.In pmc.h
+.Sh DESCRIPTION
+.Tn AMD
+and
+.Tn Intel
+processors report cumulative energy consumption through the RAPL
+(Running Average Power Limit) machine specific registers.
+The
+.Li PMC_CLASS_RAPL
+class exposes these registers as read-only 64-bit counters that may
+only be allocated in system-wide counting mode
+.Pq Li PMC_MODE_SC .
+Counter values are cumulative energy in microjoules; the hardware
+energy unit, including the fixed DRAM unit used by
+.Tn Intel
+server processors, is applied in the kernel.
+.Pp
+A counter's accumulator is not reset when a PMC is allocated or
+released; it reflects energy consumed since the counters began
+accumulating and is reset only when the
+.Xr hwpmc 4
+module is unloaded.
+An individual reading is therefore meaningful only as a difference
+between two samples.
+.Pp
+The hardware counters are 32 bits wide and wrap frequently under load.
+The kernel folds them into 64-bit accumulators and samples every
+counter periodically while at least one RAPL PMC is allocated, so the
+exported values stay monotonic across counter wrap-around.
+.Pp
+RAPL events do not support further event qualifiers.
+.Ss Event Specifiers
+The following event names are supported:
+.Bl -tag -width indent
+.It Li rapl-energy-pkg
+Energy consumed by the processor package (socket) the counter is bound
+to.
+.It Li rapl-energy-cores
+On
+.Tn AMD
+processors, energy consumed by the physical core the counter is bound
+to.
+On
+.Tn Intel
+processors, energy consumed by the PP0 (all cores) power plane of the
+package, i.e. a package-scoped value.
+.It Li rapl-energy-dram
+Energy consumed by the DRAM domain of the package.
+Present only on
+.Tn Intel
+processors that implement the DRAM domain, typically server parts.
+.El
+.Ss Counter Scope
+RAPL counters measure package or core power domains, not individual
+CPUs: every CPU of a domain reads the same underlying counter.
+Consumers should bind one PMC per domain and must not sum readings
+from CPUs that share a domain.
+.Pp
+The class advertises the
+.Li PMC_CAP_DOMWIDE
+capability, which makes
+.Xr pmcstat 8
+allocate one counter per NUMA domain instead of one per CPU.
+NUMA domains follow the firmware memory policy and need not match
+packages, in either direction:
+.Bl -bullet
+.It
+With several NUMA domains per package (for example
+.Tn AMD
+NPS2/NPS4 or
+.Tn Intel
+Sub-NUMA Clustering),
+.Xr pmcstat 8
+allocates one counter per domain, so package-scoped counters within
+one package alias each other: each reading is correct on its own,
+but they must not be summed.
+.It
+With a single NUMA domain spanning several packages (for example
+.Tn AMD
+NPS0 memory interleaving, NUMA disabled in firmware, or a kernel
+built without NUMA support),
+.Xr pmcstat 8
+allocates only one counter, on the first configured CPU, and the
+remaining packages are silently not measured.
+.El
+.Pp
+For explicit placement, allocate one system-scope PMC per package
+with
+.Xr pmc_allocate 3 ,
+or give
+.Xr pmcstat 8
+one
+.Fl c Ar cpu
+and
+.Fl s Ar event
+pair per package, for example on a two-socket system whose second
+package starts at CPU 64:
+.Dl pmcstat -c 0 -s rapl-energy-pkg -c 64 -s rapl-energy-pkg
+A single
+.Fl c
+list naming several CPUs does not achieve this: for
+.Li PMC_CAP_DOMWIDE
+counters
+.Xr pmcstat 8
+honors only the first CPU of the list.
+.Ss Privilege
+Allocating a RAPL PMC always requires the
+.Li PRIV_PMC_SYSTEM
+privilege, regardless of the
+.Va security.bsd.unprivileged_syspmcs
+sysctl: fine-grained energy readings form a power side channel
+(PLATYPUS, CVE-2020-8694 and CVE-2020-12912).
+Unprivileged allocation requests fail with
+.Er EPERM .
+.Sh SEE ALSO
+.Xr pmc 3 ,
+.Xr pmc.tsc 3 ,
+.Xr pmc_allocate 3 ,
+.Xr pmc_read 3 ,
+.Xr pmclog 3 ,
+.Xr hwpmc 4 ,
+.Xr pmcstat 8
+.Sh HISTORY
+The RAPL PMC class first appeared in
+.Fx 16.0 .

File Metadata

Mime Type
text/plain
Expires
Sun, Jul 12, 12:10 AM (19 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34893068
Default Alt Text
D58029.id181268.diff (7 KB)

Event Timeline