mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 08:02:07 +00:00
perf: Drop module reference on event init failure
When initialising an event, perf_init_event will call try_module_get() to ensure that the PMU's module cannot be removed for the lifetime of the event, with __free_event() dropping the reference when the event is finally destroyed. If something fails after the event has been initialised, but before the event is installed, perf_event_alloc will drop the reference on the module. However, if we fail to initialise an event for some reason (e.g. we ask an uncore PMU to perform sampling, and it refuses to initialise the event), we do not drop the refcount. If we try to open such a bogus event without a precise IDR type, we will loop over each PMU in the pmus list, incrementing each of their refcounts without decrementing them. This patch adds a module_put when pmu->event_init(event) fails, ensuring that the refcounts are balanced in failure cases. As the innards of the precise and search based initialisation look very similar, this logic is hoisted out into a new helper function. While the early return for the failed try_module_get is removed from the search case, this is handled by the remaining return when ret is not -ENOENT. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Will Deacon <will.deacon@arm.com> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Link: http://lkml.kernel.org/r/1420642611-22667-1-git-send-email-mark.rutland@arm.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
7c60fc0e02
commit
cc34b98bac
@ -7027,6 +7027,20 @@ void perf_pmu_unregister(struct pmu *pmu)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_pmu_unregister);
|
||||
|
||||
static int perf_try_init_event(struct pmu *pmu, struct perf_event *event)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!try_module_get(pmu->module))
|
||||
return -ENODEV;
|
||||
event->pmu = pmu;
|
||||
ret = pmu->event_init(event);
|
||||
if (ret)
|
||||
module_put(pmu->module);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct pmu *perf_init_event(struct perf_event *event)
|
||||
{
|
||||
struct pmu *pmu = NULL;
|
||||
@ -7039,24 +7053,14 @@ struct pmu *perf_init_event(struct perf_event *event)
|
||||
pmu = idr_find(&pmu_idr, event->attr.type);
|
||||
rcu_read_unlock();
|
||||
if (pmu) {
|
||||
if (!try_module_get(pmu->module)) {
|
||||
pmu = ERR_PTR(-ENODEV);
|
||||
goto unlock;
|
||||
}
|
||||
event->pmu = pmu;
|
||||
ret = pmu->event_init(event);
|
||||
ret = perf_try_init_event(pmu, event);
|
||||
if (ret)
|
||||
pmu = ERR_PTR(ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(pmu, &pmus, entry) {
|
||||
if (!try_module_get(pmu->module)) {
|
||||
pmu = ERR_PTR(-ENODEV);
|
||||
goto unlock;
|
||||
}
|
||||
event->pmu = pmu;
|
||||
ret = pmu->event_init(event);
|
||||
ret = perf_try_init_event(pmu, event);
|
||||
if (!ret)
|
||||
goto unlock;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user