aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndi Kleen <ak@suse.de>2006-01-11 16:44:36 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-11 22:04:55 -0500
commit95833c83f3b812c78e48db4eaa19f6c74958470b (patch)
tree8ad17da708141d66cf53c2113b4fe1710af929ec
parent6b050f8075823b0d9ec4fad38f4f552b74e5c5af (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.c4
-rw-r--r--arch/x86_64/kernel/irq.c2
-rw-r--r--arch/x86_64/kernel/mce_amd.c2
-rw-r--r--arch/x86_64/kernel/mce_intel.c2
-rw-r--r--arch/x86_64/kernel/process.c48
-rw-r--r--arch/x86_64/kernel/smp.c2
-rw-r--r--include/asm-x86_64/idle.h14
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
38int apic_verbosity; 39int 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)
981asmlinkage void smp_spurious_interrupt(void) 983asmlinkage 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
21atomic_t irq_err_count; 22atomic_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
14static DEFINE_PER_CPU(unsigned long, next_check); 15static 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
54asmlinkage extern void ret_from_fork(void); 56asmlinkage extern void ret_from_fork(void);
55 57
@@ -64,6 +66,50 @@ EXPORT_SYMBOL(boot_option_idle_override);
64void (*pm_idle)(void); 66void (*pm_idle)(void);
65static DEFINE_PER_CPU(unsigned int, cpu_idle_state); 67static DEFINE_PER_CPU(unsigned int, cpu_idle_state);
66 68
69static struct notifier_block *idle_notifier;
70static DEFINE_SPINLOCK(idle_notifier_lock);
71
72void 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}
79EXPORT_SYMBOL_GPL(idle_notifier_register);
80
81void 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}
88EXPORT_SYMBOL(idle_notifier_unregister);
89
90enum idle_state { CPU_IDLE, CPU_NOT_IDLE };
91static DEFINE_PER_CPU(enum idle_state, idle_state) = CPU_NOT_IDLE;
92
93void enter_idle(void)
94{
95 __get_cpu_var(idle_state) = CPU_IDLE;
96 notifier_call_chain(&idle_notifier, IDLE_START, NULL);
97}
98
99static 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 */
106void 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
7struct notifier_block;
8void idle_notifier_register(struct notifier_block *n);
9void idle_notifier_unregister(struct notifier_block *n);
10
11void enter_idle(void);
12void exit_idle(void);
13
14#endif