From: Jie Zhan <zhanjie9@hisilicon.com> driver inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9163 ---------------------------------------------------------------------- Subsystems that want to drive perf events from inside the kernel need to populate perf_event_attr->type before calling perf_event_create_kernel_counter(). The PMU type id is assigned by perf_pmu_register() at runtime and is not known to the consumer. Today no exported helper in kernel provides a dynamic PMU's type for callers. Add perf_pmu_type_by_parent_dev() that walks the existing pmus list under pmus_srcu and returns the type id of the PMU whose pmu->parent equals the supplied struct device. The lookup is intentionally racy: the PMU may be unregistered before the caller invokes perf_event_create_kernel_counter(), in which case that call returns -ENOENT. Callers should treat the lookup as a hint. Signed-off-by: Jie Zhan <zhanjie9@hisilicon.com> Signed-off-by: Hongye Lin <linhongye@h-partners.com> --- include/linux/perf_event.h | 2 ++ kernel/events/core.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 9bbcdaf9ddad..ff1dac4d565c 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1169,6 +1169,7 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, struct task_struct *task, perf_overflow_handler_t callback, void *context); +extern int perf_pmu_type_by_parent_dev(struct device *parent); extern void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu); int perf_event_read_local(struct perf_event *event, u64 *value, @@ -1889,6 +1890,7 @@ static inline void perf_event_disable(struct perf_event *event) { } static inline int __perf_event_disable(void *info) { return -1; } static inline void perf_event_task_tick(void) { } static inline int perf_event_release_kernel(struct perf_event *event) { return 0; } +static inline int perf_pmu_type_by_parent_dev(struct device *parent) { return -ENOENT; } static inline int perf_event_period(struct perf_event *event, u64 value) { return -EINVAL; diff --git a/kernel/events/core.c b/kernel/events/core.c index 859fd4be61d6..ba690fe2cf97 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -11788,6 +11788,34 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type) } EXPORT_SYMBOL_GPL(perf_pmu_register); +/** + * perf_pmu_type_by_parent_dev - Look up a PMU's type ID by its parent device + * @parent: the &struct device the PMU driver passed in pmu->parent + * + * Returns the type ID of the PMU whose pmu->parent matches @parent, or -ENOENT + * if no such PMU is currently registered. + * + * The PMU may be unregistered between this call and the perf_event creation; in + * that case perf_event_create_kernel_counter() will fail with -ENOENT, so + * callers may treat the lookup as a hint. + */ +int perf_pmu_type_by_parent_dev(struct device *parent) +{ + struct pmu *pmu; + + if (!parent) + return -EINVAL; + + guard(srcu)(&pmus_srcu); + list_for_each_entry_rcu(pmu, &pmus, entry, lockdep_is_held(&pmus_srcu)) { + if (pmu->parent == parent) + return pmu->type; + } + + return -ENOENT; +} +EXPORT_SYMBOL_GPL(perf_pmu_type_by_parent_dev); + void perf_pmu_unregister(struct pmu *pmu) { mutex_lock(&pmus_lock); -- 2.33.0