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)
/* 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;
int i;
......
......@@ -16,7 +16,7 @@
#include "mce.h"
int mce_disabled __initdata = 0;
int mce_disabled __devinitdata = 0;
int nr_mce_banks;
EXPORT_SYMBOL_GPL(nr_mce_banks); /* non-fatal.o */
......
......@@ -78,7 +78,7 @@ fastcall void smp_thermal_interrupt(struct pt_regs *regs)
}
/* 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;
unsigned int cpu = smp_processor_id();
......@@ -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;
int i;
......
......@@ -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 */
void __init intel_p6_mcheck_init(struct cpuinfo_x86 *c)
void __devinit intel_p6_mcheck_init(struct cpuinfo_x86 *c)
{
u32 l, h;
int i;
......
......@@ -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 */
void __init winchip_mcheck_init(struct cpuinfo_x86 *c)
void __devinit winchip_mcheck_init(struct cpuinfo_x86 *c)
{
u32 lo, hi;
machine_check_vector = winchip_machine_check;
......
......@@ -55,7 +55,7 @@ if ACPI_INTERPRETER
config ACPI_SLEEP
bool "Sleep States (EXPERIMENTAL)"
depends on X86
depends on X86 && (!SMP || SUSPEND_SMP)
depends on EXPERIMENTAL && PM
default y
---help---
......
......@@ -58,7 +58,7 @@ static inline int software_suspend(void)
}
#endif
#ifdef CONFIG_SMP
#ifdef CONFIG_SUSPEND_SMP
extern void disable_nonboot_cpus(void);
extern void enable_nonboot_cpus(void);
#else
......
......@@ -28,7 +28,7 @@ config PM_DEBUG
config SOFTWARE_SUSPEND
bool "Software Suspend (EXPERIMENTAL)"
depends on EXPERIMENTAL && PM && SWAP
depends on EXPERIMENTAL && PM && SWAP && (SUSPEND_SMP || !SMP)
---help---
Enable the possibility of suspending the machine.
It doesn't need APM.
......@@ -72,3 +72,7 @@ config PM_STD_PARTITION
suspended image to. It will simply pick the first available swap
device.
config SUSPEND_SMP
bool
depends on HOTPLUG_CPU && X86 && PM
default y
......@@ -3,9 +3,9 @@ ifeq ($(CONFIG_PM_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
swsusp-smp-$(CONFIG_SMP) += smp.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
......@@ -117,8 +117,8 @@ static void finish(void)
{
device_resume();
platform_finish();
enable_nonboot_cpus();
thaw_processes();
enable_nonboot_cpus();
pm_restore_console();
}
......@@ -131,28 +131,35 @@ static int prepare_processes(void)
sys_sync();
disable_nonboot_cpus();
if (freeze_processes()) {
error = -EBUSY;
return error;
goto thaw;
}
if (pm_disk_mode == PM_DISK_PLATFORM) {
if (pm_ops && pm_ops->prepare) {
if ((error = pm_ops->prepare(PM_SUSPEND_DISK)))
return error;
goto thaw;
}
}
/* Free memory before shutting down devices. */
free_some_memory();
return 0;
thaw:
thaw_processes();
enable_nonboot_cpus();
pm_restore_console();
return error;
}
static void unprepare_processes(void)
{
enable_nonboot_cpus();
platform_finish();
thaw_processes();
enable_nonboot_cpus();
pm_restore_console();
}
......@@ -160,15 +167,9 @@ static int prepare_devices(void)
{
int error;
disable_nonboot_cpus();
if ((error = device_suspend(PMSG_FREEZE))) {
if ((error = device_suspend(PMSG_FREEZE)))
printk("Some devices failed to suspend\n");
platform_finish();
enable_nonboot_cpus();
return error;
}
return 0;
return error;
}
/**
......@@ -185,9 +186,9 @@ int pm_suspend_disk(void)
int error;
error = prepare_processes();
if (!error) {
error = prepare_devices();
}
if (error)
return error;
error = prepare_devices();
if (error) {
unprepare_processes();
......@@ -250,7 +251,7 @@ static int software_resume(void)
if ((error = prepare_processes())) {
swsusp_close();
goto Cleanup;
goto Done;
}
pr_debug("PM: Reading swsusp image.\n");
......
......@@ -55,6 +55,13 @@ static int suspend_prepare(suspend_state_t state)
pm_prepare_console();
disable_nonboot_cpus();
if (num_online_cpus() != 1) {
error = -EPERM;
goto Enable_cpu;
}
if (freeze_processes()) {
error = -EAGAIN;
goto Thaw;
......@@ -75,6 +82,8 @@ static int suspend_prepare(suspend_state_t state)
pm_ops->finish(state);
Thaw:
thaw_processes();
Enable_cpu:
enable_nonboot_cpus();
pm_restore_console();
return error;
}
......@@ -113,6 +122,7 @@ static void suspend_finish(suspend_state_t state)
if (pm_ops && pm_ops->finish)
pm_ops->finish(state);
thaw_processes();
enable_nonboot_cpus();
pm_restore_console();
}
......@@ -150,12 +160,6 @@ static int enter_state(suspend_state_t state)
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]);
if ((error = suspend_prepare(state)))
goto Unlock;
......
......@@ -13,73 +13,52 @@
#include <linux/interrupt.h>
#include <linux/suspend.h>
#include <linux/module.h>
#include <linux/cpu.h>
#include <asm/atomic.h>
#include <asm/tlbflush.h>
static atomic_t cpu_counter, freeze;
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;
/* This is protected by pm_sem semaphore */
static cpumask_t frozen_cpus;
void disable_nonboot_cpus(void)
{
oldmask = current->cpus_allowed;
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? */
int cpu, error;
atomic_set(&cpu_counter, 0);
atomic_set(&freeze, 1);
smp_call_function(smp_pause, NULL, 0, 0);
while (atomic_read(&cpu_counter) < (num_online_cpus() - 1)) {
cpu_relax();
barrier();
error = 0;
cpus_clear(frozen_cpus);
printk("Freezing cpus ...\n");
for_each_online_cpu(cpu) {
if (cpu == 0)
continue;
error = cpu_down(cpu);
if (!error) {
cpu_set(cpu, frozen_cpus);
printk("CPU%d is down\n", cpu);
continue;
}
printk("Error taking cpu %d down: %d\n", cpu, error);
}
printk("ok\n");
BUG_ON(smp_processor_id() != 0);
if (error)
panic("cpus not sleeping");
}
void enable_nonboot_cpus(void)
{
printk("Restarting CPUs");
atomic_set(&freeze, 0);
while (atomic_read(&cpu_counter)) {
cpu_relax();
barrier();
}
printk("...");
set_cpus_allowed(current, oldmask);
schedule();
printk("ok\n");
int cpu, error;
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)
return "version";
if (strcmp(swsusp_info.uts.machine,system_utsname.machine))
return "machine";
#if 0
if(swsusp_info.cpus != num_online_cpus())
return "number of cpus";
#endif
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