git: 3fdd05c61216 - main - libpmc: userland support and pmc.rapl.3 for the RAPL class

Mitchell Horne <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a68c448.1f1ac.2c28beec__37474.6896953852$1785251095$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by mhorne:

URL: https://cgit.FreeBSD.org/src/commit/?id=3fdd05c612169d56c30b90d72533b75383210e81

commit 3fdd05c612169d56c30b90d72533b75383210e81
Author:     Andre Silva <[email protected]>
AuthorDate: 2026-07-28 14:52:22 +0000
Commit:     Mitchell Horne <[email protected]>
CommitDate: 2026-07-28 14:54:26 +0000

    libpmc: userland support and pmc.rapl.3 for the RAPL class
    
    Register PMC_CLASS_RAPL in libpmc: event table, allocator, class-table
    descriptor, and the event-name/class-listing lookups, all x86-guarded
    and modeled on the TSC class. Energy events are read-only and
    unqualified. The class prefix (RAPL-) supplies the friendly spelling, so
    pmcstat -S rapl-energy-pkg resolves to the canonical ENERGY_PKG event.
    
    Add a pmc.rapl.3 manual page documenting the events, counter scope, the
    microjoule unit and wrap handling, and the NUMA/package domain mapping;
    link it from pmc.3.
    
    Reviewed by:    mhorne
    Discussed with: Ali Mashtizadeh <[email protected]>
    Sponsored by:   AMD
    Differential Revision:  https://reviews.freebsd.org/D58029
---
 lib/libpmc/Makefile   |   1 +
 lib/libpmc/libpmc.c   |  35 +++++++++++++
 lib/libpmc/pmc.3      |   8 +++
 lib/libpmc/pmc.rapl.3 | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 182 insertions(+)

diff --git a/lib/libpmc/Makefile b/lib/libpmc/Makefile
index 442efdc3d9c0..d86191cc35cb 100644
--- a/lib/libpmc/Makefile
+++ b/lib/libpmc/Makefile
@@ -77,6 +77,7 @@ MAN+=	pmc.iaf.3
 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
index 63228773216e..5fc4630540fc 100644
--- a/lib/libpmc/libpmc.c
+++ b/lib/libpmc/libpmc.c
@@ -58,6 +58,8 @@ static int ibs_allocate_pmc(enum pmc_event _pe, char *_ctrspec,
     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 @@ static const struct pmc_event_descr tsc_event_table[] =
 	__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 @@ static const struct pmc_class_descr NAME##_class_table_descr =	\
 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 @@ tsc_allocate_pmc(enum pmc_event pe, char *ctrspec,
 
 	return (0);
 }
+
+static int
+rapl_allocate_pmc(enum pmc_event pe, char *ctrspec,
+    struct pmc_op_pmcallocate *pmc_config)
+{
+	if (pe < PMC_EV_RAPL_FIRST || pe > PMC_EV_RAPL_LAST)
+		return (-1);
+
+	/* RAPL events must be unqualified. */
+	if (ctrspec != NULL && *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 @@ pmc_event_names_of_class(enum pmc_class cl, const char ***eventnames,
 		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_init(void)
 			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 @@ _pmc_name_of_event(enum pmc_event pe, enum pmc_cputype cpu)
 	} 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
index cb28e0b786b9..b04c2d39cf69 100644
--- a/lib/libpmc/pmc.3
+++ b/lib/libpmc/pmc.3
@@ -233,6 +233,12 @@ Family 10h and above.
 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 @@ following manual pages:
 .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 @@ Doing otherwise is unsupported.
 .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
index 000000000000..e49d9845bbba
--- /dev/null
+++ b/lib/libpmc/pmc.rapl.3
@@ -0,0 +1,138 @@
+.\"
+.\" Copyright (c) 2026 Advanced Micro Devices, Inc.
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd July 6, 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 kernel scales the raw hardware counts by the hardware energy unit,
+so no further conversion is required.
+.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 RAPL PMC class instead provides virtual 64-bit counters that are
+guaranteed to be monotonic, by periodically sampling the hardware
+counters while at least one RAPL PMC is allocated.
+.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 .
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.