Commit 5a72e04d authored by Li Shaohua's avatar Li Shaohua Committed by Linus Torvalds

[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: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent fb69c390
...@@ -69,7 +69,7 @@ static fastcall void k7_machine_check(struct pt_regs * regs, long error_code) ...@@ -69,7 +69,7 @@ static fastcall void k7_machine_check(struct pt_regs * regs, long error_code)
/* AMD K7 machine check is Intel like */ /* AMD K7 machine check is Intel like */
void __init amd_mcheck_init(struct cpuinfo_x86 *c) void __devinit amd_mcheck_init(struct cpuinfo_x86 *c)
{ {
u32 l, h; u32 l, h;
int i; int i;
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include "mce.h" #include "mce.h"
int mce_disabled __initdata = 0; int mce_disabled __devinitdata = 0;
int nr_mce_banks; int nr_mce_banks;
EXPORT_SYMBOL_GPL(nr_mce_banks); /* non-fatal.o */ EXPORT_SYMBOL_GPL(nr_mce_banks); /* non-fatal.o */
......
...@@ -78,7 +78,7 @@ fastcall void smp_thermal_interrupt(struct pt_regs *regs) ...@@ -78,7 +78,7 @@ fastcall void smp_thermal_interrupt(struct pt_regs *regs)
} }
/* P4/Xeon Thermal regulation detect and init */ /* P4/Xeon Thermal regulation detect and init */
static void __init intel_init_thermal(struct cpuinfo_x86 *c) static void __devinit intel_init_thermal(struct cpuinfo_x86 *c)
{ {
u32 l, h; u32 l, h;
unsigned int cpu = smp_processor_id(); unsigned int cpu = smp_processor_id();
...@@ -232,7 +232,7 @@ static fastcall void intel_machine_check(struct pt_regs * regs, long error_code) ...@@ -232,7 +232,7 @@ static fastcall void intel_machine_check(struct pt_regs * regs, long error_code)
} }
void __init intel_p4_mcheck_init(struct cpuinfo_x86 *c) void __devinit intel_p4_mcheck_init(struct cpuinfo_x86 *c)
{ {
u32 l, h; u32 l, h;
int i; int i;
......
...@@ -80,7 +80,7 @@ static fastcall void intel_machine_check(struct pt_regs * regs, long error_code) ...@@ -80,7 +80,7 @@ static fastcall void intel_machine_check(struct pt_regs * regs, long error_code)
} }
/* Set up machine check reporting for processors with Intel style MCE */ /* Set up machine check reporting for processors with Intel style MCE */
void __init intel_p6_mcheck_init(struct cpuinfo_x86 *c) void __devinit intel_p6_mcheck_init(struct cpuinfo_x86 *c)
{ {
u32 l, h; u32 l, h;
int i; int i;
......
...@@ -23,7 +23,7 @@ static fastcall void winchip_machine_check(struct pt_regs * regs, long error_cod ...@@ -23,7 +23,7 @@ static fastcall void winchip_machine_check(struct pt_regs * regs, long error_cod
} }
/* Set up machine check reporting on the Winchip C6 series */ /* Set up machine check reporting on the Winchip C6 series */
void __init winchip_mcheck_init(struct cpuinfo_x86 *c) void __devinit winchip_mcheck_init(struct cpuinfo_x86 *c)
{ {
u32 lo, hi; u32 lo, hi;
machine_check_vector = winchip_machine_check; machine_check_vector = winchip_machine_check;
......
...@@ -55,7 +55,7 @@ if ACPI_INTERPRETER ...@@ -55,7 +55,7 @@ if ACPI_INTERPRETER
config ACPI_SLEEP config ACPI_SLEEP
bool "Sleep States (EXPERIMENTAL)" bool "Sleep States (EXPERIMENTAL)"
depends on X86 depends on X86 && (!SMP || SUSPEND_SMP)
depends on EXPERIMENTAL && PM depends on EXPERIMENTAL && PM
default y default y
---help--- ---help---
......
...@@ -58,7 +58,7 @@ static inline int software_suspend(void) ...@@ -58,7 +58,7 @@ static inline int software_suspend(void)
} }
#endif #endif
#ifdef CONFIG_SMP #ifdef CONFIG_SUSPEND_SMP
extern void disable_nonboot_cpus(void); extern void disable_nonboot_cpus(void);
extern void enable_nonboot_cpus(void); extern void enable_nonboot_cpus(void);
#else #else
......
...@@ -28,7 +28,7 @@ config PM_DEBUG ...@@ -28,7 +28,7 @@ config PM_DEBUG
config SOFTWARE_SUSPEND config SOFTWARE_SUSPEND
bool "Software Suspend (EXPERIMENTAL)" bool "Software Suspend (EXPERIMENTAL)"
depends on EXPERIMENTAL && PM && SWAP depends on EXPERIMENTAL && PM && SWAP && (SUSPEND_SMP || !SMP)
---help--- ---help---
Enable the possibility of suspending the machine. Enable the possibility of suspending the machine.
It doesn't need APM. It doesn't need APM.
...@@ -72,3 +72,7 @@ config PM_STD_PARTITION ...@@ -72,3 +72,7 @@ config PM_STD_PARTITION
suspended image to. It will simply pick the first available swap suspended image to. It will simply pick the first available swap
device. device.
config SUSPEND_SMP
bool
depends on HOTPLUG_CPU && X86 && PM
default y
...@@ -3,9 +3,9 @@ ifeq ($(CONFIG_PM_DEBUG),y) ...@@ -3,9 +3,9 @@ ifeq ($(CONFIG_PM_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG EXTRA_CFLAGS += -DDEBUG
endif endif
swsusp-smp-$(CONFIG_SMP) += smp.o
obj-y := main.o process.o console.o pm.o obj-y := main.o process.o console.o pm.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o $(swsusp-smp-y) disk.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o disk.o
obj-$(CONFIG_SUSPEND_SMP) += smp.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
...@@ -117,8 +117,8 @@ static void finish(void) ...@@ -117,8 +117,8 @@ static void finish(void)
{ {
device_resume(); device_resume();
platform_finish(); platform_finish();
enable_nonboot_cpus();
thaw_processes(); thaw_processes();
enable_nonboot_cpus();
pm_restore_console(); pm_restore_console();
} }
...@@ -131,28 +131,35 @@ static int prepare_processes(void) ...@@ -131,28 +131,35 @@ static int prepare_processes(void)
sys_sync(); sys_sync();
disable_nonboot_cpus();
if (freeze_processes()) { if (freeze_processes()) {
error = -EBUSY; error = -EBUSY;
return error; goto thaw;
} }
if (pm_disk_mode == PM_DISK_PLATFORM) { if (pm_disk_mode == PM_DISK_PLATFORM) {
if (pm_ops && pm_ops->prepare) { if (pm_ops && pm_ops->prepare) {
if ((error = pm_ops->prepare(PM_SUSPEND_DISK))) if ((error = pm_ops->prepare(PM_SUSPEND_DISK)))
return error; goto thaw;
} }
} }
/* Free memory before shutting down devices. */ /* Free memory before shutting down devices. */
free_some_memory(); free_some_memory();
return 0; return 0;
thaw:
thaw_processes();
enable_nonboot_cpus();
pm_restore_console();
return error;
} }
static void unprepare_processes(void) static void unprepare_processes(void)
{ {
enable_nonboot_cpus(); platform_finish();
thaw_processes(); thaw_processes();
enable_nonboot_cpus();
pm_restore_console(); pm_restore_console();
} }
...@@ -160,15 +167,9 @@ static int prepare_devices(void) ...@@ -160,15 +167,9 @@ static int prepare_devices(void)
{ {
int error; int error;
disable_nonboot_cpus(); if ((error = device_suspend(PMSG_FREEZE)))
if ((error = device_suspend(PMSG_FREEZE))) {
printk("Some devices failed to suspend\n"); printk("Some devices failed to suspend\n");
platform_finish();
enable_nonboot_cpus();
return error; return error;
}
return 0;
} }
/** /**
...@@ -185,9 +186,9 @@ int pm_suspend_disk(void) ...@@ -185,9 +186,9 @@ int pm_suspend_disk(void)
int error; int error;
error = prepare_processes(); error = prepare_processes();
if (!error) { if (error)
return error;
error = prepare_devices(); error = prepare_devices();
}
if (error) { if (error) {
unprepare_processes(); unprepare_processes();
...@@ -250,7 +251,7 @@ static int software_resume(void) ...@@ -250,7 +251,7 @@ static int software_resume(void)
if ((error = prepare_processes())) { if ((error = prepare_processes())) {
swsusp_close(); swsusp_close();
goto Cleanup; goto Done;
} }
pr_debug("PM: Reading swsusp image.\n"); pr_debug("PM: Reading swsusp image.\n");
......
...@@ -55,6 +55,13 @@ static int suspend_prepare(suspend_state_t state) ...@@ -55,6 +55,13 @@ static int suspend_prepare(suspend_state_t state)
pm_prepare_console(); pm_prepare_console();
disable_nonboot_cpus();
if (num_online_cpus() != 1) {
error = -EPERM;
goto Enable_cpu;
}
if (freeze_processes()) { if (freeze_processes()) {
error = -EAGAIN; error = -EAGAIN;
goto Thaw; goto Thaw;
...@@ -75,6 +82,8 @@ static int suspend_prepare(suspend_state_t state) ...@@ -75,6 +82,8 @@ static int suspend_prepare(suspend_state_t state)
pm_ops->finish(state); pm_ops->finish(state);
Thaw: Thaw:
thaw_processes(); thaw_processes();
Enable_cpu:
enable_nonboot_cpus();
pm_restore_console(); pm_restore_console();
return error; return error;
} }
...@@ -113,6 +122,7 @@ static void suspend_finish(suspend_state_t state) ...@@ -113,6 +122,7 @@ static void suspend_finish(suspend_state_t state)
if (pm_ops && pm_ops->finish) if (pm_ops && pm_ops->finish)
pm_ops->finish(state); pm_ops->finish(state);
thaw_processes(); thaw_processes();
enable_nonboot_cpus();
pm_restore_console(); pm_restore_console();
} }
...@@ -150,12 +160,6 @@ static int enter_state(suspend_state_t state) ...@@ -150,12 +160,6 @@ static int enter_state(suspend_state_t state)
goto Unlock; goto Unlock;
} }
/* Suspend is hard to get right on SMP. */
if (num_online_cpus() != 1) {
error = -EPERM;
goto Unlock;
}
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
if ((error = suspend_prepare(state))) if ((error = suspend_prepare(state)))
goto Unlock; goto Unlock;
......
...@@ -13,73 +13,52 @@ ...@@ -13,73 +13,52 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/cpu.h>
#include <asm/atomic.h> #include <asm/atomic.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
static atomic_t cpu_counter, freeze; /* This is protected by pm_sem semaphore */
static cpumask_t frozen_cpus;
static void smp_pause(void * data)
{
struct saved_context ctxt;
__save_processor_state(&ctxt);
printk("Sleeping in:\n");
dump_stack();
atomic_inc(&cpu_counter);
while (atomic_read(&freeze)) {
/* FIXME: restore takes place at random piece inside this.
This should probably be written in assembly, and
preserve general-purpose registers, too
What about stack? We may need to move to new stack here.
This should better be ran with interrupts disabled.
*/
cpu_relax();
barrier();
}
atomic_dec(&cpu_counter);
__restore_processor_state(&ctxt);
}
static cpumask_t oldmask;
void disable_nonboot_cpus(void) void disable_nonboot_cpus(void)
{ {
oldmask = current->cpus_allowed; int cpu, error;
set_cpus_allowed(current, cpumask_of_cpu(0));
printk("Freezing CPUs (at %d)", raw_smp_processor_id());
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ);
printk("...");
BUG_ON(raw_smp_processor_id() != 0);
/* FIXME: for this to work, all the CPUs must be running
* "idle" thread (or we deadlock). Is that guaranteed? */
atomic_set(&cpu_counter, 0); error = 0;
atomic_set(&freeze, 1); cpus_clear(frozen_cpus);
smp_call_function(smp_pause, NULL, 0, 0); printk("Freezing cpus ...\n");
while (atomic_read(&cpu_counter) < (num_online_cpus() - 1)) { for_each_online_cpu(cpu) {
cpu_relax(); if (cpu == 0)
barrier(); continue;
error = cpu_down(cpu);
if (!error) {
cpu_set(cpu, frozen_cpus);
printk("CPU%d is down\n", cpu);
continue;
} }
printk("ok\n"); printk("Error taking cpu %d down: %d\n", cpu, error);
}
BUG_ON(smp_processor_id() != 0);
if (error)
panic("cpus not sleeping");
} }
void enable_nonboot_cpus(void) void enable_nonboot_cpus(void)
{ {
printk("Restarting CPUs"); int cpu, error;
atomic_set(&freeze, 0);
while (atomic_read(&cpu_counter)) {
cpu_relax();
barrier();
}
printk("...");
set_cpus_allowed(current, oldmask);
schedule();
printk("ok\n");
printk("Thawing cpus ...\n");
for_each_cpu_mask(cpu, frozen_cpus) {
error = smp_prepare_cpu(cpu);
if (!error)
error = cpu_up(cpu);
if (!error) {
printk("CPU%d is up\n", cpu);
continue;
}
printk("Error taking cpu %d up: %d\n", cpu, error);
panic("Not enough cpus");
}
cpus_clear(frozen_cpus);
} }
...@@ -1193,8 +1193,10 @@ static const char * sanity_check(void) ...@@ -1193,8 +1193,10 @@ static const char * sanity_check(void)
return "version"; return "version";
if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) if (strcmp(swsusp_info.uts.machine,system_utsname.machine))
return "machine"; return "machine";
#if 0
if(swsusp_info.cpus != num_online_cpus()) if(swsusp_info.cpus != num_online_cpus())
return "number of cpus"; return "number of cpus";
#endif
return NULL; return NULL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment