Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Li Shaohua and committed by
Linus Torvalds
5a72e04d fb69c390

+80 -90
+1 -1
arch/i386/kernel/cpu/mcheck/k7.c
··· 69 69 70 70 71 71 /* AMD K7 machine check is Intel like */ 72 - void __init amd_mcheck_init(struct cpuinfo_x86 *c) 72 + void __devinit amd_mcheck_init(struct cpuinfo_x86 *c) 73 73 { 74 74 u32 l, h; 75 75 int i;
+1 -1
arch/i386/kernel/cpu/mcheck/mce.c
··· 16 16 17 17 #include "mce.h" 18 18 19 - int mce_disabled __initdata = 0; 19 + int mce_disabled __devinitdata = 0; 20 20 int nr_mce_banks; 21 21 22 22 EXPORT_SYMBOL_GPL(nr_mce_banks); /* non-fatal.o */
+2 -2
arch/i386/kernel/cpu/mcheck/p4.c
··· 78 78 } 79 79 80 80 /* P4/Xeon Thermal regulation detect and init */ 81 - static void __init intel_init_thermal(struct cpuinfo_x86 *c) 81 + static void __devinit intel_init_thermal(struct cpuinfo_x86 *c) 82 82 { 83 83 u32 l, h; 84 84 unsigned int cpu = smp_processor_id(); ··· 232 232 } 233 233 234 234 235 - void __init intel_p4_mcheck_init(struct cpuinfo_x86 *c) 235 + void __devinit intel_p4_mcheck_init(struct cpuinfo_x86 *c) 236 236 { 237 237 u32 l, h; 238 238 int i;
+1 -1
arch/i386/kernel/cpu/mcheck/p6.c
··· 80 80 } 81 81 82 82 /* Set up machine check reporting for processors with Intel style MCE */ 83 - void __init intel_p6_mcheck_init(struct cpuinfo_x86 *c) 83 + void __devinit intel_p6_mcheck_init(struct cpuinfo_x86 *c) 84 84 { 85 85 u32 l, h; 86 86 int i;
+1 -1
arch/i386/kernel/cpu/mcheck/winchip.c
··· 23 23 } 24 24 25 25 /* Set up machine check reporting on the Winchip C6 series */ 26 - void __init winchip_mcheck_init(struct cpuinfo_x86 *c) 26 + void __devinit winchip_mcheck_init(struct cpuinfo_x86 *c) 27 27 { 28 28 u32 lo, hi; 29 29 machine_check_vector = winchip_machine_check;
+1 -1
drivers/acpi/Kconfig
··· 55 55 56 56 config ACPI_SLEEP 57 57 bool "Sleep States (EXPERIMENTAL)" 58 - depends on X86 58 + depends on X86 && (!SMP || SUSPEND_SMP) 59 59 depends on EXPERIMENTAL && PM 60 60 default y 61 61 ---help---
+1 -1
include/linux/suspend.h
··· 58 58 } 59 59 #endif 60 60 61 - #ifdef CONFIG_SMP 61 + #ifdef CONFIG_SUSPEND_SMP 62 62 extern void disable_nonboot_cpus(void); 63 63 extern void enable_nonboot_cpus(void); 64 64 #else
+5 -1
kernel/power/Kconfig
··· 28 28 29 29 config SOFTWARE_SUSPEND 30 30 bool "Software Suspend (EXPERIMENTAL)" 31 - depends on EXPERIMENTAL && PM && SWAP 31 + depends on EXPERIMENTAL && PM && SWAP && (SUSPEND_SMP || !SMP) 32 32 ---help--- 33 33 Enable the possibility of suspending the machine. 34 34 It doesn't need APM. ··· 72 72 suspended image to. It will simply pick the first available swap 73 73 device. 74 74 75 + config SUSPEND_SMP 76 + bool 77 + depends on HOTPLUG_CPU && X86 && PM 78 + default y
+3 -3
kernel/power/Makefile
··· 3 3 EXTRA_CFLAGS += -DDEBUG 4 4 endif 5 5 6 - swsusp-smp-$(CONFIG_SMP) += smp.o 7 - 8 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 11 obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
+18 -17
kernel/power/disk.c
··· 117 117 { 118 118 device_resume(); 119 119 platform_finish(); 120 - enable_nonboot_cpus(); 121 120 thaw_processes(); 121 + enable_nonboot_cpus(); 122 122 pm_restore_console(); 123 123 } 124 124 ··· 131 131 132 132 sys_sync(); 133 133 134 + disable_nonboot_cpus(); 135 + 134 136 if (freeze_processes()) { 135 137 error = -EBUSY; 136 - return error; 138 + goto thaw; 137 139 } 138 140 139 141 if (pm_disk_mode == PM_DISK_PLATFORM) { 140 142 if (pm_ops && pm_ops->prepare) { 141 143 if ((error = pm_ops->prepare(PM_SUSPEND_DISK))) 142 - return error; 144 + goto thaw; 143 145 } 144 146 } 145 147 146 148 /* Free memory before shutting down devices. */ 147 149 free_some_memory(); 148 - 149 150 return 0; 151 + thaw: 152 + thaw_processes(); 153 + enable_nonboot_cpus(); 154 + pm_restore_console(); 155 + return error; 150 156 } 151 157 152 158 static void unprepare_processes(void) 153 159 { 154 - enable_nonboot_cpus(); 160 + platform_finish(); 155 161 thaw_processes(); 162 + enable_nonboot_cpus(); 156 163 pm_restore_console(); 157 164 } 158 165 ··· 167 160 { 168 161 int error; 169 162 170 - disable_nonboot_cpus(); 171 - if ((error = device_suspend(PMSG_FREEZE))) { 163 + if ((error = device_suspend(PMSG_FREEZE))) 172 164 printk("Some devices failed to suspend\n"); 173 - platform_finish(); 174 - enable_nonboot_cpus(); 175 - return error; 176 - } 177 - 178 - return 0; 165 + return error; 179 166 } 180 167 181 168 /** ··· 186 185 int error; 187 186 188 187 error = prepare_processes(); 189 - if (!error) { 190 - error = prepare_devices(); 191 - } 188 + if (error) 189 + return error; 190 + error = prepare_devices(); 192 191 193 192 if (error) { 194 193 unprepare_processes(); ··· 251 250 252 251 if ((error = prepare_processes())) { 253 252 swsusp_close(); 254 - goto Cleanup; 253 + goto Done; 255 254 } 256 255 257 256 pr_debug("PM: Reading swsusp image.\n");
+10 -6
kernel/power/main.c
··· 55 55 56 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 65 if (freeze_processes()) { 59 66 error = -EAGAIN; 60 67 goto Thaw; ··· 82 75 pm_ops->finish(state); 83 76 Thaw: 84 77 thaw_processes(); 78 + Enable_cpu: 79 + enable_nonboot_cpus(); 85 80 pm_restore_console(); 86 81 return error; 87 82 } ··· 122 113 if (pm_ops && pm_ops->finish) 123 114 pm_ops->finish(state); 124 115 thaw_processes(); 116 + enable_nonboot_cpus(); 125 117 pm_restore_console(); 126 118 } 127 119 ··· 157 147 158 148 if (state == PM_SUSPEND_DISK) { 159 149 error = pm_suspend_disk(); 160 - goto Unlock; 161 - } 162 - 163 - /* Suspend is hard to get right on SMP. */ 164 - if (num_online_cpus() != 1) { 165 - error = -EPERM; 166 150 goto Unlock; 167 151 } 168 152
+34 -55
kernel/power/smp.c
··· 13 13 #include <linux/interrupt.h> 14 14 #include <linux/suspend.h> 15 15 #include <linux/module.h> 16 + #include <linux/cpu.h> 16 17 #include <asm/atomic.h> 17 18 #include <asm/tlbflush.h> 18 19 19 - static atomic_t cpu_counter, freeze; 20 - 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; 20 + /* This is protected by pm_sem semaphore */ 21 + static cpumask_t frozen_cpus; 46 22 47 23 void disable_nonboot_cpus(void) 48 24 { 49 - oldmask = current->cpus_allowed; 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); 25 + int cpu, error; 56 26 57 - /* FIXME: for this to work, all the CPUs must be running 58 - * "idle" thread (or we deadlock). Is that guaranteed? */ 59 - 60 - atomic_set(&cpu_counter, 0); 61 - atomic_set(&freeze, 1); 62 - smp_call_function(smp_pause, NULL, 0, 0); 63 - while (atomic_read(&cpu_counter) < (num_online_cpus() - 1)) { 64 - cpu_relax(); 65 - barrier(); 27 + error = 0; 28 + cpus_clear(frozen_cpus); 29 + printk("Freezing cpus ...\n"); 30 + for_each_online_cpu(cpu) { 31 + if (cpu == 0) 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 46 void enable_nonboot_cpus(void) 71 47 { 72 - printk("Restarting CPUs"); 73 - atomic_set(&freeze, 0); 74 - while (atomic_read(&cpu_counter)) { 75 - cpu_relax(); 76 - barrier(); 48 + int cpu, error; 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"); 77 61 } 78 - printk("..."); 79 - set_cpus_allowed(current, oldmask); 80 - schedule(); 81 - printk("ok\n"); 82 - 62 + cpus_clear(frozen_cpus); 83 63 } 84 - 85 64
+2
kernel/power/swsusp.c
··· 1193 1193 return "version"; 1194 1194 if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) 1195 1195 return "machine"; 1196 + #if 0 1196 1197 if(swsusp_info.cpus != num_online_cpus()) 1197 1198 return "number of cpus"; 1199 + #endif 1198 1200 return NULL; 1199 1201 } 1200 1202