2005-04-16 15:20:36 -07:00
/*
* drivers / base / power / main . c - Where the driver meets power management .
*
* Copyright ( c ) 2003 Patrick Mochel
* Copyright ( c ) 2003 Open Source Development Lab
*
* This file is released under the GPLv2
*
*
* The driver model core calls device_pm_add ( ) when a device is registered .
* This will intialize the embedded device_pm_info object in the device
* and add it to the list of power - controlled devices . sysfs entries for
* controlling device power management will also be added .
*
* A different set of lists than the global subsystem list are used to
* keep track of power info because we use different lists to hold
* devices based on what stage of the power management process they
* are in . The power domain dependencies may also differ from the
* ancestral dependencies that the subsystem list maintains .
*/
# include <linux/device.h>
2007-09-21 15:36:56 -04:00
# include <linux/kallsyms.h>
2007-05-23 14:19:41 -07:00
# include <linux/mutex.h>
2007-09-21 15:36:56 -04:00
# include <linux/pm.h>
# include <linux/resume-trace.h>
2008-01-12 20:40:46 +01:00
# include <linux/rwsem.h>
2007-05-23 14:19:41 -07:00
2007-09-21 15:36:56 -04:00
# include "../base.h"
2005-04-16 15:20:36 -07:00
# include "power.h"
2008-01-12 20:40:46 +01:00
/*
* The entries in the dpm_active list are in a depth first order , simply
* because children are guaranteed to be discovered after parents , and
* are inserted at the back of the list on discovery .
*
* All the other lists are kept in the same order , for consistency .
* However the lists aren ' t always traversed in the same order .
* Semaphores must be acquired from the top ( i . e . , front ) down
* and released in the opposite order . Devices must be suspended
* from the bottom ( i . e . , end ) up and resumed in the opposite order .
* That way no parent will be suspended while it still has an active
* child .
*
* Since device_pm_add ( ) may be called with a device semaphore held ,
* we must never try to acquire a device semaphore while holding
* dpm_list_mutex .
*/
2005-04-16 15:20:36 -07:00
LIST_HEAD ( dpm_active ) ;
2007-09-21 15:36:56 -04:00
static LIST_HEAD ( dpm_off ) ;
static LIST_HEAD ( dpm_off_irq ) ;
2005-04-16 15:20:36 -07:00
2007-09-21 15:36:56 -04:00
static DEFINE_MUTEX ( dpm_list_mtx ) ;
2005-04-16 15:20:36 -07:00
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
/* 'true' if all devices have been suspended, protected by dpm_list_mtx */
static bool all_sleeping ;
2007-04-26 00:12:06 -07:00
2008-01-12 20:40:46 +01:00
/**
* device_pm_add - add a device to the list of active devices
* @ dev : Device to be added to the list
*/
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
int device_pm_add ( struct device * dev )
2005-04-16 15:20:36 -07:00
{
2008-04-23 00:48:23 +02:00
int error ;
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
2005-04-16 15:20:36 -07:00
pr_debug ( " PM: Adding info for %s:%s \n " ,
2007-04-11 01:37:18 -04:00
dev - > bus ? dev - > bus - > name : " No Bus " ,
kobject_name ( & dev - > kobj ) ) ;
2007-05-23 14:19:41 -07:00
mutex_lock ( & dpm_list_mtx ) ;
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
if ( ( dev - > parent & & dev - > parent - > power . sleeping ) | | all_sleeping ) {
if ( dev - > parent - > power . sleeping )
2008-04-23 00:48:23 +02:00
dev_warn ( dev , " parent %s is sleeping \n " ,
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
dev - > parent - > bus_id ) ;
else
2008-04-23 00:48:23 +02:00
dev_warn ( dev , " all devices are sleeping \n " ) ;
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
WARN_ON ( true ) ;
}
2008-04-23 00:48:23 +02:00
error = dpm_sysfs_add ( dev ) ;
if ( ! error )
list_add_tail ( & dev - > power . entry , & dpm_active ) ;
2007-05-23 14:19:41 -07:00
mutex_unlock ( & dpm_list_mtx ) ;
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
return error ;
2005-04-16 15:20:36 -07:00
}
2008-01-12 20:40:46 +01:00
/**
* device_pm_remove - remove a device from the list of active devices
* @ dev : Device to be removed from the list
*
* This function also removes the device ' s PM - related sysfs attributes .
*/
2007-06-13 15:53:34 +02:00
void device_pm_remove ( struct device * dev )
2005-04-16 15:20:36 -07:00
{
pr_debug ( " PM: Removing info for %s:%s \n " ,
2007-04-11 01:37:18 -04:00
dev - > bus ? dev - > bus - > name : " No Bus " ,
kobject_name ( & dev - > kobj ) ) ;
2007-05-23 14:19:41 -07:00
mutex_lock ( & dpm_list_mtx ) ;
2005-04-16 15:20:36 -07:00
dpm_sysfs_remove ( dev ) ;
list_del_init ( & dev - > power . entry ) ;
2007-05-23 14:19:41 -07:00
mutex_unlock ( & dpm_list_mtx ) ;
2008-01-12 20:40:46 +01:00
}
2007-09-21 15:36:56 -04:00
/*------------------------- Resume routines -------------------------*/
/**
2008-01-12 20:40:46 +01:00
* resume_device_early - Power on one device ( early resume ) .
2007-09-21 15:36:56 -04:00
* @ dev : Device .
*
2008-01-12 20:40:46 +01:00
* Must be called with interrupts disabled .
2007-09-21 15:36:56 -04:00
*/
2008-01-12 20:40:46 +01:00
static int resume_device_early ( struct device * dev )
2007-09-21 15:36:56 -04:00
{
int error = 0 ;
TRACE_DEVICE ( dev ) ;
TRACE_RESUME ( 0 ) ;
2008-01-12 20:40:46 +01:00
if ( dev - > bus & & dev - > bus - > resume_early ) {
dev_dbg ( dev , " EARLY resume \n " ) ;
error = dev - > bus - > resume_early ( dev ) ;
}
TRACE_RESUME ( error ) ;
return error ;
}
/**
* dpm_power_up - Power on all regular ( non - sysdev ) devices .
*
* Walk the dpm_off_irq list and power each device up . This
* is used for devices that required they be powered down with
* interrupts disabled . As devices are powered on , they are moved
* to the dpm_off list .
*
* Must be called with interrupts disabled and only one CPU running .
*/
static void dpm_power_up ( void )
{
while ( ! list_empty ( & dpm_off_irq ) ) {
struct list_head * entry = dpm_off_irq . next ;
struct device * dev = to_device ( entry ) ;
list_move_tail ( entry , & dpm_off ) ;
resume_device_early ( dev ) ;
}
}
/**
* device_power_up - Turn on all devices that need special attention .
*
* Power on system devices , then devices that required we shut them down
* with interrupts disabled .
*
* Must be called with interrupts disabled .
*/
void device_power_up ( void )
{
sysdev_resume ( ) ;
dpm_power_up ( ) ;
}
EXPORT_SYMBOL_GPL ( device_power_up ) ;
/**
* resume_device - Restore state for one device .
* @ dev : Device .
*
*/
static int resume_device ( struct device * dev )
{
int error = 0 ;
TRACE_DEVICE ( dev ) ;
TRACE_RESUME ( 0 ) ;
2007-09-21 15:36:56 -04:00
2008-02-25 00:35:04 +01:00
down ( & dev - > sem ) ;
2007-09-21 15:36:56 -04:00
if ( dev - > bus & & dev - > bus - > resume ) {
dev_dbg ( dev , " resuming \n " ) ;
error = dev - > bus - > resume ( dev ) ;
}
if ( ! error & & dev - > type & & dev - > type - > resume ) {
dev_dbg ( dev , " resuming \n " ) ;
error = dev - > type - > resume ( dev ) ;
}
if ( ! error & & dev - > class & & dev - > class - > resume ) {
dev_dbg ( dev , " class resume \n " ) ;
error = dev - > class - > resume ( dev ) ;
}
2008-02-25 00:35:04 +01:00
up ( & dev - > sem ) ;
2007-09-21 15:36:56 -04:00
TRACE_RESUME ( error ) ;
return error ;
}
2008-01-12 20:40:46 +01:00
/**
* dpm_resume - Resume every device .
*
* Resume the devices that have either not gone through
* the late suspend , or that did go through it but also
* went through the early resume .
*
* Take devices from the dpm_off_list , resume them ,
* and put them on the dpm_locked list .
2007-09-21 15:36:56 -04:00
*/
static void dpm_resume ( void )
{
mutex_lock ( & dpm_list_mtx ) ;
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
all_sleeping = false ;
2007-09-21 15:36:56 -04:00
while ( ! list_empty ( & dpm_off ) ) {
2008-01-12 20:40:46 +01:00
struct list_head * entry = dpm_off . next ;
struct device * dev = to_device ( entry ) ;
2007-09-21 15:36:56 -04:00
2008-02-25 00:35:04 +01:00
list_move_tail ( entry , & dpm_active ) ;
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
dev - > power . sleeping = false ;
2007-09-21 15:36:56 -04:00
mutex_unlock ( & dpm_list_mtx ) ;
resume_device ( dev ) ;
mutex_lock ( & dpm_list_mtx ) ;
}
mutex_unlock ( & dpm_list_mtx ) ;
}
/**
2008-01-12 20:40:46 +01:00
* device_resume - Restore state of each device in system .
2007-09-21 15:36:56 -04:00
*
2008-01-12 20:40:46 +01:00
* Resume all the devices , unlock them all , and allow new
* devices to be registered once again .
2007-09-21 15:36:56 -04:00
*/
2008-01-12 20:40:46 +01:00
void device_resume ( void )
2007-09-21 15:36:56 -04:00
{
2008-01-12 20:40:46 +01:00
might_sleep ( ) ;
dpm_resume ( ) ;
2007-09-21 15:36:56 -04:00
}
2008-01-12 20:40:46 +01:00
EXPORT_SYMBOL_GPL ( device_resume ) ;
2007-09-21 15:36:56 -04:00
/*------------------------- Suspend routines -------------------------*/
static inline char * suspend_verb ( u32 event )
{
switch ( event ) {
case PM_EVENT_SUSPEND : return " suspend " ;
case PM_EVENT_FREEZE : return " freeze " ;
case PM_EVENT_PRETHAW : return " prethaw " ;
default : return " (unknown suspend event) " ;
}
}
static void
suspend_device_dbg ( struct device * dev , pm_message_t state , char * info )
{
dev_dbg ( dev , " %s%s%s \n " , info , suspend_verb ( state . event ) ,
( ( state . event = = PM_EVENT_SUSPEND ) & & device_may_wakeup ( dev ) ) ?
" , may wakeup " : " " ) ;
}
/**
2008-01-12 20:40:46 +01:00
* suspend_device_late - Shut down one device ( late suspend ) .
2007-09-21 15:36:56 -04:00
* @ dev : Device .
* @ state : Power state device is entering .
2008-01-12 20:40:46 +01:00
*
* This is called with interrupts off and only a single CPU running .
2007-09-21 15:36:56 -04:00
*/
2008-01-12 20:40:46 +01:00
static int suspend_device_late ( struct device * dev , pm_message_t state )
{
int error = 0 ;
2007-09-21 15:36:56 -04:00
2008-01-12 20:40:46 +01:00
if ( dev - > bus & & dev - > bus - > suspend_late ) {
suspend_device_dbg ( dev , state , " LATE " ) ;
error = dev - > bus - > suspend_late ( dev , state ) ;
suspend_report_result ( dev - > bus - > suspend_late , error ) ;
}
return error ;
}
/**
* device_power_down - Shut down special devices .
* @ state : Power state to enter .
*
* Power down devices that require interrupts to be disabled
* and move them from the dpm_off list to the dpm_off_irq list .
* Then power down system devices .
*
* Must be called with interrupts disabled and only one CPU running .
*/
int device_power_down ( pm_message_t state )
{
int error = 0 ;
while ( ! list_empty ( & dpm_off ) ) {
struct list_head * entry = dpm_off . prev ;
struct device * dev = to_device ( entry ) ;
error = suspend_device_late ( dev , state ) ;
if ( error ) {
printk ( KERN_ERR " Could not power down device %s: "
" error %d \n " ,
kobject_name ( & dev - > kobj ) , error ) ;
break ;
}
2008-02-25 00:35:04 +01:00
if ( ! list_empty ( & dev - > power . entry ) )
list_move ( & dev - > power . entry , & dpm_off_irq ) ;
2008-01-12 20:40:46 +01:00
}
if ( ! error )
error = sysdev_suspend ( state ) ;
if ( error )
dpm_power_up ( ) ;
return error ;
}
EXPORT_SYMBOL_GPL ( device_power_down ) ;
/**
* suspend_device - Save state of one device .
* @ dev : Device .
* @ state : Power state device is entering .
*/
2008-02-03 22:55:18 +01:00
static int suspend_device ( struct device * dev , pm_message_t state )
2007-09-21 15:36:56 -04:00
{
int error = 0 ;
2008-02-25 00:35:04 +01:00
down ( & dev - > sem ) ;
2007-09-21 15:36:56 -04:00
if ( dev - > class & & dev - > class - > suspend ) {
suspend_device_dbg ( dev , state , " class " ) ;
error = dev - > class - > suspend ( dev , state ) ;
suspend_report_result ( dev - > class - > suspend , error ) ;
}
if ( ! error & & dev - > type & & dev - > type - > suspend ) {
suspend_device_dbg ( dev , state , " type " ) ;
error = dev - > type - > suspend ( dev , state ) ;
suspend_report_result ( dev - > type - > suspend , error ) ;
}
if ( ! error & & dev - > bus & & dev - > bus - > suspend ) {
suspend_device_dbg ( dev , state , " " ) ;
error = dev - > bus - > suspend ( dev , state ) ;
suspend_report_result ( dev - > bus - > suspend , error ) ;
}
2008-02-25 00:35:04 +01:00
up ( & dev - > sem ) ;
2007-09-21 15:36:56 -04:00
return error ;
}
/**
2008-01-12 20:40:46 +01:00
* dpm_suspend - Suspend every device .
* @ state : Power state to put each device in .
2007-09-21 15:36:56 -04:00
*
2008-01-12 20:40:46 +01:00
* Walk the dpm_locked list . Suspend each device and move it
* to the dpm_off list .
2007-09-21 15:36:56 -04:00
*
* ( For historical reasons , if it returns - EAGAIN , that used to mean
* that the device would be called again with interrupts disabled .
* These days , we use the " suspend_late() " callback for that , so we
* print a warning and consider it an error ) .
*/
2008-01-12 20:40:46 +01:00
static int dpm_suspend ( pm_message_t state )
2007-09-21 15:36:56 -04:00
{
int error = 0 ;
mutex_lock ( & dpm_list_mtx ) ;
2008-02-25 00:35:04 +01:00
while ( ! list_empty ( & dpm_active ) ) {
struct list_head * entry = dpm_active . prev ;
2008-01-12 20:40:46 +01:00
struct device * dev = to_device ( entry ) ;
2007-09-21 15:36:56 -04:00
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
WARN_ON ( dev - > parent & & dev - > parent - > power . sleeping ) ;
dev - > power . sleeping = true ;
2007-09-21 15:36:56 -04:00
mutex_unlock ( & dpm_list_mtx ) ;
error = suspend_device ( dev , state ) ;
2008-02-29 11:50:22 -05:00
mutex_lock ( & dpm_list_mtx ) ;
2008-01-12 20:40:46 +01:00
if ( error ) {
2007-09-21 15:36:56 -04:00
printk ( KERN_ERR " Could not suspend device %s: "
2008-01-12 20:40:46 +01:00
" error %d%s \n " ,
kobject_name ( & dev - > kobj ) ,
error ,
( error = = - EAGAIN ?
" (please convert to suspend_late) " :
" " ) ) ;
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
dev - > power . sleeping = false ;
2008-01-12 20:40:46 +01:00
break ;
}
2008-02-25 00:35:04 +01:00
if ( ! list_empty ( & dev - > power . entry ) )
list_move ( & dev - > power . entry , & dpm_off ) ;
2007-09-21 15:36:56 -04:00
}
PM: Handle device registrations during suspend/resume
Modify the PM core to protect its data structures, specifically the
dpm_active list, from being corrupted if a child of the currently
suspending device is registered concurrently with its ->suspend()
callback. In that case, since the new device (the child) is added
to dpm_active after its parent, the PM core will attempt to
suspend it after the parent, which is wrong.
Introduce a new member of struct dev_pm_info, called 'sleeping',
and use it to check if the parent of the device being added to
dpm_active has been suspended, in which case the device registration
fails. Also, use 'sleeping' for checking if the ordering of devices
on dpm_active is correct.
Introduce variable 'all_sleeping' that will be set to 'true' once all
devices have been suspended and make new device registrations fail
until 'all_sleeping' is reset to 'false', in order to avoid having
unsuspended devices around while the system is going into a sleep state.
Remove pm_sleep_rwsem which is not necessary any more.
Special thanks to Alan Stern for discussions and suggestions that
lead to the creation of this patch.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-12 00:57:22 +01:00
if ( ! error )
all_sleeping = true ;
2007-09-21 15:36:56 -04:00
mutex_unlock ( & dpm_list_mtx ) ;
return error ;
}
2008-01-12 20:40:46 +01:00
/**
* device_suspend - Save state and stop all devices in system .
2008-02-18 13:09:03 -08:00
* @ state : new power management state
2008-01-12 20:40:46 +01:00
*
* Prevent new devices from being registered , then lock all devices
* and suspend them .
*/
int device_suspend ( pm_message_t state )
{
int error ;
2007-09-21 15:36:56 -04:00
2008-01-12 20:40:46 +01:00
might_sleep ( ) ;
error = dpm_suspend ( state ) ;
if ( error )
device_resume ( ) ;
2007-09-21 15:36:56 -04:00
return error ;
}
2008-01-12 20:40:46 +01:00
EXPORT_SYMBOL_GPL ( device_suspend ) ;
2007-09-21 15:36:56 -04:00
void __suspend_report_result ( const char * function , void * fn , int ret )
{
if ( ret ) {
printk ( KERN_ERR " %s(): " , function ) ;
2008-05-15 17:50:37 -07:00
print_fn_descriptor_symbol ( " %s returns " , fn ) ;
2007-09-21 15:36:56 -04:00
printk ( " %d \n " , ret ) ;
}
}
EXPORT_SYMBOL_GPL ( __suspend_report_result ) ;