diff options
author | Boris Ostrovsky <boris.ostrovsky@amd.com> | 2012-03-13 14:55:09 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2012-03-30 03:23:01 -0400 |
commit | 1a022e3f1be11730bd8747b1af96a0274bf6356e (patch) | |
tree | d3c95b68626ac7d963ac3a85a07dae1cfc011906 | |
parent | e07510585a88c0f6c6c728e2e006aa913496d4ae (diff) |
idle, x86: Allow off-lined CPU to enter deeper C states
Currently when a CPU is off-lined it enters either MWAIT-based idle or,
if MWAIT is not desired or supported, HLT-based idle (which places the
processor in C1 state). This patch allows processors without MWAIT
support to stay in states deeper than C1.
Signed-off-by: Boris Ostrovsky <boris.ostrovsky@amd.com>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | arch/x86/kernel/smpboot.c | 4 | ||||
-rw-r--r-- | drivers/acpi/processor_idle.c | 31 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 28 | ||||
-rw-r--r-- | include/linux/cpuidle.h | 5 |
4 files changed, 67 insertions, 1 deletions
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 66d250c00d11..93a2a0932b51 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #include <linux/tboot.h> | 50 | #include <linux/tboot.h> |
51 | #include <linux/stackprotector.h> | 51 | #include <linux/stackprotector.h> |
52 | #include <linux/gfp.h> | 52 | #include <linux/gfp.h> |
53 | #include <linux/cpuidle.h> | ||
53 | 54 | ||
54 | #include <asm/acpi.h> | 55 | #include <asm/acpi.h> |
55 | #include <asm/desc.h> | 56 | #include <asm/desc.h> |
@@ -1422,7 +1423,8 @@ void native_play_dead(void) | |||
1422 | tboot_shutdown(TB_SHUTDOWN_WFS); | 1423 | tboot_shutdown(TB_SHUTDOWN_WFS); |
1423 | 1424 | ||
1424 | mwait_play_dead(); /* Only returns on failure */ | 1425 | mwait_play_dead(); /* Only returns on failure */ |
1425 | hlt_play_dead(); | 1426 | if (cpuidle_play_dead()) |
1427 | hlt_play_dead(); | ||
1426 | } | 1428 | } |
1427 | 1429 | ||
1428 | #else /* ... !CONFIG_HOTPLUG_CPU */ | 1430 | #else /* ... !CONFIG_HOTPLUG_CPU */ |
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 0e8e2de2ed3e..6b1d32a161ae 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c | |||
@@ -770,6 +770,35 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, | |||
770 | return index; | 770 | return index; |
771 | } | 771 | } |
772 | 772 | ||
773 | |||
774 | /** | ||
775 | * acpi_idle_play_dead - enters an ACPI state for long-term idle (i.e. off-lining) | ||
776 | * @dev: the target CPU | ||
777 | * @index: the index of suggested state | ||
778 | */ | ||
779 | static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) | ||
780 | { | ||
781 | struct cpuidle_state_usage *state_usage = &dev->states_usage[index]; | ||
782 | struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage); | ||
783 | |||
784 | ACPI_FLUSH_CPU_CACHE(); | ||
785 | |||
786 | while (1) { | ||
787 | |||
788 | if (cx->entry_method == ACPI_CSTATE_HALT) | ||
789 | halt(); | ||
790 | else if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) { | ||
791 | inb(cx->address); | ||
792 | /* See comment in acpi_idle_do_entry() */ | ||
793 | inl(acpi_gbl_FADT.xpm_timer_block.address); | ||
794 | } else | ||
795 | return -ENODEV; | ||
796 | } | ||
797 | |||
798 | /* Never reached */ | ||
799 | return 0; | ||
800 | } | ||
801 | |||
773 | /** | 802 | /** |
774 | * acpi_idle_enter_simple - enters an ACPI state without BM handling | 803 | * acpi_idle_enter_simple - enters an ACPI state without BM handling |
775 | * @dev: the target CPU | 804 | * @dev: the target CPU |
@@ -1077,12 +1106,14 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) | |||
1077 | state->flags |= CPUIDLE_FLAG_TIME_VALID; | 1106 | state->flags |= CPUIDLE_FLAG_TIME_VALID; |
1078 | 1107 | ||
1079 | state->enter = acpi_idle_enter_c1; | 1108 | state->enter = acpi_idle_enter_c1; |
1109 | state->enter_dead = acpi_idle_play_dead; | ||
1080 | drv->safe_state_index = count; | 1110 | drv->safe_state_index = count; |
1081 | break; | 1111 | break; |
1082 | 1112 | ||
1083 | case ACPI_STATE_C2: | 1113 | case ACPI_STATE_C2: |
1084 | state->flags |= CPUIDLE_FLAG_TIME_VALID; | 1114 | state->flags |= CPUIDLE_FLAG_TIME_VALID; |
1085 | state->enter = acpi_idle_enter_simple; | 1115 | state->enter = acpi_idle_enter_simple; |
1116 | state->enter_dead = acpi_idle_play_dead; | ||
1086 | drv->safe_state_index = count; | 1117 | drv->safe_state_index = count; |
1087 | break; | 1118 | break; |
1088 | 1119 | ||
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index f7cab5e9c4d6..3e146b2ada4a 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
@@ -72,6 +72,34 @@ typedef int (*cpuidle_enter_t)(struct cpuidle_device *dev, | |||
72 | static cpuidle_enter_t cpuidle_enter_ops; | 72 | static cpuidle_enter_t cpuidle_enter_ops; |
73 | 73 | ||
74 | /** | 74 | /** |
75 | * cpuidle_play_dead - cpu off-lining | ||
76 | * | ||
77 | * Only returns in case of an error | ||
78 | */ | ||
79 | int cpuidle_play_dead(void) | ||
80 | { | ||
81 | struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); | ||
82 | struct cpuidle_driver *drv = cpuidle_get_driver(); | ||
83 | int i, dead_state = -1; | ||
84 | int power_usage = -1; | ||
85 | |||
86 | /* Find lowest-power state that supports long-term idle */ | ||
87 | for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { | ||
88 | struct cpuidle_state *s = &drv->states[i]; | ||
89 | |||
90 | if (s->power_usage < power_usage && s->enter_dead) { | ||
91 | power_usage = s->power_usage; | ||
92 | dead_state = i; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | if (dead_state != -1) | ||
97 | return drv->states[dead_state].enter_dead(dev, dead_state); | ||
98 | |||
99 | return -ENODEV; | ||
100 | } | ||
101 | |||
102 | /** | ||
75 | * cpuidle_idle_call - the main idle loop | 103 | * cpuidle_idle_call - the main idle loop |
76 | * | 104 | * |
77 | * NOTE: no locks or semaphores should be used here | 105 | * NOTE: no locks or semaphores should be used here |
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index f3ebbba368b3..d557bcd0ada7 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h | |||
@@ -51,6 +51,8 @@ struct cpuidle_state { | |||
51 | int (*enter) (struct cpuidle_device *dev, | 51 | int (*enter) (struct cpuidle_device *dev, |
52 | struct cpuidle_driver *drv, | 52 | struct cpuidle_driver *drv, |
53 | int index); | 53 | int index); |
54 | |||
55 | int (*enter_dead) (struct cpuidle_device *dev, int index); | ||
54 | }; | 56 | }; |
55 | 57 | ||
56 | /* Idle State Flags */ | 58 | /* Idle State Flags */ |
@@ -147,6 +149,8 @@ extern int cpuidle_wrap_enter(struct cpuidle_device *dev, | |||
147 | struct cpuidle_driver *drv, int index, | 149 | struct cpuidle_driver *drv, int index, |
148 | int (*enter)(struct cpuidle_device *dev, | 150 | int (*enter)(struct cpuidle_device *dev, |
149 | struct cpuidle_driver *drv, int index)); | 151 | struct cpuidle_driver *drv, int index)); |
152 | extern int cpuidle_play_dead(void); | ||
153 | |||
150 | #else | 154 | #else |
151 | static inline void disable_cpuidle(void) { } | 155 | static inline void disable_cpuidle(void) { } |
152 | static inline int cpuidle_idle_call(void) { return -ENODEV; } | 156 | static inline int cpuidle_idle_call(void) { return -ENODEV; } |
@@ -168,6 +172,7 @@ static inline int cpuidle_wrap_enter(struct cpuidle_device *dev, | |||
168 | int (*enter)(struct cpuidle_device *dev, | 172 | int (*enter)(struct cpuidle_device *dev, |
169 | struct cpuidle_driver *drv, int index)) | 173 | struct cpuidle_driver *drv, int index)) |
170 | { return -ENODEV; } | 174 | { return -ENODEV; } |
175 | static inline int cpuidle_play_dead(void) {return -ENODEV; } | ||
171 | 176 | ||
172 | #endif | 177 | #endif |
173 | 178 | ||