mirror of
https://gitee.com/openharmony/kernel_linux
synced 2025-01-10 18:04:14 +00:00
[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>
This commit is contained in:
parent
fb69c3907e
commit
5a72e04df5
@ -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);
|
||||
int cpu, error;
|
||||
|
||||
/* FIXME: for this to work, all the CPUs must be running
|
||||
* "idle" thread (or we deadlock). Is that guaranteed? */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user