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> --- 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..169c45b4cc39 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -13109,6 +13109,34 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, } EXPORT_SYMBOL_GPL(perf_event_create_kernel_counter); +/** + * 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); + static void __perf_pmu_remove(struct perf_event_context *ctx, int cpu, struct pmu *pmu, struct perf_event_groups *groups, -- 2.43.0