diff options
author | Andi Kleen <ak@suse.de> | 2006-01-11 16:44:36 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-11 22:04:55 -0500 |
commit | 95833c83f3b812c78e48db4eaa19f6c74958470b (patch) | |
tree | 8ad17da708141d66cf53c2113b4fe1710af929ec | |
parent | 6b050f8075823b0d9ec4fad38f4f552b74e5c5af (diff) |
[PATCH] x86_64: Add idle notifiers
This adds a new notifier chain that is called with IDLE_START
when a CPU goes idle and IDLE_END when it goes out of idle.
The context can be idle thread or interrupt context.
Since we cannot rely on MONITOR/MWAIT existing the idle
end check currently has to be done in all interrupt
handlers.
They were originally inspired by the similar s390 implementation.
They have a variety of applications:
- They will be needed for CONFIG_NO_IDLE_HZ
- They can be used for oprofile to fix up the missing time
in idle when performance counters don't tick.
- They can be used for better C state management in ACPI
- They could be used for microstate accounting.
This is just infrastructure so far, no users.
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | arch/x86_64/kernel/apic.c | 4 | ||||
-rw-r--r-- | arch/x86_64/kernel/irq.c | 2 | ||||
-rw-r--r-- | arch/x86_64/kernel/mce_amd.c | 2 | ||||
-rw-r--r-- | arch/x86_64/kernel/mce_intel.c | 2 | ||||
-rw-r--r-- | arch/x86_64/kernel/process.c | 48 | ||||
-rw-r--r-- | arch/x86_64/kernel/smp.c | 2 | ||||
-rw-r--r-- | include/asm-x86_64/idle.h | 14 |
7 files changed, 74 insertions, 0 deletions
diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c index 40cbd60e2d0b..628aebf9f9aa 100644 --- a/arch/x86_64/kernel/apic.c +++ b/arch/x86_64/kernel/apic.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <asm/pgalloc.h> | 34 | #include <asm/pgalloc.h> |
35 | #include <asm/mach_apic.h> | 35 | #include <asm/mach_apic.h> |
36 | #include <asm/nmi.h> | 36 | #include <asm/nmi.h> |
37 | #include <asm/idle.h> | ||
37 | 38 | ||
38 | int apic_verbosity; | 39 | int apic_verbosity; |
39 | 40 | ||
@@ -922,6 +923,7 @@ void smp_apic_timer_interrupt(struct pt_regs *regs) | |||
922 | * Besides, if we don't timer interrupts ignore the global | 923 | * Besides, if we don't timer interrupts ignore the global |
923 | * interrupt lock, which is the WrongThing (tm) to do. | 924 | * interrupt lock, which is the WrongThing (tm) to do. |
924 | */ | 925 | */ |
926 | exit_idle(); | ||
925 | irq_enter(); | 927 | irq_enter(); |
926 | smp_local_timer_interrupt(regs); | 928 | smp_local_timer_interrupt(regs); |
927 | irq_exit(); | 929 | irq_exit(); |
@@ -981,6 +983,7 @@ __init int oem_force_hpet_timer(void) | |||
981 | asmlinkage void smp_spurious_interrupt(void) | 983 | asmlinkage void smp_spurious_interrupt(void) |
982 | { | 984 | { |
983 | unsigned int v; | 985 | unsigned int v; |
986 | exit_idle(); | ||
984 | irq_enter(); | 987 | irq_enter(); |
985 | /* | 988 | /* |
986 | * Check if this really is a spurious interrupt and ACK it | 989 | * Check if this really is a spurious interrupt and ACK it |
@@ -1016,6 +1019,7 @@ asmlinkage void smp_error_interrupt(void) | |||
1016 | { | 1019 | { |
1017 | unsigned int v, v1; | 1020 | unsigned int v, v1; |
1018 | 1021 | ||
1022 | exit_idle(); | ||
1019 | irq_enter(); | 1023 | irq_enter(); |
1020 | /* First tickle the hardware, only then report what went on. -- REW */ | 1024 | /* First tickle the hardware, only then report what went on. -- REW */ |
1021 | v = apic_read(APIC_ESR); | 1025 | v = apic_read(APIC_ESR); |
diff --git a/arch/x86_64/kernel/irq.c b/arch/x86_64/kernel/irq.c index d6a04a8320a3..8bf57f78d923 100644 --- a/arch/x86_64/kernel/irq.c +++ b/arch/x86_64/kernel/irq.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
18 | #include <asm/uaccess.h> | 18 | #include <asm/uaccess.h> |
19 | #include <asm/io_apic.h> | 19 | #include <asm/io_apic.h> |
20 | #include <asm/idle.h> | ||
20 | 21 | ||
21 | atomic_t irq_err_count; | 22 | atomic_t irq_err_count; |
22 | #ifdef CONFIG_X86_IO_APIC | 23 | #ifdef CONFIG_X86_IO_APIC |
@@ -98,6 +99,7 @@ asmlinkage unsigned int do_IRQ(struct pt_regs *regs) | |||
98 | /* high bits used in ret_from_ code */ | 99 | /* high bits used in ret_from_ code */ |
99 | unsigned irq = regs->orig_rax & 0xff; | 100 | unsigned irq = regs->orig_rax & 0xff; |
100 | 101 | ||
102 | exit_idle(); | ||
101 | irq_enter(); | 103 | irq_enter(); |
102 | 104 | ||
103 | __do_IRQ(irq, regs); | 105 | __do_IRQ(irq, regs); |
diff --git a/arch/x86_64/kernel/mce_amd.c b/arch/x86_64/kernel/mce_amd.c index 1f76175ace02..0b62a44ab34b 100644 --- a/arch/x86_64/kernel/mce_amd.c +++ b/arch/x86_64/kernel/mce_amd.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <asm/mce.h> | 27 | #include <asm/mce.h> |
28 | #include <asm/msr.h> | 28 | #include <asm/msr.h> |
29 | #include <asm/percpu.h> | 29 | #include <asm/percpu.h> |
30 | #include <asm/idle.h> | ||
30 | 31 | ||
31 | #define PFX "mce_threshold: " | 32 | #define PFX "mce_threshold: " |
32 | #define VERSION "version 1.00.9" | 33 | #define VERSION "version 1.00.9" |
@@ -140,6 +141,7 @@ asmlinkage void mce_threshold_interrupt(void) | |||
140 | struct mce m; | 141 | struct mce m; |
141 | 142 | ||
142 | ack_APIC_irq(); | 143 | ack_APIC_irq(); |
144 | exit_idle(); | ||
143 | irq_enter(); | 145 | irq_enter(); |
144 | 146 | ||
145 | memset(&m, 0, sizeof(m)); | 147 | memset(&m, 0, sizeof(m)); |
diff --git a/arch/x86_64/kernel/mce_intel.c b/arch/x86_64/kernel/mce_intel.c index 0be0a7959814..3ef845e8d3b0 100644 --- a/arch/x86_64/kernel/mce_intel.c +++ b/arch/x86_64/kernel/mce_intel.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <asm/msr.h> | 10 | #include <asm/msr.h> |
11 | #include <asm/mce.h> | 11 | #include <asm/mce.h> |
12 | #include <asm/hw_irq.h> | 12 | #include <asm/hw_irq.h> |
13 | #include <asm/idle.h> | ||
13 | 14 | ||
14 | static DEFINE_PER_CPU(unsigned long, next_check); | 15 | static DEFINE_PER_CPU(unsigned long, next_check); |
15 | 16 | ||
@@ -19,6 +20,7 @@ asmlinkage void smp_thermal_interrupt(void) | |||
19 | 20 | ||
20 | ack_APIC_irq(); | 21 | ack_APIC_irq(); |
21 | 22 | ||
23 | exit_idle(); | ||
22 | irq_enter(); | 24 | irq_enter(); |
23 | if (time_before(jiffies, __get_cpu_var(next_check))) | 25 | if (time_before(jiffies, __get_cpu_var(next_check))) |
24 | goto done; | 26 | goto done; |
diff --git a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c index c9df991150bb..669cf0ed3266 100644 --- a/arch/x86_64/kernel/process.c +++ b/arch/x86_64/kernel/process.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/utsname.h> | 36 | #include <linux/utsname.h> |
37 | #include <linux/random.h> | 37 | #include <linux/random.h> |
38 | #include <linux/kprobes.h> | 38 | #include <linux/kprobes.h> |
39 | #include <linux/notifier.h> | ||
39 | 40 | ||
40 | #include <asm/uaccess.h> | 41 | #include <asm/uaccess.h> |
41 | #include <asm/pgtable.h> | 42 | #include <asm/pgtable.h> |
@@ -50,6 +51,7 @@ | |||
50 | #include <asm/desc.h> | 51 | #include <asm/desc.h> |
51 | #include <asm/proto.h> | 52 | #include <asm/proto.h> |
52 | #include <asm/ia32.h> | 53 | #include <asm/ia32.h> |
54 | #include <asm/idle.h> | ||
53 | 55 | ||
54 | asmlinkage extern void ret_from_fork(void); | 56 | asmlinkage extern void ret_from_fork(void); |
55 | 57 | ||
@@ -64,6 +66,50 @@ EXPORT_SYMBOL(boot_option_idle_override); | |||
64 | void (*pm_idle)(void); | 66 | void (*pm_idle)(void); |
65 | static DEFINE_PER_CPU(unsigned int, cpu_idle_state); | 67 | static DEFINE_PER_CPU(unsigned int, cpu_idle_state); |
66 | 68 | ||
69 | static struct notifier_block *idle_notifier; | ||
70 | static DEFINE_SPINLOCK(idle_notifier_lock); | ||
71 | |||
72 | void idle_notifier_register(struct notifier_block *n) | ||
73 | { | ||
74 | unsigned long flags; | ||
75 | spin_lock_irqsave(&idle_notifier_lock, flags); | ||
76 | notifier_chain_register(&idle_notifier, n); | ||
77 | spin_unlock_irqrestore(&idle_notifier_lock, flags); | ||
78 | } | ||
79 | EXPORT_SYMBOL_GPL(idle_notifier_register); | ||
80 | |||
81 | void idle_notifier_unregister(struct notifier_block *n) | ||
82 | { | ||
83 | unsigned long flags; | ||
84 | spin_lock_irqsave(&idle_notifier_lock, flags); | ||
85 | notifier_chain_unregister(&idle_notifier, n); | ||
86 | spin_unlock_irqrestore(&idle_notifier_lock, flags); | ||
87 | } | ||
88 | EXPORT_SYMBOL(idle_notifier_unregister); | ||
89 | |||
90 | enum idle_state { CPU_IDLE, CPU_NOT_IDLE }; | ||
91 | static DEFINE_PER_CPU(enum idle_state, idle_state) = CPU_NOT_IDLE; | ||
92 | |||
93 | void enter_idle(void) | ||
94 | { | ||
95 | __get_cpu_var(idle_state) = CPU_IDLE; | ||
96 | notifier_call_chain(&idle_notifier, IDLE_START, NULL); | ||
97 | } | ||
98 | |||
99 | static void __exit_idle(void) | ||
100 | { | ||
101 | __get_cpu_var(idle_state) = CPU_NOT_IDLE; | ||
102 | notifier_call_chain(&idle_notifier, IDLE_END, NULL); | ||
103 | } | ||
104 | |||
105 | /* Called from interrupts to signify idle end */ | ||
106 | void exit_idle(void) | ||
107 | { | ||
108 | if (current->pid | read_pda(irqcount)) | ||
109 | return; | ||
110 | __exit_idle(); | ||
111 | } | ||
112 | |||
67 | /* | 113 | /* |
68 | * We use this if we don't have any better | 114 | * We use this if we don't have any better |
69 | * idle routine.. | 115 | * idle routine.. |
@@ -180,7 +226,9 @@ void cpu_idle (void) | |||
180 | idle = default_idle; | 226 | idle = default_idle; |
181 | if (cpu_is_offline(smp_processor_id())) | 227 | if (cpu_is_offline(smp_processor_id())) |
182 | play_dead(); | 228 | play_dead(); |
229 | enter_idle(); | ||
183 | idle(); | 230 | idle(); |
231 | __exit_idle(); | ||
184 | } | 232 | } |
185 | 233 | ||
186 | preempt_enable_no_resched(); | 234 | preempt_enable_no_resched(); |
diff --git a/arch/x86_64/kernel/smp.c b/arch/x86_64/kernel/smp.c index cfc3d9dccbd9..6a666d248c12 100644 --- a/arch/x86_64/kernel/smp.c +++ b/arch/x86_64/kernel/smp.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <asm/mmu_context.h> | 27 | #include <asm/mmu_context.h> |
28 | #include <asm/proto.h> | 28 | #include <asm/proto.h> |
29 | #include <asm/apicdef.h> | 29 | #include <asm/apicdef.h> |
30 | #include <asm/idle.h> | ||
30 | 31 | ||
31 | /* | 32 | /* |
32 | * Smarter SMP flushing macros. | 33 | * Smarter SMP flushing macros. |
@@ -512,6 +513,7 @@ asmlinkage void smp_call_function_interrupt(void) | |||
512 | /* | 513 | /* |
513 | * At this point the info structure may be out of scope unless wait==1 | 514 | * At this point the info structure may be out of scope unless wait==1 |
514 | */ | 515 | */ |
516 | exit_idle(); | ||
515 | irq_enter(); | 517 | irq_enter(); |
516 | (*func)(info); | 518 | (*func)(info); |
517 | irq_exit(); | 519 | irq_exit(); |
diff --git a/include/asm-x86_64/idle.h b/include/asm-x86_64/idle.h new file mode 100644 index 000000000000..6bd47dcf2067 --- /dev/null +++ b/include/asm-x86_64/idle.h | |||
@@ -0,0 +1,14 @@ | |||
1 | #ifndef _ASM_X86_64_IDLE_H | ||
2 | #define _ASM_X86_64_IDLE_H 1 | ||
3 | |||
4 | #define IDLE_START 1 | ||
5 | #define IDLE_END 2 | ||
6 | |||
7 | struct notifier_block; | ||
8 | void idle_notifier_register(struct notifier_block *n); | ||
9 | void idle_notifier_unregister(struct notifier_block *n); | ||
10 | |||
11 | void enter_idle(void); | ||
12 | void exit_idle(void); | ||
13 | |||
14 | #endif | ||