Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177548627
D60767.1794684766.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
14 KB
Referenced Files
None
Subscribers
None
D60767.1794684766.diff
View Options
Index: lib/libpmc/Makefile
===================================================================
--- lib/libpmc/Makefile
+++ lib/libpmc/Makefile
@@ -95,6 +95,7 @@
pmc_capabilities.3 pmc_pmcinfo.3 \
pmc_capabilities.3 pmc_cpuinfo.3 \
pmc_capabilities.3 pmc_width.3 \
+ pmc_capabilities.3 pmc_cpumask.3 \
pmc_configure_logfile.3 pmc_flush_logfile.3 \
pmc_configure_logfile.3 pmc_writelog.3 \
pmc_disable.3 pmc_enable.3 \
Index: lib/libpmc/libpmc.c
===================================================================
--- lib/libpmc/libpmc.c
+++ lib/libpmc/libpmc.c
@@ -28,6 +28,7 @@
#include <sys/types.h>
#include <sys/param.h>
+#include <sys/cpuset.h>
#include <sys/module.h>
#include <sys/pmc.h>
#include <sys/syscall.h>
@@ -1396,6 +1397,33 @@
return (status);
}
+int
+pmc_cpumask(pmc_id_t pmcid, cpuset_t *mask)
+{
+ struct pmc_op_getcpumask *args;
+ size_t argsize;
+ int cpu, status;
+
+ CPU_ZERO(mask);
+
+ argsize = sizeof(*args) + sizeof(*mask);
+ if ((args = calloc(1, argsize)) == NULL)
+ return (-1);
+
+ args->pm_pmcid = pmcid;
+ args->pm_setsize = sizeof(*mask);
+
+ status = PMC_CALL(PMC_OP_GETCPUMASK, args);
+ if (status == 0) {
+ for (cpu = 0; cpu < CPU_SETSIZE; cpu++)
+ if (isset(args->pm_mask, cpu))
+ CPU_SET(cpu, mask);
+ }
+
+ free(args);
+ return (status);
+}
+
int
pmc_configure_logfile(int fd)
{
Index: lib/libpmc/pmc.h
===================================================================
--- lib/libpmc/pmc.h
+++ lib/libpmc/pmc.h
@@ -30,6 +30,7 @@
#define _PMC_H_
#include <sys/types.h>
+#include <sys/_cpuset.h>
#include <sys/pmc.h>
/*
@@ -75,6 +76,7 @@
int _cpu, pmc_id_t *_pmcid, uint64_t count);
int pmc_attach(pmc_id_t _pmcid, pid_t _pid);
int pmc_capabilities(pmc_id_t _pmc, uint32_t *_caps);
+int pmc_cpumask(pmc_id_t _pmc, cpuset_t *_mask);
int pmc_configure_logfile(int _fd);
int pmc_flush_logfile(void);
int pmc_close_logfile(void);
Index: sys/dev/hwpmc/hwpmc_amd.c
===================================================================
--- sys/dev/hwpmc/hwpmc_amd.c
+++ sys/dev/hwpmc/hwpmc_amd.c
@@ -1224,6 +1224,107 @@
return (0);
}
+/*
+ * Return the set of active CPUs sharing the L3 slice that "cpu" is part of.
+ */
+static void
+amd_l3_cpumask(struct cpu_group *cg, int cpu, cpuset_t *mask)
+{
+ int i, other;
+
+ if (!CPU_ISSET(cpu, &cg->cg_mask))
+ return;
+
+ if (cg->cg_level == CG_SHARE_L3) {
+ CPU_FOREACH_ISSET(other, &cg->cg_mask) {
+ if (pmc_cpu_is_active(other))
+ CPU_SET(other, mask);
+ }
+ return;
+ }
+
+ for (i = 0; i < cg->cg_children; i++)
+ amd_l3_cpumask(&cg->cg_child[i], cpu, mask);
+}
+
+/* Grouping key for "cpu": equal key => same package (DF/UMC share it). */
+static u_int
+amd_pkg_key(int cpu)
+{
+ u_int shift;
+
+ shift = (cpu_procinfo2 & AMDID_COREID_SIZE) >> AMDID_COREID_SIZE_SHIFT;
+ if (shift == 0)
+ shift = fls(cpu_procinfo2 & AMDID_CMP_CORES);
+
+ return (pcpu_find(cpu)->pc_apic_id >> shift);
+}
+
+static void
+amd_pkg_cpumask(int cpu, cpuset_t *mask)
+{
+ u_int key;
+ int other;
+
+ key = amd_pkg_key(cpu);
+ CPU_FOREACH(other) {
+ if (pmc_cpu_is_active(other) && amd_pkg_key(other) == key)
+ CPU_SET(other, mask);
+ }
+}
+
+/*
+ * UMC counters are shared by the CPUs with the same NodeId (CPUID
+ * 0x8000001e ECX[7:0]). Current parts have one node per package, so
+ * the package key is used. Zen 6 parts can have more than one I/O die
+ * per socket; use NodeId here then (amd_pkg_key() has the same
+ * assumption for DF).
+ */
+static u_int
+amd_umc_sharing_key(int cpu)
+{
+ return (amd_pkg_key(cpu));
+}
+
+static void
+amd_umc_cpumask(int cpu, cpuset_t *mask)
+{
+ u_int key;
+ int other;
+
+ key = amd_umc_sharing_key(cpu);
+ CPU_FOREACH(other) {
+ if (pmc_cpu_is_active(other) && amd_umc_sharing_key(other) == key)
+ CPU_SET(other, mask);
+ }
+}
+
+/* Return the cpus sharing ri's counter hardware, relative to "cpu". */
+static int
+amd_get_cpumask(int ri, int cpu, cpuset_t *mask)
+{
+ KASSERT(ri >= 0 && ri < amd_npmcs,
+ ("[amd,%d] ri %d out of range", __LINE__, ri));
+
+ CPU_ZERO(mask);
+ switch (amd_pmcdesc[ri].pm_subclass) {
+ case PMC_AMD_SUB_CLASS_L3_CACHE:
+ amd_l3_cpumask(cpu_top, cpu, mask);
+ break;
+ case PMC_AMD_SUB_CLASS_DATA_FABRIC:
+ amd_pkg_cpumask(cpu, mask);
+ break;
+ case PMC_AMD_SUB_CLASS_UMC:
+ amd_umc_cpumask(cpu, mask);
+ break;
+ default:
+ CPU_SET(cpu, mask);
+ break;
+ }
+
+ return (0);
+}
+
static u_int
amd_umc_pkg_of(int cpu)
{
@@ -1798,6 +1899,7 @@
pcd->pcd_stop_pmc = amd_stop_pmc;
pcd->pcd_write_pmc = amd_write_pmc;
pcd->pcd_get_caps = amd_get_caps;
+ pcd->pcd_get_cpumask = amd_get_cpumask;
pmc_mdep->pmd_cputype = cputype;
pmc_mdep->pmd_intr = amd_intr;
Index: sys/dev/hwpmc/hwpmc_mod.c
===================================================================
--- sys/dev/hwpmc/hwpmc_mod.c
+++ sys/dev/hwpmc/hwpmc_mod.c
@@ -3899,6 +3899,46 @@
return (error);
}
+/*
+ * Default PMC_OP_GETCPUMASK implementation, used when a class has no
+ * pcd_get_cpumask method, or as the starting point before it is applied.
+ *
+ * A syswide-capable pmc's hardware is shared by every active CPU; a
+ * domwide-capable one is shared by every active CPU in cpu's NUMA domain.
+ * Classes with a finer sharing granularity (e.g. per-L3-slice, per-DF/UMC
+ * instance) override via pcd_get_cpumask.
+ */
+static void
+pmc_default_cpumask(uint32_t caps, int cpu, cpuset_t *mask)
+{
+ struct pcpu *pc;
+ int dom, other;
+
+ CPU_ZERO(mask);
+
+ if ((caps & PMC_CAP_SYSWIDE) != 0) {
+ CPU_FOREACH(other) {
+ if (pmc_cpu_is_active(other))
+ CPU_SET(other, mask);
+ }
+ return;
+ }
+
+ if ((caps & PMC_CAP_DOMWIDE) != 0) {
+ pc = pcpu_find(cpu);
+ dom = pc->pc_domain;
+ CPU_FOREACH(other) {
+ if (!pmc_cpu_is_active(other))
+ continue;
+ if (pcpu_find(other)->pc_domain == dom)
+ CPU_SET(other, mask);
+ }
+ return;
+ }
+
+ CPU_SET(cpu, mask);
+}
+
static int
pmc_syscall_handler(struct thread *td, void *syscall_args)
{
@@ -4725,6 +4765,71 @@
}
break;
+ /*
+ * Get the set of CPUs sharing an already-allocated pmc's
+ * underlying hardware counter instance.
+ */
+
+ case PMC_OP_GETCPUMASK:
+ {
+ struct pmc_op_getcpumask gcm;
+ struct pmc *pm;
+ struct pmc_classdep *pcd;
+ cpuset_t mask;
+ uint8_t bits[sizeof(cpuset_t)];
+ uint32_t caps;
+ size_t len;
+ int adjri, cpu, ri, setcpu;
+
+ PMC_DOWNGRADE_SX();
+
+ if ((error = copyin(arg, &gcm, sizeof(gcm))) != 0)
+ break;
+
+ if ((error = pmc_find_pmc(gcm.pm_pmcid, &pm)) != 0)
+ break;
+
+ ri = PMC_TO_ROWINDEX(pm);
+
+ /*
+ * Virtual-mode pmcs encode PMC_CPU_ANY (not a real cpu id)
+ * into pm_id; the cpumask question only makes sense for a
+ * pmc pinned to one cpu's hardware, so reject anything else
+ * before PMC_TO_CPU() is used as a cpumask/pcpu index below.
+ */
+ if (!PMC_IS_SYSTEM_MODE(PMC_TO_MODE(pm))) {
+ error = EINVAL;
+ break;
+ }
+
+ cpu = PMC_TO_CPU(pm);
+ pcd = pmc_ri_to_classdep(md, ri, &adjri);
+
+ if (pcd->pcd_get_caps == NULL)
+ caps = pcd->pcd_caps;
+ else if ((error = (*pcd->pcd_get_caps)(adjri, &caps)) != 0)
+ break;
+
+ if (pcd->pcd_get_cpumask == NULL)
+ pmc_default_cpumask(caps, cpu, &mask);
+ else if ((error = (*pcd->pcd_get_cpumask)(adjri, cpu,
+ &mask)) != 0)
+ break;
+
+ len = MIN(gcm.pm_setsize, sizeof(mask));
+ if (CPU_FLS(&mask) > (int)(len * NBBY)) {
+ error = ERANGE;
+ break;
+ }
+
+ memset(bits, 0, sizeof(bits));
+ CPU_FOREACH_ISSET(setcpu, &mask)
+ setbit(bits, setcpu);
+ error = copyout(bits, (char *)arg +
+ offsetof(struct pmc_op_getcpumask, pm_mask), len);
+ }
+ break;
+
default:
error = EINVAL;
break;
Index: sys/sys/pmc.h
===================================================================
--- sys/sys/pmc.h
+++ sys/sys/pmc.h
@@ -351,7 +351,8 @@
__PMC_OP(WRITELOG, "Write a cookie to the log file") \
__PMC_OP(CLOSELOG, "Close log file") \
__PMC_OP(GETDYNEVENTINFO, "Get dynamic events list") \
- __PMC_OP(GETCAPS, "Get capabilities")
+ __PMC_OP(GETCAPS, "Get capabilities") \
+ __PMC_OP(GETCPUMASK, "Get the cpus sharing a pmc's counter")
enum pmc_ops {
#undef __PMC_OP
@@ -654,6 +655,20 @@
uint32_t pm_caps; /* capabilities */
};
+/*
+ * OP GETCPUMASK
+ *
+ * Retrieve the set of CPUs that share the given pmc's underlying hardware
+ * counter instance (e.g. all CPUs behind one L3 slice, DF/UMC instance, or
+ * NUMA domain). For a syswide-capable pmc this is every active CPU.
+ */
+
+struct pmc_op_getcpumask {
+ pmc_id_t pm_pmcid; /* allocated pmc id */
+ uint32_t pm_setsize; /* size of pm_mask in bytes */
+ uint8_t pm_mask[]; /* CPU n is bit n % 8 of byte n / 8 */
+};
+
#ifdef _KERNEL
#include <sys/malloc.h>
@@ -1064,6 +1079,7 @@
int (*pcd_describe)(int _cpu, int _ri, struct pmc_info *_pi,
struct pmc **_ppmc);
int (*pcd_get_caps)(int _ri, uint32_t *_caps);
+ int (*pcd_get_cpumask)(int _ri, int _cpu, cpuset_t *_mask);
/* class-dependent initialization & finalization */
int (*pcd_pcpu_init)(struct pmc_mdep *_md, int _cpu);
Index: usr.sbin/pmcstat/pmcstat.c
===================================================================
--- usr.sbin/pmcstat/pmcstat.c
+++ usr.sbin/pmcstat/pmcstat.c
@@ -438,15 +438,15 @@
int
main(int argc, char **argv)
{
- cpuset_t cpumask, dommask, rootmask;
+ cpuset_t cpumask, pmcmask, remaining, repmask, rootmask, single;
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, domains;
- int do_userspace, i;
- size_t len;
+ int c, check_driver_stats;
+ int cpu, do_callchain, do_descendants, do_logproccsw, do_logprocexit;
+ int explicit_cpumask;
+ int do_print, do_read, do_listcounters, do_descr;
+ int do_userspace;
int graphdepth;
int pipefd[2], rfd;
int use_cumulative_counts;
@@ -461,7 +461,7 @@
struct kevent kev;
struct winsize ws;
struct stat sb;
- uint32_t caps;
+ pmc_id_t probe_pmcid;
check_driver_stats = 0;
current_sampling_count = 0;
@@ -472,8 +472,8 @@
do_logproccsw = 0;
do_logprocexit = 0;
do_listcounters = 0;
- domains = 0;
use_cumulative_counts = 0;
+ explicit_cpumask = 0;
graphfilename = "-";
args.pa_required = 0;
args.pa_flags = 0;
@@ -502,12 +502,8 @@
bzero(&ds_end, sizeof(ds_end));
ev = NULL;
event = NULL;
- caps = 0;
CPU_ZERO(&cpumask);
-
- len = sizeof(domains);
- if (sysctlbyname("vm.ndomains", &domains, &len, NULL, 0) == -1)
- err(EX_OSERR, "ERROR: Cannot get number of domains");
+ CPU_ZERO(&repmask);
/*
* The initial CPU mask specifies the root mask of this process
@@ -542,6 +538,7 @@
else
pmcstat_get_cpumask(optarg, &cpumask);
+ explicit_cpumask = 1;
args.pa_flags |= FLAGS_HAS_CPUMASK;
args.pa_required |= FLAG_HAS_SYSTEM_PMCS;
break;
@@ -639,7 +636,6 @@
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.");
@@ -712,10 +708,11 @@
if (args.pa_flags & FLAG_HAS_SYSTEM_PMCS) {
/*
- * We need to check the capabilities of the
- * desired event to determine if it should be
- * allocated on every CPU, or only a subset of
- * them. This requires allocating a PMC now.
+ * Allocate the pmc on its first cpu, then ask
+ * the kernel which cpus share its underlying
+ * hardware counter instance; the loop below
+ * only needs to cover whatever cpus (if any)
+ * fall outside that set.
*/
if (pmc_allocate(ev->ev_spec, ev->ev_mode,
ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid,
@@ -723,47 +720,58 @@
err(EX_OSERR, "ERROR: Cannot allocate "
"system-mode pmc with specification"
" \"%s\"", ev->ev_spec);
- if (pmc_capabilities(ev->ev_pmcid, &caps)) {
+ if (pmc_cpumask(ev->ev_pmcid, &pmcmask)) {
pmc_release(ev->ev_pmcid);
err(EX_OSERR, "ERROR: Cannot get pmc "
- "capabilities");
+ "cpumask");
}
/*
- * Release the PMC now that we have caps; we
- * will reallocate shortly.
+ * Release the PMC now that we have its
+ * cpumask; it will be reallocated, along
+ * with any clones below, once pmcstat
+ * actually starts running.
*/
pmc_release(ev->ev_pmcid);
ev->ev_pmcid = PMC_ID_INVALID;
+ args.pa_flags |= FLAGS_HAS_CPUMASK;
+ CPU_SET(ev->ev_cpu, &repmask);
}
STAILQ_INSERT_TAIL(&args.pa_events, ev, ev_next);
- if ((caps & PMC_CAP_SYSWIDE) == PMC_CAP_SYSWIDE) {
- CPU_ZERO(&cpumask);
- CPU_SET(0, &cpumask);
- args.pa_flags |= FLAGS_HAS_CPUMASK;
- }
- 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);
- pmcstat_clone_event_descriptor(ev, &cpumask, &args);
- CPU_SET(ev->ev_cpu, &cpumask);
+ CPU_COPY(&cpumask, &remaining);
+ CPU_CLR(ev->ev_cpu, &remaining);
+ CPU_ANDNOT(&remaining, &remaining, &pmcmask);
+
+ while (!CPU_EMPTY(&remaining)) {
+ cpu = CPU_FFS(&remaining) - 1;
+
+ if (pmc_allocate(ev->ev_spec,
+ ev->ev_mode, ev->ev_flags, cpu,
+ &probe_pmcid, ev->ev_count) < 0)
+ err(EX_OSERR, "ERROR: Cannot "
+ "allocate system-mode pmc "
+ "with specification \"%s\"",
+ ev->ev_spec);
+ if (pmc_cpumask(probe_pmcid,
+ &pmcmask)) {
+ pmc_release(probe_pmcid);
+ err(EX_OSERR, "ERROR: Cannot "
+ "get pmc cpumask");
+ }
+ pmc_release(probe_pmcid);
+
+ CPU_ZERO(&single);
+ CPU_SET(cpu, &single);
+ pmcstat_clone_event_descriptor(ev,
+ &single, &args);
+ CPU_SET(cpu, &repmask);
+
+ CPU_ANDNOT(&remaining, &remaining,
+ &pmcmask);
+ }
}
break;
@@ -896,6 +904,17 @@
(args.pa_flags & FLAGS_HAS_CPUMASK) == 0)
CPU_FILL(&cpumask);
+ /*
+ * Absent an explicit -c, the -R replay filter must track the cpus
+ * that actually got a system-mode pmc allocated above (one
+ * representative per sharing group), not the full -c/root mask
+ * those representatives were chosen from -- otherwise it silently
+ * admits every cpu during replay instead of just the ones with
+ * real samples.
+ */
+ if (!explicit_cpumask && !CPU_EMPTY(&repmask))
+ CPU_COPY(&repmask, &cpumask);
+
args.pa_cpumask = cpumask; /* For selecting CPUs using -R. */
if (argc) /* command line present */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 14, 7:32 PM (11 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41710841
Default Alt Text
D60767.1794684766.diff (14 KB)
Attached To
Mode
D60767: hwpmc: add PMC_OP_GETCPUMASK
Attached
Detach File
Event Timeline