diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2010-06-16 08:37:10 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-09-09 14:46:30 -0400 |
commit | a4eaf7f14675cb512d69f0c928055e73d0c6d252 (patch) | |
tree | e8a0f631fc28d4bd9becd2e9e2c71743c64ee3ec /kernel/perf_event.c | |
parent | fa407f35e0298d841e4088f95a7f9cf6e725c6d5 (diff) |
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r-- | kernel/perf_event.c | 140 |
1 files changed, 72 insertions, 68 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 1a6cdbf0d091..3bace4fd0355 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
@@ -424,7 +424,7 @@ event_sched_out(struct perf_event *event, | |||
424 | event->state = PERF_EVENT_STATE_OFF; | 424 | event->state = PERF_EVENT_STATE_OFF; |
425 | } | 425 | } |
426 | event->tstamp_stopped = ctx->time; | 426 | event->tstamp_stopped = ctx->time; |
427 | event->pmu->disable(event); | 427 | event->pmu->del(event, 0); |
428 | event->oncpu = -1; | 428 | event->oncpu = -1; |
429 | 429 | ||
430 | if (!is_software_event(event)) | 430 | if (!is_software_event(event)) |
@@ -649,7 +649,7 @@ event_sched_in(struct perf_event *event, | |||
649 | */ | 649 | */ |
650 | smp_wmb(); | 650 | smp_wmb(); |
651 | 651 | ||
652 | if (event->pmu->enable(event)) { | 652 | if (event->pmu->add(event, PERF_EF_START)) { |
653 | event->state = PERF_EVENT_STATE_INACTIVE; | 653 | event->state = PERF_EVENT_STATE_INACTIVE; |
654 | event->oncpu = -1; | 654 | event->oncpu = -1; |
655 | return -EAGAIN; | 655 | return -EAGAIN; |
@@ -1482,22 +1482,6 @@ do { \ | |||
1482 | return div64_u64(dividend, divisor); | 1482 | return div64_u64(dividend, divisor); |
1483 | } | 1483 | } |
1484 | 1484 | ||
1485 | static void perf_event_stop(struct perf_event *event) | ||
1486 | { | ||
1487 | if (!event->pmu->stop) | ||
1488 | return event->pmu->disable(event); | ||
1489 | |||
1490 | return event->pmu->stop(event); | ||
1491 | } | ||
1492 | |||
1493 | static int perf_event_start(struct perf_event *event) | ||
1494 | { | ||
1495 | if (!event->pmu->start) | ||
1496 | return event->pmu->enable(event); | ||
1497 | |||
1498 | return event->pmu->start(event); | ||
1499 | } | ||
1500 | |||
1501 | static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count) | 1485 | static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count) |
1502 | { | 1486 | { |
1503 | struct hw_perf_event *hwc = &event->hw; | 1487 | struct hw_perf_event *hwc = &event->hw; |
@@ -1517,9 +1501,9 @@ static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count) | |||
1517 | hwc->sample_period = sample_period; | 1501 | hwc->sample_period = sample_period; |
1518 | 1502 | ||
1519 | if (local64_read(&hwc->period_left) > 8*sample_period) { | 1503 | if (local64_read(&hwc->period_left) > 8*sample_period) { |
1520 | perf_event_stop(event); | 1504 | event->pmu->stop(event, PERF_EF_UPDATE); |
1521 | local64_set(&hwc->period_left, 0); | 1505 | local64_set(&hwc->period_left, 0); |
1522 | perf_event_start(event); | 1506 | event->pmu->start(event, PERF_EF_RELOAD); |
1523 | } | 1507 | } |
1524 | } | 1508 | } |
1525 | 1509 | ||
@@ -1548,7 +1532,7 @@ static void perf_ctx_adjust_freq(struct perf_event_context *ctx) | |||
1548 | */ | 1532 | */ |
1549 | if (interrupts == MAX_INTERRUPTS) { | 1533 | if (interrupts == MAX_INTERRUPTS) { |
1550 | perf_log_throttle(event, 1); | 1534 | perf_log_throttle(event, 1); |
1551 | event->pmu->unthrottle(event); | 1535 | event->pmu->start(event, 0); |
1552 | } | 1536 | } |
1553 | 1537 | ||
1554 | if (!event->attr.freq || !event->attr.sample_freq) | 1538 | if (!event->attr.freq || !event->attr.sample_freq) |
@@ -2506,6 +2490,9 @@ int perf_event_task_disable(void) | |||
2506 | 2490 | ||
2507 | static int perf_event_index(struct perf_event *event) | 2491 | static int perf_event_index(struct perf_event *event) |
2508 | { | 2492 | { |
2493 | if (event->hw.state & PERF_HES_STOPPED) | ||
2494 | return 0; | ||
2495 | |||
2509 | if (event->state != PERF_EVENT_STATE_ACTIVE) | 2496 | if (event->state != PERF_EVENT_STATE_ACTIVE) |
2510 | return 0; | 2497 | return 0; |
2511 | 2498 | ||
@@ -4120,8 +4107,6 @@ static int __perf_event_overflow(struct perf_event *event, int nmi, | |||
4120 | struct hw_perf_event *hwc = &event->hw; | 4107 | struct hw_perf_event *hwc = &event->hw; |
4121 | int ret = 0; | 4108 | int ret = 0; |
4122 | 4109 | ||
4123 | throttle = (throttle && event->pmu->unthrottle != NULL); | ||
4124 | |||
4125 | if (!throttle) { | 4110 | if (!throttle) { |
4126 | hwc->interrupts++; | 4111 | hwc->interrupts++; |
4127 | } else { | 4112 | } else { |
@@ -4246,7 +4231,7 @@ static void perf_swevent_overflow(struct perf_event *event, u64 overflow, | |||
4246 | } | 4231 | } |
4247 | } | 4232 | } |
4248 | 4233 | ||
4249 | static void perf_swevent_add(struct perf_event *event, u64 nr, | 4234 | static void perf_swevent_event(struct perf_event *event, u64 nr, |
4250 | int nmi, struct perf_sample_data *data, | 4235 | int nmi, struct perf_sample_data *data, |
4251 | struct pt_regs *regs) | 4236 | struct pt_regs *regs) |
4252 | { | 4237 | { |
@@ -4272,6 +4257,9 @@ static void perf_swevent_add(struct perf_event *event, u64 nr, | |||
4272 | static int perf_exclude_event(struct perf_event *event, | 4257 | static int perf_exclude_event(struct perf_event *event, |
4273 | struct pt_regs *regs) | 4258 | struct pt_regs *regs) |
4274 | { | 4259 | { |
4260 | if (event->hw.state & PERF_HES_STOPPED) | ||
4261 | return 0; | ||
4262 | |||
4275 | if (regs) { | 4263 | if (regs) { |
4276 | if (event->attr.exclude_user && user_mode(regs)) | 4264 | if (event->attr.exclude_user && user_mode(regs)) |
4277 | return 1; | 4265 | return 1; |
@@ -4371,7 +4359,7 @@ static void do_perf_sw_event(enum perf_type_id type, u32 event_id, | |||
4371 | 4359 | ||
4372 | hlist_for_each_entry_rcu(event, node, head, hlist_entry) { | 4360 | hlist_for_each_entry_rcu(event, node, head, hlist_entry) { |
4373 | if (perf_swevent_match(event, type, event_id, data, regs)) | 4361 | if (perf_swevent_match(event, type, event_id, data, regs)) |
4374 | perf_swevent_add(event, nr, nmi, data, regs); | 4362 | perf_swevent_event(event, nr, nmi, data, regs); |
4375 | } | 4363 | } |
4376 | end: | 4364 | end: |
4377 | rcu_read_unlock(); | 4365 | rcu_read_unlock(); |
@@ -4415,7 +4403,7 @@ static void perf_swevent_read(struct perf_event *event) | |||
4415 | { | 4403 | { |
4416 | } | 4404 | } |
4417 | 4405 | ||
4418 | static int perf_swevent_enable(struct perf_event *event) | 4406 | static int perf_swevent_add(struct perf_event *event, int flags) |
4419 | { | 4407 | { |
4420 | struct hw_perf_event *hwc = &event->hw; | 4408 | struct hw_perf_event *hwc = &event->hw; |
4421 | struct perf_cpu_context *cpuctx; | 4409 | struct perf_cpu_context *cpuctx; |
@@ -4428,6 +4416,8 @@ static int perf_swevent_enable(struct perf_event *event) | |||
4428 | perf_swevent_set_period(event); | 4416 | perf_swevent_set_period(event); |
4429 | } | 4417 | } |
4430 | 4418 | ||
4419 | hwc->state = !(flags & PERF_EF_START); | ||
4420 | |||
4431 | head = find_swevent_head(cpuctx, event); | 4421 | head = find_swevent_head(cpuctx, event); |
4432 | if (WARN_ON_ONCE(!head)) | 4422 | if (WARN_ON_ONCE(!head)) |
4433 | return -EINVAL; | 4423 | return -EINVAL; |
@@ -4437,18 +4427,19 @@ static int perf_swevent_enable(struct perf_event *event) | |||
4437 | return 0; | 4427 | return 0; |
4438 | } | 4428 | } |
4439 | 4429 | ||
4440 | static void perf_swevent_disable(struct perf_event *event) | 4430 | static void perf_swevent_del(struct perf_event *event, int flags) |
4441 | { | 4431 | { |
4442 | hlist_del_rcu(&event->hlist_entry); | 4432 | hlist_del_rcu(&event->hlist_entry); |
4443 | } | 4433 | } |
4444 | 4434 | ||
4445 | static void perf_swevent_void(struct perf_event *event) | 4435 | static void perf_swevent_start(struct perf_event *event, int flags) |
4446 | { | 4436 | { |
4437 | event->hw.state = 0; | ||
4447 | } | 4438 | } |
4448 | 4439 | ||
4449 | static int perf_swevent_int(struct perf_event *event) | 4440 | static void perf_swevent_stop(struct perf_event *event, int flags) |
4450 | { | 4441 | { |
4451 | return 0; | 4442 | event->hw.state = PERF_HES_STOPPED; |
4452 | } | 4443 | } |
4453 | 4444 | ||
4454 | /* Deref the hlist from the update side */ | 4445 | /* Deref the hlist from the update side */ |
@@ -4604,12 +4595,11 @@ static int perf_swevent_init(struct perf_event *event) | |||
4604 | 4595 | ||
4605 | static struct pmu perf_swevent = { | 4596 | static struct pmu perf_swevent = { |
4606 | .event_init = perf_swevent_init, | 4597 | .event_init = perf_swevent_init, |
4607 | .enable = perf_swevent_enable, | 4598 | .add = perf_swevent_add, |
4608 | .disable = perf_swevent_disable, | 4599 | .del = perf_swevent_del, |
4609 | .start = perf_swevent_int, | 4600 | .start = perf_swevent_start, |
4610 | .stop = perf_swevent_void, | 4601 | .stop = perf_swevent_stop, |
4611 | .read = perf_swevent_read, | 4602 | .read = perf_swevent_read, |
4612 | .unthrottle = perf_swevent_void, /* hwc->interrupts already reset */ | ||
4613 | }; | 4603 | }; |
4614 | 4604 | ||
4615 | #ifdef CONFIG_EVENT_TRACING | 4605 | #ifdef CONFIG_EVENT_TRACING |
@@ -4657,7 +4647,7 @@ void perf_tp_event(u64 addr, u64 count, void *record, int entry_size, | |||
4657 | 4647 | ||
4658 | hlist_for_each_entry_rcu(event, node, head, hlist_entry) { | 4648 | hlist_for_each_entry_rcu(event, node, head, hlist_entry) { |
4659 | if (perf_tp_event_match(event, &data, regs)) | 4649 | if (perf_tp_event_match(event, &data, regs)) |
4660 | perf_swevent_add(event, count, 1, &data, regs); | 4650 | perf_swevent_event(event, count, 1, &data, regs); |
4661 | } | 4651 | } |
4662 | 4652 | ||
4663 | perf_swevent_put_recursion_context(rctx); | 4653 | perf_swevent_put_recursion_context(rctx); |
@@ -4696,12 +4686,11 @@ static int perf_tp_event_init(struct perf_event *event) | |||
4696 | 4686 | ||
4697 | static struct pmu perf_tracepoint = { | 4687 | static struct pmu perf_tracepoint = { |
4698 | .event_init = perf_tp_event_init, | 4688 | .event_init = perf_tp_event_init, |
4699 | .enable = perf_trace_enable, | 4689 | .add = perf_trace_add, |
4700 | .disable = perf_trace_disable, | 4690 | .del = perf_trace_del, |
4701 | .start = perf_swevent_int, | 4691 | .start = perf_swevent_start, |
4702 | .stop = perf_swevent_void, | 4692 | .stop = perf_swevent_stop, |
4703 | .read = perf_swevent_read, | 4693 | .read = perf_swevent_read, |
4704 | .unthrottle = perf_swevent_void, | ||
4705 | }; | 4694 | }; |
4706 | 4695 | ||
4707 | static inline void perf_tp_register(void) | 4696 | static inline void perf_tp_register(void) |
@@ -4757,8 +4746,8 @@ void perf_bp_event(struct perf_event *bp, void *data) | |||
4757 | 4746 | ||
4758 | perf_sample_data_init(&sample, bp->attr.bp_addr); | 4747 | perf_sample_data_init(&sample, bp->attr.bp_addr); |
4759 | 4748 | ||
4760 | if (!perf_exclude_event(bp, regs)) | 4749 | if (!bp->hw.state && !perf_exclude_event(bp, regs)) |
4761 | perf_swevent_add(bp, 1, 1, &sample, regs); | 4750 | perf_swevent_event(bp, 1, 1, &sample, regs); |
4762 | } | 4751 | } |
4763 | #endif | 4752 | #endif |
4764 | 4753 | ||
@@ -4834,32 +4823,39 @@ static void perf_swevent_cancel_hrtimer(struct perf_event *event) | |||
4834 | 4823 | ||
4835 | static void cpu_clock_event_update(struct perf_event *event) | 4824 | static void cpu_clock_event_update(struct perf_event *event) |
4836 | { | 4825 | { |
4837 | int cpu = raw_smp_processor_id(); | ||
4838 | s64 prev; | 4826 | s64 prev; |
4839 | u64 now; | 4827 | u64 now; |
4840 | 4828 | ||
4841 | now = cpu_clock(cpu); | 4829 | now = local_clock(); |
4842 | prev = local64_xchg(&event->hw.prev_count, now); | 4830 | prev = local64_xchg(&event->hw.prev_count, now); |
4843 | local64_add(now - prev, &event->count); | 4831 | local64_add(now - prev, &event->count); |
4844 | } | 4832 | } |
4845 | 4833 | ||
4846 | static int cpu_clock_event_enable(struct perf_event *event) | 4834 | static void cpu_clock_event_start(struct perf_event *event, int flags) |
4847 | { | 4835 | { |
4848 | struct hw_perf_event *hwc = &event->hw; | 4836 | local64_set(&event->hw.prev_count, local_clock()); |
4849 | int cpu = raw_smp_processor_id(); | ||
4850 | |||
4851 | local64_set(&hwc->prev_count, cpu_clock(cpu)); | ||
4852 | perf_swevent_start_hrtimer(event); | 4837 | perf_swevent_start_hrtimer(event); |
4853 | |||
4854 | return 0; | ||
4855 | } | 4838 | } |
4856 | 4839 | ||
4857 | static void cpu_clock_event_disable(struct perf_event *event) | 4840 | static void cpu_clock_event_stop(struct perf_event *event, int flags) |
4858 | { | 4841 | { |
4859 | perf_swevent_cancel_hrtimer(event); | 4842 | perf_swevent_cancel_hrtimer(event); |
4860 | cpu_clock_event_update(event); | 4843 | cpu_clock_event_update(event); |
4861 | } | 4844 | } |
4862 | 4845 | ||
4846 | static int cpu_clock_event_add(struct perf_event *event, int flags) | ||
4847 | { | ||
4848 | if (flags & PERF_EF_START) | ||
4849 | cpu_clock_event_start(event, flags); | ||
4850 | |||
4851 | return 0; | ||
4852 | } | ||
4853 | |||
4854 | static void cpu_clock_event_del(struct perf_event *event, int flags) | ||
4855 | { | ||
4856 | cpu_clock_event_stop(event, flags); | ||
4857 | } | ||
4858 | |||
4863 | static void cpu_clock_event_read(struct perf_event *event) | 4859 | static void cpu_clock_event_read(struct perf_event *event) |
4864 | { | 4860 | { |
4865 | cpu_clock_event_update(event); | 4861 | cpu_clock_event_update(event); |
@@ -4878,8 +4874,10 @@ static int cpu_clock_event_init(struct perf_event *event) | |||
4878 | 4874 | ||
4879 | static struct pmu perf_cpu_clock = { | 4875 | static struct pmu perf_cpu_clock = { |
4880 | .event_init = cpu_clock_event_init, | 4876 | .event_init = cpu_clock_event_init, |
4881 | .enable = cpu_clock_event_enable, | 4877 | .add = cpu_clock_event_add, |
4882 | .disable = cpu_clock_event_disable, | 4878 | .del = cpu_clock_event_del, |
4879 | .start = cpu_clock_event_start, | ||
4880 | .stop = cpu_clock_event_stop, | ||
4883 | .read = cpu_clock_event_read, | 4881 | .read = cpu_clock_event_read, |
4884 | }; | 4882 | }; |
4885 | 4883 | ||
@@ -4897,25 +4895,29 @@ static void task_clock_event_update(struct perf_event *event, u64 now) | |||
4897 | local64_add(delta, &event->count); | 4895 | local64_add(delta, &event->count); |
4898 | } | 4896 | } |
4899 | 4897 | ||
4900 | static int task_clock_event_enable(struct perf_event *event) | 4898 | static void task_clock_event_start(struct perf_event *event, int flags) |
4901 | { | 4899 | { |
4902 | struct hw_perf_event *hwc = &event->hw; | 4900 | local64_set(&event->hw.prev_count, event->ctx->time); |
4903 | u64 now; | ||
4904 | |||
4905 | now = event->ctx->time; | ||
4906 | |||
4907 | local64_set(&hwc->prev_count, now); | ||
4908 | |||
4909 | perf_swevent_start_hrtimer(event); | 4901 | perf_swevent_start_hrtimer(event); |
4910 | |||
4911 | return 0; | ||
4912 | } | 4902 | } |
4913 | 4903 | ||
4914 | static void task_clock_event_disable(struct perf_event *event) | 4904 | static void task_clock_event_stop(struct perf_event *event, int flags) |
4915 | { | 4905 | { |
4916 | perf_swevent_cancel_hrtimer(event); | 4906 | perf_swevent_cancel_hrtimer(event); |
4917 | task_clock_event_update(event, event->ctx->time); | 4907 | task_clock_event_update(event, event->ctx->time); |
4908 | } | ||
4909 | |||
4910 | static int task_clock_event_add(struct perf_event *event, int flags) | ||
4911 | { | ||
4912 | if (flags & PERF_EF_START) | ||
4913 | task_clock_event_start(event, flags); | ||
4918 | 4914 | ||
4915 | return 0; | ||
4916 | } | ||
4917 | |||
4918 | static void task_clock_event_del(struct perf_event *event, int flags) | ||
4919 | { | ||
4920 | task_clock_event_stop(event, PERF_EF_UPDATE); | ||
4919 | } | 4921 | } |
4920 | 4922 | ||
4921 | static void task_clock_event_read(struct perf_event *event) | 4923 | static void task_clock_event_read(struct perf_event *event) |
@@ -4947,8 +4949,10 @@ static int task_clock_event_init(struct perf_event *event) | |||
4947 | 4949 | ||
4948 | static struct pmu perf_task_clock = { | 4950 | static struct pmu perf_task_clock = { |
4949 | .event_init = task_clock_event_init, | 4951 | .event_init = task_clock_event_init, |
4950 | .enable = task_clock_event_enable, | 4952 | .add = task_clock_event_add, |
4951 | .disable = task_clock_event_disable, | 4953 | .del = task_clock_event_del, |
4954 | .start = task_clock_event_start, | ||
4955 | .stop = task_clock_event_stop, | ||
4952 | .read = task_clock_event_read, | 4956 | .read = task_clock_event_read, |
4953 | }; | 4957 | }; |
4954 | 4958 | ||