git: b6e28991bf3a - main - System wide and NUMA domain wide counters support. PMC classes for ARM DMC-620 and CMN-600.

From: Toomas Soome <tsoome_at_FreeBSD.org>
Date: Sun, 26 Jun 2022 09:12:04 UTC
The branch main has been updated by tsoome:

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

commit b6e28991bf3aadb74c54c7e6dfc2992b29abae5a
Author:     Aleksandr Rybalko <ray@freebsd.org>
AuthorDate: 2022-02-16 00:17:02 +0000
Commit:     Toomas Soome <tsoome@FreeBSD.org>
CommitDate: 2022-06-26 05:31:03 +0000

    System wide and NUMA domain wide counters support. PMC classes for ARM DMC-620 and CMN-600.
    
    Add support for system wide and NUMA domain wide counters support.
    Add 3 new PMC classes for ARM DMC-620 and CMN-600 controllers PMU.
    
    Reviewed by:    mhorne
    Sponsored By:   ARM
    Sponsored By:   Ampere Computing
    Differential Revision: https://reviews.freebsd.org/D35342
---
 lib/libpmc/pmc.3           |  6 +++-
 sys/sys/pmc.h              | 13 ++++---
 usr.sbin/pmcstat/pmcstat.c | 84 +++++++++++++++++++++++++++++++++++++---------
 3 files changed, 83 insertions(+), 20 deletions(-)

diff --git a/lib/libpmc/pmc.3 b/lib/libpmc/pmc.3
index 3325cb1e3d40..abe9f3208030 100644
--- a/lib/libpmc/pmc.3
+++ b/lib/libpmc/pmc.3
@@ -23,7 +23,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd August 10, 2021
+.Dd May 28, 2022
 .Dt PMC 3
 .Os
 .Sh NAME
@@ -200,6 +200,8 @@ Supported capabilities include:
 .Bl -tag -width "Li PMC_CAP_INTERRUPT" -compact
 .It Li PMC_CAP_CASCADE
 The ability to cascade counters.
+.It Li PMC_CAP_DOMWIDE
+Separate counters tied to each NUMA domain.
 .It Li PMC_CAP_EDGE
 The ability to count negated to asserted transitions of the hardware
 conditions being probed for.
@@ -218,6 +220,8 @@ The ability to read from performance counters.
 .It Li PMC_CAP_SYSTEM
 The ability to restrict counting of hardware events to when the CPU is
 running privileged code.
+.It Li PMC_CAP_SYSWIDE
+A single counter aggregating events for the whole system.
 .It Li PMC_CAP_THRESHOLD
 The ability to ignore simultaneous hardware events below a
 programmable threshold.
