diff options
author | Li Shaohua <shaohua.li@intel.com> | 2005-06-25 17:55:06 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-25 19:24:32 -0400 |
commit | 5a72e04df5470df0ec646029d31e5528167ab1a7 (patch) | |
tree | 11be00c3ed3d8bad775e6eec44078c9f942537b9 /kernel/power | |
parent | fb69c3907ead36b9e9f41ea6f0d0e0ae10a38a47 (diff) |
[PATCH] suspend/resume SMP support
Using CPU hotplug to support suspend/resume SMP. Both S3 and S4 use
disable/enable_nonboot_cpus API. The S4 part is based on Pavel's original S4
SMP patch.
Signed-off-by: Li Shaohua<shaohua.li@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/power')
-rw-r--r-- | kernel/power/Kconfig | 6 | ||||
-rw-r--r-- | kernel/power/Makefile | 6 | ||||
-rw-r--r-- | kernel/power/disk.c | 35 | ||||
-rw-r--r-- | kernel/power/main.c | 16 | ||||
-rw-r--r-- | kernel/power/smp.c | 89 | ||||
-rw-r--r-- | kernel/power/swsusp.c | 2 |
6 files changed, 72 insertions, 82 deletions
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 696387ffe49c..fdb377636505 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig | |||
@@ -28,7 +28,7 @@ config PM_DEBUG | |||
28 | 28 | ||
29 | config SOFTWARE_SUSPEND | 29 | config SOFTWARE_SUSPEND |
30 | bool "Software Suspend (EXPERIMENTAL)" | 30 | bool "Software Suspend (EXPERIMENTAL)" |
31 | depends on EXPERIMENTAL && PM && SWAP | 31 | depends on EXPERIMENTAL && PM && SWAP && (SUSPEND_SMP || !SMP) |
32 | ---help--- | 32 | ---help--- |
33 | Enable the possibility of suspending the machine. | 33 | Enable the possibility of suspending the machine. |
34 | It doesn't need APM. | 34 | It doesn't need APM. |
@@ -72,3 +72,7 @@ config PM_STD_PARTITION | |||
72 | suspended image to. It will simply pick the first available swap | 72 | suspended image to. It will simply pick the first available swap |
73 | device. | 73 | device. |
74 | 74 | ||
75 | config SUSPEND_SMP | ||
76 | bool | ||
77 | depends on HOTPLUG_CPU && X86 && PM | ||
78 | default y | ||
diff --git a/kernel/power/Makefile b/kernel/power/Makefile index fbdc634135a7..2f438d0eaa13 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile | |||
@@ -3,9 +3,9 @@ ifeq ($(CONFIG_PM_DEBUG),y) | |||
3 | EXTRA_CFLAGS += -DDEBUG | 3 | EXTRA_CFLAGS += -DDEBUG |
4 | endif | 4 | endif |
5 | 5 | ||
6 | swsusp-smp-$(CONFIG_SMP) += smp.o | ||
7 | |||
8 | obj-y := main.o process.o console.o pm.o | 6 | obj-y := main.o process.o console.o pm.o |
9 | obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o $(swsusp-smp-y) disk.o | 7 | obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o |
8 | |||
9 | obj-$(CONFIG_SUSPEND_SMP) += smp.o | ||
10 | 10 | ||
11 | obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o | 11 | obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o |
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 02b6764034dc..fb8de63c2919 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -117,8 +117,8 @@ static void finish(void) | |||
117 | { | 117 | { |
118 | device_resume(); | 118 | device_resume(); |
119 | platform_finish(); | 119 | platform_finish(); |
120 | enable_nonboot_cpus(); | ||
121 | thaw_processes(); | 120 | thaw_processes(); |
121 | enable_nonboot_cpus(); | ||
122 | pm_restore_console(); | 122 | pm_restore_console(); |
123 | } | 123 | } |
124 | 124 | ||
@@ -131,28 +131,35 @@ static int prepare_processes(void) | |||
131 | 131 | ||
132 | sys_sync(); | 132 | sys_sync(); |
133 | 133 | ||
134 | disable_nonboot_cpus(); | ||
135 | |||
134 | if (freeze_processes()) { | 136 | if (freeze_processes()) { |
135 | error = -EBUSY; | 137 | error = -EBUSY; |
136 | return error; | 138 | goto thaw; |
137 | } | 139 | } |
138 | 140 | ||
139 | if (pm_disk_mode == PM_DISK_PLATFORM) { | 141 | if (pm_disk_mode == PM_DISK_PLATFORM) { |
140 | if (pm_ops && pm_ops->prepare) { | 142 | if (pm_ops && pm_ops->prepare) { |
141 | if ((error = pm_ops->prepare(PM_SUSPEND_DISK))) | 143 | if ((error = pm_ops->prepare(PM_SUSPEND_DISK))) |
142 | return error; | 144 | goto thaw; |
143 | } | 145 | } |
144 | } | 146 | } |
145 | 147 | ||
146 | /* Free memory before shutting down devices. */ | 148 | /* Free memory before shutting down devices. */ |
147 | free_some_memory(); | 149 | free_some_memory(); |
148 | |||
149 | return 0; | 150 | return 0; |
151 | thaw: | ||
152 | thaw_processes(); | ||
153 | enable_nonboot_cpus(); | ||
154 | pm_restore_console(); | ||
155 | return error; | ||
150 | } | 156 | } |
151 | 157 | ||
152 | static void unprepare_processes(void) | 158 | static void unprepare_processes(void) |
153 | { | 159 | { |
154 | enable_nonboot_cpus(); | 160 | platform_finish(); |
155 | thaw_processes(); | 161 | thaw_processes(); |
162 | enable_nonboot_cpus(); | ||
156 | pm_restore_console(); | 163 | pm_restore_console(); |
157 | } | 164 | } |
158 | 165 | ||
@@ -160,15 +167,9 @@ static int prepare_devices(void) | |||
160 | { | 167 | { |
161 | int error; | 168 | int error; |
162 | 169 | ||
163 | disable_nonboot_cpus(); | 170 | if ((error = device_suspend(PMSG_FREEZE))) |
164 | if ((error = device_suspend(PMSG_FREEZE))) { | ||
165 | printk("Some devices failed to suspend\n"); | 171 | printk("Some devices failed to suspend\n"); |
166 | platform_finish(); | 172 | return error; |
167 | enable_nonboot_cpus(); | ||
168 | return error; | ||
169 | } | ||
170 | |||
171 | return 0; | ||
172 | } | 173 | } |
173 | 174 | ||
174 | /** | 175 | /** |
@@ -185,9 +186,9 @@ int pm_suspend_disk(void) | |||
185 | int error; | 186 | int error; |
186 | 187 | ||
187 | error = prepare_processes(); | 188 | error = prepare_processes(); |
188 | if (!error) { | 189 | if (error) |
189 | error = prepare_devices(); | 190 | return error; |
190 | } | 191 | error = prepare_devices(); |
191 | 192 | ||
192 | if (error) { | 193 | if (error) { |
193 | unprepare_processes(); | 194 | unprepare_processes(); |
@@ -250,7 +251,7 @@ static int software_resume(void) | |||
250 | 251 | ||
251 | if ((error = prepare_processes())) { | 252 | if ((error = prepare_processes())) { |
252 | swsusp_close(); | 253 | swsusp_close(); |
253 | goto Cleanup; | 254 | goto Done; |
254 | } | 255 | } |
255 | 256 | ||
256 | pr_debug("PM: Reading swsusp image.\n"); | 257 | pr_debug("PM: Reading swsusp image.\n"); |
diff --git a/kernel/power/main.c b/kernel/power/main.c index 4cdebc972ff2..c94cb9e95090 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
@@ -55,6 +55,13 @@ static int suspend_prepare(suspend_state_t state) | |||
55 | 55 | ||
56 | pm_prepare_console(); | 56 | pm_prepare_console(); |
57 | 57 | ||
58 | disable_nonboot_cpus(); | ||
59 | |||
60 | if (num_online_cpus() != 1) { | ||
61 | error = -EPERM; | ||
62 | goto Enable_cpu; | ||
63 | } | ||
64 | |||
58 | if (freeze_processes()) { | 65 | if (freeze_processes()) { |
59 | error = -EAGAIN; | 66 | error = -EAGAIN; |
60 | goto Thaw; | 67 | goto Thaw; |
@@ -75,6 +82,8 @@ static int suspend_prepare(suspend_state_t state) | |||
75 | pm_ops->finish(state); | 82 | pm_ops->finish(state); |
76 | Thaw: | 83 | Thaw: |
77 | thaw_processes(); | 84 | thaw_processes(); |
85 | Enable_cpu: | ||
86 | enable_nonboot_cpus(); | ||
78 | pm_restore_console(); | 87 | pm_restore_console(); |
79 | return error; | 88 | return error; |
80 | } | 89 | } |
@@ -113,6 +122,7 @@ static void suspend_finish(suspend_state_t state) | |||
113 | if (pm_ops && pm_ops->finish) | 122 | if (pm_ops && pm_ops->finish) |
114 | pm_ops->finish(state); | 123 | pm_ops->finish(state); |
115 | thaw_processes(); | 124 | thaw_processes(); |
125 | enable_nonboot_cpus(); | ||
116 | pm_restore_console(); | 126 | pm_restore_console(); |
117 | } | 127 | } |
118 | 128 | ||
@@ -150,12 +160,6 @@ static int enter_state(suspend_state_t state) | |||
150 | goto Unlock; | 160 | goto Unlock; |
151 | } | 161 | } |
152 | 162 | ||
153 | /* Suspend is hard to get right on SMP. */ | ||
154 | if (num_online_cpus() != 1) { | ||
155 | error = -EPERM; | ||
156 | goto Unlock; | ||
157 | } | ||
158 | |||
159 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); | 163 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); |
160 | if ((error = suspend_prepare(state))) | 164 | if ((error = suspend_prepare(state))) |
161 | goto Unlock; | 165 | goto Unlock; |
diff --git a/kernel/power/smp.c b/kernel/power/smp.c index 457c2302ed42..bbe23079c62c 100644 --- a/kernel/power/smp.c +++ b/kernel/power/smp.c | |||
@@ -13,73 +13,52 @@ | |||
13 | #include <linux/interrupt.h> | 13 | #include <linux/interrupt.h> |
14 | #include <linux/suspend.h> | 14 | #include <linux/suspend.h> |
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/cpu.h> | ||
16 | #include <asm/atomic.h> | 17 | #include <asm/atomic.h> |
17 | #include <asm/tlbflush.h> | 18 | #include <asm/tlbflush.h> |
18 | 19 | ||
19 | static atomic_t cpu_counter, freeze; | 20 | /* This is protected by pm_sem semaphore */ |
20 | 21 | static cpumask_t frozen_cpus; | |
21 | |||
22 | static void smp_pause(void * data) | ||
23 | { | ||
24 | struct saved_context ctxt; | ||
25 | __save_processor_state(&ctxt); | ||
26 | printk("Sleeping in:\n"); | ||
27 | dump_stack(); | ||
28 | atomic_inc(&cpu_counter); | ||
29 | while (atomic_read(&freeze)) { | ||
30 | /* FIXME: restore takes place at random piece inside this. | ||
31 | This should probably be written in assembly, and | ||
32 | preserve general-purpose registers, too | ||
33 | |||
34 | What about stack? We may need to move to new stack here. | ||
35 | |||
36 | This should better be ran with interrupts disabled. | ||
37 | */ | ||
38 | cpu_relax(); | ||
39 | barrier(); | ||
40 | } | ||
41 | atomic_dec(&cpu_counter); | ||
42 | __restore_processor_state(&ctxt); | ||
43 | } | ||
44 | |||
45 | static cpumask_t oldmask; | ||
46 | 22 | ||
47 | void disable_nonboot_cpus(void) | 23 | void disable_nonboot_cpus(void) |
48 | { | 24 | { |
49 | oldmask = current->cpus_allowed; | 25 | int cpu, error; |
50 | set_cpus_allowed(current, cpumask_of_cpu(0)); | ||
51 | printk("Freezing CPUs (at %d)", raw_smp_processor_id()); | ||
52 | current->state = TASK_INTERRUPTIBLE; | ||
53 | schedule_timeout(HZ); | ||
54 | printk("..."); | ||
55 | BUG_ON(raw_smp_processor_id() != 0); | ||
56 | |||
57 | /* FIXME: for this to work, all the CPUs must be running | ||
58 | * "idle" thread (or we deadlock). Is that guaranteed? */ | ||
59 | 26 | ||
60 | atomic_set(&cpu_counter, 0); | 27 | error = 0; |
61 | atomic_set(&freeze, 1); | 28 | cpus_clear(frozen_cpus); |
62 | smp_call_function(smp_pause, NULL, 0, 0); | 29 | printk("Freezing cpus ...\n"); |
63 | while (atomic_read(&cpu_counter) < (num_online_cpus() - 1)) { | 30 | for_each_online_cpu(cpu) { |
64 | cpu_relax(); | 31 | if (cpu == 0) |
65 | barrier(); | 32 | continue; |
33 | error = cpu_down(cpu); | ||
34 | if (!error) { | ||
35 | cpu_set(cpu, frozen_cpus); | ||
36 | printk("CPU%d is down\n", cpu); | ||
37 | continue; | ||
38 | } | ||
39 | printk("Error taking cpu %d down: %d\n", cpu, error); | ||
66 | } | 40 | } |
67 | printk("ok\n"); | 41 | BUG_ON(smp_processor_id() != 0); |
42 | if (error) | ||
43 | panic("cpus not sleeping"); | ||
68 | } | 44 | } |
69 | 45 | ||
70 | void enable_nonboot_cpus(void) | 46 | void enable_nonboot_cpus(void) |
71 | { | 47 | { |
72 | printk("Restarting CPUs"); | 48 | int cpu, error; |
73 | atomic_set(&freeze, 0); | ||
74 | while (atomic_read(&cpu_counter)) { | ||
75 | cpu_relax(); | ||
76 | barrier(); | ||
77 | } | ||
78 | printk("..."); | ||
79 | set_cpus_allowed(current, oldmask); | ||
80 | schedule(); | ||
81 | printk("ok\n"); | ||
82 | 49 | ||
50 | printk("Thawing cpus ...\n"); | ||
51 | for_each_cpu_mask(cpu, frozen_cpus) { | ||
52 | error = smp_prepare_cpu(cpu); | ||
53 | if (!error) | ||
54 | error = cpu_up(cpu); | ||
55 | if (!error) { | ||
56 | printk("CPU%d is up\n", cpu); | ||
57 | continue; | ||
58 | } | ||
59 | printk("Error taking cpu %d up: %d\n", cpu, error); | ||
60 | panic("Not enough cpus"); | ||
61 | } | ||
62 | cpus_clear(frozen_cpus); | ||
83 | } | 63 | } |
84 | 64 | ||
85 | |||
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 53f9f8720ee4..339b5c3735bd 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c | |||
@@ -1193,8 +1193,10 @@ static const char * sanity_check(void) | |||
1193 | return "version"; | 1193 | return "version"; |
1194 | if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) | 1194 | if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) |
1195 | return "machine"; | 1195 | return "machine"; |
1196 | #if 0 | ||
1196 | if(swsusp_info.cpus != num_online_cpus()) | 1197 | if(swsusp_info.cpus != num_online_cpus()) |
1197 | return "number of cpus"; | 1198 | return "number of cpus"; |
1199 | #endif | ||
1198 | return NULL; | 1200 | return NULL; |
1199 | } | 1201 | } |
1200 | 1202 | ||