diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/power/Makefile | 1 | ||||
| -rw-r--r-- | kernel/power/smp.c | 85 | ||||
| -rw-r--r-- | kernel/power/swsusp.c | 19 |
3 files changed, 98 insertions, 7 deletions
diff --git a/kernel/power/Makefile b/kernel/power/Makefile index d00edd15c0fd..079dd4d678ce 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -1,4 +1,5 @@ obj-y := main.o process.o console.o pm.o +obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o obj-$(CONFIG_PM_DISK) += disk.o pmdisk.o diff --git a/kernel/power/smp.c b/kernel/power/smp.c new file mode 100644 index 000000000000..cda77cdfb8c1 --- /dev/null +++ b/kernel/power/smp.c @@ -0,0 +1,85 @@ +/* + * drivers/power/smp.c - Functions for stopping other CPUs. + * + * Copyright 2004 Pavel Machek <pavel@suse.cz> + * Copyright (C) 2002-2003 Nigel Cunningham <ncunningham@clear.net.nz> + * + * This file is released under the GPLv2. + */ + +#undef DEBUG + +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/suspend.h> +#include <linux/module.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); +} + +cpumask_t oldmask; + +void disable_nonboot_cpus(void) +{ + printk("Freezing CPUs (at %d)", smp_processor_id()); + oldmask = current->cpus_allowed; + set_cpus_allowed(current, cpumask_of_cpu(0)); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ); + printk("..."); + BUG_ON(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); + atomic_set(&freeze, 1); + smp_call_function(smp_pause, NULL, 0, 0); + while (atomic_read(&cpu_counter) < (num_online_cpus() - 1)) { + cpu_relax(); + barrier(); + } + printk("ok\n"); +} + +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"); + +} + + diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index ff8fdbc21a45..6cfe0551088d 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -696,6 +696,7 @@ static void suspend_power_down(void) else #endif { + device_suspend(3); device_shutdown(); machine_power_off(); } @@ -716,7 +717,7 @@ asmlinkage void do_magic_resume_1(void) mb(); spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ - device_power_down(4); + device_power_down(3); PRINTK( "Waiting for DMAs to settle down...\n"); mdelay(1000); /* We do not want some readahead with DMA to corrupt our memory, right? Do it with disabled interrupts for best effect. That way, if some @@ -785,7 +786,7 @@ asmlinkage void do_magic_suspend_2(void) { int is_problem; read_swapfiles(); - device_power_down(4); + device_power_down(3); is_problem = suspend_prepare_image(); device_power_up(); spin_unlock_irq(&suspend_pagedir_lock); @@ -802,7 +803,6 @@ asmlinkage void do_magic_suspend_2(void) barrier(); mb(); spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ - mdelay(1000); free_pages((unsigned long) pagedir_nosave, pagedir_order); spin_unlock_irq(&suspend_pagedir_lock); @@ -839,9 +839,10 @@ int software_suspend(void) need half of memory free. */ free_some_memory(); - - /* Save state of all device drivers, and stop them. */ - if ((res = device_suspend(4))==0) + disable_nonboot_cpus(); + /* Save state of all device drivers, and stop them. */ + printk("Suspending devices... "); + if ((res = device_suspend(3))==0) { /* If stopping device drivers worked, we proceed basically into * suspend_save_image. * @@ -852,7 +853,9 @@ int software_suspend(void) * using normal kernel mechanism. */ do_magic(0); + } thaw_processes(); + enable_nonboot_cpus(); } else res = -EBUSY; software_suspend_enabled = 1; @@ -1192,7 +1195,9 @@ static int __init software_resume(void) printk( "resuming from %s\n", resume_file); if (read_suspend_image(resume_file, 0)) goto read_failure; - device_suspend(4); + /* FIXME: Should we stop processes here, just to be safer? */ + disable_nonboot_cpus(); + device_suspend(3); do_magic(1); panic("This never returns"); |
