[PATCH] suspend/resume SMP support
Li Shaohua [Sat, 25 Jun 2005 21:55:06 +0000 (14:55 -0700)]
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>

13 files changed:
arch/i386/kernel/cpu/mcheck/k7.c
arch/i386/kernel/cpu/mcheck/mce.c
arch/i386/kernel/cpu/mcheck/p4.c
arch/i386/kernel/cpu/mcheck/p6.c
arch/i386/kernel/cpu/mcheck/winchip.c
drivers/acpi/Kconfig
include/linux/suspend.h
kernel/power/Kconfig
kernel/power/Makefile
kernel/power/disk.c
kernel/power/main.c
kernel/power/smp.c
kernel/power/swsusp.c

index 8df52e8..c4abe76 100644 (file)
@@ -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;
index 7218a73..2cf25d2 100644 (file)
@@ -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 */
index 8b16ceb..0abccb6 100644 (file)
@@ -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;
index 46640f8..f01b73f 100644 (file)
@@ -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;
index 753fa7a..7bae68f 100644 (file)
@@ -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;
index 670fdb5..86c5252 100644 (file)
@@ -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---
index 2bf0d5f..f2e96fd 100644 (file)
@@ -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
index 696387f..fdb3776 100644 (file)
@@ -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
index fbdc634..2f438d0 100644 (file)
@@ -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
index 02b6764..fb8de63 100644 (file)
@@ -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");
index 4cdebc9..c94cb9e 100644 (file)
@@ -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;
index 457c230..bbe2307 100644 (file)
 #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);
 }
 
-
index 53f9f87..339b5c3 100644 (file)
@@ -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;
 }