Skip to content

Commit ab60bd5

Browse files
committed
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
2 parents 042751d + 003659f commit ab60bd5

File tree

1 file changed

+28
-4
lines changed

1 file changed

+28
-4
lines changed

kernel/events/core.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11830,6 +11830,21 @@ static int pmu_dev_alloc(struct pmu *pmu)
1183011830
static struct lock_class_key cpuctx_mutex;
1183111831
static struct lock_class_key cpuctx_lock;
1183211832

11833+
static bool idr_cmpxchg(struct idr *idr, unsigned long id, void *old, void *new)
11834+
{
11835+
void *tmp, *val = idr_find(idr, id);
11836+
11837+
if (val != old)
11838+
return false;
11839+
11840+
tmp = idr_replace(idr, new, id);
11841+
if (IS_ERR(tmp))
11842+
return false;
11843+
11844+
WARN_ON_ONCE(tmp != val);
11845+
return true;
11846+
}
11847+
1183311848
int perf_pmu_register(struct pmu *pmu, const char *name, int type)
1183411849
{
1183511850
int cpu, ret, max = PERF_TYPE_MAX;
@@ -11856,14 +11871,15 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type)
1185611871
if (type >= 0)
1185711872
max = type;
1185811873

11859-
ret = idr_alloc(&pmu_idr, pmu, max, 0, GFP_KERNEL);
11874+
ret = idr_alloc(&pmu_idr, NULL, max, 0, GFP_KERNEL);
1186011875
if (ret < 0)
1186111876
goto free_pdc;
1186211877

1186311878
WARN_ON(type >= 0 && ret != type);
1186411879

1186511880
type = ret;
1186611881
pmu->type = type;
11882+
atomic_set(&pmu->exclusive_cnt, 0);
1186711883

1186811884
if (pmu_bus_running && !pmu->dev) {
1186911885
ret = pmu_dev_alloc(pmu);
@@ -11912,14 +11928,22 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type)
1191211928
if (!pmu->event_idx)
1191311929
pmu->event_idx = perf_event_idx_default;
1191411930

11931+
/*
11932+
* Now that the PMU is complete, make it visible to perf_try_init_event().
11933+
*/
11934+
if (!idr_cmpxchg(&pmu_idr, pmu->type, NULL, pmu))
11935+
goto free_context;
1191511936
list_add_rcu(&pmu->entry, &pmus);
11916-
atomic_set(&pmu->exclusive_cnt, 0);
11937+
1191711938
ret = 0;
1191811939
unlock:
1191911940
mutex_unlock(&pmus_lock);
1192011941

1192111942
return ret;
1192211943

11944+
free_context:
11945+
free_percpu(pmu->cpu_pmu_context);
11946+
1192311947
free_dev:
1192411948
if (pmu->dev && pmu->dev != PMU_NULL_DEV) {
1192511949
device_del(pmu->dev);
@@ -11939,6 +11963,8 @@ void perf_pmu_unregister(struct pmu *pmu)
1193911963
{
1194011964
mutex_lock(&pmus_lock);
1194111965
list_del_rcu(&pmu->entry);
11966+
idr_remove(&pmu_idr, pmu->type);
11967+
mutex_unlock(&pmus_lock);
1194211968

1194311969
/*
1194411970
* We dereference the pmu list under both SRCU and regular RCU, so
@@ -11948,15 +11974,13 @@ void perf_pmu_unregister(struct pmu *pmu)
1194811974
synchronize_rcu();
1194911975

1195011976
free_percpu(pmu->pmu_disable_count);
11951-
idr_remove(&pmu_idr, pmu->type);
1195211977
if (pmu_bus_running && pmu->dev && pmu->dev != PMU_NULL_DEV) {
1195311978
if (pmu->nr_addr_filters)
1195411979
device_remove_file(pmu->dev, &dev_attr_nr_addr_filters);
1195511980
device_del(pmu->dev);
1195611981
put_device(pmu->dev);
1195711982
}
1195811983
free_pmu_context(pmu);
11959-
mutex_unlock(&pmus_lock);
1196011984
}
1196111985
EXPORT_SYMBOL_GPL(perf_pmu_unregister);
1196211986

0 commit comments

Comments
 (0)