aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_counter.c
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2009-05-13 10:21:38 -0400
committerIngo Molnar <mingo@elte.hu>2009-05-15 03:47:02 -0400
commit9e35ad388bea89f7d6f375af4c0ae98803688666 (patch)
tree9abbce9f6c9a914b1ea8d8dae82e159366030e4a /kernel/perf_counter.c
parent962bf7a66edca4d36a730a38ff8410a67f560e40 (diff)
perf_counter: Rework the perf counter disable/enable
The current disable/enable mechanism is: token = hw_perf_save_disable(); ... /* do bits */ ... hw_perf_restore(token); This works well, provided that the use nests properly. Except we don't. x86 NMI/INT throttling has non-nested use of this, breaking things. Therefore provide a reference counter disable/enable interface, where the first disable disables the hardware, and the last enable enables the hardware again. [ Impact: refactor, simplify the PMU disable/enable logic ] Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com> LKML-Reference: <new-submission> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_counter.c')
-rw-r--r--kernel/perf_counter.c76
1 files changed, 47 insertions, 29 deletions
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c
index 985be0b662af..e814ff04d7ca 100644
--- a/kernel/perf_counter.c
+++ b/kernel/perf_counter.c
@@ -60,8 +60,9 @@ extern __weak const struct pmu *hw_perf_counter_init(struct perf_counter *counte
60 return NULL; 60 return NULL;
61} 61}
62 62
63u64 __weak hw_perf_save_disable(void) { return 0; } 63void __weak hw_perf_disable(void) { barrier(); }
64void __weak hw_perf_restore(u64 ctrl) { barrier(); } 64void __weak hw_perf_enable(void) { barrier(); }
65
65void __weak hw_perf_counter_setup(int cpu) { barrier(); } 66void __weak hw_perf_counter_setup(int cpu) { barrier(); }
66int __weak hw_perf_group_sched_in(struct perf_counter *group_leader, 67int __weak hw_perf_group_sched_in(struct perf_counter *group_leader,
67 struct perf_cpu_context *cpuctx, 68 struct perf_cpu_context *cpuctx,
@@ -72,6 +73,32 @@ int __weak hw_perf_group_sched_in(struct perf_counter *group_leader,
72 73
73void __weak perf_counter_print_debug(void) { } 74void __weak perf_counter_print_debug(void) { }
74 75
76static DEFINE_PER_CPU(int, disable_count);
77
78void __perf_disable(void)
79{
80 __get_cpu_var(disable_count)++;
81}
82
83bool __perf_enable(void)
84{
85 return !--__get_cpu_var(disable_count);
86}
87
88void perf_disable(void)
89{
90 __perf_disable();
91 hw_perf_disable();
92}
93EXPORT_SYMBOL_GPL(perf_disable); /* ACPI idle */
94
95void perf_enable(void)
96{
97 if (__perf_enable())
98 hw_perf_enable();
99}
100EXPORT_SYMBOL_GPL(perf_enable); /* ACPI idle */
101
75static void 102static void
76list_add_counter(struct perf_counter *counter, struct perf_counter_context *ctx) 103list_add_counter(struct perf_counter *counter, struct perf_counter_context *ctx)
77{ 104{
@@ -170,7 +197,6 @@ static void __perf_counter_remove_from_context(void *info)
170 struct perf_counter *counter = info; 197 struct perf_counter *counter = info;
171 struct perf_counter_context *ctx = counter->ctx; 198 struct perf_counter_context *ctx = counter->ctx;
172 unsigned long flags; 199 unsigned long flags;
173 u64 perf_flags;
174 200
175 /* 201 /*
176 * If this is a task context, we need to check whether it is 202 * If this is a task context, we need to check whether it is
@@ -191,9 +217,9 @@ static void __perf_counter_remove_from_context(void *info)
191 * Protect the list operation against NMI by disabling the 217 * Protect the list operation against NMI by disabling the
192 * counters on a global level. NOP for non NMI based counters. 218 * counters on a global level. NOP for non NMI based counters.
193 */ 219 */
194 perf_flags = hw_perf_save_disable(); 220 perf_disable();
195 list_del_counter(counter, ctx); 221 list_del_counter(counter, ctx);
196 hw_perf_restore(perf_flags); 222 perf_enable();
197 223
198 if (!ctx->task) { 224 if (!ctx->task) {
199 /* 225 /*
@@ -538,7 +564,6 @@ static void __perf_install_in_context(void *info)
538 struct perf_counter *leader = counter->group_leader; 564 struct perf_counter *leader = counter->group_leader;
539 int cpu = smp_processor_id(); 565 int cpu = smp_processor_id();
540 unsigned long flags; 566 unsigned long flags;
541 u64 perf_flags;
542 int err; 567 int err;
543 568
544 /* 569 /*
@@ -556,7 +581,7 @@ static void __perf_install_in_context(void *info)
556 * Protect the list operation against NMI by disabling the 581 * Protect the list operation against NMI by disabling the
557 * counters on a global level. NOP for non NMI based counters. 582 * counters on a global level. NOP for non NMI based counters.
558 */ 583 */
559 perf_flags = hw_perf_save_disable(); 584 perf_disable();
560 585
561 add_counter_to_ctx(counter, ctx); 586 add_counter_to_ctx(counter, ctx);
562 587
@@ -596,7 +621,7 @@ static void __perf_install_in_context(void *info)
596 cpuctx->max_pertask--; 621 cpuctx->max_pertask--;
597 622
598 unlock: 623 unlock:
599 hw_perf_restore(perf_flags); 624 perf_enable();
600 625
601 spin_unlock_irqrestore(&ctx->lock, flags); 626 spin_unlock_irqrestore(&ctx->lock, flags);
602} 627}
@@ -663,7 +688,6 @@ static void __perf_counter_enable(void *info)
663 struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); 688 struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
664 struct perf_counter_context *ctx = counter->ctx; 689 struct perf_counter_context *ctx = counter->ctx;
665 struct perf_counter *leader = counter->group_leader; 690 struct perf_counter *leader = counter->group_leader;
666 unsigned long pmuflags;
667 unsigned long flags; 691 unsigned long flags;
668 int err; 692 int err;
669 693
@@ -693,14 +717,14 @@ static void __perf_counter_enable(void *info)
693 if (!group_can_go_on(counter, cpuctx, 1)) { 717 if (!group_can_go_on(counter, cpuctx, 1)) {
694 err = -EEXIST; 718 err = -EEXIST;
695 } else { 719 } else {
696 pmuflags = hw_perf_save_disable(); 720 perf_disable();
697 if (counter == leader) 721 if (counter == leader)
698 err = group_sched_in(counter, cpuctx, ctx, 722 err = group_sched_in(counter, cpuctx, ctx,
699 smp_processor_id()); 723 smp_processor_id());
700 else 724 else
701 err = counter_sched_in(counter, cpuctx, ctx, 725 err = counter_sched_in(counter, cpuctx, ctx,
702 smp_processor_id()); 726 smp_processor_id());
703 hw_perf_restore(pmuflags); 727 perf_enable();
704 } 728 }
705 729
706 if (err) { 730 if (err) {
@@ -795,7 +819,6 @@ void __perf_counter_sched_out(struct perf_counter_context *ctx,
795 struct perf_cpu_context *cpuctx) 819 struct perf_cpu_context *cpuctx)
796{ 820{
797 struct perf_counter *counter; 821 struct perf_counter *counter;
798 u64 flags;
799 822
800 spin_lock(&ctx->lock); 823 spin_lock(&ctx->lock);
801 ctx->is_active = 0; 824 ctx->is_active = 0;
@@ -803,12 +826,12 @@ void __perf_counter_sched_out(struct perf_counter_context *ctx,
803 goto out; 826 goto out;
804 update_context_time(ctx); 827 update_context_time(ctx);
805 828
806 flags = hw_perf_save_disable(); 829 perf_disable();
807 if (ctx->nr_active) { 830 if (ctx->nr_active) {
808 list_for_each_entry(counter, &ctx->counter_list, list_entry) 831 list_for_each_entry(counter, &ctx->counter_list, list_entry)
809 group_sched_out(counter, cpuctx, ctx); 832 group_sched_out(counter, cpuctx, ctx);
810 } 833 }
811 hw_perf_restore(flags); 834 perf_enable();
812 out: 835 out:
813 spin_unlock(&ctx->lock); 836 spin_unlock(&ctx->lock);
814} 837}
@@ -860,7 +883,6 @@ __perf_counter_sched_in(struct perf_counter_context *ctx,
860 struct perf_cpu_context *cpuctx, int cpu) 883 struct perf_cpu_context *cpuctx, int cpu)
861{ 884{
862 struct perf_counter *counter; 885 struct perf_counter *counter;
863 u64 flags;
864 int can_add_hw = 1; 886 int can_add_hw = 1;
865 887
866 spin_lock(&ctx->lock); 888 spin_lock(&ctx->lock);
@@ -870,7 +892,7 @@ __perf_counter_sched_in(struct perf_counter_context *ctx,
870 892
871 ctx->timestamp = perf_clock(); 893 ctx->timestamp = perf_clock();
872 894
873 flags = hw_perf_save_disable(); 895 perf_disable();
874 896
875 /* 897 /*
876 * First go through the list and put on any pinned groups 898 * First go through the list and put on any pinned groups
@@ -917,7 +939,7 @@ __perf_counter_sched_in(struct perf_counter_context *ctx,
917 can_add_hw = 0; 939 can_add_hw = 0;
918 } 940 }
919 } 941 }
920 hw_perf_restore(flags); 942 perf_enable();
921 out: 943 out:
922 spin_unlock(&ctx->lock); 944 spin_unlock(&ctx->lock);
923} 945}
@@ -955,7 +977,6 @@ int perf_counter_task_disable(void)
955 struct perf_counter_context *ctx = &curr->perf_counter_ctx; 977 struct perf_counter_context *ctx = &curr->perf_counter_ctx;
956 struct perf_counter *counter; 978 struct perf_counter *counter;
957 unsigned long flags; 979 unsigned long flags;
958 u64 perf_flags;
959 980
960 if (likely(!ctx->nr_counters)) 981 if (likely(!ctx->nr_counters))
961 return 0; 982 return 0;
@@ -969,7 +990,7 @@ int perf_counter_task_disable(void)
969 /* 990 /*
970 * Disable all the counters: 991 * Disable all the counters:
971 */ 992 */
972 perf_flags = hw_perf_save_disable(); 993 perf_disable();
973 994
974 list_for_each_entry(counter, &ctx->counter_list, list_entry) { 995 list_for_each_entry(counter, &ctx->counter_list, list_entry) {
975 if (counter->state != PERF_COUNTER_STATE_ERROR) { 996 if (counter->state != PERF_COUNTER_STATE_ERROR) {
@@ -978,7 +999,7 @@ int perf_counter_task_disable(void)
978 } 999 }
979 } 1000 }
980 1001
981 hw_perf_restore(perf_flags); 1002 perf_enable();
982 1003
983 spin_unlock_irqrestore(&ctx->lock, flags); 1004 spin_unlock_irqrestore(&ctx->lock, flags);
984 1005
@@ -991,7 +1012,6 @@ int perf_counter_task_enable(void)
991 struct perf_counter_context *ctx = &curr->perf_counter_ctx; 1012 struct perf_counter_context *ctx = &curr->perf_counter_ctx;
992 struct perf_counter *counter; 1013 struct perf_counter *counter;
993 unsigned long flags; 1014 unsigned long flags;
994 u64 perf_flags;
995 int cpu; 1015 int cpu;
996 1016
997 if (likely(!ctx->nr_counters)) 1017 if (likely(!ctx->nr_counters))
@@ -1007,7 +1027,7 @@ int perf_counter_task_enable(void)
1007 /* 1027 /*
1008 * Disable all the counters: 1028 * Disable all the counters:
1009 */ 1029 */
1010 perf_flags = hw_perf_save_disable(); 1030 perf_disable();
1011 1031
1012 list_for_each_entry(counter, &ctx->counter_list, list_entry) { 1032 list_for_each_entry(counter, &ctx->counter_list, list_entry) {
1013 if (counter->state > PERF_COUNTER_STATE_OFF) 1033 if (counter->state > PERF_COUNTER_STATE_OFF)
@@ -1017,7 +1037,7 @@ int perf_counter_task_enable(void)
1017 ctx->time - counter->total_time_enabled; 1037 ctx->time - counter->total_time_enabled;
1018 counter->hw_event.disabled = 0; 1038 counter->hw_event.disabled = 0;
1019 } 1039 }
1020 hw_perf_restore(perf_flags); 1040 perf_enable();
1021 1041
1022 spin_unlock(&ctx->lock); 1042 spin_unlock(&ctx->lock);
1023 1043
@@ -1034,7 +1054,6 @@ int perf_counter_task_enable(void)
1034static void rotate_ctx(struct perf_counter_context *ctx) 1054static void rotate_ctx(struct perf_counter_context *ctx)
1035{ 1055{
1036 struct perf_counter *counter; 1056 struct perf_counter *counter;
1037 u64 perf_flags;
1038 1057
1039 if (!ctx->nr_counters) 1058 if (!ctx->nr_counters)
1040 return; 1059 return;
@@ -1043,12 +1062,12 @@ static void rotate_ctx(struct perf_counter_context *ctx)
1043 /* 1062 /*
1044 * Rotate the first entry last (works just fine for group counters too): 1063 * Rotate the first entry last (works just fine for group counters too):
1045 */ 1064 */
1046 perf_flags = hw_perf_save_disable(); 1065 perf_disable();
1047 list_for_each_entry(counter, &ctx->counter_list, list_entry) { 1066 list_for_each_entry(counter, &ctx->counter_list, list_entry) {
1048 list_move_tail(&counter->list_entry, &ctx->counter_list); 1067 list_move_tail(&counter->list_entry, &ctx->counter_list);
1049 break; 1068 break;
1050 } 1069 }
1051 hw_perf_restore(perf_flags); 1070 perf_enable();
1052 1071
1053 spin_unlock(&ctx->lock); 1072 spin_unlock(&ctx->lock);
1054} 1073}
@@ -3194,7 +3213,6 @@ __perf_counter_exit_task(struct task_struct *child,
3194 } else { 3213 } else {
3195 struct perf_cpu_context *cpuctx; 3214 struct perf_cpu_context *cpuctx;
3196 unsigned long flags; 3215 unsigned long flags;
3197 u64 perf_flags;
3198 3216
3199 /* 3217 /*
3200 * Disable and unlink this counter. 3218 * Disable and unlink this counter.
@@ -3203,7 +3221,7 @@ __perf_counter_exit_task(struct task_struct *child,
3203 * could still be processing it: 3221 * could still be processing it:
3204 */ 3222 */
3205 local_irq_save(flags); 3223 local_irq_save(flags);
3206 perf_flags = hw_perf_save_disable(); 3224 perf_disable();
3207 3225
3208 cpuctx = &__get_cpu_var(perf_cpu_context); 3226 cpuctx = &__get_cpu_var(perf_cpu_context);
3209 3227
@@ -3214,7 +3232,7 @@ __perf_counter_exit_task(struct task_struct *child,
3214 3232
3215 child_ctx->nr_counters--; 3233 child_ctx->nr_counters--;
3216 3234
3217 hw_perf_restore(perf_flags); 3235 perf_enable();
3218 local_irq_restore(flags); 3236 local_irq_restore(flags);
3219 } 3237 }
3220 3238