Page MenuHomeFreeBSD

D60767.1794684766.diff
No OneTemporary

Authored By
Unknown
Size
14 KB
Referenced Files
None
Subscribers
None

D60767.1794684766.diff

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

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)

Event Timeline