From 6be3a3d91e4de8f16262e802b8453a0821341eb2 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Mon, 4 Aug 2003 23:54:58 -0700 Subject: [power] Fix compilation error when CONFIG_PREEMPT=y --- kernel/power/process.c | 1 + 1 file changed, 1 insertion(+) (limited to 'kernel') diff --git a/kernel/power/process.c b/kernel/power/process.c index 671730612e0f..15c1b340c2ed 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -8,6 +8,7 @@ #undef DEBUG +#include #include #include #include -- cgit v1.2.3 From f670dfbac34062d9ee4f154f77937d0dce5dea75 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 5 Aug 2003 23:59:26 -0700 Subject: [power] Move pm.c and mark functions depcrecated. - Move kernel/pm.c to kernel/power/pm.c - Move poweroff sysrq registration to kernel/power/poweroff.c - Mark pm_* functions deprecated to prevent new uers. --- include/linux/pm.h | 14 +- kernel/Makefile | 2 +- kernel/pm.c | 333 ------------------------------------------------ kernel/power/Makefile | 4 +- kernel/power/pm.c | 296 ++++++++++++++++++++++++++++++++++++++++++ kernel/power/poweroff.c | 44 +++++++ 6 files changed, 351 insertions(+), 342 deletions(-) delete mode 100644 kernel/pm.c create mode 100644 kernel/power/pm.c create mode 100644 kernel/power/poweroff.c (limited to 'kernel') diff --git a/include/linux/pm.h b/include/linux/pm.h index ab9305b0953a..58b416929205 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -118,29 +118,29 @@ extern int pm_active; /* * Register a device with power management */ -struct pm_dev *pm_register(pm_dev_t type, - unsigned long id, - pm_callback callback); +struct pm_dev __deprecated *pm_register(pm_dev_t type, + unsigned long id, + pm_callback callback); /* * Unregister a device with power management */ -void pm_unregister(struct pm_dev *dev); +void __deprecated pm_unregister(struct pm_dev *dev); /* * Unregister all devices with matching callback */ -void pm_unregister_all(pm_callback callback); +void __deprecated pm_unregister_all(pm_callback callback); /* * Send a request to a single device */ -int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data); +int __deprecated pm_send(struct pm_dev *dev, pm_request_t rqst, void *data); /* * Send a request to all devices */ -int pm_send_all(pm_request_t rqst, void *data); +int __deprecated pm_send_all(pm_request_t rqst, void *data); /* * Find a device diff --git a/kernel/Makefile b/kernel/Makefile index 495214d708bd..72eb0287e843 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -14,7 +14,7 @@ obj-$(CONFIG_SMP) += cpu.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += ksyms.o module.o obj-$(CONFIG_KALLSYMS) += kallsyms.o -obj-$(CONFIG_PM) += pm.o power/ +obj-$(CONFIG_PM) += power/ obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_COMPAT) += compat.o diff --git a/kernel/pm.c b/kernel/pm.c deleted file mode 100644 index afffd046c2f6..000000000000 --- a/kernel/pm.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * pm.c - Power management interface - * - * Copyright (C) 2000 Andrew Henroid - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include - -int pm_active; - -/* - * Locking notes: - * pm_devs_lock can be a semaphore providing pm ops are not called - * from an interrupt handler (already a bad idea so no change here). Each - * change must be protected so that an unlink of an entry doesn't clash - * with a pm send - which is permitted to sleep in the current architecture - * - * Module unloads clashing with pm events now work out safely, the module - * unload path will block until the event has been sent. It may well block - * until a resume but that will be fine. - */ - -static DECLARE_MUTEX(pm_devs_lock); -static LIST_HEAD(pm_devs); - -/** - * pm_register - register a device with power management - * @type: device type - * @id: device ID - * @callback: callback function - * - * Add a device to the list of devices that wish to be notified about - * power management events. A &pm_dev structure is returned on success, - * on failure the return is %NULL. - * - * The callback function will be called in process context and - * it may sleep. - */ - -struct pm_dev *pm_register(pm_dev_t type, - unsigned long id, - pm_callback callback) -{ - struct pm_dev *dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL); - if (dev) { - memset(dev, 0, sizeof(*dev)); - dev->type = type; - dev->id = id; - dev->callback = callback; - - down(&pm_devs_lock); - list_add(&dev->entry, &pm_devs); - up(&pm_devs_lock); - } - return dev; -} - -/** - * pm_unregister - unregister a device with power management - * @dev: device to unregister - * - * Remove a device from the power management notification lists. The - * dev passed must be a handle previously returned by pm_register. - */ - -void pm_unregister(struct pm_dev *dev) -{ - if (dev) { - down(&pm_devs_lock); - list_del(&dev->entry); - up(&pm_devs_lock); - - kfree(dev); - } -} - -static void __pm_unregister(struct pm_dev *dev) -{ - if (dev) { - list_del(&dev->entry); - kfree(dev); - } -} - -/** - * pm_unregister_all - unregister all devices with matching callback - * @callback: callback function pointer - * - * Unregister every device that would call the callback passed. This - * is primarily meant as a helper function for loadable modules. It - * enables a module to give up all its managed devices without keeping - * its own private list. - */ - -void pm_unregister_all(pm_callback callback) -{ - struct list_head *entry; - - if (!callback) - return; - - down(&pm_devs_lock); - entry = pm_devs.next; - while (entry != &pm_devs) { - struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); - entry = entry->next; - if (dev->callback == callback) - __pm_unregister(dev); - } - up(&pm_devs_lock); -} - -/** - * pm_send - send request to a single device - * @dev: device to send to - * @rqst: power management request - * @data: data for the callback - * - * Issue a power management request to a given device. The - * %PM_SUSPEND and %PM_RESUME events are handled specially. The - * data field must hold the intended next state. No call is made - * if the state matches. - * - * BUGS: what stops two power management requests occurring in parallel - * and conflicting. - * - * WARNING: Calling pm_send directly is not generally recommended, in - * particular there is no locking against the pm_dev going away. The - * caller must maintain all needed locking or have 'inside knowledge' - * on the safety. Also remember that this function is not locked against - * pm_unregister. This means that you must handle SMP races on callback - * execution and unload yourself. - */ - -int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - int status = 0; - unsigned long prev_state, next_state; - - if (in_interrupt()) - BUG(); - - switch (rqst) { - case PM_SUSPEND: - case PM_RESUME: - prev_state = dev->state; - next_state = (unsigned long) data; - if (prev_state != next_state) { - if (dev->callback) - status = (*dev->callback)(dev, rqst, data); - if (!status) { - dev->state = next_state; - dev->prev_state = prev_state; - } - } - else { - dev->prev_state = prev_state; - } - break; - default: - if (dev->callback) - status = (*dev->callback)(dev, rqst, data); - break; - } - return status; -} - -/* - * Undo incomplete request - */ -static void pm_undo_all(struct pm_dev *last) -{ - struct list_head *entry = last->entry.prev; - while (entry != &pm_devs) { - struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); - if (dev->state != dev->prev_state) { - /* previous state was zero (running) resume or - * previous state was non-zero (suspended) suspend - */ - pm_request_t undo = (dev->prev_state - ? PM_SUSPEND:PM_RESUME); - pm_send(dev, undo, (void*) dev->prev_state); - } - entry = entry->prev; - } -} - -/** - * pm_send_all - send request to all managed devices - * @rqst: power management request - * @data: data for the callback - * - * Issue a power management request to a all devices. The - * %PM_SUSPEND events are handled specially. Any device is - * permitted to fail a suspend by returning a non zero (error) - * value from its callback function. If any device vetoes a - * suspend request then all other devices that have suspended - * during the processing of this request are restored to their - * previous state. - * - * WARNING: This function takes the pm_devs_lock. The lock is not dropped until - * the callbacks have completed. This prevents races against pm locking - * functions, races against module unload pm_unregister code. It does - * mean however that you must not issue pm_ functions within the callback - * or you will deadlock and users will hate you. - * - * Zero is returned on success. If a suspend fails then the status - * from the device that vetoes the suspend is returned. - * - * BUGS: what stops two power management requests occurring in parallel - * and conflicting. - */ - -int pm_send_all(pm_request_t rqst, void *data) -{ - struct list_head *entry; - - down(&pm_devs_lock); - entry = pm_devs.next; - while (entry != &pm_devs) { - struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); - if (dev->callback) { - int status = pm_send(dev, rqst, data); - if (status) { - /* return devices to previous state on - * failed suspend request - */ - if (rqst == PM_SUSPEND) - pm_undo_all(dev); - up(&pm_devs_lock); - return status; - } - } - entry = entry->next; - } - up(&pm_devs_lock); - return 0; -} - -/** - * pm_find - find a device - * @type: type of device - * @from: where to start looking - * - * Scan the power management list for devices of a specific type. The - * return value for a matching device may be passed to further calls - * to this function to find further matches. A %NULL indicates the end - * of the list. - * - * To search from the beginning pass %NULL as the @from value. - * - * The caller MUST hold the pm_devs_lock lock when calling this - * function. The instant that the lock is dropped all pointers returned - * may become invalid. - */ - -struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from) -{ - struct list_head *entry = from ? from->entry.next:pm_devs.next; - while (entry != &pm_devs) { - struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); - if (type == PM_UNKNOWN_DEV || dev->type == type) - return dev; - entry = entry->next; - } - return 0; -} - -EXPORT_SYMBOL(pm_register); -EXPORT_SYMBOL(pm_unregister); -EXPORT_SYMBOL(pm_unregister_all); -EXPORT_SYMBOL(pm_send); -EXPORT_SYMBOL(pm_send_all); -EXPORT_SYMBOL(pm_find); -EXPORT_SYMBOL(pm_active); - - -#ifdef CONFIG_MAGIC_SYSRQ - -/** - * handle_poweroff - sysrq callback for power down - * @key: key pressed (unused) - * @pt_regs: register state (unused) - * @kbd: keyboard state (unused) - * @tty: tty involved (unused) - * - * When the user hits Sys-Rq o to power down the machine this is the - * callback we use. - */ - -static void handle_poweroff (int key, struct pt_regs *pt_regs, - struct tty_struct *tty) -{ - if (pm_power_off) - pm_power_off(); -} - -static struct sysrq_key_op sysrq_poweroff_op = { - .handler = handle_poweroff, - .help_msg = "powerOff", - .action_msg = "Power Off\n" -}; - -#endif /* CONFIG_MAGIC_SYSRQ */ - - -static int pm_init(void) -{ - register_sysrq_key('o', &sysrq_poweroff_op); - return 0; -} - -subsys_initcall(pm_init); diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 2a1e6368f7e5..3a11202cc91e 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -1,2 +1,4 @@ -obj-y := process.o console.o +obj-y := process.o console.o pm.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o + +obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff --git a/kernel/power/pm.c b/kernel/power/pm.c new file mode 100644 index 000000000000..09d55363cefc --- /dev/null +++ b/kernel/power/pm.c @@ -0,0 +1,296 @@ +/* + * pm.c - Power management interface + * + * Copyright (C) 2000 Andrew Henroid + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include + +int pm_active; + +/* + * Locking notes: + * pm_devs_lock can be a semaphore providing pm ops are not called + * from an interrupt handler (already a bad idea so no change here). Each + * change must be protected so that an unlink of an entry doesn't clash + * with a pm send - which is permitted to sleep in the current architecture + * + * Module unloads clashing with pm events now work out safely, the module + * unload path will block until the event has been sent. It may well block + * until a resume but that will be fine. + */ + +static DECLARE_MUTEX(pm_devs_lock); +static LIST_HEAD(pm_devs); + +/** + * pm_register - register a device with power management + * @type: device type + * @id: device ID + * @callback: callback function + * + * Add a device to the list of devices that wish to be notified about + * power management events. A &pm_dev structure is returned on success, + * on failure the return is %NULL. + * + * The callback function will be called in process context and + * it may sleep. + */ + +struct pm_dev *pm_register(pm_dev_t type, + unsigned long id, + pm_callback callback) +{ + struct pm_dev *dev = kmalloc(sizeof(struct pm_dev), GFP_KERNEL); + if (dev) { + memset(dev, 0, sizeof(*dev)); + dev->type = type; + dev->id = id; + dev->callback = callback; + + down(&pm_devs_lock); + list_add(&dev->entry, &pm_devs); + up(&pm_devs_lock); + } + return dev; +} + +/** + * pm_unregister - unregister a device with power management + * @dev: device to unregister + * + * Remove a device from the power management notification lists. The + * dev passed must be a handle previously returned by pm_register. + */ + +void pm_unregister(struct pm_dev *dev) +{ + if (dev) { + down(&pm_devs_lock); + list_del(&dev->entry); + up(&pm_devs_lock); + + kfree(dev); + } +} + +static void __pm_unregister(struct pm_dev *dev) +{ + if (dev) { + list_del(&dev->entry); + kfree(dev); + } +} + +/** + * pm_unregister_all - unregister all devices with matching callback + * @callback: callback function pointer + * + * Unregister every device that would call the callback passed. This + * is primarily meant as a helper function for loadable modules. It + * enables a module to give up all its managed devices without keeping + * its own private list. + */ + +void pm_unregister_all(pm_callback callback) +{ + struct list_head *entry; + + if (!callback) + return; + + down(&pm_devs_lock); + entry = pm_devs.next; + while (entry != &pm_devs) { + struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); + entry = entry->next; + if (dev->callback == callback) + __pm_unregister(dev); + } + up(&pm_devs_lock); +} + +/** + * pm_send - send request to a single device + * @dev: device to send to + * @rqst: power management request + * @data: data for the callback + * + * Issue a power management request to a given device. The + * %PM_SUSPEND and %PM_RESUME events are handled specially. The + * data field must hold the intended next state. No call is made + * if the state matches. + * + * BUGS: what stops two power management requests occurring in parallel + * and conflicting. + * + * WARNING: Calling pm_send directly is not generally recommended, in + * particular there is no locking against the pm_dev going away. The + * caller must maintain all needed locking or have 'inside knowledge' + * on the safety. Also remember that this function is not locked against + * pm_unregister. This means that you must handle SMP races on callback + * execution and unload yourself. + */ + +int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + int status = 0; + unsigned long prev_state, next_state; + + if (in_interrupt()) + BUG(); + + switch (rqst) { + case PM_SUSPEND: + case PM_RESUME: + prev_state = dev->state; + next_state = (unsigned long) data; + if (prev_state != next_state) { + if (dev->callback) + status = (*dev->callback)(dev, rqst, data); + if (!status) { + dev->state = next_state; + dev->prev_state = prev_state; + } + } + else { + dev->prev_state = prev_state; + } + break; + default: + if (dev->callback) + status = (*dev->callback)(dev, rqst, data); + break; + } + return status; +} + +/* + * Undo incomplete request + */ +static void pm_undo_all(struct pm_dev *last) +{ + struct list_head *entry = last->entry.prev; + while (entry != &pm_devs) { + struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); + if (dev->state != dev->prev_state) { + /* previous state was zero (running) resume or + * previous state was non-zero (suspended) suspend + */ + pm_request_t undo = (dev->prev_state + ? PM_SUSPEND:PM_RESUME); + pm_send(dev, undo, (void*) dev->prev_state); + } + entry = entry->prev; + } +} + +/** + * pm_send_all - send request to all managed devices + * @rqst: power management request + * @data: data for the callback + * + * Issue a power management request to a all devices. The + * %PM_SUSPEND events are handled specially. Any device is + * permitted to fail a suspend by returning a non zero (error) + * value from its callback function. If any device vetoes a + * suspend request then all other devices that have suspended + * during the processing of this request are restored to their + * previous state. + * + * WARNING: This function takes the pm_devs_lock. The lock is not dropped until + * the callbacks have completed. This prevents races against pm locking + * functions, races against module unload pm_unregister code. It does + * mean however that you must not issue pm_ functions within the callback + * or you will deadlock and users will hate you. + * + * Zero is returned on success. If a suspend fails then the status + * from the device that vetoes the suspend is returned. + * + * BUGS: what stops two power management requests occurring in parallel + * and conflicting. + */ + +int pm_send_all(pm_request_t rqst, void *data) +{ + struct list_head *entry; + + down(&pm_devs_lock); + entry = pm_devs.next; + while (entry != &pm_devs) { + struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); + if (dev->callback) { + int status = pm_send(dev, rqst, data); + if (status) { + /* return devices to previous state on + * failed suspend request + */ + if (rqst == PM_SUSPEND) + pm_undo_all(dev); + up(&pm_devs_lock); + return status; + } + } + entry = entry->next; + } + up(&pm_devs_lock); + return 0; +} + +/** + * pm_find - find a device + * @type: type of device + * @from: where to start looking + * + * Scan the power management list for devices of a specific type. The + * return value for a matching device may be passed to further calls + * to this function to find further matches. A %NULL indicates the end + * of the list. + * + * To search from the beginning pass %NULL as the @from value. + * + * The caller MUST hold the pm_devs_lock lock when calling this + * function. The instant that the lock is dropped all pointers returned + * may become invalid. + */ + +struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from) +{ + struct list_head *entry = from ? from->entry.next:pm_devs.next; + while (entry != &pm_devs) { + struct pm_dev *dev = list_entry(entry, struct pm_dev, entry); + if (type == PM_UNKNOWN_DEV || dev->type == type) + return dev; + entry = entry->next; + } + return 0; +} + +EXPORT_SYMBOL(pm_register); +EXPORT_SYMBOL(pm_unregister); +EXPORT_SYMBOL(pm_unregister_all); +EXPORT_SYMBOL(pm_send); +EXPORT_SYMBOL(pm_send_all); +EXPORT_SYMBOL(pm_find); +EXPORT_SYMBOL(pm_active); + + diff --git a/kernel/power/poweroff.c b/kernel/power/poweroff.c new file mode 100644 index 000000000000..9abe4ac23f02 --- /dev/null +++ b/kernel/power/poweroff.c @@ -0,0 +1,44 @@ +/* + * poweroff.c - sysrq handler to gracefully power down machine. + * + * This file is released under the GPL v2 + */ + +#include +#include +#include +#include + + +/** + * handle_poweroff - sysrq callback for power down + * @key: key pressed (unused) + * @pt_regs: register state (unused) + * @kbd: keyboard state (unused) + * @tty: tty involved (unused) + * + * When the user hits Sys-Rq o to power down the machine this is the + * callback we use. + */ + +static void handle_poweroff (int key, struct pt_regs *pt_regs, + struct tty_struct *tty) +{ + if (pm_power_off) + pm_power_off(); +} + +static struct sysrq_key_op sysrq_poweroff_op = { + .handler = handle_poweroff, + .help_msg = "powerOff", + .action_msg = "Power Off\n" +}; + + +static int pm_sysrq_init(void) +{ + register_sysrq_key('o', &sysrq_poweroff_op); + return 0; +} + +subsys_initcall(pm_sysrq_init); -- cgit v1.2.3 From 86aa5b107fca2ecd606cc9a4f5b81b4f05790c1b Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 12 Aug 2003 20:03:47 -0700 Subject: [power] Check device_suspend() return value in swsusp. From Pavel Machek. --- kernel/power/swsusp.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'kernel') diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index b9cd4bd18358..4ef3892bc04b 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -479,19 +479,23 @@ static void drivers_unsuspend(void) /* Called from process context */ static int drivers_suspend(void) { - device_suspend(4, SUSPEND_NOTIFY); - device_suspend(4, SUSPEND_SAVE_STATE); - device_suspend(4, SUSPEND_DISABLE); - if(!pm_suspend_state) { + 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(1); + return -EIO; } pm_suspend_state=1; } else printk(KERN_WARNING "PM suspend state already raised\n"); + device_suspend(4, SUSPEND_DISABLE); - return(0); + return 0; } #define RESUME_PHASE1 1 /* Called from interrupts disabled */ @@ -504,7 +508,7 @@ static void drivers_resume(int flags) device_resume(RESUME_ENABLE); } if (flags & RESUME_PHASE2) { - if(pm_suspend_state) { + if (pm_suspend_state) { if(pm_send_all(PM_RESUME,(void *)0)) printk(KERN_WARNING "Problem while sending resume event\n"); pm_suspend_state=0; @@ -715,7 +719,7 @@ static void do_software_suspend(void) blk_run_queues(); /* Save state of all device drivers, and stop them. */ - if(drivers_suspend()==0) + if (drivers_suspend()==0) /* If stopping device drivers worked, we proceed basically into * suspend_save_image. * -- cgit v1.2.3 From 5e29fc2e90e6389f9cc3f8f8df6c54cb09eb5d47 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 12 Aug 2003 20:04:08 -0700 Subject: [power] Minor cleanups. From Pavel Machek. --- kernel/power/console.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'kernel') diff --git a/kernel/power/console.c b/kernel/power/console.c index bfaf3f14c114..c05d0e43675f 100644 --- a/kernel/power/console.c +++ b/kernel/power/console.c @@ -1,3 +1,9 @@ +/* + * drivers/power/process.c - Functions for saving/restoring console. + * + * Originally from swsusp. + */ + #include #include #include "power.h" @@ -14,13 +20,13 @@ int pm_prepare_console(void) #ifdef SUSPEND_CONSOLE orig_fgconsole = fg_console; - if(vc_allocate(SUSPEND_CONSOLE)) + if (vc_allocate(SUSPEND_CONSOLE)) /* we can't have a free VC for now. Too bad, * we don't want to mess the screen for now. */ return 1; - set_console (SUSPEND_CONSOLE); - if(vt_waitactive(SUSPEND_CONSOLE)) { + set_console(SUSPEND_CONSOLE); + if (vt_waitactive(SUSPEND_CONSOLE)) { pr_debug("Suspend: Can't switch VCs."); return 1; } @@ -34,7 +40,7 @@ void pm_restore_console(void) { console_loglevel = orig_loglevel; #ifdef SUSPEND_CONSOLE - set_console (orig_fgconsole); + set_console(orig_fgconsole); #endif return; } -- cgit v1.2.3 From 05f11b379c8af946cfc48b8f4823e285fb926df7 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 12 Aug 2003 20:55:13 -0700 Subject: [swsusp] Remove two panic()s. - Make software_suspend() return an int, so caller can tell what happened. - Do check for HIGHMEM and DISCONTIGMEM early in software_suspend() and fail gracefully, instead of checking far down the call chain and having to call panic(). --- include/linux/suspend.h | 5 +++-- kernel/power/swsusp.c | 31 ++++++++++++++++++------------- 2 files changed, 21 insertions(+), 15 deletions(-) (limited to 'kernel') diff --git a/include/linux/suspend.h b/include/linux/suspend.h index af0143331c94..6fe7aeec7249 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -50,7 +50,7 @@ extern int shrink_mem(void); extern void drain_local_pages(void); /* kernel/suspend.c */ -extern void software_suspend(void); +extern int software_suspend(void); extern void software_resume(void); extern int register_suspend_notifier(struct notifier_block *); @@ -72,8 +72,9 @@ extern void do_suspend_lowlevel(int resume); extern void do_suspend_lowlevel_s4bios(int resume); #else /* CONFIG_SOFTWARE_SUSPEND */ -static inline void software_suspend(void) +static inline int software_suspend(void) { + return -EPERM; } #define software_resume() do { } while(0) #define register_suspend_notifier(a) do { } while(0) diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 4ef3892bc04b..441e712155c5 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -352,15 +352,10 @@ static int count_and_copy_data_pages(struct pbe *pagedir_p) int pfn; struct page *page; -#ifdef CONFIG_DISCONTIGMEM - panic("Discontingmem not supported"); -#else BUG_ON (max_pfn != num_physpages); -#endif + for (pfn = 0; pfn < max_pfn; pfn++) { page = pfn_to_page(pfn); - if (PageHighMem(page)) - panic("Swsusp not supported on highmem boxes. Send 1GB of RAM to and try again ;-)."); if (!PageReserved(page)) { if (PageNosave(page)) @@ -700,7 +695,7 @@ void do_magic_suspend_2(void) mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME); } -static void do_software_suspend(void) +static int do_software_suspend(void) { arch_prepare_suspend(); if (pm_prepare_console()) @@ -735,20 +730,30 @@ static void do_software_suspend(void) software_suspend_enabled = 1; MDELAY(1000); pm_restore_console(); + return 0; } -/* - * This is main interface to the outside world. It needs to be - * called from process context. + +/** + * software_suspend - initiate suspend-to-swap transition. + * + * This is main interface to the outside world. It needs to be + * called from process context. */ -void software_suspend(void) + +int software_suspend(void) { if(!software_suspend_enabled) - return; + return -EINVAL; + +#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(); - do_software_suspend(); + return do_software_suspend(); } /* More restore stuff */ -- cgit v1.2.3 From f2df1a5f499f8d559f66900b0e3b3fbf52f88d49 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 12 Aug 2003 21:59:26 -0700 Subject: [power] Various swsusp cleanups. - Move SMP check to software_suspend() (from software_resume()), so we will not even attempt to sleep with it enabled. - Make software_resume() a late initcall, removing the explicit call from prepare_namespace(). - Initialize software_suspend_enabled to 1, instead of doing it manually in software_resume(). - Don't explicitly initialzie resume_file. - Remove resume_status variable, as we can simply check for (non-) NULL resume_file string. - "noresume" setup function changed to simply zero first byte of resume_file string, simplifying logic. - Don't attempt to reset swap signature if noresume is specified. - Downstream function (bdev_write_page() wasn't implemented anyway, so we can just remove that also). If noresume is specified, there will still be a suspend image left on the swap partition. It may behoove us to never reset the swap signature, and always leave the image intact on the disk, since it is a valid snapshot that we can resume from at anytime. This unconditional behavior would force the user to add 'mkswap ' to their init scripts to reset the partition to swap use. IMO, this is better anyway. --- include/linux/suspend.h | 5 --- init/do_mounts.c | 4 -- kernel/power/swsusp.c | 104 ++++++++++++++---------------------------------- 3 files changed, 30 insertions(+), 83 deletions(-) (limited to 'kernel') diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 6fe7aeec7249..28788d8a65ff 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -11,9 +11,6 @@ extern unsigned char software_suspend_enabled; -#define NORESUME 1 -#define RESUME_SPECIFIED 2 - #ifdef CONFIG_SOFTWARE_SUSPEND /* page backup entry */ typedef struct pbe { @@ -51,7 +48,6 @@ extern void drain_local_pages(void); /* kernel/suspend.c */ extern int software_suspend(void); -extern void software_resume(void); extern int register_suspend_notifier(struct notifier_block *); extern int unregister_suspend_notifier(struct notifier_block *); @@ -76,7 +72,6 @@ static inline int software_suspend(void) { return -EPERM; } -#define software_resume() do { } while(0) #define register_suspend_notifier(a) do { } while(0) #define unregister_suspend_notifier(a) do { } while(0) #endif /* CONFIG_SOFTWARE_SUSPEND */ diff --git a/init/do_mounts.c b/init/do_mounts.c index f67ced75d3e1..1f6b3e575609 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -370,10 +370,6 @@ void __init prepare_namespace(void) is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; - /* This has to be before mounting root, because even readonly mount of reiserfs would replay - log corrupting stuff */ - software_resume(); - if (initrd_load()) goto out; diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index 441e712155c5..3da9c0142676 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -67,7 +67,7 @@ extern long sys_sync(void); -unsigned char software_suspend_enabled = 0; +unsigned char software_suspend_enabled = 1; #define __ADDRESS(x) ((unsigned long) phys_to_virt(x)) #define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT) @@ -85,8 +85,7 @@ spinlock_t suspend_pagedir_lock __nosavedata = SPIN_LOCK_UNLOCKED; static int pagedir_order_check; static int nr_copy_pages_check; -static int resume_status; -static char resume_file[256] = ""; /* For resume= kernel option */ +static char resume_file[256]; /* For resume= kernel option */ static dev_t resume_device; /* Local variables that should not be affected by save */ unsigned int nr_copy_pages __nosavedata = 0; @@ -746,6 +745,11 @@ 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; @@ -899,31 +903,9 @@ static int bdev_read_page(struct block_device *bdev, long pos, void *buf) return 0; } -static int bdev_write_page(struct block_device *bdev, long pos, void *buf) -{ -#if 0 - struct buffer_head *bh; - BUG_ON (pos%PAGE_SIZE); - bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE); - if (!bh || (!bh->b_data)) { - return -1; - } - memcpy(bh->b_data, buf, PAGE_SIZE); /* FIXME: may need kmap() */ - BUG_ON(!buffer_uptodate(bh)); - generic_make_request(WRITE, bh); - if (!buffer_uptodate(bh)) - printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unsuccessful...\n", name_resume, resume_file); - wait_on_buffer(bh); - brelse(bh); - return 0; -#endif - printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unimplemented...\n", name_resume, resume_file); - return 0; -} - extern dev_t __init name_to_dev_t(const char *line); -static int __read_suspend_image(struct block_device *bdev, union diskpage *cur, int noresume) +static int __read_suspend_image(struct block_device *bdev, union diskpage *cur) { swp_entry_t next; int i, nr_pgdir_pages; @@ -948,18 +930,9 @@ static int __read_suspend_image(struct block_device *bdev, union diskpage *cur, else if (!memcmp("S2",cur->swh.magic.magic,2)) memcpy(cur->swh.magic.magic,"SWAPSPACE2",10); else { - if (noresume) - return -EINVAL; - panic("%sUnable to find suspended-data signature (%.10s - misspelled?\n", + printk("swsusp: %s: Unable to find suspended-data signature (%.10s - misspelled?\n", name_resume, cur->swh.magic.magic); - } - if (noresume) { - /* We don't do a sanity check here: we want to restore the swap - whatever version of kernel made the suspend image; - We need to write swap, but swap is *not* enabled so - we must write the device directly */ - printk("%s: Fixing swap signatures %s...\n", name_resume, resume_file); - bdev_write_page(bdev, 0, cur); + return -EFAULT; } printk( "%sSignature found, resuming\n", name_resume ); @@ -1009,7 +982,7 @@ static int __read_suspend_image(struct block_device *bdev, union diskpage *cur, return 0; } -static int read_suspend_image(const char * specialfile, int noresume) +static int read_suspend_image(const char * specialfile) { union diskpage *cur; unsigned long scratch_page = 0; @@ -1028,7 +1001,7 @@ static int read_suspend_image(const char * specialfile, int noresume) error = PTR_ERR(bdev); } else { set_blocksize(bdev, PAGE_SIZE); - error = __read_suspend_image(bdev, cur, noresume); + error = __read_suspend_image(bdev, cur); blkdev_put(bdev, BDEV_RAW); } } else error = -ENOMEM; @@ -1057,64 +1030,47 @@ static int read_suspend_image(const char * specialfile, int noresume) return error; } -/* - * Called from init kernel_thread. - * We check if we have an image and if so we try to resume +/** + * 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. */ -void software_resume(void) +static int software_resume(void) { - if (num_online_cpus() > 1) { - printk(KERN_WARNING "Software Suspend has malfunctioning SMP support. Disabled :(\n"); - return; - } - /* We enable the possibility of machine suspend */ - software_suspend_enabled = 1; - if (!resume_status) - return; - - printk( "%s", name_resume ); - if (resume_status == NORESUME) { - if(resume_file[0]) - read_suspend_image(resume_file, 1); - printk( "disabled\n" ); - return; - } - MDELAY(1000); + if (!strlen(resume_file)) + return 0; if (pm_prepare_console()) printk("swsusp: Can't allocate a console... proceeding\n"); - if (!resume_file[0] && resume_status == RESUME_SPECIFIED) { - printk( "suspension device unspecified\n" ); - return; - } + printk("swsusp: %s\n", name_resume ); + + MDELAY(1000); - printk( "resuming from %s\n", resume_file); - if (read_suspend_image(resume_file, 0)) + printk("swsusp: resuming from %s\n", resume_file); + if (read_suspend_image(resume_file)) goto read_failure; do_magic(1); - panic("This never returns"); + printk("swsusp: Resume failed. Continuing.\n"); read_failure: pm_restore_console(); - return; + return -EFAULT; } +late_initcall(software_resume); + static int __init resume_setup(char *str) { - if (resume_status == NORESUME) - return 1; - strncpy( resume_file, str, 255 ); - resume_status = RESUME_SPECIFIED; - return 1; } static int __init noresume_setup(char *str) { - resume_status = NORESUME; + resume_file[0] = '\0'; return 1; } -- cgit v1.2.3