diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index 4e5873593056003a57f0b3e3cc1ff2ad13ab96e6..7239f6109cc0be39d36ad23918e28aa6b36b78f1 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -1810,6 +1810,22 @@ ssize_t x86_event_sysfs_show(char *page, u64 config, u64 event) static struct attribute_group x86_pmu_attr_group; static struct attribute_group x86_pmu_caps_group; +static int x86_pmu_attr_update_notify(struct notifier_block *nb, + unsigned long ret, void *group) +{ + struct pmu *tmp = group; + + if (tmp != &pmu) + return NOTIFY_DONE; + + *(int *)ret = sysfs_update_groups(&tmp->dev->kobj, x86_pmu.attr_update); + return NOTIFY_STOP; +} + +static struct notifier_block x86_pmu_attr_update_notifier = { + .notifier_call = &x86_pmu_attr_update_notify, +}; + static int __init init_hw_perf_events(void) { struct x86_pmu_quirk *quirk; @@ -1868,7 +1884,7 @@ static int __init init_hw_perf_events(void) if (!x86_pmu.events_sysfs_show) x86_pmu_events_group.attrs = &empty_attrs; - pmu.attr_update = x86_pmu.attr_update; + pmu_attr_update_register_notifier(&x86_pmu_attr_update_notifier); pr_info("... version: %d\n", x86_pmu.version); pr_info("... bit width: %d\n", x86_pmu.cntval_bits); diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 53869bbd8b5d8afb7b00730cd432e229bde9a4b0..41bc1d3b311fc99a968523ae378364ffe80ae4c1 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -279,7 +279,6 @@ struct pmu { struct module *module; struct device *dev; const struct attribute_group **attr_groups; - const struct attribute_group **attr_update; const char *name; int type; @@ -897,6 +896,9 @@ extern void perf_event_itrace_started(struct perf_event *event); extern int perf_pmu_register(struct pmu *pmu, const char *name, int type); extern void perf_pmu_unregister(struct pmu *pmu); +extern int pmu_attr_update_register_notifier(struct notifier_block *nb); +extern int pmu_attr_update_unregister_notifier(struct notifier_block *nb); + extern int perf_num_counters(void); extern const char *perf_pmu_name(void); extern void __perf_event_task_sched_in(struct task_struct *prev, diff --git a/kernel/events/core.c b/kernel/events/core.c index 56617b9124751a16a8d2e803ff6a1a24db21fd7d..09869287b7ae3f104a97c5872d8e22a5d1ab2705 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -9690,6 +9690,30 @@ static struct bus_type pmu_bus = { .name = "event_source", .dev_groups = pmu_dev_groups, }; +static BLOCKING_NOTIFIER_HEAD(pmu_attr_update_notifier_chain); + +int pmu_attr_update_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register( + &pmu_attr_update_notifier_chain, nb); +} +EXPORT_SYMBOL_GPL(pmu_attr_update_register_notifier); + +int pmu_attr_update_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister( + &pmu_attr_update_notifier_chain, nb); +} +EXPORT_SYMBOL_GPL(pmu_attr_update_unregister_notifier); + +static int pmu_attr_update_notifier_call_chain(struct pmu *pmu) +{ + int ret = 0; + + blocking_notifier_call_chain(&pmu_attr_update_notifier_chain, + (unsigned long)&ret, (void *)pmu); + return ret; +} static void pmu_dev_release(struct device *dev) { @@ -9724,9 +9748,7 @@ static int pmu_dev_alloc(struct pmu *pmu) if (ret) goto del_dev; - if (pmu->attr_update) - ret = sysfs_update_groups(&pmu->dev->kobj, pmu->attr_update); - + ret = pmu_attr_update_notifier_call_chain(pmu); if (ret) goto del_dev;