mirror of
https://github.com/torvalds/linux.git
synced 2026-03-14 00:56:20 +01:00
Fix a race between PMU registration and event creation,
and fix pmus_lock vs. pmus_srcu lock ordering. Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmfK5KQRHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1idYA//V2VL3zyIzqOEa/Oq+3fNM6ek8HJo3SAB KDSh/mMqY2IvqcrOLhQ/szRNMc4fuyHrhhMszqr4lPj+SAmN9VhPX+cige5/sisj LAYQnbS1CTejJ3KEOiiOwWn4cgdc2DQBHNFUj1I6GsZVknO9K+AowfmAjOPWny8T 1iLxNxleTKhZOJ5+NoIHqX1ymUCz7rb4kmN5VmwtMn806E0wCWQCy2WwEh4QJ0K4 FDQrNtgmdOpg3SwXuusEYDg78BYZHjteRN263QKcfWmYjlFvdNuTJioG88TgZms5 R/KatAqU0Ruko06f+t9ZRfIa0xf/UMyBsxV8luSS9e30g98Egf8Y6BsAmxtbKnWX BTiK/jpLKYS1UCfAQddPLO7JaoQT37toMxGirOMEDsSwwOurIjXJTGQEu8vjg3jR fGWJg/j3T5ePbiQJJhMYuZHrOtCPG22CSx+HpdGJ0VFqrr6phKKjr5oN8T0WJ7c0 QNB0uxoSjcNPjyZGA8G5Fqdh86nRSbkQsl9vTyTQidyxywTkTibba+dlNS/DIOXA wyCVNwV/3Buciu0WLcidLzS2p19bWuQgsPu/KT7EbObThLJNACS0tp1iTi/q3Nwo 3/Y+UjZw48Btw2a8hib+27iCzbqibOwF+zoiN1g/rijbdggIIobP9pOSYXkLaT1U zKFVFg5d9RE= =1QpA -----END PGP SIGNATURE----- Merge tag 'perf-urgent-2025-03-07' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull perf event fixes from Ingo Molnar: "Fix a race between PMU registration and event creation, and fix pmus_lock vs. pmus_srcu lock ordering" * tag 'perf-urgent-2025-03-07' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: perf/core: Fix perf_pmu_register() vs. perf_init_event() perf/core: Fix pmus_lock vs. pmus_srcu ordering
This commit is contained in:
commit
ab60bd5731
1 changed files with 28 additions and 4 deletions
|
|
@ -11830,6 +11830,21 @@ free_dev:
|
|||
static struct lock_class_key cpuctx_mutex;
|
||||
static struct lock_class_key cpuctx_lock;
|
||||
|
||||
static bool idr_cmpxchg(struct idr *idr, unsigned long id, void *old, void *new)
|
||||
{
|
||||
void *tmp, *val = idr_find(idr, id);
|
||||
|
||||
if (val != old)
|
||||
return false;
|
||||
|
||||
tmp = idr_replace(idr, new, id);
|
||||
if (IS_ERR(tmp))
|
||||
return false;
|
||||
|
||||
WARN_ON_ONCE(tmp != val);
|
||||
return true;
|
||||
}
|
||||
|
||||
int perf_pmu_register(struct pmu *pmu, const char *name, int type)
|
||||
{
|
||||
int cpu, ret, max = PERF_TYPE_MAX;
|
||||
|
|
@ -11856,7 +11871,7 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type)
|
|||
if (type >= 0)
|
||||
max = type;
|
||||
|
||||
ret = idr_alloc(&pmu_idr, pmu, max, 0, GFP_KERNEL);
|
||||
ret = idr_alloc(&pmu_idr, NULL, max, 0, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
goto free_pdc;
|
||||
|
||||
|
|
@ -11864,6 +11879,7 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type)
|
|||
|
||||
type = ret;
|
||||
pmu->type = type;
|
||||
atomic_set(&pmu->exclusive_cnt, 0);
|
||||
|
||||
if (pmu_bus_running && !pmu->dev) {
|
||||
ret = pmu_dev_alloc(pmu);
|
||||
|
|
@ -11912,14 +11928,22 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type)
|
|||
if (!pmu->event_idx)
|
||||
pmu->event_idx = perf_event_idx_default;
|
||||
|
||||
/*
|
||||
* Now that the PMU is complete, make it visible to perf_try_init_event().
|
||||
*/
|
||||
if (!idr_cmpxchg(&pmu_idr, pmu->type, NULL, pmu))
|
||||
goto free_context;
|
||||
list_add_rcu(&pmu->entry, &pmus);
|
||||
atomic_set(&pmu->exclusive_cnt, 0);
|
||||
|
||||
ret = 0;
|
||||
unlock:
|
||||
mutex_unlock(&pmus_lock);
|
||||
|
||||
return ret;
|
||||
|
||||
free_context:
|
||||
free_percpu(pmu->cpu_pmu_context);
|
||||
|
||||
free_dev:
|
||||
if (pmu->dev && pmu->dev != PMU_NULL_DEV) {
|
||||
device_del(pmu->dev);
|
||||
|
|
@ -11939,6 +11963,8 @@ void perf_pmu_unregister(struct pmu *pmu)
|
|||
{
|
||||
mutex_lock(&pmus_lock);
|
||||
list_del_rcu(&pmu->entry);
|
||||
idr_remove(&pmu_idr, pmu->type);
|
||||
mutex_unlock(&pmus_lock);
|
||||
|
||||
/*
|
||||
* We dereference the pmu list under both SRCU and regular RCU, so
|
||||
|
|
@ -11948,7 +11974,6 @@ void perf_pmu_unregister(struct pmu *pmu)
|
|||
synchronize_rcu();
|
||||
|
||||
free_percpu(pmu->pmu_disable_count);
|
||||
idr_remove(&pmu_idr, pmu->type);
|
||||
if (pmu_bus_running && pmu->dev && pmu->dev != PMU_NULL_DEV) {
|
||||
if (pmu->nr_addr_filters)
|
||||
device_remove_file(pmu->dev, &dev_attr_nr_addr_filters);
|
||||
|
|
@ -11956,7 +11981,6 @@ void perf_pmu_unregister(struct pmu *pmu)
|
|||
put_device(pmu->dev);
|
||||
}
|
||||
free_pmu_context(pmu);
|
||||
mutex_unlock(&pmus_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_pmu_unregister);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue