diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2009-03-30 13:07:02 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-04-06 03:30:36 -0400 |
commit | 925d519ab82b6dd7aca9420d809ee83819c08db2 (patch) | |
tree | aa05bd7eb607915aa691d5434ec74521b487b466 /arch | |
parent | 53cfbf593758916aac41db728f029986a62f1254 (diff) |
perf_counter: unify and fix delayed counter wakeup
While going over the wakeup code I noticed delayed wakeups only work
for hardware counters but basically all software counters rely on
them.
This patch unifies and generalizes the delayed wakeup to fix this
issue.
Since we're dealing with NMI context bits here, use a cmpxchg() based
single link list implementation to track counters that have pending
wakeups.
[ This should really be generic code for delayed wakeups, but since we
cannot use cmpxchg()/xchg() in generic code, I've let it live in the
perf_counter code. -- Eric Dumazet could use it to aggregate the
network wakeups. ]
Furthermore, the x86 method of using TIF flags was flawed in that its
quite possible to end up setting the bit on the idle task, loosing the
wakeup.
The powerpc method uses per-cpu storage and does appear to be
sufficient.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Acked-by: Paul Mackerras <paulus@samba.org>
Orig-LKML-Reference: <20090330171023.153932974@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/include/asm/hw_irq.h | 4 | ||||
-rw-r--r-- | arch/powerpc/kernel/irq.c | 2 | ||||
-rw-r--r-- | arch/powerpc/kernel/perf_counter.c | 22 | ||||
-rw-r--r-- | arch/x86/include/asm/perf_counter.h | 5 | ||||
-rw-r--r-- | arch/x86/include/asm/thread_info.h | 4 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_counter.c | 29 | ||||
-rw-r--r-- | arch/x86/kernel/signal.c | 6 |
7 files changed, 9 insertions, 63 deletions
diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h index cb32d571c9c7..20a44d0c9fdd 100644 --- a/arch/powerpc/include/asm/hw_irq.h +++ b/arch/powerpc/include/asm/hw_irq.h | |||
@@ -132,7 +132,7 @@ static inline int irqs_disabled_flags(unsigned long flags) | |||
132 | struct irq_chip; | 132 | struct irq_chip; |
133 | 133 | ||
134 | #ifdef CONFIG_PERF_COUNTERS | 134 | #ifdef CONFIG_PERF_COUNTERS |
135 | static inline unsigned long get_perf_counter_pending(void) | 135 | static inline unsigned long test_perf_counter_pending(void) |
136 | { | 136 | { |
137 | unsigned long x; | 137 | unsigned long x; |
138 | 138 | ||
@@ -160,7 +160,7 @@ extern void perf_counter_do_pending(void); | |||
160 | 160 | ||
161 | #else | 161 | #else |
162 | 162 | ||
163 | static inline unsigned long get_perf_counter_pending(void) | 163 | static inline unsigned long test_perf_counter_pending(void) |
164 | { | 164 | { |
165 | return 0; | 165 | return 0; |
166 | } | 166 | } |
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 469e9635ff04..2cd471f92fe6 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c | |||
@@ -135,7 +135,7 @@ notrace void raw_local_irq_restore(unsigned long en) | |||
135 | iseries_handle_interrupts(); | 135 | iseries_handle_interrupts(); |
136 | } | 136 | } |
137 | 137 | ||
138 | if (get_perf_counter_pending()) { | 138 | if (test_perf_counter_pending()) { |
139 | clear_perf_counter_pending(); | 139 | clear_perf_counter_pending(); |
140 | perf_counter_do_pending(); | 140 | perf_counter_do_pending(); |
141 | } | 141 | } |
diff --git a/arch/powerpc/kernel/perf_counter.c b/arch/powerpc/kernel/perf_counter.c index df007fe0cc0b..cde720fc495c 100644 --- a/arch/powerpc/kernel/perf_counter.c +++ b/arch/powerpc/kernel/perf_counter.c | |||
@@ -650,24 +650,6 @@ hw_perf_counter_init(struct perf_counter *counter) | |||
650 | } | 650 | } |
651 | 651 | ||
652 | /* | 652 | /* |
653 | * Handle wakeups. | ||
654 | */ | ||
655 | void perf_counter_do_pending(void) | ||
656 | { | ||
657 | int i; | ||
658 | struct cpu_hw_counters *cpuhw = &__get_cpu_var(cpu_hw_counters); | ||
659 | struct perf_counter *counter; | ||
660 | |||
661 | for (i = 0; i < cpuhw->n_counters; ++i) { | ||
662 | counter = cpuhw->counter[i]; | ||
663 | if (counter && counter->wakeup_pending) { | ||
664 | counter->wakeup_pending = 0; | ||
665 | wake_up(&counter->waitq); | ||
666 | } | ||
667 | } | ||
668 | } | ||
669 | |||
670 | /* | ||
671 | * A counter has overflowed; update its count and record | 653 | * A counter has overflowed; update its count and record |
672 | * things if requested. Note that interrupts are hard-disabled | 654 | * things if requested. Note that interrupts are hard-disabled |
673 | * here so there is no possibility of being interrupted. | 655 | * here so there is no possibility of being interrupted. |
@@ -720,7 +702,7 @@ static void perf_counter_interrupt(struct pt_regs *regs) | |||
720 | struct cpu_hw_counters *cpuhw = &__get_cpu_var(cpu_hw_counters); | 702 | struct cpu_hw_counters *cpuhw = &__get_cpu_var(cpu_hw_counters); |
721 | struct perf_counter *counter; | 703 | struct perf_counter *counter; |
722 | long val; | 704 | long val; |
723 | int need_wakeup = 0, found = 0; | 705 | int found = 0; |
724 | 706 | ||
725 | for (i = 0; i < cpuhw->n_counters; ++i) { | 707 | for (i = 0; i < cpuhw->n_counters; ++i) { |
726 | counter = cpuhw->counter[i]; | 708 | counter = cpuhw->counter[i]; |
@@ -761,7 +743,7 @@ static void perf_counter_interrupt(struct pt_regs *regs) | |||
761 | * immediately; otherwise we'll have do the wakeup when interrupts | 743 | * immediately; otherwise we'll have do the wakeup when interrupts |
762 | * get soft-enabled. | 744 | * get soft-enabled. |
763 | */ | 745 | */ |
764 | if (get_perf_counter_pending() && regs->softe) { | 746 | if (test_perf_counter_pending() && regs->softe) { |
765 | irq_enter(); | 747 | irq_enter(); |
766 | clear_perf_counter_pending(); | 748 | clear_perf_counter_pending(); |
767 | perf_counter_do_pending(); | 749 | perf_counter_do_pending(); |
diff --git a/arch/x86/include/asm/perf_counter.h b/arch/x86/include/asm/perf_counter.h index 1662043b340f..e2b0e66b2353 100644 --- a/arch/x86/include/asm/perf_counter.h +++ b/arch/x86/include/asm/perf_counter.h | |||
@@ -84,8 +84,9 @@ union cpuid10_edx { | |||
84 | #define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b | 84 | #define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b |
85 | #define X86_PMC_IDX_FIXED_BUS_CYCLES (X86_PMC_IDX_FIXED + 2) | 85 | #define X86_PMC_IDX_FIXED_BUS_CYCLES (X86_PMC_IDX_FIXED + 2) |
86 | 86 | ||
87 | #define set_perf_counter_pending() \ | 87 | #define set_perf_counter_pending() do { } while (0) |
88 | set_tsk_thread_flag(current, TIF_PERF_COUNTERS); | 88 | #define clear_perf_counter_pending() do { } while (0) |
89 | #define test_perf_counter_pending() (0) | ||
89 | 90 | ||
90 | #ifdef CONFIG_PERF_COUNTERS | 91 | #ifdef CONFIG_PERF_COUNTERS |
91 | extern void init_hw_perf_counters(void); | 92 | extern void init_hw_perf_counters(void); |
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index 3ffd5d2a3676..8820a73ae090 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h | |||
@@ -83,7 +83,6 @@ struct thread_info { | |||
83 | #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ | 83 | #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ |
84 | #define TIF_SECCOMP 8 /* secure computing */ | 84 | #define TIF_SECCOMP 8 /* secure computing */ |
85 | #define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */ | 85 | #define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */ |
86 | #define TIF_PERF_COUNTERS 11 /* notify perf counter work */ | ||
87 | #define TIF_NOTSC 16 /* TSC is not accessible in userland */ | 86 | #define TIF_NOTSC 16 /* TSC is not accessible in userland */ |
88 | #define TIF_IA32 17 /* 32bit process */ | 87 | #define TIF_IA32 17 /* 32bit process */ |
89 | #define TIF_FORK 18 /* ret_from_fork */ | 88 | #define TIF_FORK 18 /* ret_from_fork */ |
@@ -107,7 +106,6 @@ struct thread_info { | |||
107 | #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) | 106 | #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) |
108 | #define _TIF_SECCOMP (1 << TIF_SECCOMP) | 107 | #define _TIF_SECCOMP (1 << TIF_SECCOMP) |
109 | #define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY) | 108 | #define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY) |
110 | #define _TIF_PERF_COUNTERS (1 << TIF_PERF_COUNTERS) | ||
111 | #define _TIF_NOTSC (1 << TIF_NOTSC) | 109 | #define _TIF_NOTSC (1 << TIF_NOTSC) |
112 | #define _TIF_IA32 (1 << TIF_IA32) | 110 | #define _TIF_IA32 (1 << TIF_IA32) |
113 | #define _TIF_FORK (1 << TIF_FORK) | 111 | #define _TIF_FORK (1 << TIF_FORK) |
@@ -141,7 +139,7 @@ struct thread_info { | |||
141 | 139 | ||
142 | /* Only used for 64 bit */ | 140 | /* Only used for 64 bit */ |
143 | #define _TIF_DO_NOTIFY_MASK \ | 141 | #define _TIF_DO_NOTIFY_MASK \ |
144 | (_TIF_SIGPENDING|_TIF_MCE_NOTIFY|_TIF_PERF_COUNTERS|_TIF_NOTIFY_RESUME) | 142 | (_TIF_SIGPENDING|_TIF_MCE_NOTIFY|_TIF_NOTIFY_RESUME) |
145 | 143 | ||
146 | /* flags to check in __switch_to() */ | 144 | /* flags to check in __switch_to() */ |
147 | #define _TIF_WORK_CTXSW \ | 145 | #define _TIF_WORK_CTXSW \ |
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 3f95b0cdc550..7aab177fb566 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
@@ -227,7 +227,6 @@ static int __hw_perf_counter_init(struct perf_counter *counter) | |||
227 | */ | 227 | */ |
228 | hwc->config |= pmc_ops->event_map(perf_event_id(hw_event)); | 228 | hwc->config |= pmc_ops->event_map(perf_event_id(hw_event)); |
229 | } | 229 | } |
230 | counter->wakeup_pending = 0; | ||
231 | 230 | ||
232 | return 0; | 231 | return 0; |
233 | } | 232 | } |
@@ -773,34 +772,6 @@ void smp_perf_counter_interrupt(struct pt_regs *regs) | |||
773 | irq_exit(); | 772 | irq_exit(); |
774 | } | 773 | } |
775 | 774 | ||
776 | /* | ||
777 | * This handler is triggered by NMI contexts: | ||
778 | */ | ||
779 | void perf_counter_notify(struct pt_regs *regs) | ||
780 | { | ||
781 | struct cpu_hw_counters *cpuc; | ||
782 | unsigned long flags; | ||
783 | int bit, cpu; | ||
784 | |||
785 | local_irq_save(flags); | ||
786 | cpu = smp_processor_id(); | ||
787 | cpuc = &per_cpu(cpu_hw_counters, cpu); | ||
788 | |||
789 | for_each_bit(bit, cpuc->used, X86_PMC_IDX_MAX) { | ||
790 | struct perf_counter *counter = cpuc->counters[bit]; | ||
791 | |||
792 | if (!counter) | ||
793 | continue; | ||
794 | |||
795 | if (counter->wakeup_pending) { | ||
796 | counter->wakeup_pending = 0; | ||
797 | wake_up(&counter->waitq); | ||
798 | } | ||
799 | } | ||
800 | |||
801 | local_irq_restore(flags); | ||
802 | } | ||
803 | |||
804 | void perf_counters_lapic_init(int nmi) | 775 | void perf_counters_lapic_init(int nmi) |
805 | { | 776 | { |
806 | u32 apic_val; | 777 | u32 apic_val; |
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index 611615a92c90..0a813b17b172 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c | |||
@@ -6,7 +6,6 @@ | |||
6 | * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes | 6 | * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes |
7 | * 2000-2002 x86-64 support by Andi Kleen | 7 | * 2000-2002 x86-64 support by Andi Kleen |
8 | */ | 8 | */ |
9 | #include <linux/perf_counter.h> | ||
10 | #include <linux/sched.h> | 9 | #include <linux/sched.h> |
11 | #include <linux/mm.h> | 10 | #include <linux/mm.h> |
12 | #include <linux/smp.h> | 11 | #include <linux/smp.h> |
@@ -872,11 +871,6 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) | |||
872 | tracehook_notify_resume(regs); | 871 | tracehook_notify_resume(regs); |
873 | } | 872 | } |
874 | 873 | ||
875 | if (thread_info_flags & _TIF_PERF_COUNTERS) { | ||
876 | clear_thread_flag(TIF_PERF_COUNTERS); | ||
877 | perf_counter_notify(regs); | ||
878 | } | ||
879 | |||
880 | #ifdef CONFIG_X86_32 | 874 | #ifdef CONFIG_X86_32 |
881 | clear_thread_flag(TIF_IRET); | 875 | clear_thread_flag(TIF_IRET); |
882 | #endif /* CONFIG_X86_32 */ | 876 | #endif /* CONFIG_X86_32 */ |