aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_event.c
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2010-06-16 08:37:10 -0400
committerIngo Molnar <mingo@elte.hu>2010-09-09 14:46:30 -0400
commita4eaf7f14675cb512d69f0c928055e73d0c6d252 (patch)
treee8a0f631fc28d4bd9becd2e9e2c71743c64ee3ec /kernel/perf_event.c
parentfa407f35e0298d841e4088f95a7f9cf6e725c6d5 (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.c140
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
1485static 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
1493static 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
1501static void perf_adjust_period(struct perf_event *event, u64 nsec, u64 count) 1485static 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
2507static int perf_event_index(struct perf_event *event) 2491static 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
4249static void perf_swevent_add(struct perf_event *event, u64 nr, 4234static 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,
4272static int perf_exclude_event(struct perf_event *event, 4257static 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 }
4376end: 4364end:
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
4418static int perf_swevent_enable(struct perf_event *event) 4406static 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
4440static void perf_swevent_disable(struct perf_event *event) 4430static 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
4445static void perf_swevent_void(struct perf_event *event) 4435static void perf_swevent_start(struct perf_event *event, int flags)
4446{ 4436{
4437 event->hw.state = 0;
4447} 4438}
4448 4439
4449static int perf_swevent_int(struct perf_event *event) 4440static 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
4605static struct pmu perf_swevent = { 4596static 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
4697static struct pmu perf_tracepoint = { 4687static 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
4707static inline void perf_tp_register(void) 4696static 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
4835static void cpu_clock_event_update(struct perf_event *event) 4824static 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
4846static int cpu_clock_event_enable(struct perf_event *event) 4834static 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
4857static void cpu_clock_event_disable(struct perf_event *event) 4840static 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
4846static 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
4854static void cpu_clock_event_del(struct perf_event *event, int flags)
4855{
4856 cpu_clock_event_stop(event, flags);
4857}
4858
4863static void cpu_clock_event_read(struct perf_event *event) 4859static 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
4879static struct pmu perf_cpu_clock = { 4875static 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
4900static int task_clock_event_enable(struct perf_event *event) 4898static 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
4914static void task_clock_event_disable(struct perf_event *event) 4904static 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
4910static 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
4918static 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
4921static void task_clock_event_read(struct perf_event *event) 4923static 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
4948static struct pmu perf_task_clock = { 4950static 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