diff --git a/lib/libpmc/libpmc.c b/lib/libpmc/libpmc.c index 6be91a7501f..ceba40aa7b3 100644 --- a/lib/libpmc/libpmc.c +++ b/lib/libpmc/libpmc.c @@ -1211,17 +1211,16 @@ pmc_attach(pmc_id_t pmc, pid_t pid) int pmc_capabilities(pmc_id_t pmcid, uint32_t *caps) { - unsigned int i; - enum pmc_class cl; + struct pmc_op_caps args; + int status; - cl = PMC_ID_TO_CLASS(pmcid); - for (i = 0; i < cpu_info.pm_nclass; i++) - if (cpu_info.pm_classes[i].pm_class == cl) { - *caps = cpu_info.pm_classes[i].pm_caps; - return (0); - } - errno = EINVAL; - return (-1); + args.pm_pmcid = pmcid; + args.pm_caps = 0; + + status = PMC_CALL(PMC_OP_GETCAPS, &args); + *caps = args.pm_caps; + + return (status); } int diff --git a/share/man/man4/hwpmc.4 b/share/man/man4/hwpmc.4 index 54a251dcca7..1ab690e5009 100644 --- a/share/man/man4/hwpmc.4 +++ b/share/man/man4/hwpmc.4 @@ -325,6 +325,10 @@ This operation returns to the caller after the write operation has returned. The returned error code reflects any pending error state inside .Nm . +.It Dv PMC_OP_GETCAPS +Retrieve the capabilities associated with a specific PMC counter. +Some capabilities may be limited to specific indices (i.e., not available +across all counters within a class). .It Dv PMC_OP_GETCPUINFO Retrieve information about the highest possible CPU number for the system, and the number of hardware performance monitoring counters available per CPU. diff --git a/sys/dev/hwpmc/hwpmc_amd.c b/sys/dev/hwpmc/hwpmc_amd.c index c27d93995d5..51505bfcff3 100644 --- a/sys/dev/hwpmc/hwpmc_amd.c +++ b/sys/dev/hwpmc/hwpmc_amd.c @@ -702,6 +702,20 @@ amd_get_msr(int ri, uint32_t *msr) return (0); } +/* + * Return the capabilities of the given PMC. + */ +static int +amd_get_caps(int ri, uint32_t *caps) +{ + KASSERT(ri >= 0 && ri < amd_npmcs, + ("[amd,%d] ri %d out of range", __LINE__, ri)); + + *caps = amd_pmcdesc[ri].pm_descr.pd_caps; + + return (0); +} + /* * Processor-dependent initialization. */ @@ -958,6 +972,7 @@ pmc_amd_initialize(void) pcd->pcd_start_pmc = amd_start_pmc; pcd->pcd_stop_pmc = amd_stop_pmc; pcd->pcd_write_pmc = amd_write_pmc; + pcd->pcd_get_caps = amd_get_caps; pmc_mdep->pmd_cputype = cputype; pmc_mdep->pmd_intr = amd_intr; diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c index 1fa021429c5..fb1fdf83239 100644 --- a/sys/dev/hwpmc/hwpmc_mod.c +++ b/sys/dev/hwpmc/hwpmc_mod.c @@ -4538,6 +4538,51 @@ pmc_syscall_handler(struct thread *td, void *syscall_args) } break; + /* + * Get the PMC capabilities + */ + + case PMC_OP_GETCAPS: + { + struct pmc_op_caps c; + struct pmc *pm; + struct pmc_classdep *pcd; + pmc_id_t pmcid; + int adjri, ri; + + PMC_DOWNGRADE_SX(); + + if ((error = copyin(arg, &c, sizeof(c))) != 0) + break; + + pmcid = c.pm_pmcid; + + if ((error = pmc_find_pmc(pmcid, &pm)) != 0) + break; + + KASSERT(pmcid == pm->pm_id, + ("[pmc,%d] pmc id %x != pmcid %x", __LINE__, + pm->pm_id, pmcid)); + + ri = PMC_TO_ROWINDEX(pm); + pcd = pmc_ri_to_classdep(md, ri, &adjri); + + /* + * If PMC class has no GETCAPS return the class capabilities + * otherwise get the per counter capabilities. + */ + if (pcd->pcd_get_caps == NULL) { + c.pm_caps = pcd->pcd_caps; + } else { + error = (*pcd->pcd_get_caps)(adjri, &c.pm_caps); + if (error < 0) + break; + } + + if ((error = copyout(&c, arg, sizeof(c))) < 0) + break; + } + break; default: error = EINVAL; diff --git a/sys/sys/pmc.h b/sys/sys/pmc.h index 7640a9b96c8..244a18e90c3 100644 --- a/sys/sys/pmc.h +++ b/sys/sys/pmc.h @@ -346,7 +346,8 @@ enum pmc_event { __PMC_OP(PMCSTOP, "Stop a PMC") \ __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(GETDYNEVENTINFO, "Get dynamic events list") \ + __PMC_OP(GETCAPS, "Get capabilities") enum pmc_ops { #undef __PMC_OP @@ -638,6 +639,17 @@ struct pmc_op_getdyneventinfo { struct pmc_dyn_event_descr pm_events[PMC_EV_DYN_COUNT]; }; +/* + * OP GETCAPS + * + * Retrieve the PMC capabilties flags for this type of counter. + */ + +struct pmc_op_caps { + pmc_id_t pm_pmcid; /* allocated pmc id */ + uint32_t pm_caps; /* capabilities */ +}; + #ifdef _KERNEL #include @@ -1040,6 +1052,7 @@ struct pmc_classdep { /* description */ int (*pcd_describe)(int _cpu, int _ri, struct pmc_info *_pi, struct pmc **_ppmc); + int (*pcd_get_caps)(int _ri, uint32_t *_caps); /* class-dependent initialization & finalization */ int (*pcd_pcpu_init)(struct pmc_mdep *_md, int _cpu);