diff --git a/sys/sys/pmc.h b/sys/sys/pmc.h
index 18c38ca36659..5465d0532a2a 100644
--- a/sys/sys/pmc.h
+++ b/sys/sys/pmc.h
@@ -171,7 +171,10 @@ enum pmc_cputype {
 	__PMC_CLASS(MIPS74K,	0x12,	"MIPS 74K")			\
 	__PMC_CLASS(E500,	0x13,	"Freescale e500 class")		\
 	__PMC_CLASS(BERI,	0x14,	"MIPS BERI")			\
-	__PMC_CLASS(POWER8,	0x15,	"IBM POWER8 class")
+	__PMC_CLASS(POWER8,	0x15,	"IBM POWER8 class")		\
+	__PMC_CLASS(DMC620_PMU_CD2, 0x16, "ARM DMC620 Memory Controller PMU CLKDIV2") \
+	__PMC_CLASS(DMC620_PMU_C, 0x17, "ARM DMC620 Memory Controller PMU CLK") \
+	__PMC_CLASS(CMN600_PMU, 0x18,	"Arm CoreLink CMN600 Coherent Mesh Network PMU")
 
 enum pmc_class {
 #undef  __PMC_CLASS
@@ -180,7 +183,7 @@ enum pmc_class {
 };
 
 #define	PMC_CLASS_FIRST	PMC_CLASS_TSC
-#define	PMC_CLASS_LAST	PMC_CLASS_POWER8
+#define	PMC_CLASS_LAST	PMC_CLASS_CMN600_PMU
 
 /*
  * A PMC can be in the following states:
@@ -308,7 +311,9 @@ enum pmc_disp {
 	__PMC_CAP(QUALIFIER,	8, "further qualify monitored events")	\
 	__PMC_CAP(PRECISE,	9, "perform precise sampling")		\
 	__PMC_CAP(TAGGING,	10, "tag upstream events")		\
-	__PMC_CAP(CASCADE,	11, "cascade counters")
+	__PMC_CAP(CASCADE,	11, "cascade counters")			\
+	__PMC_CAP(SYSWIDE,	12, "system wide counter")		\
+	__PMC_CAP(DOMWIDE,	13, "NUMA domain wide counter")
 
 enum pmc_caps
 {
@@ -318,7 +323,7 @@ enum pmc_caps
 };
 
 #define	PMC_CAP_FIRST		PMC_CAP_INTERRUPT
-#define	PMC_CAP_LAST		PMC_CAP_CASCADE
+#define	PMC_CAP_LAST		PMC_CAP_DOMWIDE
 
 /*
  * PMC Event Numbers
diff --git a/usr.sbin/pmcstat/pmcstat.c b/usr.sbin/pmcstat/pmcstat.c
index 3e2d101ab113..08e43d5d446a 100644
--- a/usr.sbin/pmcstat/pmcstat.c
+++ b/usr.sbin/pmcstat/pmcstat.c
@@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$");
 #include <regex.h>
 #include <signal.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -116,6 +117,7 @@ static int	pmcstat_kq;
 static kvm_t	*pmcstat_kvm;
 static struct kinfo_proc *pmcstat_plist;
 struct pmcstat_args args;
+static bool	libpmc_initialized = false;
 
 static void
 pmcstat_get_cpumask(const char *cpuspec, cpuset_t *cpumask)
@@ -419,6 +421,22 @@ pmcstat_topexit(void)
 	endwin();
 }
 
+static inline void
+libpmc_initialize(int *npmc)
+{
+
+	if (libpmc_initialized)
+		return;
+	if (pmc_init() < 0)
+		err(EX_UNAVAILABLE, "ERROR: Initialization of the pmc(3)"
+		    " library failed");
+
+	/* assume all CPUs are identical */
+	if ((*npmc = pmc_npmc(0)) < 0)
+		err(EX_OSERR, "ERROR: Cannot determine the number of PMCs on "
+		    "CPU %d", 0);
+	libpmc_initialized = true;
+}
 /*
  * Main
  */
@@ -426,14 +444,14 @@ pmcstat_topexit(void)
 int
 main(int argc, char **argv)
 {
-	cpuset_t cpumask, rootmask;
+	cpuset_t cpumask, dommask, rootmask;
 	double interval;
 	double duration;
 	int option, npmc;
 	int c, check_driver_stats; 
 	int do_callchain, do_descendants, do_logproccsw, do_logprocexit;
-	int do_print, do_read, do_listcounters, do_descr;
-	int do_userspace;
+	int do_print, do_read, do_listcounters, do_descr, domains;
+	int do_userspace, i;
 	size_t len;
 	int graphdepth;
 	int pipefd[2], rfd;
@@ -450,6 +468,7 @@ main(int argc, char **argv)
 	struct winsize ws;
 	struct stat sb;
 	char buffer[PATH_MAX];
+	uint32_t caps;
 
 	check_driver_stats      = 0;
 	current_sampling_count  = 0;
@@ -460,6 +479,7 @@ main(int argc, char **argv)
 	do_logproccsw           = 0;
 	do_logprocexit          = 0;
 	do_listcounters         = 0;
+	domains			= 0;
 	use_cumulative_counts   = 0;
 	graphfilename		= "-";
 	args.pa_required	= 0;
@@ -489,8 +509,10 @@ main(int argc, char **argv)
 	bzero(&ds_end, sizeof(ds_end));
 	ev = NULL;
 	event = NULL;
+	caps = 0;
 	CPU_ZERO(&cpumask);
 
+
 	/* Default to using the running system kernel. */
 	len = 0;
 	if (sysctlbyname("kern.bootfile", NULL, &len, NULL, 0) == -1)
@@ -500,6 +522,9 @@ main(int argc, char **argv)
 		errx(EX_SOFTWARE, "ERROR: Out of memory.");
 	if (sysctlbyname("kern.bootfile", args.pa_kernel, &len, NULL, 0) == -1)
 		err(EX_OSERR, "ERROR: Cannot determine path of running kernel");
+	len = sizeof(domains);
+	if (sysctlbyname("vm.ndomains", &domains, &len, NULL, 0) == -1)
+		err(EX_OSERR, "ERROR: Cannot get number of domains");
 
 	/*
 	 * The initial CPU mask specifies the root mask of this process
@@ -640,6 +665,7 @@ main(int argc, char **argv)
 		case 's':	/* system-wide counting PMC */
 		case 'P':	/* process virtual sampling PMC */
 		case 'S':	/* system-wide sampling PMC */
+			caps = 0;
 			if ((ev = malloc(sizeof(*ev))) == NULL)
 				errx(EX_SOFTWARE, "ERROR: Out of memory.");
 
@@ -707,12 +733,48 @@ main(int argc, char **argv)
 				errx(EX_SOFTWARE, "ERROR: Out of memory.");
 			(void) strncpy(ev->ev_name, optarg, c);
 			*(ev->ev_name + c) = '\0';
+			libpmc_initialize(&npmc);
+			if (args.pa_flags & FLAG_HAS_SYSTEM_PMCS) {
+				if (pmc_allocate(ev->ev_spec, ev->ev_mode,
+				    ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid,
+				    ev->ev_count) < 0)
+					err(EX_OSERR, "ERROR: Cannot allocate "
+					    "system-mode pmc with specification"
+					    " \"%s\"", ev->ev_spec);
+				if (pmc_capabilities(ev->ev_pmcid, &caps)) {
+					pmc_release(ev->ev_pmcid);
+					err(EX_OSERR, "ERROR: Cannot get pmc "
+					    "capabilities");
+				}
+			}
+
 
 			STAILQ_INSERT_TAIL(&args.pa_events, ev, ev_next);
 
+			if ((caps & PMC_CAP_SYSWIDE) == PMC_CAP_SYSWIDE)
+				break;
+			if ((caps & PMC_CAP_DOMWIDE) == PMC_CAP_DOMWIDE) {
+				CPU_ZERO(&cpumask);
+				/*
+				 * Get number of domains and allocate one
+				 * counter in each.
+				 * First already allocated.
+				 */
+				for (i = 1; i < domains; i++) {
+					CPU_ZERO(&dommask);
+					cpuset_getaffinity(CPU_LEVEL_WHICH,
+					    CPU_WHICH_DOMAIN, i, sizeof(dommask),
+					    &dommask);
+					CPU_SET(CPU_FFS(&dommask) - 1, &cpumask);
+				}
+				args.pa_flags |= FLAGS_HAS_CPUMASK;
+			}
 			if (option == 's' || option == 'S') {
 				CPU_CLR(ev->ev_cpu, &cpumask);
+				pmc_id_t saved_pmcid = ev->ev_pmcid;
+				ev->ev_pmcid = PMC_ID_INVALID;
 				pmcstat_clone_event_descriptor(ev, &cpumask, &args);
+				ev->ev_pmcid = saved_pmcid;
 				CPU_SET(ev->ev_cpu, &cpumask);
 			}
 
@@ -1050,17 +1112,8 @@ main(int argc, char **argv)
 	}
 
 	/* if we've been asked to process a log file, skip init */
-	if ((args.pa_flags & FLAG_READ_LOGFILE) == 0) {
-		if (pmc_init() < 0)
-			err(EX_UNAVAILABLE,
-			    "ERROR: Initialization of the pmc(3) library failed"
-			    );
-
-		if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */
-			err(EX_OSERR,
-"ERROR: Cannot determine the number of PMCs on CPU %d",
-			    0);
-	}
+	if ((args.pa_flags & FLAG_READ_LOGFILE) == 0)
+		libpmc_initialize(&npmc);
 
 	/* Allocate a kqueue */
 	if ((pmcstat_kq = kqueue()) < 0)
@@ -1134,7 +1187,8 @@ main(int argc, char **argv)
 	 */
 
 	STAILQ_FOREACH(ev, &args.pa_events, ev_next) {
-		if (pmc_allocate(ev->ev_spec, ev->ev_mode,
+		if (ev->ev_pmcid == PMC_ID_INVALID &&
+		    pmc_allocate(ev->ev_spec, ev->ev_mode,
 			ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid,
 			ev->ev_count) < 0)
 			err(EX_OSERR,