Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 28 Jul 2026 15:01:28 +0000
From:      Mitchell Horne <mhorne@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Cc:        Andre Silva <andasilv@amd.com>
Subject:   git: 3fdd05c61216 - main - libpmc: userland support and pmc.rapl.3 for the RAPL class
Message-ID:  <6a68c448.1f1ac.2c28beec@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by mhorne:

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

commit 3fdd05c612169d56c30b90d72533b75383210e81
Author:     Andre Silva <andasilv@amd.com>
AuthorDate: 2026-07-28 14:52:22 +0000
Commit:     Mitchell Horne <mhorne@FreeBSD.org>
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 <ali@mashtizadeh.com>
    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 .


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a68c448.1f1ac.2c28beec>