diff options
| author | Yan, Zheng <zheng.z.yan@intel.com> | 2014-03-18 04:56:41 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2014-04-18 06:54:45 -0400 |
| commit | c464c76eec4be587604ca082e8cded7e6b89f3bf (patch) | |
| tree | 673036d1b0058e3703ef53b69cf7547ae469141d | |
| parent | 1111b680d34bc19190f02a1b4479c3fcc592c22e (diff) | |
perf: Allow building PMU drivers as modules
This patch adds support for building PMU driver as module. It exports
the functions perf_pmu_{register,unregister}() and adds reference tracking
for the PMU driver module.
When the PMU driver is built as a module, each active event of the PMU
holds a reference to the driver module.
Signed-off-by: Yan, Zheng <zheng.z.yan@intel.com>
Signed-off-by: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/1395133004-23205-1-git-send-email-zheng.z.yan@intel.com
Cc: eranian@google.com
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
| -rw-r--r-- | include/linux/perf_event.h | 1 | ||||
| -rw-r--r-- | kernel/events/core.c | 15 |
2 files changed, 16 insertions, 0 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 3356abcfff18..af6dcf1d9e47 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
| @@ -172,6 +172,7 @@ struct perf_event; | |||
| 172 | struct pmu { | 172 | struct pmu { |
| 173 | struct list_head entry; | 173 | struct list_head entry; |
| 174 | 174 | ||
| 175 | struct module *module; | ||
| 175 | struct device *dev; | 176 | struct device *dev; |
| 176 | const struct attribute_group **attr_groups; | 177 | const struct attribute_group **attr_groups; |
| 177 | const char *name; | 178 | const char *name; |
diff --git a/kernel/events/core.c b/kernel/events/core.c index f83a71a3e46d..5129b1201050 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
| @@ -39,6 +39,7 @@ | |||
| 39 | #include <linux/hw_breakpoint.h> | 39 | #include <linux/hw_breakpoint.h> |
| 40 | #include <linux/mm_types.h> | 40 | #include <linux/mm_types.h> |
| 41 | #include <linux/cgroup.h> | 41 | #include <linux/cgroup.h> |
| 42 | #include <linux/module.h> | ||
| 42 | 43 | ||
| 43 | #include "internal.h" | 44 | #include "internal.h" |
| 44 | 45 | ||
| @@ -3229,6 +3230,9 @@ static void __free_event(struct perf_event *event) | |||
| 3229 | if (event->ctx) | 3230 | if (event->ctx) |
| 3230 | put_ctx(event->ctx); | 3231 | put_ctx(event->ctx); |
| 3231 | 3232 | ||
| 3233 | if (event->pmu) | ||
| 3234 | module_put(event->pmu->module); | ||
| 3235 | |||
| 3232 | call_rcu(&event->rcu_head, free_event_rcu); | 3236 | call_rcu(&event->rcu_head, free_event_rcu); |
| 3233 | } | 3237 | } |
| 3234 | static void free_event(struct perf_event *event) | 3238 | static void free_event(struct perf_event *event) |
| @@ -6551,6 +6555,7 @@ free_pdc: | |||
| 6551 | free_percpu(pmu->pmu_disable_count); | 6555 | free_percpu(pmu->pmu_disable_count); |
| 6552 | goto unlock; | 6556 | goto unlock; |
| 6553 | } | 6557 | } |
| 6558 | EXPORT_SYMBOL_GPL(perf_pmu_register); | ||
| 6554 | 6559 | ||
| 6555 | void perf_pmu_unregister(struct pmu *pmu) | 6560 | void perf_pmu_unregister(struct pmu *pmu) |
| 6556 | { | 6561 | { |
| @@ -6572,6 +6577,7 @@ void perf_pmu_unregister(struct pmu *pmu) | |||
| 6572 | put_device(pmu->dev); | 6577 | put_device(pmu->dev); |
| 6573 | free_pmu_context(pmu); | 6578 | free_pmu_context(pmu); |
| 6574 | } | 6579 | } |
| 6580 | EXPORT_SYMBOL_GPL(perf_pmu_unregister); | ||
| 6575 | 6581 | ||
| 6576 | struct pmu *perf_init_event(struct perf_event *event) | 6582 | struct pmu *perf_init_event(struct perf_event *event) |
| 6577 | { | 6583 | { |
| @@ -6585,6 +6591,10 @@ struct pmu *perf_init_event(struct perf_event *event) | |||
| 6585 | pmu = idr_find(&pmu_idr, event->attr.type); | 6591 | pmu = idr_find(&pmu_idr, event->attr.type); |
| 6586 | rcu_read_unlock(); | 6592 | rcu_read_unlock(); |
| 6587 | if (pmu) { | 6593 | if (pmu) { |
| 6594 | if (!try_module_get(pmu->module)) { | ||
| 6595 | pmu = ERR_PTR(-ENODEV); | ||
| 6596 | goto unlock; | ||
| 6597 | } | ||
| 6588 | event->pmu = pmu; | 6598 | event->pmu = pmu; |
| 6589 | ret = pmu->event_init(event); | 6599 | ret = pmu->event_init(event); |
| 6590 | if (ret) | 6600 | if (ret) |
| @@ -6593,6 +6603,10 @@ struct pmu *perf_init_event(struct perf_event *event) | |||
| 6593 | } | 6603 | } |
| 6594 | 6604 | ||
| 6595 | list_for_each_entry_rcu(pmu, &pmus, entry) { | 6605 | list_for_each_entry_rcu(pmu, &pmus, entry) { |
| 6606 | if (!try_module_get(pmu->module)) { | ||
| 6607 | pmu = ERR_PTR(-ENODEV); | ||
| 6608 | goto unlock; | ||
| 6609 | } | ||
| 6596 | event->pmu = pmu; | 6610 | event->pmu = pmu; |
| 6597 | ret = pmu->event_init(event); | 6611 | ret = pmu->event_init(event); |
| 6598 | if (!ret) | 6612 | if (!ret) |
| @@ -6771,6 +6785,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, | |||
| 6771 | err_pmu: | 6785 | err_pmu: |
| 6772 | if (event->destroy) | 6786 | if (event->destroy) |
| 6773 | event->destroy(event); | 6787 | event->destroy(event); |
| 6788 | module_put(pmu->module); | ||
| 6774 | err_ns: | 6789 | err_ns: |
| 6775 | if (event->ns) | 6790 | if (event->ns) |
| 6776 | put_pid_ns(event->ns); | 6791 | put_pid_ns(event->ns); |
