diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2010-11-17 17:17:37 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-12-16 05:36:43 -0500 |
commit | abe43400579d5de0078c2d3a760e6598e183f871 (patch) | |
tree | 1f6faf8a1ab60f3f9bfb26265c84c2725e6c4aaa /kernel/perf_event.c | |
parent | 2e80a82a49c4c7eca4e35734380f28298ba5db19 (diff) |
perf: Sysfs enumeration
Simple sysfs emumeration of the PMUs.
Use a "event_source" bus, and add PMU devices using their name.
Each PMU device has a type attribute which contrains the value needed
for perf_event_attr::type to identify this PMU.
This is the minimal stub needed to start using this interface,
we'll consider extending the sysfs usage later.
Cc: Kay Sievers <kay.sievers@vrfy.org>
Cc: Greg KH <gregkh@suse.de>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <20101117222056.316982569@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r-- | kernel/perf_event.c | 95 |
1 files changed, 94 insertions, 1 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 8f09bc877a30..11847bf1e8cc 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/ptrace.h> | 24 | #include <linux/ptrace.h> |
25 | #include <linux/reboot.h> | 25 | #include <linux/reboot.h> |
26 | #include <linux/vmstat.h> | 26 | #include <linux/vmstat.h> |
27 | #include <linux/device.h> | ||
27 | #include <linux/vmalloc.h> | 28 | #include <linux/vmalloc.h> |
28 | #include <linux/hardirq.h> | 29 | #include <linux/hardirq.h> |
29 | #include <linux/rculist.h> | 30 | #include <linux/rculist.h> |
@@ -5308,6 +5309,58 @@ out: | |||
5308 | } | 5309 | } |
5309 | static struct idr pmu_idr; | 5310 | static struct idr pmu_idr; |
5310 | 5311 | ||
5312 | static ssize_t | ||
5313 | type_show(struct device *dev, struct device_attribute *attr, char *page) | ||
5314 | { | ||
5315 | struct pmu *pmu = dev_get_drvdata(dev); | ||
5316 | |||
5317 | return snprintf(page, PAGE_SIZE-1, "%d\n", pmu->type); | ||
5318 | } | ||
5319 | |||
5320 | static struct device_attribute pmu_dev_attrs[] = { | ||
5321 | __ATTR_RO(type), | ||
5322 | __ATTR_NULL, | ||
5323 | }; | ||
5324 | |||
5325 | static int pmu_bus_running; | ||
5326 | static struct bus_type pmu_bus = { | ||
5327 | .name = "event_source", | ||
5328 | .dev_attrs = pmu_dev_attrs, | ||
5329 | }; | ||
5330 | |||
5331 | static void pmu_dev_release(struct device *dev) | ||
5332 | { | ||
5333 | kfree(dev); | ||
5334 | } | ||
5335 | |||
5336 | static int pmu_dev_alloc(struct pmu *pmu) | ||
5337 | { | ||
5338 | int ret = -ENOMEM; | ||
5339 | |||
5340 | pmu->dev = kzalloc(sizeof(struct device), GFP_KERNEL); | ||
5341 | if (!pmu->dev) | ||
5342 | goto out; | ||
5343 | |||
5344 | device_initialize(pmu->dev); | ||
5345 | ret = dev_set_name(pmu->dev, "%s", pmu->name); | ||
5346 | if (ret) | ||
5347 | goto free_dev; | ||
5348 | |||
5349 | dev_set_drvdata(pmu->dev, pmu); | ||
5350 | pmu->dev->bus = &pmu_bus; | ||
5351 | pmu->dev->release = pmu_dev_release; | ||
5352 | ret = device_add(pmu->dev); | ||
5353 | if (ret) | ||
5354 | goto free_dev; | ||
5355 | |||
5356 | out: | ||
5357 | return ret; | ||
5358 | |||
5359 | free_dev: | ||
5360 | put_device(pmu->dev); | ||
5361 | goto out; | ||
5362 | } | ||
5363 | |||
5311 | int perf_pmu_register(struct pmu *pmu, char *name, int type) | 5364 | int perf_pmu_register(struct pmu *pmu, char *name, int type) |
5312 | { | 5365 | { |
5313 | int cpu, ret; | 5366 | int cpu, ret; |
@@ -5336,6 +5389,12 @@ int perf_pmu_register(struct pmu *pmu, char *name, int type) | |||
5336 | } | 5389 | } |
5337 | pmu->type = type; | 5390 | pmu->type = type; |
5338 | 5391 | ||
5392 | if (pmu_bus_running) { | ||
5393 | ret = pmu_dev_alloc(pmu); | ||
5394 | if (ret) | ||
5395 | goto free_idr; | ||
5396 | } | ||
5397 | |||
5339 | skip_type: | 5398 | skip_type: |
5340 | pmu->pmu_cpu_context = find_pmu_context(pmu->task_ctx_nr); | 5399 | pmu->pmu_cpu_context = find_pmu_context(pmu->task_ctx_nr); |
5341 | if (pmu->pmu_cpu_context) | 5400 | if (pmu->pmu_cpu_context) |
@@ -5343,7 +5402,7 @@ skip_type: | |||
5343 | 5402 | ||
5344 | pmu->pmu_cpu_context = alloc_percpu(struct perf_cpu_context); | 5403 | pmu->pmu_cpu_context = alloc_percpu(struct perf_cpu_context); |
5345 | if (!pmu->pmu_cpu_context) | 5404 | if (!pmu->pmu_cpu_context) |
5346 | goto free_ird; | 5405 | goto free_dev; |
5347 | 5406 | ||
5348 | for_each_possible_cpu(cpu) { | 5407 | for_each_possible_cpu(cpu) { |
5349 | struct perf_cpu_context *cpuctx; | 5408 | struct perf_cpu_context *cpuctx; |
@@ -5387,6 +5446,10 @@ unlock: | |||
5387 | 5446 | ||
5388 | return ret; | 5447 | return ret; |
5389 | 5448 | ||
5449 | free_dev: | ||
5450 | device_del(pmu->dev); | ||
5451 | put_device(pmu->dev); | ||
5452 | |||
5390 | free_idr: | 5453 | free_idr: |
5391 | if (pmu->type >= PERF_TYPE_MAX) | 5454 | if (pmu->type >= PERF_TYPE_MAX) |
5392 | idr_remove(&pmu_idr, pmu->type); | 5455 | idr_remove(&pmu_idr, pmu->type); |
@@ -5412,6 +5475,8 @@ void perf_pmu_unregister(struct pmu *pmu) | |||
5412 | free_percpu(pmu->pmu_disable_count); | 5475 | free_percpu(pmu->pmu_disable_count); |
5413 | if (pmu->type >= PERF_TYPE_MAX) | 5476 | if (pmu->type >= PERF_TYPE_MAX) |
5414 | idr_remove(&pmu_idr, pmu->type); | 5477 | idr_remove(&pmu_idr, pmu->type); |
5478 | device_del(pmu->dev); | ||
5479 | put_device(pmu->dev); | ||
5415 | free_pmu_context(pmu); | 5480 | free_pmu_context(pmu); |
5416 | } | 5481 | } |
5417 | 5482 | ||
@@ -6603,3 +6668,31 @@ void __init perf_event_init(void) | |||
6603 | ret = init_hw_breakpoint(); | 6668 | ret = init_hw_breakpoint(); |
6604 | WARN(ret, "hw_breakpoint initialization failed with: %d", ret); | 6669 | WARN(ret, "hw_breakpoint initialization failed with: %d", ret); |
6605 | } | 6670 | } |
6671 | |||
6672 | static int __init perf_event_sysfs_init(void) | ||
6673 | { | ||
6674 | struct pmu *pmu; | ||
6675 | int ret; | ||
6676 | |||
6677 | mutex_lock(&pmus_lock); | ||
6678 | |||
6679 | ret = bus_register(&pmu_bus); | ||
6680 | if (ret) | ||
6681 | goto unlock; | ||
6682 | |||
6683 | list_for_each_entry(pmu, &pmus, entry) { | ||
6684 | if (!pmu->name || pmu->type < 0) | ||
6685 | continue; | ||
6686 | |||
6687 | ret = pmu_dev_alloc(pmu); | ||
6688 | WARN(ret, "Failed to register pmu: %s, reason %d\n", pmu->name, ret); | ||
6689 | } | ||
6690 | pmu_bus_running = 1; | ||
6691 | ret = 0; | ||
6692 | |||
6693 | unlock: | ||
6694 | mutex_unlock(&pmus_lock); | ||
6695 | |||
6696 | return ret; | ||
6697 | } | ||
6698 | device_initcall(perf_event_sysfs_init); | ||