Commit f2a93dbd authored by Patrick Mochel's avatar Patrick Mochel

[power] Adapt swsusp to new PM core. Clean up heavily.

- Split suspend/resume code into the four functions called from the PM core.
- Remove now-duplicated code. 
- Make sure PM core frees memory and sync's disks before we shut down devices.
- Remove software_suspend(), in favor of pm_suspend(). 
- Remove unused definitions from suspend.h
parent 72c0542e
......@@ -8,8 +8,7 @@
#include <linux/notifier.h>
#include <linux/config.h>
#include <linux/init.h>
extern unsigned char software_suspend_enabled;
#include <linux/pm.h>
#ifdef CONFIG_SOFTWARE_SUSPEND
/* page backup entry */
......@@ -46,12 +45,6 @@ extern int shrink_mem(void);
/* mm/page_alloc.c */
extern void drain_local_pages(void);
/* kernel/suspend.c */
extern int software_suspend(void);
extern int register_suspend_notifier(struct notifier_block *);
extern int unregister_suspend_notifier(struct notifier_block *);
extern unsigned int nr_copy_pages __nosavedata;
extern suspend_pagedir_t *pagedir_nosave __nosavedata;
......@@ -72,31 +65,16 @@ static inline int software_suspend(void)
{
return -EPERM;
}
#define register_suspend_notifier(a) do { } while(0)
#define unregister_suspend_notifier(a) do { } while(0)
#endif /* CONFIG_SOFTWARE_SUSPEND */
#ifdef CONFIG_PM
extern void refrigerator(unsigned long);
extern int freeze_processes(void);
extern void thaw_processes(void);
extern int pm_prepare_console(void);
extern void pm_restore_console(void);
#else
static inline void refrigerator(unsigned long flag)
{
}
static inline int freeze_processes(void)
{
return 0;
}
static inline void thaw_processes(void)
{
}
#endif /* CONFIG_PM */
......
......@@ -15,6 +15,8 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/pm.h>
#include <linux/fs.h>
#include "power.h"
......@@ -30,6 +32,8 @@ static int have_swsusp = 1;
static int have_swsusp = 0;
#endif
extern long sys_sync(void);
/**
* pm_set_ops - Set the global power method table.
......@@ -128,6 +132,25 @@ static int power_down(u32 mode)
static int in_suspend __nosavedata = 0;
/**
* free_some_memory - Try to free as much memory as possible
*
* ... but do not OOM-kill anyone
*
* Notice: all userland should be stopped at this point, or
* livelock is possible.
*/
static void free_some_memory(void)
{
printk("Freeing memory: ");
while (shrink_all_memory(10000))
printk(".");
printk("|\n");
blk_run_queues();
}
/**
* pm_suspend_disk - The granpappy of power management.
*
......@@ -197,6 +220,7 @@ static int suspend_prepare(u32 state)
pm_prepare_console();
sys_sync();
if (freeze_processes()) {
error = -EAGAIN;
goto Thaw;
......@@ -207,6 +231,10 @@ static int suspend_prepare(u32 state)
goto Thaw;
}
/* Free memory before shutting down devices. */
if (state == PM_SUSPEND_DISK)
free_some_memory();
if ((error = device_pm_suspend(state)))
goto Finish;
......
......@@ -37,3 +37,10 @@ static inline int swsusp_free(void)
return 0;
}
#endif
extern int freeze_processes(void);
extern void thaw_processes(void);
extern int pm_prepare_console(void);
extern void pm_restore_console(void);
......@@ -65,8 +65,6 @@
#include "power.h"
extern long sys_sync(void);
unsigned char software_suspend_enabled = 1;
#define __ADDRESS(x) ((unsigned long) phys_to_virt(x))
......@@ -439,29 +437,6 @@ static suspend_pagedir_t *create_suspend_pagedir(int nr_copy_pages)
return pagedir;
}
static int prepare_suspend_processes(void)
{
sys_sync(); /* Syncing needs pdflushd, so do it before stopping processes */
if (freeze_processes()) {
printk( KERN_ERR "Suspend failed: Not all processes stopped!\n" );
thaw_processes();
return 1;
}
return 0;
}
/*
* Try to free as much memory as possible, but do not OOM-kill anyone
*
* Notice: all userland should be stopped at this point, or livelock is possible.
*/
static void free_some_memory(void)
{
printk("Freeing memory: ");
while (shrink_all_memory(10000))
printk(".");
printk("|\n");
}
/* Make disk drivers accept operations, again */
static void drivers_unsuspend(void)
......@@ -470,28 +445,6 @@ static void drivers_unsuspend(void)
device_resume(RESUME_ENABLE);
}
/* Called from process context */
static int drivers_suspend(void)
{
if (device_suspend(4, SUSPEND_NOTIFY))
return -EIO;
if (device_suspend(4, SUSPEND_SAVE_STATE)) {
device_resume(RESUME_RESTORE_STATE);
return -EIO;
}
if (!pm_suspend_state) {
if(pm_send_all(PM_SUSPEND,(void *)3)) {
printk(KERN_WARNING "Problem while sending suspend event\n");
return -EIO;
}
pm_suspend_state=1;
} else
printk(KERN_WARNING "PM suspend state already raised\n");
device_suspend(4, SUSPEND_DISABLE);
return 0;
}
#define RESUME_PHASE1 1 /* Called from interrupts disabled */
#define RESUME_PHASE2 2 /* Called with interrupts enabled */
#define RESUME_ALL_PHASES (RESUME_PHASE1 | RESUME_PHASE2)
......@@ -694,72 +647,6 @@ void do_magic_suspend_2(void)
mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
}
static int do_software_suspend(void)
{
arch_prepare_suspend();
if (pm_prepare_console())
printk( "%sCan't allocate a console... proceeding\n", name_suspend);
if (!prepare_suspend_processes()) {
/* At this point, all user processes and "dangerous"
kernel threads are stopped. Free some memory, as we
need half of memory free. */
free_some_memory();
/* No need to invalidate any vfsmnt list --
* they will be valid after resume, anyway.
*/
blk_run_queues();
/* Save state of all device drivers, and stop them. */
if (drivers_suspend()==0)
/* If stopping device drivers worked, we proceed basically into
* suspend_save_image.
*
* do_magic(0) returns after system is resumed.
*
* do_magic() copies all "used" memory to "free" memory, then
* unsuspends all device drivers, and writes memory to disk
* using normal kernel mechanism.
*/
do_magic(0);
thaw_processes();
}
software_suspend_enabled = 1;
MDELAY(1000);
pm_restore_console();
return 0;
}
/**
* software_suspend - initiate suspend-to-swap transition.
*
* This is main interface to the outside world. It needs to be
* called from process context.
*/
int software_suspend(void)
{
if(!software_suspend_enabled)
return -EINVAL;
if (num_online_cpus() > 1) {
printk(KERN_WARNING "swsusp does not support SMP.\n");
return -EPERM;
}
#if defined (CONFIG_HIGHMEM) || defined (COFNIG_DISCONTIGMEM)
printk("swsusp is not supported with high- or discontig-mem.\n");
return -EPERM;
#endif
software_suspend_enabled = 0;
might_sleep();
return do_software_suspend();
}
/* More restore stuff */
/* FIXME: Why not memcpy(to, from, 1<<pagedir_order*PAGE_SIZE)? */
......@@ -1030,55 +917,35 @@ static int read_suspend_image(const char * specialfile)
return error;
}
/**
* software_resume - Check and load saved image from swap.
*
* Defined as a late_initcall, so it gets called after all devices
* have been probed and initialized, but before we've mounted anything.
*/
static int software_resume(void)
{
if (!strlen(resume_file))
return 0;
if (pm_prepare_console())
printk("swsusp: Can't allocate a console... proceeding\n");
printk("swsusp: %s\n", name_resume );
MDELAY(1000);
printk("swsusp: resuming from %s\n", resume_file);
if (read_suspend_image(resume_file))
goto read_failure;
do_magic(1);
printk("swsusp: Resume failed. Continuing.\n");
read_failure:
pm_restore_console();
return -EFAULT;
}
late_initcall(software_resume);
/**
* swsusp_save - Snapshot memory
*/
int swsusp_save(void)
{
#if defined (CONFIG_HIGHMEM) || defined (COFNIG_DISCONTIGMEM)
printk("swsusp is not supported with high- or discontig-mem.\n");
return -EPERM;
#endif
return 0;
}
/**
* swsusp_write - Write saved memory image to swap.
*
* do_magic(0) returns after system is resumed.
*
* do_magic() copies all "used" memory to "free" memory, then
* unsuspends all device drivers, and writes memory to disk
* using normal kernel mechanism.
*/
int swsusp_write(void)
{
arch_prepare_suspend();
do_magic(0);
MDELAY(1000);
return 0;
}
......@@ -1089,7 +956,11 @@ int swsusp_write(void)
int swsusp_read(void)
{
if (!strlen(resume_file))
return -ENOENT;
printk("swsusp: %s\n", name_resume );
MDELAY(1000);
return read_suspend_image(resume_file);
}
......@@ -1099,6 +970,7 @@ int swsusp_read(void)
int swsusp_restore(void)
{
do_magic(1);
return 0;
}
......@@ -1114,7 +986,8 @@ int swsusp_free(void)
static int __init resume_setup(char *str)
{
strncpy( resume_file, str, 255 );
if (strlen(str))
strncpy(resume_file, str, 255);
return 1;
}
......@@ -1127,5 +1000,3 @@ static int __init noresume_setup(char *str)
__setup("noresume", noresume_setup);
__setup("resume=", resume_setup);
EXPORT_SYMBOL(software_suspend);
EXPORT_SYMBOL(software_suspend_enabled);
......@@ -456,11 +456,8 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user
#ifdef CONFIG_SOFTWARE_SUSPEND
case LINUX_REBOOT_CMD_SW_SUSPEND:
if (!software_suspend_enabled) {
unlock_kernel();
return -EAGAIN;
}
software_suspend();
if (!pm_suspend(PM_SUSPEND_DISK))
break;
do_exit(0);
break;
#endif
......
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