PM: Asynchronous suspend and resume of devices
Subsystems (7)
cover PM: Asynchronous suspend and resume of devices
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
The idea is explained in the changelogs of the first two patches.
Comments welcome.
Thanks,
Rafael
PATCH 1/6 PM: Introduce PM links framework
From: Rafael J. Wysocki <[email protected]>
Introduce a framework for representing off-tree PM dependencies between devices.
There are PM dependencies between devices that are not reflected by the structure of the device tree. In other words, as far as PM is concerned, a device may depend on some other devices which are not its children and none of which is its parent.
Every such dependency involves two devices, one of which is a "master" and the other of which is a "slave", meaning that the "slave" have to be suspended before the "master" and cannot be woken up before it. Thus every device can be given two lists of "dependency objects", one for the dependencies where the device is the "master" and the other for the dependencies where the device is the "slave". Then, each "dependency object" can be represented as
struct pm_link {
struct device *master;
struct list_head master_hook;
struct device *slave;
struct list_head slave_hook;
};Add some synchronization, helpers for adding and removing 'struct pm_link' objects.
In addition to checking a device's parent, walk the list of its "masters", in addition to walking the list of a device's children, walk the list of its "slaves".
'struct pm_link' objects are created automatically for ACPI devices and "regular" devices associated with them. In the other cases such objects will have to be added directly by platforms / bus types / drivers etc.
--- drivers/acpi/glue.c | 3 drivers/base/core.c | 4 drivers/base/power/Makefile | 2 drivers/base/power/common.c | 279 +++++++++++++++++++++++++++++++++++++++++++ drivers/base/power/main.c | 27 ---- drivers/base/power/power.h | 31 ++-- drivers/base/power/runtime.c | 4 include/linux/pm.h | 4 include/linux/pm_link.h | 30 ++++ 9 files changed, 342 insertions(+), 42 deletions(-)
include/linux/pm.h +3 -1
@@ -408,6 +408,9 @@ enum rpm_request { };struct dev_pm_info { +spinlock_t lock; +struct list_head master_links; +struct list_head slave_links;pm_message_t power_state;unsigned int can_wakeup:1 ;unsigned int should_wakeup:1 ;
@@ -420,7 +423,6 @@ struct dev_pm_info {unsigned long timer_expires;struct work_struct work;wait_queue_head_t wait_queue; -spinlock_t lock;atomic_t usage_count;atomic_t child_count; unsigned int disable_depth:3;
drivers/base/power/power.h +17 -14
@@ -1,3 +1,7 @@ +extern void device_pm_init (struct device *dev); +extern void device_pm_add (struct device *dev); +extern void device_pm_remove (struct device *dev); +#ifdef CONFIG_PM_RUNTIME extern void pm_runtime_init (struct device *dev);
@@ -23,7 +27,8 @@ static inline struct device *to_device(s return container_of(entry, struct device, power.entry); } -extern void device_pm_init (struct device *dev); +extern void device_pm_list_add (struct device *dev); +extern void device_pm_list_remove (struct device *dev);extern void device_pm_add (struct device *);extern void device_pm_remove (struct device *);extern void device_pm_move_before (struct device *,struct device *);
@@ -32,17 +37,8 @@ extern void device_pm_move_last(struct d#else /* !CONFIG_PM_SLEEP */ -static inline void device_pm_init (struct device *dev) -{ -pm_runtime_init (dev); -} - -static inline void device_pm_remove (struct device *dev) -{ -pm_runtime_remove (dev); -} - -static inline void device_pm_add (struct device *dev) {} +static inline void device_pm_list_add (struct device *dev) {} +static inline void device_pm_list_remove (struct device *dev) {}static inline void device_pm_move_before (struct device *deva,struct device *devb) {} static inline void device_pm_move_after(struct device *deva,
@@ -60,7 +56,11 @@ static inline void device_pm_move_last(sextern int dpm_sysfs_add (struct device *);extern void dpm_sysfs_remove (struct device *); -#else /* CONFIG_PM */ +/* drivers/base/power/link.c */ +extern int pm_link_init (void ); +extern void pm_link_remove_all (struct device *dev); + +#else /* !CONFIG_PM */ static inline int dpm_sysfs_add (struct device *dev) {
@@ -71,4 +71,7 @@ static inline void dpm_sysfs_remove(stru { } -#endif +static inline int pm_link_init (void ) {return 0 ; } +static inline void pm_link_remove_all (struct device *dev) {} + +#endif /* !CONFIG_PM */
drivers/base/core.c +4 -0
@@ -1252,9 +1252,13 @@ int __init devices_init(void) sysfs_dev_char_kobj =kobject_create_and_add ("char" , dev_kobj);if (!sysfs_dev_char_kobj) goto char_kobj_err; +if (pm_link_init ()) + goto pm_link_err;return 0 ; + pm_link_err: +kobject_put (sysfs_dev_char_kobj); char_kobj_err:kobject_put (sysfs_dev_block_kobj); block_kobj_err:
include/linux/pm_link.h new file +30 -0
@@ -0,0 +1,30 @@ +/* +* include/linux/pm_link.h - PM links manipulation core. +* +* Copyright (c) 2009 Rafael J. Wysocki <[email protected]>, Novell Inc. +* +* This file is released under the GPLv2. +*/ + +#ifndef _LINUX_PM_LINK_H +#define _LINUX_PM_LINK_H + +#include <linux/list.h> + +struct device ; + +struct pm_link { +struct device *master; +struct list_head master_hook; +struct device *slave; +struct list_head slave_hook; +}; + +extern int pm_link_add (struct device *slave,struct device *master); +extern void pm_link_remove (struct device *dev,struct device *master); +extern int device_for_each_master (struct device *slave,void *data, +int (*fn)(struct device *dev,void *data)); +extern int device_for_each_slave (struct device *master,void *data, +int (*fn)(struct device *dev,void *data)); + +#endif
drivers/base/power/Makefile +1 -1
@@ -1,4 +1,4 @@ -obj-$(CONFIG_PM) += sysfs.o +obj-$(CONFIG_PM) += sysfs.o common.o obj-$(CONFIG_PM_SLEEP) += main.o obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o
drivers/base/power/runtime.c +0 -4
@@ -972,8 +972,6 @@ EXPORT_SYMBOL_GPL(pm_runtime_enable);*/ void pm_runtime_init (struct device *dev) { -spin_lock_init (&dev->power.lock); - dev->power.runtime_status =RPM_SUSPENDED ; dev->power.idle_notification = false;
@@ -993,8 +991,6 @@ void pm_runtime_init(struct device *dev) dev->power.timer_expires =0 ;setup_timer (&dev->power.suspend_timer, pm_suspend_timer_fn, (unsigned long )dev); - -init_waitqueue_head (&dev->power.wait_queue); }/**
drivers/base/power/main.c +5 -22
@@ -21,6 +21,7 @@#include <linux/kallsyms.h> #include <linux/mutex.h> #include <linux/pm.h> +#include <linux/pm_link.h> #include <linux/pm_runtime.h> #include <linux/resume-trace.h> #include <linux/rwsem.h>
@@ -50,16 +51,6 @@ static DEFINE_MUTEX(dpm_list_mtx);static bool transition_started;/** -* device_pm_init - Initialize the PM-related part of a device object. -* @dev: Device object being initialized. -*/ -void device_pm_init (struct device *dev) -{ - dev->power.status =DPM_ON ; -pm_runtime_init (dev); -} - -/** * device_pm_lock - Lock the list of active devices used by the PM core. */ void device_pm_lock (void )
@@ -76,14 +67,11 @@ void device_pm_unlock(void) }/** -* device_pm_add - Add a device to the PM core's list of active devices. +* device_pm_list_add - Add a device to the PM core's list of active devices. * @dev: Device to add to the list. */ -void device_pm_add (struct device *dev) +void device_pm_list_add (struct device *dev) { -pr_debug ("PM: Adding info for %s:%s\n" , - dev->bus ? dev->bus->name :"No Bus" , -kobject_name (&dev->kobj));mutex_lock (&dpm_list_mtx);if (dev->parent) {if (dev->parent->power.status >=DPM_SUSPENDING )
@@ -97,24 +85,19 @@ void device_pm_add(struct device *dev)*/ dev_WARN (dev,"Parentless device registered during a PM transaction\n" ); } -list_add_tail (&dev->power.entry, &dpm_list);mutex_unlock (&dpm_list_mtx); }/** -* device_pm_remove - Remove a device from the PM core's list of active devices. +* device_pm_list_remove - Remove a device from the PM core's list of devices. * @dev: Device to be removed from the list. */ -void device_pm_remove (struct device *dev) +void device_pm_list_remove (struct device *dev) { -pr_debug ("PM: Removing info for %s:%s\n" , - dev->bus ? dev->bus->name :"No Bus" , -kobject_name (&dev->kobj));mutex_lock (&dpm_list_mtx);list_del_init (&dev->power.entry);mutex_unlock (&dpm_list_mtx); -pm_runtime_remove (dev); }/**
drivers/base/power/common.c new file +279 -0
279 lines not on this page · raw message
drivers/acpi/glue.c +3 -0
@@ -11,6 +11,7 @@#include <linux/device.h> #include <linux/rwsem.h> #include <linux/acpi.h> +#include <linux/pm_link.h> #define ACPI_GLUE_DEBUG 0#if ACPI_GLUE_DEBUG
@@ -170,6 +171,7 @@ static int acpi_bind_one(struct device *device_set_wakeup_enable (dev, acpi_dev->wakeup.state.enabled); } +pm_link_add (dev, &acpi_dev->dev); }return 0 ;
@@ -189,6 +191,7 @@ static int acpi_unbind_one(struct device &acpi_dev)) {sysfs_remove_link (&dev->kobj,"firmware_node" );sysfs_remove_link (&acpi_dev->dev.kobj,"physical_node" ); +pm_link_remove (dev, &acpi_dev->dev); } acpi_detach_data(dev->archdata.acpi_handle,
Re: [PATCH 1/6] PM: Introduce PM links framework
Quoted 40 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Introduce a framework for representing off-tree PM dependencies between devices.
There are PM dependencies between devices that are not reflected by the structure of the device tree. In other words, as far as PM is concerned, a device may depend on some other devices which are not its children and none of which is its parent.
Every such dependency involves two devices, one of which is a "master" and the other of which is a "slave", meaning that the "slave" have to be suspended before the "master" and cannot be woken up before it. Thus every device can be given two lists of "dependency objects", one for the dependencies where the device is the "master" and the other for the dependencies where the device is the "slave". Then, each "dependency object" can be represented as
Index:
linux-2.6/drivers/base/power/common.c
===================================================================drivers/base/power/common.c new file +16
-0+/** +* device_for_each_master - Execute given function for each master of a device. +* @slave: Device whose masters to execute the function for. +* @data: Data pointer to pass to the function. +* @fn: Function to execute for each master of @slave. +* +* The function is executed for the parent of the device, if there is one, and +* for each device connected to it via a pm_link object where @slave is the +* "slave". +*/ +int device_for_each_master (struct device *slave,void *data, +int (*fn)(struct device *dev,void *data)) +{ +struct pm_link *link; +int idx; +int error =0 ;
drivers/base/power/common.c line 225
Quoted 10 lines
drivers/base/power/common.c new file +10
-0+ +if (slave->parent) { + error =fn (slave->parent, data); +if (error) +return error; + } + + idx =srcu_read_lock (&pm_link_ss); + +list_for_each_entry (link, &slave->power.slave_links, slave_hook) {
This needs to use list_for_each_entry_rcu. Likewise in device_for_each_slave().
Alan Stern
Re: [PATCH 1/6] PM: Introduce PM links framework
Quoted 47 lines · Alan Stern
Quoted 45 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Introduce a framework for representing off-tree PM dependencies between devices.
There are PM dependencies between devices that are not reflected by the structure of the device tree. In other words, as far as PM is concerned, a device may depend on some other devices which are not its children and none of which is its parent.
Every such dependency involves two devices, one of which is a "master" and the other of which is a "slave", meaning that the "slave" have to be suspended before the "master" and cannot be woken up before it. Thus every device can be given two lists of "dependency objects", one for the dependencies where the device is the "master" and the other for the dependencies where the device is the "slave". Then, each "dependency object" can be represented as
Index:
linux-2.6/drivers/base/power/common.c
===================================================================drivers/base/power/common.c new file +21
-0+/** +* device_for_each_master - Execute given function for each master of a device. +* @slave: Device whose masters to execute the function for. +* @data: Data pointer to pass to the function. +* @fn: Function to execute for each master of @slave. +* +* The function is executed for the parent of the device, if there is one, and +* for each device connected to it via a pm_link object where @slave is the +* "slave". +*/ +int device_for_each_master (struct device *slave,void *data, +int (*fn)(struct device *dev,void *data)) +{ +struct pm_link *link; +int idx; +int error =0 ; + +if (slave->parent) { + error =fn (slave->parent, data); +if (error) +return error;
Quoted 8 lines
drivers/base/power/common.c line 225
Quoted 5 lines
drivers/base/power/common.c new file +5
-0+ } + + idx =srcu_read_lock (&pm_link_ss); + +list_for_each_entry (link, &slave->power.slave_links, slave_hook) {This needs to use
list_for_each_entry_rcu. Likewise indevice_for_each_slave().
OK, I wasn't sure about that.
Thanks,
Rafael
Re: [PATCH 1/6] PM: Introduce PM links framework
In reply to Alan SternQuoted 47 lines · Alan Stern
Quoted 45 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Introduce a framework for representing off-tree PM dependencies between devices.
There are PM dependencies between devices that are not reflected by the structure of the device tree. In other words, as far as PM is concerned, a device may depend on some other devices which are not its children and none of which is its parent.
Every such dependency involves two devices, one of which is a "master" and the other of which is a "slave", meaning that the "slave" have to be suspended before the "master" and cannot be woken up before it. Thus every device can be given two lists of "dependency objects", one for the dependencies where the device is the "master" and the other for the dependencies where the device is the "slave". Then, each "dependency object" can be represented as
Index:
linux-2.6/drivers/base/power/common.c
===================================================================drivers/base/power/common.c new file +21
-0+/** +* device_for_each_master - Execute given function for each master of a device. +* @slave: Device whose masters to execute the function for. +* @data: Data pointer to pass to the function. +* @fn: Function to execute for each master of @slave. +* +* The function is executed for the parent of the device, if there is one, and +* for each device connected to it via a pm_link object where @slave is the +* "slave". +*/ +int device_for_each_master (struct device *slave,void *data, +int (*fn)(struct device *dev,void *data)) +{ +struct pm_link *link; +int idx; +int error =0 ; + +if (slave->parent) { + error =fn (slave->parent, data); +if (error) +return error;
Quoted 8 lines
drivers/base/power/common.c line 225
Quoted 5 lines
drivers/base/power/common.c new file +5
-0+ } + + idx =srcu_read_lock (&pm_link_ss); + +list_for_each_entry (link, &slave->power.slave_links, slave_hook) {This needs to use
list_for_each_entry_rcu. Likewise indevice_for_each_slave().
OK, I wasn't sure about that.
Thanks,
Rafael
Re: [PATCH 1/6] PM: Introduce PM links framework
In reply to Rafael J. WysockiQuoted 40 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Introduce a framework for representing off-tree PM dependencies between devices.
There are PM dependencies between devices that are not reflected by the structure of the device tree. In other words, as far as PM is concerned, a device may depend on some other devices which are not its children and none of which is its parent.
Every such dependency involves two devices, one of which is a "master" and the other of which is a "slave", meaning that the "slave" have to be suspended before the "master" and cannot be woken up before it. Thus every device can be given two lists of "dependency objects", one for the dependencies where the device is the "master" and the other for the dependencies where the device is the "slave". Then, each "dependency object" can be represented as
Index:
linux-2.6/drivers/base/power/common.c
===================================================================drivers/base/power/common.c new file +16
-0+/** +* device_for_each_master - Execute given function for each master of a device. +* @slave: Device whose masters to execute the function for. +* @data: Data pointer to pass to the function. +* @fn: Function to execute for each master of @slave. +* +* The function is executed for the parent of the device, if there is one, and +* for each device connected to it via a pm_link object where @slave is the +* "slave". +*/ +int device_for_each_master (struct device *slave,void *data, +int (*fn)(struct device *dev,void *data)) +{ +struct pm_link *link; +int idx; +int error =0 ;
drivers/base/power/common.c line 225
Quoted 10 lines
drivers/base/power/common.c new file +10
-0+ +if (slave->parent) { + error =fn (slave->parent, data); +if (error) +return error; + } + + idx =srcu_read_lock (&pm_link_ss); + +list_for_each_entry (link, &slave->power.slave_links, slave_hook) {
This needs to use list_for_each_entry_rcu. Likewise in device_for_each_slave().
Alan Stern
PATCH 1/6 PM: Introduce PM links framework
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Introduce a framework for representing off-tree PM dependencies between devices.
There are PM dependencies between devices that are not reflected by the structure of the device tree. In other words, as far as PM is concerned, a device may depend on some other devices which are not its children and none of which is its parent.
Every such dependency involves two devices, one of which is a "master" and the other of which is a "slave", meaning that the "slave" have to be suspended before the "master" and cannot be woken up before it. Thus every device can be given two lists of "dependency objects", one for the dependencies where the device is the "master" and the other for the dependencies where the device is the "slave". Then, each "dependency object" can be represented as
struct pm_link {
struct device *master;
struct list_head master_hook;
struct device *slave;
struct list_head slave_hook;
};Add some synchronization, helpers for adding and removing 'struct pm_link' objects.
In addition to checking a device's parent, walk the list of its "masters", in addition to walking the list of a device's children, walk the list of its "slaves".
'struct pm_link' objects are created automatically for ACPI devices and "regular" devices associated with them. In the other cases such objects will have to be added directly by platforms / bus types / drivers etc.
--- drivers/acpi/glue.c | 3 drivers/base/core.c | 4 drivers/base/power/Makefile | 2 drivers/base/power/common.c | 279 +++++++++++++++++++++++++++++++++++++++++++ drivers/base/power/main.c | 27 ---- drivers/base/power/power.h | 31 ++-- drivers/base/power/runtime.c | 4 include/linux/pm.h | 4 include/linux/pm_link.h | 30 ++++ 9 files changed, 342 insertions(+), 42 deletions(-)
include/linux/pm.h +3 -1
@@ -408,6 +408,9 @@ enum rpm_request { };struct dev_pm_info { +spinlock_t lock; +struct list_head master_links; +struct list_head slave_links;pm_message_t power_state;unsigned int can_wakeup:1 ;unsigned int should_wakeup:1 ;
@@ -420,7 +423,6 @@ struct dev_pm_info {unsigned long timer_expires;struct work_struct work;wait_queue_head_t wait_queue; -spinlock_t lock;atomic_t usage_count;atomic_t child_count; unsigned int disable_depth:3;
drivers/base/power/power.h +17 -14
@@ -1,3 +1,7 @@ +extern void device_pm_init (struct device *dev); +extern void device_pm_add (struct device *dev); +extern void device_pm_remove (struct device *dev); +#ifdef CONFIG_PM_RUNTIME extern void pm_runtime_init (struct device *dev);
@@ -23,7 +27,8 @@ static inline struct device *to_device(s return container_of(entry, struct device, power.entry); } -extern void device_pm_init (struct device *dev); +extern void device_pm_list_add (struct device *dev); +extern void device_pm_list_remove (struct device *dev);extern void device_pm_add (struct device *);extern void device_pm_remove (struct device *);extern void device_pm_move_before (struct device *,struct device *);
@@ -32,17 +37,8 @@ extern void device_pm_move_last(struct d#else /* !CONFIG_PM_SLEEP */ -static inline void device_pm_init (struct device *dev) -{ -pm_runtime_init (dev); -} - -static inline void device_pm_remove (struct device *dev) -{ -pm_runtime_remove (dev); -} - -static inline void device_pm_add (struct device *dev) {} +static inline void device_pm_list_add (struct device *dev) {} +static inline void device_pm_list_remove (struct device *dev) {}static inline void device_pm_move_before (struct device *deva,struct device *devb) {} static inline void device_pm_move_after(struct device *deva,
@@ -60,7 +56,11 @@ static inline void device_pm_move_last(sextern int dpm_sysfs_add (struct device *);extern void dpm_sysfs_remove (struct device *); -#else /* CONFIG_PM */ +/* drivers/base/power/link.c */ +extern int pm_link_init (void ); +extern void pm_link_remove_all (struct device *dev); + +#else /* !CONFIG_PM */ static inline int dpm_sysfs_add (struct device *dev) {
@@ -71,4 +71,7 @@ static inline void dpm_sysfs_remove(stru { } -#endif +static inline int pm_link_init (void ) {return 0 ; } +static inline void pm_link_remove_all (struct device *dev) {} + +#endif /* !CONFIG_PM */
drivers/base/core.c +4 -0
@@ -1252,9 +1252,13 @@ int __init devices_init(void) sysfs_dev_char_kobj =kobject_create_and_add ("char" , dev_kobj);if (!sysfs_dev_char_kobj) goto char_kobj_err; +if (pm_link_init ()) + goto pm_link_err;return 0 ; + pm_link_err: +kobject_put (sysfs_dev_char_kobj); char_kobj_err:kobject_put (sysfs_dev_block_kobj); block_kobj_err:
include/linux/pm_link.h new file +30 -0
@@ -0,0 +1,30 @@ +/* +* include/linux/pm_link.h - PM links manipulation core. +* +* Copyright (c) 2009 Rafael J. Wysocki <[email protected]>, Novell Inc. +* +* This file is released under the GPLv2. +*/ + +#ifndef _LINUX_PM_LINK_H +#define _LINUX_PM_LINK_H + +#include <linux/list.h> + +struct device ; + +struct pm_link { +struct device *master; +struct list_head master_hook; +struct device *slave; +struct list_head slave_hook; +}; + +extern int pm_link_add (struct device *slave,struct device *master); +extern void pm_link_remove (struct device *dev,struct device *master); +extern int device_for_each_master (struct device *slave,void *data, +int (*fn)(struct device *dev,void *data)); +extern int device_for_each_slave (struct device *master,void *data, +int (*fn)(struct device *dev,void *data)); + +#endif
drivers/base/power/Makefile +1 -1
@@ -1,4 +1,4 @@ -obj-$(CONFIG_PM) += sysfs.o +obj-$(CONFIG_PM) += sysfs.o common.o obj-$(CONFIG_PM_SLEEP) += main.o obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o
drivers/base/power/runtime.c +0 -4
@@ -972,8 +972,6 @@ EXPORT_SYMBOL_GPL(pm_runtime_enable);*/ void pm_runtime_init (struct device *dev) { -spin_lock_init (&dev->power.lock); - dev->power.runtime_status =RPM_SUSPENDED ; dev->power.idle_notification = false;
@@ -993,8 +991,6 @@ void pm_runtime_init(struct device *dev) dev->power.timer_expires =0 ;setup_timer (&dev->power.suspend_timer, pm_suspend_timer_fn, (unsigned long )dev); - -init_waitqueue_head (&dev->power.wait_queue); }/**
drivers/base/power/main.c +5 -22
@@ -21,6 +21,7 @@#include <linux/kallsyms.h> #include <linux/mutex.h> #include <linux/pm.h> +#include <linux/pm_link.h> #include <linux/pm_runtime.h> #include <linux/resume-trace.h> #include <linux/rwsem.h>
@@ -50,16 +51,6 @@ static DEFINE_MUTEX(dpm_list_mtx);static bool transition_started;/** -* device_pm_init - Initialize the PM-related part of a device object. -* @dev: Device object being initialized. -*/ -void device_pm_init (struct device *dev) -{ - dev->power.status =DPM_ON ; -pm_runtime_init (dev); -} - -/** * device_pm_lock - Lock the list of active devices used by the PM core. */ void device_pm_lock (void )
@@ -76,14 +67,11 @@ void device_pm_unlock(void) }/** -* device_pm_add - Add a device to the PM core's list of active devices. +* device_pm_list_add - Add a device to the PM core's list of active devices. * @dev: Device to add to the list. */ -void device_pm_add (struct device *dev) +void device_pm_list_add (struct device *dev) { -pr_debug ("PM: Adding info for %s:%s\n" , - dev->bus ? dev->bus->name :"No Bus" , -kobject_name (&dev->kobj));mutex_lock (&dpm_list_mtx);if (dev->parent) {if (dev->parent->power.status >=DPM_SUSPENDING )
@@ -97,24 +85,19 @@ void device_pm_add(struct device *dev)*/ dev_WARN (dev,"Parentless device registered during a PM transaction\n" ); } -list_add_tail (&dev->power.entry, &dpm_list);mutex_unlock (&dpm_list_mtx); }/** -* device_pm_remove - Remove a device from the PM core's list of active devices. +* device_pm_list_remove - Remove a device from the PM core's list of devices. * @dev: Device to be removed from the list. */ -void device_pm_remove (struct device *dev) +void device_pm_list_remove (struct device *dev) { -pr_debug ("PM: Removing info for %s:%s\n" , - dev->bus ? dev->bus->name :"No Bus" , -kobject_name (&dev->kobj));mutex_lock (&dpm_list_mtx);list_del_init (&dev->power.entry);mutex_unlock (&dpm_list_mtx); -pm_runtime_remove (dev); }/**
drivers/base/power/common.c new file +279 -0
279 lines not on this page · raw message
drivers/acpi/glue.c +3 -0
@@ -11,6 +11,7 @@#include <linux/device.h> #include <linux/rwsem.h> #include <linux/acpi.h> +#include <linux/pm_link.h> #define ACPI_GLUE_DEBUG 0#if ACPI_GLUE_DEBUG
@@ -170,6 +171,7 @@ static int acpi_bind_one(struct device *device_set_wakeup_enable (dev, acpi_dev->wakeup.state.enabled); } +pm_link_add (dev, &acpi_dev->dev); }return 0 ;
@@ -189,6 +191,7 @@ static int acpi_unbind_one(struct device &acpi_dev)) {sysfs_remove_link (&dev->kobj,"firmware_node" );sysfs_remove_link (&acpi_dev->dev.kobj,"physical_node" ); +pm_link_remove (dev, &acpi_dev->dev); } acpi_detach_data(dev->archdata.acpi_handle,
PATCH 2/6 PM: Asynchronous resume of devices
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Theoretically, the total time of system sleep transitions (suspend to RAM, hibernation) can be reduced by running suspend and resume callbacks of device drivers in parallel with each other. However, there are dependencies between devices such that, for example, we may not be allowed to put one device into a low power state before anohter one has been suspended (e.g. we cannot suspend a bridge before suspending all devices behind it). In particular, we're not allowed to suspend the parent of a device before suspending the device itself. Analogously, we're not allowed to resume a device before resuming its parent.
Thus, to make it possible to execute suspend and resume callbacks provided by device drivers in parallel with each other, we need to provide a synchronization mechanism preventing the dependencies between devices from being violated.
The patch below allows some devices to be resumed asynchronously, with the following rules:
(1) There is a wait queue head, dev->power.wait_queue, and an
"operation complete" flag, dev->power.op_complete for each device
object.(2) All of the power.op_complete flags are reset before suspend as well as after each resume stage (dpm_resume_noirq(), dpm_resume()).
(3) If power.async_suspend is set for dev or for one of devices it depends on, the PM core waits for the "master" device's power.op_complete flag to be set before attempting to run the resume callbacks, appropriate for this particular stage of resume, for dev.
(4) dev->power.op_complete is set for each device after running its resume callbacks at each stage of resume (dpm_resume_noirq(), dpm_resume()) and all threads waiting in the devices wait queue are woken up.
With this mechanism in place, the drivers wanting their resume callbacks to be executed asynchronously can set dev->power.async_suspend for them, with the help of device_enable_async_suspend(). In addition to that, the PM off-tree dependencies between devices have to be represented by 'struct pm_link' objects introduced by the previous patch.
In this version of the patch the async threads started to execute the resume callbacks of specific device don't exit immediately having done that, but search dpm_list for devices whose PM dependencies have already been satisfied and execute their callbacks without waiting.
--- drivers/base/power/common.c | 5 drivers/base/power/main.c | 327 ++++++++++++++++++++++++++++++++++++++++++-- include/linux/device.h | 6 include/linux/pm.h | 6 4 files changed, 329 insertions(+), 15 deletions(-)
include/linux/pm.h +5 -1
@@ -26,6 +26,7 @@#include <linux/spinlock.h> #include <linux/wait.h> #include <linux/timer.h> +#include <linux/completion.h> /* * Callbacks for platform drivers to implement.
@@ -409,20 +410,23 @@ enum rpm_request {struct dev_pm_info {spinlock_t lock; +wait_queue_head_t wait_queue;struct list_head master_links;struct list_head slave_links;pm_message_t power_state;unsigned int can_wakeup:1 ;unsigned int should_wakeup:1 ; +unsigned int async_suspend:1 ;enum dpm_state status;/* Owned by the PM core */ #ifdef CONFIG_PM_SLEEP struct list_head entry; +unsigned int op_started:1 ; +unsigned int op_complete:1 ;#endif #ifdef CONFIG_PM_RUNTIME struct timer_list suspend_timer;unsigned long timer_expires;struct work_struct work; -wait_queue_head_t wait_queue;atomic_t usage_count;atomic_t child_count;unsigned int disable_depth:3 ;
include/linux/device.h +6 -0
@@ -472,6 +472,12 @@ static inline int device_is_registered(sreturn dev->kobj.state_in_sysfs; } +static inline void device_enable_async_suspend (struct device *dev,bool enable) +{ +if (dev->power.status ==DPM_ON ) + dev->power.async_suspend = enable; +} +void driver_init (void );/*
drivers/base/power/main.c +314 -13
436 lines not on this page · raw message
drivers/base/power/common.c +4 -1
@@ -19,10 +19,11 @@*/ void device_pm_init (struct device *dev) { - dev->power.status =DPM_ON ;spin_lock_init (&dev->power.lock); +init_waitqueue_head (&dev->power.wait_queue);INIT_LIST_HEAD (&dev->power.master_links);INIT_LIST_HEAD (&dev->power.slave_links); + dev->power.status =DPM_ON ;pm_runtime_init (dev); }
@@ -117,6 +118,8 @@ int pm_link_add(struct device *slave, streturn 0 ; err_link: + master->power.async_suspend = false; + slave->power.async_suspend = false; error = -ENOMEM ;put_device (slave);
PATCH 2/6 PM: Asynchronous resume of devices
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Theoretically, the total time of system sleep transitions (suspend to RAM, hibernation) can be reduced by running suspend and resume callbacks of device drivers in parallel with each other. However, there are dependencies between devices such that, for example, we may not be allowed to put one device into a low power state before anohter one has been suspended (e.g. we cannot suspend a bridge before suspending all devices behind it). In particular, we're not allowed to suspend the parent of a device before suspending the device itself. Analogously, we're not allowed to resume a device before resuming its parent.
Thus, to make it possible to execute suspend and resume callbacks provided by device drivers in parallel with each other, we need to provide a synchronization mechanism preventing the dependencies between devices from being violated.
The patch below allows some devices to be resumed asynchronously, with the following rules:
(1) There is a wait queue head, dev->power.wait_queue, and an
"operation complete" flag, dev->power.op_complete for each device
object.(2) All of the power.op_complete flags are reset before suspend as well as after each resume stage (dpm_resume_noirq(), dpm_resume()).
(3) If power.async_suspend is set for dev or for one of devices it depends on, the PM core waits for the "master" device's power.op_complete flag to be set before attempting to run the resume callbacks, appropriate for this particular stage of resume, for dev.
(4) dev->power.op_complete is set for each device after running its resume callbacks at each stage of resume (dpm_resume_noirq(), dpm_resume()) and all threads waiting in the devices wait queue are woken up.
With this mechanism in place, the drivers wanting their resume callbacks to be executed asynchronously can set dev->power.async_suspend for them, with the help of device_enable_async_suspend(). In addition to that, the PM off-tree dependencies between devices have to be represented by 'struct pm_link' objects introduced by the previous patch.
In this version of the patch the async threads started to execute the resume callbacks of specific device don't exit immediately having done that, but search dpm_list for devices whose PM dependencies have already been satisfied and execute their callbacks without waiting.
--- drivers/base/power/common.c | 5 drivers/base/power/main.c | 327 ++++++++++++++++++++++++++++++++++++++++++-- include/linux/device.h | 6 include/linux/pm.h | 6 4 files changed, 329 insertions(+), 15 deletions(-)
include/linux/pm.h +5 -1
@@ -26,6 +26,7 @@#include <linux/spinlock.h> #include <linux/wait.h> #include <linux/timer.h> +#include <linux/completion.h> /* * Callbacks for platform drivers to implement.
@@ -409,20 +410,23 @@ enum rpm_request {struct dev_pm_info {spinlock_t lock; +wait_queue_head_t wait_queue;struct list_head master_links;struct list_head slave_links;pm_message_t power_state;unsigned int can_wakeup:1 ;unsigned int should_wakeup:1 ; +unsigned int async_suspend:1 ;enum dpm_state status;/* Owned by the PM core */ #ifdef CONFIG_PM_SLEEP struct list_head entry; +unsigned int op_started:1 ; +unsigned int op_complete:1 ;#endif #ifdef CONFIG_PM_RUNTIME struct timer_list suspend_timer;unsigned long timer_expires;struct work_struct work; -wait_queue_head_t wait_queue;atomic_t usage_count;atomic_t child_count;unsigned int disable_depth:3 ;
include/linux/device.h +6 -0
@@ -472,6 +472,12 @@ static inline int device_is_registered(sreturn dev->kobj.state_in_sysfs; } +static inline void device_enable_async_suspend (struct device *dev,bool enable) +{ +if (dev->power.status ==DPM_ON ) + dev->power.async_suspend = enable; +} +void driver_init (void );/*
drivers/base/power/main.c +314 -13
436 lines not on this page · raw message
drivers/base/power/common.c +4 -1
@@ -19,10 +19,11 @@*/ void device_pm_init (struct device *dev) { - dev->power.status =DPM_ON ;spin_lock_init (&dev->power.lock); +init_waitqueue_head (&dev->power.wait_queue);INIT_LIST_HEAD (&dev->power.master_links);INIT_LIST_HEAD (&dev->power.slave_links); + dev->power.status =DPM_ON ;pm_runtime_init (dev); }
@@ -117,6 +118,8 @@ int pm_link_add(struct device *slave, streturn 0 ; err_link: + master->power.async_suspend = false; + slave->power.async_suspend = false; error = -ENOMEM ;put_device (slave);
Re: [PATCH 2/6] PM: Asynchronous resume of devices
Quoted 13 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Theoretically, the total time of system sleep transitions (suspend to RAM, hibernation) can be reduced by running suspend and resume callbacks of device drivers in parallel with each other. However, there are dependencies between devices such that, for example, we may not be allowed to put one device into a low power state before anohter one has been suspended (e.g. we cannot suspend a bridge before suspending all devices behind it). In particular, we're not allowed to suspend the parent of a device before suspending the device itself. Analogously, we're not allowed to resume a device before resuming its parent.
Quoted 4 lines
In this version of the patch the async threads started to execute the resume callbacks of specific device don't exit immediately having done that, but search
dpm_listfor devices whose PM dependencies have already been satisfied and execute their callbacks without waiting.
Given this design, why bother to invoke device_resume() for the async devices? Why not just start up a bunch of async threads, each of which calls async_resume() repeatedly until everything is finished? (And rearrange async_resume() to scan the list first and do the actual resume second.)
The same goes for the noirq versions.
Alan Stern
Re: [PATCH 2/6] PM: Asynchronous resume of devices
Quoted 20 lines · Alan Stern
Quoted 13 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Theoretically, the total time of system sleep transitions (suspend to RAM, hibernation) can be reduced by running suspend and resume callbacks of device drivers in parallel with each other. However, there are dependencies between devices such that, for example, we may not be allowed to put one device into a low power state before anohter one has been suspended (e.g. we cannot suspend a bridge before suspending all devices behind it). In particular, we're not allowed to suspend the parent of a device before suspending the device itself. Analogously, we're not allowed to resume a device before resuming its parent.
Quoted 4 lines
In this version of the patch the async threads started to execute the resume callbacks of specific device don't exit immediately having done that, but search
dpm_listfor devices whose PM dependencies have already been satisfied and execute their callbacks without waiting.
Quoted 7 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to start
- when to start them
- stop condition
I had a few ideas, but then I thought it would be simpler to start an async thread when we know there's some async work to do (ie. there's an async device to handle) and make each async thread browse the list just once (the rationale is that we've just handled a device, so there's a chance there are some devices with satisfied dependencies down the list).
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
Quoted 10 lines · Rafael J. Wysocki
Quoted 7 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to start
That's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
I would expect that a reasonably small number of threads would suffice to achieve most of the possible time savings. Something on the order of 10 should work well. If the majority of the time is spent handling N devices then N+1 threads would be enough. Judging from some of the comments posted earlier, even 4 threads would give a big advantage.
Quoted 1 line
- when to start them
You might as well start them at the beginning of dpm_resume and dpm_resume_noirq. That way they can overlap with the synchronous operations.
Quoted 1 line
- stop condition
When an error occurs or when op_started has been set for every remaining async device.
Quoted 5 lines
I had a few ideas, but then I thought it would be simpler to start an async thread when we know there's some async work to do (ie. there's an async device to handle) and make each async thread browse the list just once (the rationale is that we've just handled a device, so there's a chance there are some devices with satisfied dependencies down the list).
It comes down to this: Should there be many threads, each of which browses the list only once, or should there be a few threads, each of which browses the list many times?
Alan Stern
Re: [PATCH 2/6] PM: Asynchronous resume of devices
Quoted 8 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Quoted 6 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to startThat's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
First, the async framework will not start more than MAX_THREADS threads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.
Second, no new async threads are started while the main thread is handling the sync devices , so the existing threads have a chance to do their job. If there's a "cluster" of sync devices in dpm_list, the number of async threads running is likely to drop rapidly while those devices are being handled. (BTW, if there were no sync devices, the whole thing would be much simpler, but I don't think it's realistic to assume we'll be able to get rid of them any time soon).
Finally, but not least importantly, async threads are not started for the async devices that were previously handled "out of order" by the already running async threads (or by async threads that have already finished). My testing shows that there are quite a few of them on the average. For example, on the HP nx6325 typically there are as many as 580 async devices handled "out of order" during a _single_ suspend-resume cycle (including the "early" and "late" phases), while only a few (below 10) devices are waited for by at least one async thread.
I can try to monitor the number of asyn threads started if you're interested.
Quoted 6 lines
I would expect that a reasonably small number of threads would suffice to achieve most of the possible time savings. Something on the order of 10 should work well. If the majority of the time is spent handling N devices then N+1 threads would be enough. Judging from some of the comments posted earlier, even 4 threads would give a big advantage.
That unfortunately is not the case with the set of async devices including PCI, ACPI and serio devices only. The average time savings are between 5% to 14%, depending on the system and the phase of the cycle (the relative savings are typically greater for suspend). Still, that amounts to .5 s in some cases.
Quoted 5 lines
Quoted 1 line
- when to start them
You might as well start them at the beginning of
dpm_resumeanddpm_resume_noirq. That way they can overlap with the synchronous operations.
In that case they would have to wait in the beginning, so I'd need a mechanism to wake them up.
Alternatively, there could be a limit to the number of async threads started within the current design, but I'd prefer to leave that to the async framework (namely, if MAX_THREADS makes sense for boot, it's also likely to make sense for PM).
Quoted 4 lines
Quoted 1 line
- stop condition
When an error occurs or when
op_startedhas been set for every remaining async device.
Yeah, that's the easy one. :-)
Quoted 9 lines
Quoted 5 lines
I had a few ideas, but then I thought it would be simpler to start an async thread when we know there's some async work to do (ie. there's an async device to handle) and make each async thread browse the list just once (the rationale is that we've just handled a device, so there's a chance there are some devices with satisfied dependencies down the list).
It comes down to this: Should there be many threads, each of which browses the list only once, or should there be a few threads, each of which browses the list many times?
Well, quite obviously I prefer the many threads version. :-)
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
Quoted 40 lines · Rafael J. Wysocki
Quoted 8 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Quoted 6 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)Quoted 7 lines
Quoted 4 lines
Quoted 1 line
The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to startThat's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
First, the async framework will not start more than
MAX_THREADSthreads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.Second, no new async threads are started while the main thread is handling the sync devices , so the existing threads have a chance to do their job. If there's a "cluster" of sync devices in
dpm_list, the number of async threads running is likely to drop rapidly while those devices are being handled. (BTW, if there were no sync devices, the whole thing would be much simpler, but I don't think it's realistic to assume we'll be able to get rid of them any time soon).Finally, but not least importantly, async threads are not started for the async devices that were previously handled "out of order" by the already running async threads (or by async threads that have already finished). My testing shows that there are quite a few of them on the average. For example, on the HP nx6325 typically there are as many as 580 async devices handled "out of order" during a _single_ suspend-resume cycle (including the "early" and "late" phases), while only a few (below 10) devices are waited for by at least one async thread.
Quoted 1 line
I can try to monitor the number of asyn threads started if you're interested.
I did that out of curiousity. To be precise, I applied the appended patch on top of the previous ones and the maximum number of scheduled async operations I've got so far is 14 (strangely enough, during "late" suspend).
So, that's not like there are hundreds of async threads being started.
Thanks,
Rafael
--- drivers/base/power/main.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/base/power/main.c +9 -0
@@ -53,6 +53,8 @@ static pm_message_t pm_transition;*/ static bool transition_started; +static unsigned int nr_async; +/** * device_pm_lock - Lock the list of active devices used by the PM core. */
@@ -166,6 +168,8 @@ static void dpm_reset_all(void)list_for_each_entry (dev, &dpm_list, power.entry)dpm_reset (dev); +printk (KERN_INFO "PM: Scheduled %d async operations\n" , nr_async); + nr_async =0 ; }/**
@@ -563,6 +567,7 @@ static int device_resume_noirq(struct deif (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {async_schedule (async_resume_noirq, dev); + nr_async++;return 0 ; }
@@ -723,6 +728,7 @@ static int device_resume(struct device *if (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_resume, dev); + nr_async++;return 0 ; }
@@ -968,6 +974,7 @@ static int device_suspend_noirq(struct dif (pm_async_enabled && dev->power.async_suspend) {async_schedule (async_suspend_noirq, dev); + nr_async++;return 0 ; }
@@ -1143,6 +1150,7 @@ static int device_suspend(struct deviceif (pm_async_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_suspend, dev); + nr_async++;return 0 ; }
@@ -1247,6 +1255,7 @@ static int dpm_prepare(pm_message_t statmutex_lock (&dpm_list_mtx); transition_started = true;atomic_set (&async_error,0 ); + nr_async =0 ;while (!list_empty (&dpm_list)) {struct device *dev =to_device (dpm_list.next);
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiQuoted 40 lines · Rafael J. Wysocki
Quoted 8 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Quoted 6 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)Quoted 7 lines
Quoted 4 lines
Quoted 1 line
The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to startThat's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
First, the async framework will not start more than
MAX_THREADSthreads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.Second, no new async threads are started while the main thread is handling the sync devices , so the existing threads have a chance to do their job. If there's a "cluster" of sync devices in
dpm_list, the number of async threads running is likely to drop rapidly while those devices are being handled. (BTW, if there were no sync devices, the whole thing would be much simpler, but I don't think it's realistic to assume we'll be able to get rid of them any time soon).Finally, but not least importantly, async threads are not started for the async devices that were previously handled "out of order" by the already running async threads (or by async threads that have already finished). My testing shows that there are quite a few of them on the average. For example, on the HP nx6325 typically there are as many as 580 async devices handled "out of order" during a _single_ suspend-resume cycle (including the "early" and "late" phases), while only a few (below 10) devices are waited for by at least one async thread.
Quoted 1 line
I can try to monitor the number of asyn threads started if you're interested.
I did that out of curiousity. To be precise, I applied the appended patch on top of the previous ones and the maximum number of scheduled async operations I've got so far is 14 (strangely enough, during "late" suspend).
So, that's not like there are hundreds of async threads being started.
Thanks,
Rafael
--- drivers/base/power/main.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/base/power/main.c +9 -0
@@ -53,6 +53,8 @@ static pm_message_t pm_transition;*/ static bool transition_started; +static unsigned int nr_async; +/** * device_pm_lock - Lock the list of active devices used by the PM core. */
@@ -166,6 +168,8 @@ static void dpm_reset_all(void)list_for_each_entry (dev, &dpm_list, power.entry)dpm_reset (dev); +printk (KERN_INFO "PM: Scheduled %d async operations\n" , nr_async); + nr_async =0 ; }/**
@@ -563,6 +567,7 @@ static int device_resume_noirq(struct deif (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {async_schedule (async_resume_noirq, dev); + nr_async++;return 0 ; }
@@ -723,6 +728,7 @@ static int device_resume(struct device *if (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_resume, dev); + nr_async++;return 0 ; }
@@ -968,6 +974,7 @@ static int device_suspend_noirq(struct dif (pm_async_enabled && dev->power.async_suspend) {async_schedule (async_suspend_noirq, dev); + nr_async++;return 0 ; }
@@ -1143,6 +1150,7 @@ static int device_suspend(struct deviceif (pm_async_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_suspend, dev); + nr_async++;return 0 ; }
@@ -1247,6 +1255,7 @@ static int dpm_prepare(pm_message_t statmutex_lock (&dpm_list_mtx); transition_started = true;atomic_set (&async_error,0 ); + nr_async =0 ;while (!list_empty (&dpm_list)) {struct device *dev =to_device (dpm_list.next);
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiQuoted 14 lines · Rafael J. Wysocki
Quoted 13 lines · Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 7 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to start
Quoted 8 lines
Quoted 2 lines
That's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
First, the async framework will not start more than
MAX_THREADSthreads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.
Okay, but MAX_THREADS isn't under your control. Remember also that each thread takes up some memory, and during hibernation we are in a memory-constrained situation.
Quoted 7 lines
Second, no new async threads are started while the main thread is handling the sync devices , so the existing threads have a chance to do their job. If there's a "cluster" of sync devices in
dpm_list, the number of async threads running is likely to drop rapidly while those devices are being handled. (BTW, if there were no sync devices, the whole thing would be much simpler, but I don't think it's realistic to assume we'll be able to get rid of them any time soon).
Perhaps not, but it would be interesting to see what happens if every device is async. Maybe you can try it and get a meaningful result.
Quoted 8 lines
Finally, but not least importantly, async threads are not started for the async devices that were previously handled "out of order" by the already running async threads (or by async threads that have already finished). My testing shows that there are quite a few of them on the average. For example, on the HP nx6325 typically there are as many as 580 async devices handled "out of order" during a _single_ suspend-resume cycle (including the "early" and "late" phases), while only a few (below 10) devices are waited for by at least one async thread.
That is a difficult sort of thing to know in advance. It ought to be highly influenced by the percentage of async devices; that's another reason for wanting to know what happens when every device is async.
Quoted 11 lines
Quoted 6 lines
I would expect that a reasonably small number of threads would suffice to achieve most of the possible time savings. Something on the order of 10 should work well. If the majority of the time is spent handling N devices then N+1 threads would be enough. Judging from some of the comments posted earlier, even 4 threads would give a big advantage.
That unfortunately is not the case with the set of async devices including PCI, ACPI and serio devices only. The average time savings are between 5% to 14%, depending on the system and the phase of the cycle (the relative savings are typically greater for suspend). Still, that amounts to .5 s in some cases.
Without context it's hard to be sure, but I don't think your numbers contradict what I said. If you get between 5% and 14% time savings with 14 threads, then you might get between 4% and 10% savings with only 4 threads.
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
- when to start them
You might as well start them at the beginning of
dpm_resumeanddpm_resume_noirq. That way they can overlap with the synchronous operations.In that case they would have to wait in the beginning, so I'd need a mechanism to wake them up.
You already have two such mechanisms: dpm_list_mtx and the embedded wait_queue_heads. Although in the scheme I'm proposing, no async threads would ever have to wait on a per-device waitqueue. A system-wide waitqueue might work out better (for use when a thread reaches the end of the list and then waits before starting over at the beginning).
Quoted 4 lines
Alternatively, there could be a limit to the number of async threads started within the current design, but I'd prefer to leave that to the async framework (namely, if
MAX_THREADSmakes sense for boot, it's also likely to make sense for PM).
Strictly speaking, a new thread should be started only when needed. That is, only when all the existing threads are busy running a callback. It shouldn't be too hard to keep track of when that happens.
Quoted 5 lines
Quoted 3 lines
It comes down to this: Should there be many threads, each of which browses the list only once, or should there be a few threads, each of which browses the list many times?
Well, quite obviously I prefer the many threads version. :-)
Okay, clearly it's a matter of taste. To me the many-threads version seems less elegant and less well controlled.
Alan Stern
Re: [PATCH 2/6] PM: Asynchronous resume of devices
Quoted 21 lines · Alan Stern
Quoted 19 lines · Rafael J. Wysocki
Quoted 8 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Quoted 6 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)Quoted 7 lines
Quoted 4 lines
Quoted 1 line
The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to startThat's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
Quoted 7 lines
Quoted 3 lines
First, the async framework will not start more than
MAX_THREADSthreads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.Okay, but
MAX_THREADSisn't under your control. Remember also that each thread takes up some memory, and during hibernation we are in a memory-constrained situation.
We keep some extra free memory for things like this. It's not likely to be exhausted by the async threads alone.
Quoted 10 lines
Quoted 7 lines
Second, no new async threads are started while the main thread is handling the sync devices , so the existing threads have a chance to do their job. If there's a "cluster" of sync devices in
dpm_list, the number of async threads running is likely to drop rapidly while those devices are being handled. (BTW, if there were no sync devices, the whole thing would be much simpler, but I don't think it's realistic to assume we'll be able to get rid of them any time soon).Perhaps not, but it would be interesting to see what happens if every device is async. Maybe you can try it and get a meaningful result.
I could if it didn't crash. Perhaps I can with init=/bin/bash.
Quoted 9 lines
Quoted 8 lines
Finally, but not least importantly, async threads are not started for the async devices that were previously handled "out of order" by the already running async threads (or by async threads that have already finished). My testing shows that there are quite a few of them on the average. For example, on the HP nx6325 typically there are as many as 580 async devices handled "out of order" during a _single_ suspend-resume cycle (including the "early" and "late" phases), while only a few (below 10) devices are waited for by at least one async thread.
Quoted 3 lines
That is a difficult sort of thing to know in advance. It ought to be highly influenced by the percentage of async devices; that's another reason for wanting to know what happens when every device is async.
There are a few factors beyond our direct control influencing this, like for example how much time it takes to handle each individual device (that may vary from .5 s to microseconds AFAICS).
In addition to this not only the percentage, but also the distribution of sync devices in dpm_list has an effect. For example, the situations where every second device is sync and where the first half of devices are async and the other is sync would lead to different kinds of behavior.
Quoted 12 lines
Quoted 11 lines
Quoted 6 lines
I would expect that a reasonably small number of threads would suffice to achieve most of the possible time savings. Something on the order of 10 should work well. If the majority of the time is spent handling N devices then N+1 threads would be enough. Judging from some of the comments posted earlier, even 4 threads would give a big advantage.
That unfortunately is not the case with the set of async devices including PCI, ACPI and serio devices only. The average time savings are between 5% to 14%, depending on the system and the phase of the cycle (the relative savings are typically greater for suspend). Still, that amounts to .5 s in some cases.
Quoted 4 lines
Without context it's hard to be sure, but I don't think your numbers contradict what I said. If you get between 5% and 14% time savings with 14 threads, then you might get between 4% and 10% savings with only 4 threads.
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 9 lines
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
- when to start them
You might as well start them at the beginning of
dpm_resumeanddpm_resume_noirq. That way they can overlap with the synchronous operations.In that case they would have to wait in the beginning, so I'd need a mechanism to wake them up.
Quoted 6 lines
You already have two such mechanisms:
dpm_list_mtxand the embeddedwait_queue_heads. Although in the scheme I'm proposing, no async threads would ever have to wait on a per-device waitqueue. A system-wide waitqueue might work out better (for use when a thread reaches the end of the list and then waits before starting over at the beginning).
However, sync devices may depend on the async ones and they need the per-device wait queues for this purpose.
Quoted 8 lines
Quoted 4 lines
Alternatively, there could be a limit to the number of async threads started within the current design, but I'd prefer to leave that to the async framework (namely, if
MAX_THREADSmakes sense for boot, it's also likely to make sense for PM).Strictly speaking, a new thread should be started only when needed. That is, only when all the existing threads are busy running a callback. It shouldn't be too hard to keep track of when that happens.
The async framework does that for us. :-)
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
Quoted 1 line · Rafael J. Wysocki
On Saturday 29 August 2009, Alan Stern wrote:
...
Quoted 5 lines
Quoted 3 lines
Strictly speaking, a new thread should be started only when needed. That is, only when all the existing threads are busy running a callback. It shouldn't be too hard to keep track of when that happens.
The async framework does that for us. :-)
So, when I said "async threads", I should have rather said "async functions", because one async thread can execute multiple functions scheduled with async_schedule().
Taking that into consideration, it all works like this. When there's a new async device to handle and there are no async threads available, a new thread is started and it runs the async function that handles the device and "opportunistically" searches dpm_list (once) for other device that are ready for handling. After this function has returned, the thread goes to sleep and it only exits after being idle for 1 s. In turn, when there's a new async device to handle and there are async threads available, on of them gets the async function to run.
I don't really think we can do much better than this in any other approach.
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
Quoted 22 lines · Rafael J. Wysocki
Quoted 1 line · Rafael J. Wysocki
On Saturday 29 August 2009, Alan Stern wrote:
...
Quoted 5 lines
Quoted 3 lines
Strictly speaking, a new thread should be started only when needed. That is, only when all the existing threads are busy running a callback. It shouldn't be too hard to keep track of when that happens.
The async framework does that for us. :-)
So, when I said "async threads", I should have rather said "async functions", because one async thread can execute multiple functions scheduled with
async_schedule().Taking that into consideration, it all works like this. When there's a new async device to handle and there are no async threads available, a new thread is started and it runs the async function that handles the device and "opportunistically" searches
dpm_list(once) for other device that are ready for handling. After this function has returned, the thread goes to sleep and it only exits after being idle for 1 s. In turn, when there's a new async device to handle and there are async threads available, on of them gets the async function to run.
Quoted 1 line
I don't really think we can do much better than this in any other approach.
Since it obviously works and is relatively simple, I guess we ought to adopt it.
Alan Stern
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiQuoted 22 lines · Rafael J. Wysocki
Quoted 1 line · Rafael J. Wysocki
On Saturday 29 August 2009, Alan Stern wrote:
...
Quoted 5 lines
Quoted 3 lines
Strictly speaking, a new thread should be started only when needed. That is, only when all the existing threads are busy running a callback. It shouldn't be too hard to keep track of when that happens.
The async framework does that for us. :-)
So, when I said "async threads", I should have rather said "async functions", because one async thread can execute multiple functions scheduled with
async_schedule().Taking that into consideration, it all works like this. When there's a new async device to handle and there are no async threads available, a new thread is started and it runs the async function that handles the device and "opportunistically" searches
dpm_list(once) for other device that are ready for handling. After this function has returned, the thread goes to sleep and it only exits after being idle for 1 s. In turn, when there's a new async device to handle and there are async threads available, on of them gets the async function to run.
Quoted 1 line
I don't really think we can do much better than this in any other approach.
Since it obviously works and is relatively simple, I guess we ought to adopt it.
Alan Stern
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiQuoted 1 line · Rafael J. Wysocki
On Saturday 29 August 2009, Alan Stern wrote:
...
Quoted 5 lines
Quoted 3 lines
Strictly speaking, a new thread should be started only when needed. That is, only when all the existing threads are busy running a callback. It shouldn't be too hard to keep track of when that happens.
The async framework does that for us. :-)
So, when I said "async threads", I should have rather said "async functions", because one async thread can execute multiple functions scheduled with async_schedule().
Taking that into consideration, it all works like this. When there's a new async device to handle and there are no async threads available, a new thread is started and it runs the async function that handles the device and "opportunistically" searches dpm_list (once) for other device that are ready for handling. After this function has returned, the thread goes to sleep and it only exits after being idle for 1 s. In turn, when there's a new async device to handle and there are async threads available, on of them gets the async function to run.
I don't really think we can do much better than this in any other approach.
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiQuoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Alan Stern
Re: [PATCH 2/6] PM: Asynchronous resume of devices
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
The purpose of patch 9 is basically to allow one to see how much time is spent on the handling of devices overall and to compare that with the time spent on the other operation during suspend-resume.
Thanks,
Rafael
PATCH 1/1 PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devices
If verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)
drivers/base/power/main.c +46 -5
@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)
@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),
@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /**
@@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error; }
@@ -639,7 +669,9 @@ EXPORT_SYMBOL_GPL(dpm_resume_noirq);static int __device_resume (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -681,6 +713,7 @@ static int __device_resume(struct devicewake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,NULL );return error; }
@@ -923,6 +956,9 @@ static pm_message_t resume_event(pm_messstatic int __device_suspend_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); + +TIMER_START (timer);if (dev->bus && dev->bus->pm) {pm_dev_dbg (dev, state,"LATE " );
@@ -932,6 +968,7 @@ static int __device_suspend_noirq(struct dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +TIMER_STOP (timer, dev, state,"LATE" );return error; }
@@ -1063,6 +1100,9 @@ EXPORT_SYMBOL_GPL(dpm_suspend_noirq);static int __device_suspend (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); + +TIMER_START (timer);down (&dev->sem);
@@ -1103,6 +1143,7 @@ static int __device_suspend(struct devic dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +TIMER_STOP (timer, dev, state,NULL );return error; }
kernel/power/swsusp.c +1 -1
@@ -169,7 +169,7 @@ int swsusp_swap_in_use(void)void swsusp_show_speed (struct timeval *start,struct timeval *stop,unsigned nr_pages,char *msg) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ;int k;int kps;
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
Quoted 120 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 5 lines
drivers/base/power/main.c +1
-0@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;
Hm, these CPP macros are rather ugly. Why is there a need for the TIMER_DECLARE() wrapper - if a proper inline function is used there's no need for that. There's other all-capitals macros in that code implementing code (and not constants) - is that really justified/clean?
Ingo
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);
Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.
I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Quoted 2 lines
There's other all-capitals macros in that code implementing code (and not constants) - is that really justified/clean?
Do you mean the TRACE_* macros? Please ask Linus about that, they are from him. :-)
Thanks,
Rafael
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
Quoted 134 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.
Quoted 6 lines
I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Quoted 5 lines
Quoted 2 lines
There's other all-capitals macros in that code implementing code (and not constants) - is that really justified/clean?
Do you mean the TRACE_* macros? Please ask Linus about that, they are from him. :-)
hey, Linus is not immune to crap either :) IIRC the TRACE_*() stuff was a quick hack originally to debug some nasty suspend/resume hang. Combined with the DECLARE/START/STOP macros it definitely starts looking a bit ugly. If you find something cleaner looking i doubt Linus will complain. (no biggie if you keep it in place either)
Ingo
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Rafael J. WysockiQuoted 134 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.
Quoted 6 lines
I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Quoted 5 lines
Quoted 2 lines
There's other all-capitals macros in that code implementing code (and not constants) - is that really justified/clean?
Do you mean the TRACE_* macros? Please ask Linus about that, they are from him. :-)
hey, Linus is not immune to crap either :) IIRC the TRACE_*() stuff was a quick hack originally to debug some nasty suspend/resume hang. Combined with the DECLARE/START/STOP macros it definitely starts looking a bit ugly. If you find something cleaner looking i doubt Linus will complain. (no biggie if you keep it in place either)
Ingo
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
Quoted 141 lines · Ingo Molnar
Quoted 138 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be
(a) confusing (why define a variable you don't use) and (b) wasteful.
Perhaps the names of the macros should be directly related to debugging?
Best,
Rafael
PATCH 1/1 PM: Measure suspend and resume times for individual devices
Quoted 1 line · Rafael J. Wysocki
On Monday 31 August 2009, Ingo Molnar wrote:
...
Quoted 15 lines
Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Quoted 8 lines
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be
(a) confusing (why define a variable you don't use) and (b) wasteful.Perhaps the names of the macros should be directly related to debugging?
Something like in the patch below, maybe?
Best,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devices
If verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)
drivers/base/power/main.c +46 -5
@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)
@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),
@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define DEBUG_TIMEVAL (tv) struct timeval tv +#define DEBUG_TIME_START (tv) do { \ + do_gettimeofday(&tv); \ + } while (0) +#define DEBUG_TIME_SHOW (tv, dev, state, info) do { \ + device_show_time(&tv, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define DEBUG_TIMEVAL (tv) +#define DEBUG_TIME_START (tv) do { } while (0) +#define DEBUG_TIME_SHOW (tv, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /**
@@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +DEBUG_TIMEVAL (start); +DEBUG_TIME_START (start);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +DEBUG_TIME_SHOW (start, dev, state,"EARLY" );return error; }
@@ -639,7 +669,9 @@ EXPORT_SYMBOL_GPL(dpm_resume_noirq);static int __device_resume (struct device *dev,pm_message_t state) {int error =0 ; +DEBUG_TIMEVAL (start); +DEBUG_TIME_START (start);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -681,6 +713,7 @@ static int __device_resume(struct devicewake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +DEBUG_TIME_SHOW (start, dev, state,NULL );return error; }
@@ -923,6 +956,9 @@ static pm_message_t resume_event(pm_messstatic int __device_suspend_noirq (struct device *dev,pm_message_t state) {int error =0 ; +DEBUG_TIMEVAL (start); + +DEBUG_TIME_START (start);if (dev->bus && dev->bus->pm) {pm_dev_dbg (dev, state,"LATE " );
@@ -932,6 +968,7 @@ static int __device_suspend_noirq(struct dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +DEBUG_TIME_SHOW (start, dev, state,"LATE" );return error; }
@@ -1063,6 +1100,9 @@ EXPORT_SYMBOL_GPL(dpm_suspend_noirq);static int __device_suspend (struct device *dev,pm_message_t state) {int error =0 ; +DEBUG_TIMEVAL (start); + +DEBUG_TIME_START (start);down (&dev->sem);
@@ -1103,6 +1143,7 @@ static int __device_suspend(struct devic dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +DEBUG_TIME_SHOW (start, dev, state,NULL );return error; }
kernel/power/swsusp.c +1 -1
@@ -169,7 +169,7 @@ int swsusp_swap_in_use(void)void swsusp_show_speed (struct timeval *start,struct timeval *stop,unsigned nr_pages,char *msg) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ;int k;int kps;
PATCH 1/1 PM: Measure suspend and resume times for individual devices
In reply to Rafael J. WysockiQuoted 1 line · Rafael J. Wysocki
On Monday 31 August 2009, Ingo Molnar wrote:
...
Quoted 15 lines
Quoted 8 lines
Quoted 8 lines
Quoted 3 lines
Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Quoted 8 lines
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be
(a) confusing (why define a variable you don't use) and (b) wasteful.Perhaps the names of the macros should be directly related to debugging?
Something like in the patch below, maybe?
Best,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devices
If verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)
drivers/base/power/main.c +46 -5
@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)
@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),
@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define DEBUG_TIMEVAL (tv) struct timeval tv +#define DEBUG_TIME_START (tv) do { \ + do_gettimeofday(&tv); \ + } while (0) +#define DEBUG_TIME_SHOW (tv, dev, state, info) do { \ + device_show_time(&tv, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define DEBUG_TIMEVAL (tv) +#define DEBUG_TIME_START (tv) do { } while (0) +#define DEBUG_TIME_SHOW (tv, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /**
@@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +DEBUG_TIMEVAL (start); +DEBUG_TIME_START (start);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +DEBUG_TIME_SHOW (start, dev, state,"EARLY" );return error; }
@@ -639,7 +669,9 @@ EXPORT_SYMBOL_GPL(dpm_resume_noirq);static int __device_resume (struct device *dev,pm_message_t state) {int error =0 ; +DEBUG_TIMEVAL (start); +DEBUG_TIME_START (start);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -681,6 +713,7 @@ static int __device_resume(struct devicewake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +DEBUG_TIME_SHOW (start, dev, state,NULL );return error; }
@@ -923,6 +956,9 @@ static pm_message_t resume_event(pm_messstatic int __device_suspend_noirq (struct device *dev,pm_message_t state) {int error =0 ; +DEBUG_TIMEVAL (start); + +DEBUG_TIME_START (start);if (dev->bus && dev->bus->pm) {pm_dev_dbg (dev, state,"LATE " );
@@ -932,6 +968,7 @@ static int __device_suspend_noirq(struct dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +DEBUG_TIME_SHOW (start, dev, state,"LATE" );return error; }
@@ -1063,6 +1100,9 @@ EXPORT_SYMBOL_GPL(dpm_suspend_noirq);static int __device_suspend (struct device *dev,pm_message_t state) {int error =0 ; +DEBUG_TIMEVAL (start); + +DEBUG_TIME_START (start);down (&dev->sem);
@@ -1103,6 +1143,7 @@ static int __device_suspend(struct devic dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +DEBUG_TIME_SHOW (start, dev, state,NULL );return error; }
kernel/power/swsusp.c +1 -1
@@ -169,7 +169,7 @@ int swsusp_swap_in_use(void)void swsusp_show_speed (struct timeval *start,struct timeval *stop,unsigned nr_pages,char *msg) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ;int k;int kps;
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Rafael J. WysockiQuoted 149 lines · Rafael J. Wysocki
Quoted 141 lines · Ingo Molnar
Quoted 138 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Quoted 7 lines
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be (a) confusing (why define a variable you don't use) and (b) wasteful.
Well the compiler will eliminate them, doesnt it?
Ingo
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
Quoted 155 lines · Ingo Molnar
Quoted 152 lines · Rafael J. Wysocki
Quoted 141 lines · Ingo Molnar
Quoted 138 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Quoted 6 lines
Quoted 4 lines
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be (a) confusing (why define a variable you don't use) and (b) wasteful.
Well the compiler will eliminate them, doesnt it?
If the compiler is guaranteed to eliminate them (without generating a "variable defined but not used" warning for that matter), then they can be always defined.
Thanks,
Rafael
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
Quoted 161 lines · Rafael J. Wysocki
Quoted 160 lines · Ingo Molnar
Quoted 156 lines · Rafael J. Wysocki
Quoted 141 lines · Ingo Molnar
Quoted 138 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be (a) confusing (why define a variable you don't use) and (b) wasteful.
Quoted 5 lines
Quoted 1 line
Well the compiler will eliminate them, doesnt it?
If the compiler is guaranteed to eliminate them (without generating a "variable defined but not used" warning for that matter), then they can be always defined.
Why should such a warning be generated if what makes use of it is an inline function?
(it would be generated if it's a macro.)
Ingo
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Rafael J. WysockiQuoted 161 lines · Rafael J. Wysocki
Quoted 160 lines · Ingo Molnar
Quoted 156 lines · Rafael J. Wysocki
Quoted 141 lines · Ingo Molnar
Quoted 138 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be (a) confusing (why define a variable you don't use) and (b) wasteful.
Quoted 5 lines
Quoted 1 line
Well the compiler will eliminate them, doesnt it?
If the compiler is guaranteed to eliminate them (without generating a "variable defined but not used" warning for that matter), then they can be always defined.
Why should such a warning be generated if what makes use of it is an inline function?
(it would be generated if it's a macro.)
Ingo
PATCH 1/1 PM: Measure suspend and resume times for individual devices
Quoted 166 lines · Ingo Molnar
Quoted 163 lines · Rafael J. Wysocki
Quoted 161 lines · Ingo Molnar
Quoted 149 lines · Rafael J. Wysocki
Quoted 141 lines · Ingo Molnar
Quoted 138 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Quoted 7 lines
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be (a) confusing (why define a variable you don't use) and (b) wasteful.
Well the compiler will eliminate them, doesnt it?
Quoted 8 lines
Quoted 3 lines
If the compiler is guaranteed to eliminate them (without generating a "variable defined but not used" warning for that matter), then they can be always defined.
Why should such a warning be generated if what makes use of it is an inline function?
(it would be generated if it's a macro.)
OK, updated patch is appended.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devices
If verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
--- drivers/base/power/main.c | 50 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 46 insertions(+), 6 deletions(-)
drivers/base/power/main.c +45 -5
@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)
@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),
@@ -497,6 +497,32 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void dbg_get_time (struct timeval *start) +{ +do_gettimeofday (start); +} + +static void dbg_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#else /* !DEBUG */ +static void dbg_get_time (struct timeval *start) {} +static void dbg_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) {} + +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /**
@@ -510,7 +536,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +struct timeval start; +dbg_get_time (&start);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -523,6 +551,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +dbg_show_time (&start, dev, state,"EARLY" );return error; }
@@ -639,7 +668,9 @@ EXPORT_SYMBOL_GPL(dpm_resume_noirq);static int __device_resume (struct device *dev,pm_message_t state) {int error =0 ; +struct timeval start; +dbg_get_time (&start);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -681,6 +712,7 @@ static int __device_resume(struct devicewake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +dbg_show_time (&start, dev, state,NULL );return error; }
@@ -923,6 +955,9 @@ static pm_message_t resume_event(pm_messstatic int __device_suspend_noirq (struct device *dev,pm_message_t state) {int error =0 ; +struct timeval start; + +dbg_get_time (&start);if (dev->bus && dev->bus->pm) {pm_dev_dbg (dev, state,"LATE " );
@@ -932,6 +967,7 @@ static int __device_suspend_noirq(struct dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +dbg_show_time (&start, dev, state,"LATE" );return error; }
@@ -1063,6 +1099,9 @@ EXPORT_SYMBOL_GPL(dpm_suspend_noirq);static int __device_suspend (struct device *dev,pm_message_t state) {int error =0 ; +struct timeval start; + +dbg_get_time (&start);down (&dev->sem);
@@ -1103,6 +1142,7 @@ static int __device_suspend(struct devic dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +dbg_show_time (&start, dev, state,NULL );return error; }
kernel/power/swsusp.c +1 -1
@@ -169,7 +169,7 @@ int swsusp_swap_in_use(void)void swsusp_show_speed (struct timeval *start,struct timeval *stop,unsigned nr_pages,char *msg) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ;int k;int kps;
Re: [PATCH 10 update 2x] PM: Measure suspend and resume times for individual devices
Thanks Rafael, the code looks a lot more natural IMHO.
this bit:
drivers/base/power/main.c static void pm_dev_err(struct device *de · line 504
Quoted 5 lines
drivers/base/power/main.c +5
-0+#ifdef DEBUG +static void dbg_get_time (struct timeval *start) +{ +do_gettimeofday (start); +}
is a plain wrapper over gettimeofday so you might want to inline it. (although GCC will do it too most of the time)
Ingo
Re: [PATCH 10 update 2x] PM: Measure suspend and resume times for individual devices
Quoted 5 lines · Ingo Molnar
Thanks Rafael, the code looks a lot more natural IMHO.
this bit:
Quoted 8 lines
drivers/base/power/main.c static void pm_dev_err(struct device *de · line 504
Quoted 5 lines
drivers/base/power/main.c +5
-0+#ifdef DEBUG +static void dbg_get_time (struct timeval *start) +{ +do_gettimeofday (start); +}is a plain wrapper over gettimeofday so you might want to inline it. (although GCC will do it too most of the time)
I have a rule of not using 'inline' except for in headers. In this case, however, it's almost like a header, so I'll follow your suggestion.
Thanks,
Rafael
Re: [PATCH 10 update 2x] PM: Measure suspend and resume times for individual devices
In reply to Ingo MolnarQuoted 5 lines · Ingo Molnar
Thanks Rafael, the code looks a lot more natural IMHO.
this bit:
Quoted 8 lines
drivers/base/power/main.c static void pm_dev_err(struct device *de · line 504
Quoted 5 lines
drivers/base/power/main.c +5
-0+#ifdef DEBUG +static void dbg_get_time (struct timeval *start) +{ +do_gettimeofday (start); +}is a plain wrapper over gettimeofday so you might want to inline it. (although GCC will do it too most of the time)
I have a rule of not using 'inline' except for in headers. In this case, however, it's almost like a header, so I'll follow your suggestion.
Thanks,
Rafael
Re: [PATCH 10 update 2x] PM: Measure suspend and resume times for individual devices
In reply to Rafael J. WysockiThanks Rafael, the code looks a lot more natural IMHO.
this bit:
drivers/base/power/main.c static void pm_dev_err(struct device *de · line 504
Quoted 5 lines
drivers/base/power/main.c +5
-0+#ifdef DEBUG +static void dbg_get_time (struct timeval *start) +{ +do_gettimeofday (start); +}
is a plain wrapper over gettimeofday so you might want to inline it. (although GCC will do it too most of the time)
Ingo
PATCH 1/1 PM: Measure suspend and resume times for individual devices
In reply to Ingo MolnarQuoted 166 lines · Ingo Molnar
Quoted 163 lines · Rafael J. Wysocki
Quoted 161 lines · Ingo Molnar
Quoted 149 lines · Rafael J. Wysocki
Quoted 141 lines · Ingo Molnar
Quoted 138 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Quoted 7 lines
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be (a) confusing (why define a variable you don't use) and (b) wasteful.
Well the compiler will eliminate them, doesnt it?
Quoted 8 lines
Quoted 3 lines
If the compiler is guaranteed to eliminate them (without generating a "variable defined but not used" warning for that matter), then they can be always defined.
Why should such a warning be generated if what makes use of it is an inline function?
(it would be generated if it's a macro.)
OK, updated patch is appended.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devices
If verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
--- drivers/base/power/main.c | 50 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 46 insertions(+), 6 deletions(-)
drivers/base/power/main.c +45 -5
@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)
@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),
@@ -497,6 +497,32 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void dbg_get_time (struct timeval *start) +{ +do_gettimeofday (start); +} + +static void dbg_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#else /* !DEBUG */ +static void dbg_get_time (struct timeval *start) {} +static void dbg_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) {} + +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /**
@@ -510,7 +536,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +struct timeval start; +dbg_get_time (&start);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -523,6 +551,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +dbg_show_time (&start, dev, state,"EARLY" );return error; }
@@ -639,7 +668,9 @@ EXPORT_SYMBOL_GPL(dpm_resume_noirq);static int __device_resume (struct device *dev,pm_message_t state) {int error =0 ; +struct timeval start; +dbg_get_time (&start);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -681,6 +712,7 @@ static int __device_resume(struct devicewake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +dbg_show_time (&start, dev, state,NULL );return error; }
@@ -923,6 +955,9 @@ static pm_message_t resume_event(pm_messstatic int __device_suspend_noirq (struct device *dev,pm_message_t state) {int error =0 ; +struct timeval start; + +dbg_get_time (&start);if (dev->bus && dev->bus->pm) {pm_dev_dbg (dev, state,"LATE " );
@@ -932,6 +967,7 @@ static int __device_suspend_noirq(struct dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +dbg_show_time (&start, dev, state,"LATE" );return error; }
@@ -1063,6 +1099,9 @@ EXPORT_SYMBOL_GPL(dpm_suspend_noirq);static int __device_suspend (struct device *dev,pm_message_t state) {int error =0 ; +struct timeval start; + +dbg_get_time (&start);down (&dev->sem);
@@ -1103,6 +1142,7 @@ static int __device_suspend(struct devic dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +dbg_show_time (&start, dev, state,NULL );return error; }
kernel/power/swsusp.c +1 -1
@@ -169,7 +169,7 @@ int swsusp_swap_in_use(void)void swsusp_show_speed (struct timeval *start,struct timeval *stop,unsigned nr_pages,char *msg) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ;int k;int kps;
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Ingo MolnarQuoted 155 lines · Ingo Molnar
Quoted 152 lines · Rafael J. Wysocki
Quoted 141 lines · Ingo Molnar
Quoted 138 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Quoted 6 lines
Quoted 4 lines
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be (a) confusing (why define a variable you don't use) and (b) wasteful.
Well the compiler will eliminate them, doesnt it?
If the compiler is guaranteed to eliminate them (without generating a "variable defined but not used" warning for that matter), then they can be always defined.
Thanks,
Rafael
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Rafael J. WysockiQuoted 149 lines · Rafael J. Wysocki
Quoted 141 lines · Ingo Molnar
Quoted 138 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Quoted 7 lines
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be (a) confusing (why define a variable you don't use) and (b) wasteful.
Well the compiler will eliminate them, doesnt it?
Ingo
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Ingo MolnarQuoted 141 lines · Ingo Molnar
Quoted 138 lines · Rafael J. Wysocki
Quoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
Quoted 6 lines
Quoted 2 lines
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Well, why not use an inline function like i suggested? [which does nothing in the !enabled case] You can keep the local variable always defined.
Well, I used the macros _exactly_ because I didn't want to keep the local variable always defined.
Now, if you think that adding several useless bytes to the stack frame is OK, perhaps it can be always defined. However, IMnsHO that would be
(a) confusing (why define a variable you don't use) and (b) wasteful.
Perhaps the names of the macros should be directly related to debugging?
Best,
Rafael
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Ingo MolnarQuoted 125 lines · Ingo Molnar
Quoted 122 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);
Quoted 7 lines
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 3 lines
drivers/base/power/main.c +1
-0TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;Hm, these CPP macros are rather ugly. Why is there a need for the
TIMER_DECLARE()wrapper - if a proper inline function is used there's no need for that.
I need a variable to be declared so that I can save the "start" timestamp in it. I don't need the variable if DEBUG is unset, though.
How would you do that without using a macro? Or #ifdef #endif block that would be uglier IMO (which is why I didn't do that)?
Quoted 2 lines
There's other all-capitals macros in that code implementing code (and not constants) - is that really justified/clean?
Do you mean the TRACE_* macros? Please ask Linus about that, they are from him. :-)
Thanks,
Rafael
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Rafael J. WysockiQuoted 120 lines · Rafael J. Wysocki
Quoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devicesIf verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)Index:
linux-2.6/drivers/base/power/main.c
===================================================================drivers/base/power/main.c +34
-5@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /** @@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );
drivers/base/power/main.c static int __device_resume_noirq(struct · line 556
Quoted 5 lines
drivers/base/power/main.c +1
-0@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error;
Hm, these CPP macros are rather ugly. Why is there a need for the TIMER_DECLARE() wrapper - if a proper inline function is used there's no need for that. There's other all-capitals macros in that code implementing code (and not constants) - is that really justified/clean?
Ingo
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Rafael J. WysockiQuoted 11 lines · Rafael J. Wysocki
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
Does that include the start timestamps? I don't see them anywhere in the patch. Without the start timestamps we have no way to know how much time was spent waiting for dpm_list_mtx or other resources as opposed to actually carrying out the operation.
Alan Stern
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
Quoted 11 lines · Alan Stern
Quoted 9 lines · Rafael J. Wysocki
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
Quoted 7 lines
Quoted 2 lines
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
Does that include the start timestamps? I don't see them anywhere in the patch. Without the start timestamps we have no way to know how much time was spent waiting for
dpm_list_mtxor other resources as opposed to actually carrying out the operation.
If the callback in question is actually defined, there will be additional debug printouts before executing it from which we can get the start timestamps.
If the callback is not defined, the time elapsed will be 0 anyway, which is kind of untinteresting.
Thanks,
Rafael
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Alan SternQuoted 11 lines · Alan Stern
Quoted 9 lines · Rafael J. Wysocki
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
Quoted 7 lines
Quoted 2 lines
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
Does that include the start timestamps? I don't see them anywhere in the patch. Without the start timestamps we have no way to know how much time was spent waiting for
dpm_list_mtxor other resources as opposed to actually carrying out the operation.
If the callback in question is actually defined, there will be additional debug printouts before executing it from which we can get the start timestamps.
If the callback is not defined, the time elapsed will be 0 anyway, which is kind of untinteresting.
Thanks,
Rafael
Re: [PATCH 10] PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Rafael J. WysockiQuoted 11 lines · Rafael J. Wysocki
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
Does that include the start timestamps? I don't see them anywhere in the patch. Without the start timestamps we have no way to know how much time was spent waiting for dpm_list_mtx or other resources as opposed to actually carrying out the operation.
Alan Stern
PATCH 1/1 PM: Measure suspend and resume times for individual devices (was: Re: [PATCH 2/6] PM: Asynchronous resume of devices)
In reply to Rafael J. WysockiQuoted 15 lines · Rafael J. Wysocki
Quoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 8 lines
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
OK, below is a patch for that. It only prints the time elapsed, because the timestamps themselves can be obtained from the usual kernel timestamping.
It's on top of all the previous patches.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure suspend and resume times for individual devices
If verbose PM debugging is enabled, measure and print the time of suspending and resuming of individual devices.
--- drivers/base/power/main.c | 51 +++++++++++++++++++++++++++++++++++++++++----- kernel/power/swsusp.c | 2 - 2 files changed, 47 insertions(+), 6 deletions(-)
drivers/base/power/main.c +46 -5
@@ -442,11 +442,11 @@ static bool pm_op_started(struct device*/ int pm_time_elapsed (struct timeval *start,struct timeval *stop) { -s64 elapsed_centisecs64; +s64 elapsed_msecs64; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); -return elapsed_centisecs64; + elapsed_msecs64 =timeval_to_ns (stop) -timeval_to_ns (start); +do_div (elapsed_msecs64,NSEC_PER_SEC /1000 ); +return elapsed_msecs64; }static char *pm_verb (int event)
@@ -476,7 +476,7 @@ static char *pm_verb(int event)static void dpm_show_time (struct timeval *start,struct timeval *stop,pm_message_t state,const char *info) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ; printk(KERN_INFO "PM: %s%s%s of devices complete in %d.%02d seconds\n", info ? info : "", info ? " " : "", pm_verb(state.event),
@@ -497,6 +497,33 @@ static void pm_dev_err(struct device *de kobject_name(&dev->kobj), pm_verb(state.event), info, error); } +#ifdef DEBUG +static void device_show_time (struct timeval *start,struct device *dev, +pm_message_t state,char *info) +{ +struct timeval stop; +int msecs; + +do_gettimeofday (&stop); + msecs =pm_time_elapsed (start, &stop); +dev_dbg (dev,"PID %d: %s%s%s complete in %d.%03d seconds\n" , +task_pid_nr (current), info ? info :"" , info ?" " :"" , +pm_verb (state.event), msecs /1000 , msecs %1000 ); +} + +#define TIMER_DECLARE (timer) struct timeval timer +#define TIMER_START (timer) do { \ + do_gettimeofday(&timer); \ + } while (0) +#define TIMER_STOP (timer, dev, state, info) do { \ + device_show_time(&timer, dev, state, info); \ + } while (0) +#else /* !DEBUG */ +#define TIMER_DECLARE (timer) +#define TIMER_START (timer) do { } while (0) +#define TIMER_STOP (timer, dev, state, info) do { } while (0) +#endif /* !DEBUG */ +/*------------------------- Resume routines -------------------------*/ /**
@@ -510,7 +537,9 @@ static void pm_dev_err(struct device *destatic int __device_resume_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -523,6 +552,7 @@ static int __device_resume_noirq(structwake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,"EARLY" );return error; }
@@ -639,7 +669,9 @@ EXPORT_SYMBOL_GPL(dpm_resume_noirq);static int __device_resume (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); +TIMER_START (timer);TRACE_DEVICE (dev);TRACE_RESUME (0 );
@@ -681,6 +713,7 @@ static int __device_resume(struct devicewake_up_all (&dev->power.wait_queue);TRACE_RESUME (error); +TIMER_STOP (timer, dev, state,NULL );return error; }
@@ -923,6 +956,9 @@ static pm_message_t resume_event(pm_messstatic int __device_suspend_noirq (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); + +TIMER_START (timer);if (dev->bus && dev->bus->pm) {pm_dev_dbg (dev, state,"LATE " );
@@ -932,6 +968,7 @@ static int __device_suspend_noirq(struct dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +TIMER_STOP (timer, dev, state,"LATE" );return error; }
@@ -1063,6 +1100,9 @@ EXPORT_SYMBOL_GPL(dpm_suspend_noirq);static int __device_suspend (struct device *dev,pm_message_t state) {int error =0 ; +TIMER_DECLARE (timer); + +TIMER_START (timer);down (&dev->sem);
@@ -1103,6 +1143,7 @@ static int __device_suspend(struct devic dev->power.op_complete = true;wake_up_all (&dev->power.wait_queue); +TIMER_STOP (timer, dev, state,NULL );return error; }
kernel/power/swsusp.c +1 -1
@@ -169,7 +169,7 @@ int swsusp_swap_in_use(void)void swsusp_show_speed (struct timeval *start,struct timeval *stop,unsigned nr_pages,char *msg) { -int centisecs =pm_time_elapsed (start, stop); +int centisecs =pm_time_elapsed (start, stop) /10 ;int k;int kps;
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Alan SternQuoted 14 lines · Alan Stern
Quoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 6 lines
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Of course it would. I think I'll implement it.
The purpose of patch 9 is basically to allow one to see how much time is spent on the handling of devices overall and to compare that with the time spent on the other operation during suspend-resume.
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiQuoted 11 lines · Rafael J. Wysocki
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
For testing purposes it would be nice to have a one-line summary for each device containing a thread ID, start timestamp, end timestamp, and elapsed time. With that information you could evaluate the amount of parallelism and determine where the bottlenecks are. It would give a much more detailed picture of the entire process than the total time of your recent patch 9.
Alan Stern
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiHi!
Quoted 14 lines
Quoted 14 lines
Quoted 13 lines
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to startThat's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
First, the async framework will not start more than
MAX_THREADSthreads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.
Quoted 6 lines
Quoted 3 lines
Okay, but
MAX_THREADSisn't under your control. Remember also that each thread takes up some memory, and during hibernation we are in a memory-constrained situation.We keep some extra free memory for things like this. It's not likely to be exhausted by the async threads alone.
What extra memory? You are creating quite a lot of threads. For 256 of them, it would take cca 2MB... You recently removed code from s2disk that freed 4MB of extra memory, so you are now essentially relying on min_free_kbytes -- and even then is not reliable, user might s2disk under memory pressure. min_free_kbytes is 1MB on my system.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiHi!
Quoted 14 lines
Quoted 14 lines
Quoted 13 lines
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to startThat's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
First, the async framework will not start more than
MAX_THREADSthreads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.
Quoted 6 lines
Quoted 3 lines
Okay, but
MAX_THREADSisn't under your control. Remember also that each thread takes up some memory, and during hibernation we are in a memory-constrained situation.We keep some extra free memory for things like this. It's not likely to be exhausted by the async threads alone.
What extra memory? You are creating quite a lot of threads. For 256 of them, it would take cca 2MB... You recently removed code from s2disk that freed 4MB of extra memory, so you are now essentially relying on min_free_kbytes -- and even then is not reliable, user might s2disk under memory pressure. min_free_kbytes is 1MB on my system.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [PATCH 2/6] PM: Asynchronous resume of devices
Quoted 20 lines · Pavel Machek
Hi!
Quoted 18 lines
Quoted 17 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 1 line
The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to startQuoted 8 lines
Quoted 2 lines
That's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
First, the async framework will not start more than
MAX_THREADSthreads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.Okay, but
MAX_THREADSisn't under your control. Remember also that each thread takes up some memory, and during hibernation we are in a memory-constrained situation.
Quoted 5 lines
Quoted 2 lines
We keep some extra free memory for things like this. It's not likely to be exhausted by the async threads alone.
What extra memory? You are creating quite a lot of threads. For 256 of them, it would take cca 2MB...
We never start that many threads and even if there's not enough memory to start a new thread, the async framework will handle that for us.
Quoted 1 line
You recently removed code from s2disk that freed 4MB of extra memory,
That was removed from s2ram. For STD we still have PAGES_FOR_IO and SPARE_PAGES, nothing's changed there.
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Pavel MachekQuoted 20 lines · Pavel Machek
Hi!
Quoted 18 lines
Quoted 17 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 1 line
The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to startQuoted 8 lines
Quoted 2 lines
That's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
First, the async framework will not start more than
MAX_THREADSthreads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.Okay, but
MAX_THREADSisn't under your control. Remember also that each thread takes up some memory, and during hibernation we are in a memory-constrained situation.
Quoted 5 lines
Quoted 2 lines
We keep some extra free memory for things like this. It's not likely to be exhausted by the async threads alone.
What extra memory? You are creating quite a lot of threads. For 256 of them, it would take cca 2MB...
We never start that many threads and even if there's not enough memory to start a new thread, the async framework will handle that for us.
Quoted 1 line
You recently removed code from s2disk that freed 4MB of extra memory,
That was removed from s2ram. For STD we still have PAGES_FOR_IO and SPARE_PAGES, nothing's changed there.
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Alan SternQuoted 21 lines · Alan Stern
Quoted 19 lines · Rafael J. Wysocki
Quoted 8 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Quoted 6 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)Quoted 7 lines
Quoted 4 lines
Quoted 1 line
The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to startThat's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
Quoted 7 lines
Quoted 3 lines
First, the async framework will not start more than
MAX_THREADSthreads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.Okay, but
MAX_THREADSisn't under your control. Remember also that each thread takes up some memory, and during hibernation we are in a memory-constrained situation.
We keep some extra free memory for things like this. It's not likely to be exhausted by the async threads alone.
Quoted 10 lines
Quoted 7 lines
Second, no new async threads are started while the main thread is handling the sync devices , so the existing threads have a chance to do their job. If there's a "cluster" of sync devices in
dpm_list, the number of async threads running is likely to drop rapidly while those devices are being handled. (BTW, if there were no sync devices, the whole thing would be much simpler, but I don't think it's realistic to assume we'll be able to get rid of them any time soon).Perhaps not, but it would be interesting to see what happens if every device is async. Maybe you can try it and get a meaningful result.
I could if it didn't crash. Perhaps I can with init=/bin/bash.
Quoted 9 lines
Quoted 8 lines
Finally, but not least importantly, async threads are not started for the async devices that were previously handled "out of order" by the already running async threads (or by async threads that have already finished). My testing shows that there are quite a few of them on the average. For example, on the HP nx6325 typically there are as many as 580 async devices handled "out of order" during a _single_ suspend-resume cycle (including the "early" and "late" phases), while only a few (below 10) devices are waited for by at least one async thread.
Quoted 3 lines
That is a difficult sort of thing to know in advance. It ought to be highly influenced by the percentage of async devices; that's another reason for wanting to know what happens when every device is async.
There are a few factors beyond our direct control influencing this, like for example how much time it takes to handle each individual device (that may vary from .5 s to microseconds AFAICS).
In addition to this not only the percentage, but also the distribution of sync devices in dpm_list has an effect. For example, the situations where every second device is sync and where the first half of devices are async and the other is sync would lead to different kinds of behavior.
Quoted 12 lines
Quoted 11 lines
Quoted 6 lines
I would expect that a reasonably small number of threads would suffice to achieve most of the possible time savings. Something on the order of 10 should work well. If the majority of the time is spent handling N devices then N+1 threads would be enough. Judging from some of the comments posted earlier, even 4 threads would give a big advantage.
That unfortunately is not the case with the set of async devices including PCI, ACPI and serio devices only. The average time savings are between 5% to 14%, depending on the system and the phase of the cycle (the relative savings are typically greater for suspend). Still, that amounts to .5 s in some cases.
Quoted 4 lines
Without context it's hard to be sure, but I don't think your numbers contradict what I said. If you get between 5% and 14% time savings with 14 threads, then you might get between 4% and 10% savings with only 4 threads.
I only wanted to say that the advantage is not really that "big". :-)
Quoted 2 lines
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
It's not directly controlled, but there are some interactions between the async threads, the main threads and the async framework that don't allow this number to grow too much.
IMO it sometimes is better to allow things to work themselves out, as long as they don't explode, than to try to keep everything under strict control. YMMV.
Quoted 9 lines
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
- when to start them
You might as well start them at the beginning of
dpm_resumeanddpm_resume_noirq. That way they can overlap with the synchronous operations.In that case they would have to wait in the beginning, so I'd need a mechanism to wake them up.
Quoted 6 lines
You already have two such mechanisms:
dpm_list_mtxand the embeddedwait_queue_heads. Although in the scheme I'm proposing, no async threads would ever have to wait on a per-device waitqueue. A system-wide waitqueue might work out better (for use when a thread reaches the end of the list and then waits before starting over at the beginning).
However, sync devices may depend on the async ones and they need the per-device wait queues for this purpose.
Quoted 8 lines
Quoted 4 lines
Alternatively, there could be a limit to the number of async threads started within the current design, but I'd prefer to leave that to the async framework (namely, if
MAX_THREADSmakes sense for boot, it's also likely to make sense for PM).Strictly speaking, a new thread should be started only when needed. That is, only when all the existing threads are busy running a callback. It shouldn't be too hard to keep track of when that happens.
The async framework does that for us. :-)
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiQuoted 14 lines · Rafael J. Wysocki
Quoted 13 lines · Alan Stern
Quoted 10 lines · Rafael J. Wysocki
Quoted 7 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to start
Quoted 8 lines
Quoted 2 lines
That's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
First, the async framework will not start more than
MAX_THREADSthreads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.
Okay, but MAX_THREADS isn't under your control. Remember also that each thread takes up some memory, and during hibernation we are in a memory-constrained situation.
Quoted 7 lines
Second, no new async threads are started while the main thread is handling the sync devices , so the existing threads have a chance to do their job. If there's a "cluster" of sync devices in
dpm_list, the number of async threads running is likely to drop rapidly while those devices are being handled. (BTW, if there were no sync devices, the whole thing would be much simpler, but I don't think it's realistic to assume we'll be able to get rid of them any time soon).
Perhaps not, but it would be interesting to see what happens if every device is async. Maybe you can try it and get a meaningful result.
Quoted 8 lines
Finally, but not least importantly, async threads are not started for the async devices that were previously handled "out of order" by the already running async threads (or by async threads that have already finished). My testing shows that there are quite a few of them on the average. For example, on the HP nx6325 typically there are as many as 580 async devices handled "out of order" during a _single_ suspend-resume cycle (including the "early" and "late" phases), while only a few (below 10) devices are waited for by at least one async thread.
That is a difficult sort of thing to know in advance. It ought to be highly influenced by the percentage of async devices; that's another reason for wanting to know what happens when every device is async.
Quoted 11 lines
Quoted 6 lines
I would expect that a reasonably small number of threads would suffice to achieve most of the possible time savings. Something on the order of 10 should work well. If the majority of the time is spent handling N devices then N+1 threads would be enough. Judging from some of the comments posted earlier, even 4 threads would give a big advantage.
That unfortunately is not the case with the set of async devices including PCI, ACPI and serio devices only. The average time savings are between 5% to 14%, depending on the system and the phase of the cycle (the relative savings are typically greater for suspend). Still, that amounts to .5 s in some cases.
Without context it's hard to be sure, but I don't think your numbers contradict what I said. If you get between 5% and 14% time savings with 14 threads, then you might get between 4% and 10% savings with only 4 threads.
I must agree, 14 threads isn't a lot. But at the moment that number is random, not under your control.
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
- when to start them
You might as well start them at the beginning of
dpm_resumeanddpm_resume_noirq. That way they can overlap with the synchronous operations.In that case they would have to wait in the beginning, so I'd need a mechanism to wake them up.
You already have two such mechanisms: dpm_list_mtx and the embedded wait_queue_heads. Although in the scheme I'm proposing, no async threads would ever have to wait on a per-device waitqueue. A system-wide waitqueue might work out better (for use when a thread reaches the end of the list and then waits before starting over at the beginning).
Quoted 4 lines
Alternatively, there could be a limit to the number of async threads started within the current design, but I'd prefer to leave that to the async framework (namely, if
MAX_THREADSmakes sense for boot, it's also likely to make sense for PM).
Strictly speaking, a new thread should be started only when needed. That is, only when all the existing threads are busy running a callback. It shouldn't be too hard to keep track of when that happens.
Quoted 5 lines
Quoted 3 lines
It comes down to this: Should there be many threads, each of which browses the list only once, or should there be a few threads, each of which browses the list many times?
Well, quite obviously I prefer the many threads version. :-)
Okay, clearly it's a matter of taste. To me the many-threads version seems less elegant and less well controlled.
Alan Stern
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Alan SternQuoted 8 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Quoted 6 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to startThat's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
In fact they are substantially fewer than that, for the following reasons.
First, the async framework will not start more than MAX_THREADS threads, which is 256 at the moment. This number is less than the number of async devices to handle on an average system.
Second, no new async threads are started while the main thread is handling the sync devices , so the existing threads have a chance to do their job. If there's a "cluster" of sync devices in dpm_list, the number of async threads running is likely to drop rapidly while those devices are being handled. (BTW, if there were no sync devices, the whole thing would be much simpler, but I don't think it's realistic to assume we'll be able to get rid of them any time soon).
Finally, but not least importantly, async threads are not started for the async devices that were previously handled "out of order" by the already running async threads (or by async threads that have already finished). My testing shows that there are quite a few of them on the average. For example, on the HP nx6325 typically there are as many as 580 async devices handled "out of order" during a _single_ suspend-resume cycle (including the "early" and "late" phases), while only a few (below 10) devices are waited for by at least one async thread.
I can try to monitor the number of asyn threads started if you're interested.
Quoted 6 lines
I would expect that a reasonably small number of threads would suffice to achieve most of the possible time savings. Something on the order of 10 should work well. If the majority of the time is spent handling N devices then N+1 threads would be enough. Judging from some of the comments posted earlier, even 4 threads would give a big advantage.
That unfortunately is not the case with the set of async devices including PCI, ACPI and serio devices only. The average time savings are between 5% to 14%, depending on the system and the phase of the cycle (the relative savings are typically greater for suspend). Still, that amounts to .5 s in some cases.
Quoted 5 lines
Quoted 1 line
- when to start them
You might as well start them at the beginning of
dpm_resumeanddpm_resume_noirq. That way they can overlap with the synchronous operations.
In that case they would have to wait in the beginning, so I'd need a mechanism to wake them up.
Alternatively, there could be a limit to the number of async threads started within the current design, but I'd prefer to leave that to the async framework (namely, if MAX_THREADS makes sense for boot, it's also likely to make sense for PM).
Quoted 4 lines
Quoted 1 line
- stop condition
When an error occurs or when
op_startedhas been set for every remaining async device.
Yeah, that's the easy one. :-)
Quoted 9 lines
Quoted 5 lines
I had a few ideas, but then I thought it would be simpler to start an async thread when we know there's some async work to do (ie. there's an async device to handle) and make each async thread browse the list just once (the rationale is that we've just handled a device, so there's a chance there are some devices with satisfied dependencies down the list).
It comes down to this: Should there be many threads, each of which browses the list only once, or should there be a few threads, each of which browses the list many times?
Well, quite obviously I prefer the many threads version. :-)
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiQuoted 10 lines · Rafael J. Wysocki
Quoted 7 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to start
That's a tough question. Right now you start roughly as many threads as there are async devices. That seems like overkill.
I would expect that a reasonably small number of threads would suffice to achieve most of the possible time savings. Something on the order of 10 should work well. If the majority of the time is spent handling N devices then N+1 threads would be enough. Judging from some of the comments posted earlier, even 4 threads would give a big advantage.
Quoted 1 line
- when to start them
You might as well start them at the beginning of dpm_resume and dpm_resume_noirq. That way they can overlap with the synchronous operations.
Quoted 1 line
- stop condition
When an error occurs or when op_started has been set for every remaining async device.
Quoted 5 lines
I had a few ideas, but then I thought it would be simpler to start an async thread when we know there's some async work to do (ie. there's an async device to handle) and make each async thread browse the list just once (the rationale is that we've just handled a device, so there's a chance there are some devices with satisfied dependencies down the list).
It comes down to this: Should there be many threads, each of which browses the list only once, or should there be a few threads, each of which browses the list many times?
Alan Stern
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Alan SternQuoted 20 lines · Alan Stern
Quoted 13 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Theoretically, the total time of system sleep transitions (suspend to RAM, hibernation) can be reduced by running suspend and resume callbacks of device drivers in parallel with each other. However, there are dependencies between devices such that, for example, we may not be allowed to put one device into a low power state before anohter one has been suspended (e.g. we cannot suspend a bridge before suspending all devices behind it). In particular, we're not allowed to suspend the parent of a device before suspending the device itself. Analogously, we're not allowed to resume a device before resuming its parent.
Quoted 4 lines
In this version of the patch the async threads started to execute the resume callbacks of specific device don't exit immediately having done that, but search
dpm_listfor devices whose PM dependencies have already been satisfied and execute their callbacks without waiting.
Quoted 7 lines
Given this design, why bother to invoke
device_resume()for the async devices? Why not just start up a bunch of async threads, each of which callsasync_resume()repeatedly until everything is finished? (And rearrangeasync_resume()to scan the list first and do the actual resume second.)The same goes for the noirq versions.
I thought about that, but there are a few things to figure out:
- how many threads to start
- when to start them
- stop condition
I had a few ideas, but then I thought it would be simpler to start an async thread when we know there's some async work to do (ie. there's an async device to handle) and make each async thread browse the list just once (the rationale is that we've just handled a device, so there's a chance there are some devices with satisfied dependencies down the list).
Thanks,
Rafael
Re: [PATCH 2/6] PM: Asynchronous resume of devices
In reply to Rafael J. WysockiQuoted 13 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Theoretically, the total time of system sleep transitions (suspend to RAM, hibernation) can be reduced by running suspend and resume callbacks of device drivers in parallel with each other. However, there are dependencies between devices such that, for example, we may not be allowed to put one device into a low power state before anohter one has been suspended (e.g. we cannot suspend a bridge before suspending all devices behind it). In particular, we're not allowed to suspend the parent of a device before suspending the device itself. Analogously, we're not allowed to resume a device before resuming its parent.
Quoted 4 lines
In this version of the patch the async threads started to execute the resume callbacks of specific device don't exit immediately having done that, but search
dpm_listfor devices whose PM dependencies have already been satisfied and execute their callbacks without waiting.
Given this design, why bother to invoke device_resume() for the async devices? Why not just start up a bunch of async threads, each of which calls async_resume() repeatedly until everything is finished? (And rearrange async_resume() to scan the list first and do the actual resume second.)
The same goes for the noirq versions.
Alan Stern
PATCH 3/6 PM: Asynchronous suspend of devices
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Extend the approach used in the previous patch to the suspend part of the PM core.
Asynchronous suspend is slightly more complicated, because if any of the suspend callbacks executed asynchronously returns error code, the entire suspend has to be terminated and rolled back. Apart from this, it's completely analogous to the asynchronous resume.
--- drivers/base/power/main.c | 225 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 214 insertions(+), 11 deletions(-)
drivers/base/power/main.c +214 -11
@@ -240,6 +240,15 @@ static void device_pm_wait_for_masters(s }/** +* device_pm_wait_for_slaves - Wait for all slaves of given device. +* @slave: Device to wait for the slaves of. +*/ +static void device_pm_wait_for_slaves (struct device *master) +{ +device_for_each_slave (master, master, device_pm_wait_fn); +} + +/** * device_pm_check - Check the power.op_complete flag of given device. * @dev: Device to check. */
@@ -273,6 +282,15 @@ static int device_pm_check_masters(struc }/** +* device_pm_check_slaves - Check power.op_complete for slaves of a device. +* @slave: Device to check the slaves of. +*/ +static int device_pm_check_slaves (struct device *master) +{ +return device_for_each_slave (master,NULL , device_pm_check_fn); +} + +/** * pm_op - Execute the PM operation appropriate for given PM event. * @dev: Device to handle. * @ops: PM operations to choose from.
@@ -832,6 +850,8 @@ EXPORT_SYMBOL_GPL(dpm_resume_end);/*------------------------- Suspend routines -------------------------*/ +static atomic_t async_error; +/** * resume_event - Return a "resume" message for given "suspend" sleep state. * @sleep_state: PM message representing a sleep state.
@@ -861,21 +881,100 @@ static pm_message_t resume_event(pm_mess* The driver of @dev will not receive interrupts while this function is being * executed. */ -static int device_suspend_noirq (struct device *dev,pm_message_t state) +static int __device_suspend_noirq (struct device *dev,pm_message_t state) {int error =0 ; -if (!dev->bus) -return 0 ; - -if (dev->bus->pm) { +if (dev->bus && dev->bus->pm) {pm_dev_dbg (dev, state,"LATE " ); error =pm_noirq_op (dev, dev->bus->pm, state); } + + dev->power.op_complete = true; +wake_up_all (&dev->power.wait_queue); +return error; }/** +* async_device_suspend_noirq - Wrapper of __device_suspend_noirq(). +* @dev: Device to suspend. +* +* Execute __device_suspend_noirq() for given device unless async_error is +* set and if that returns error code, copy it to async_error and change the +* PM status of @dev to DPM_OFF. +*/ +static void async_device_suspend_noirq (struct device *dev) +{ +int error =atomic_read (&async_error); + +if (error) +return ; +pm_dev_dbg (dev, pm_transition,"async LATE " ); + error =__device_suspend_noirq (dev, pm_transition); +if (!error) +return ; +pm_dev_err (dev, pm_transition," async LATE" , error); + dev->power.status =DPM_OFF ; +atomic_set (&async_error, error); +} + +/** +* async_suspend_noirq - Execute "late" suspend callbacks asynchronously. +* @data: Pointer to the first device to suspend. +* @cookie: Ignored. +* +* The execution of this function is scheduled with async_schedule(), so it runs +* in its own kernel thread. It first calls the "late" suspend callback for the +* device passed to it as @data. Next, it walks dpm_list looking for devices +* that can be suspended without waiting for their "slaves". If such a device +* is found, its "late" suspend callback is run. +*/ +static void async_suspend_noirq (void *data,async_cookie_t cookie) +{ +struct device *dev = (struct device *)data; + +device_pm_wait_for_slaves (dev); +async_device_suspend_noirq (dev); + +list_for_each_entry_continue_reverse (dev, &dpm_list, power.entry) { +if (!dev->power.async_suspend) +continue ; + +if (device_pm_check_slaves (dev)) +continue ; + +if (pm_op_started (dev)) +continue ; + +pm_dev_dbg (dev, pm_transition,"out of order LATE " ); +async_device_suspend_noirq (dev); + } +} + +/** +* device_suspend_noirq - Execute or schedule "late" suspend callback. +* @dev: Device to suspend. +* +* If @dev can be resumed asynchronously, schedule the execution of +* async_suspend_noirq() for it. Otherwise, execute its "late" suspend callback +* directly. +*/ +static int device_suspend_noirq (struct device *dev) +{ +if (pm_op_started (dev)) +return 0 ; + +if (dev->power.async_suspend) { +async_schedule (async_suspend_noirq, dev); +return 0 ; + } + +device_pm_wait_for_slaves (dev); +return __device_suspend_noirq (dev, pm_transition); +} + +/** * dpm_suspend_noirq - Execute "late suspend" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. *
@@ -889,14 +988,20 @@ int dpm_suspend_noirq(pm_message_t statesuspend_device_irqs ();mutex_lock (&dpm_list_mtx); + pm_transition = state;list_for_each_entry_reverse (dev, &dpm_list, power.entry) { - error =device_suspend_noirq (dev, state); + dev->power.status =DPM_OFF_IRQ ; + error =device_suspend_noirq (dev);if (error) { -pm_dev_err (dev, state," late" , error); +pm_dev_err (dev, state," LATE" , error); + dev->power.status =DPM_OFF ;break ; } - dev->power.status =DPM_OFF_IRQ ; + error =atomic_read (&async_error); +if (error) +break ; } +dpm_synchronize_noirq ();mutex_unlock (&dpm_list_mtx);if (error)dpm_resume_noirq (resume_event (state));
@@ -909,7 +1014,7 @@ EXPORT_SYMBOL_GPL(dpm_suspend_noirq);* @dev: Device to handle. * @state: PM transition of the system being carried out. */ -static int device_suspend (struct device *dev,pm_message_t state) +static int __device_suspend (struct device *dev,pm_message_t state) {int error =0 ;
@@ -949,11 +1054,102 @@ static int device_suspend(struct device } End:up (&dev->sem); + dev->power.op_complete = true; +wake_up_all (&dev->power.wait_queue);return error; }/** +* async_device_suspend - Wrapper of __device_suspend(). +* @dev: Device to suspend. +* +* Execute __device_suspend() for given device unless async_error is set and if +* that returns error code, copy it to async_error and change the PM status of +* @dev to DPM_SUSPENDING. +*/ +static void async_device_suspend (struct device *dev) +{ +int error =atomic_read (&async_error); + +if (error) +return ; +pm_dev_dbg (dev, pm_transition,"async " ); + error =__device_suspend (dev, pm_transition); +if (!error) +return ; +pm_dev_err (dev, pm_transition," async" , error); +mutex_lock (&dpm_list_mtx); + dev->power.status =DPM_SUSPENDING ; +atomic_set (&async_error, error); +mutex_unlock (&dpm_list_mtx); +} + +/** +* async_suspend - Execute suspend callbacks asynchronously. +* @data: Pointer to the first device to resume. +* @cookie: Ignored. +* +* The execution of this function is scheduled with async_schedule(), so it runs +* in its own kernel thread. It first calls the suspend callbacks for the +* device passed to it as @data. Next, it walks dpm_list looking for devices +* that can be suspended without waiting for their "slaves". If such a device +* is found, its suspend callbacks are run. +*/ +static void async_suspend (void *data,async_cookie_t cookie) +{ +struct device *dev = (struct device *)data; + +device_pm_wait_for_slaves (dev); + + repeat: +async_device_suspend (dev); +put_device (dev); + +mutex_lock (&dpm_list_mtx); +if (dev->power.status >=DPM_OFF ) + dev =to_device (dpm_list.prev); +list_for_each_entry_continue_reverse (dev, &dpm_list, power.entry) { +if (!dev->power.async_suspend) +continue ; + +if (device_pm_check_slaves (dev)) +continue ; + +if (pm_op_started (dev)) +continue ; + +get_device (dev); +mutex_unlock (&dpm_list_mtx); +pm_dev_dbg (dev, pm_transition,"out of order " ); + goto repeat; + } +mutex_unlock (&dpm_list_mtx); +} + +/** +* device_suspend - Execute or schedule suspend callbacks for given device. +* @dev: Device to suspend. +* +* If @dev can be suspended asynchronously, schedule the execution of +* async_suspend() for it. Otherwise, execute its suspend callbacks directly. +*/ +static int device_suspend (struct device *dev) +{ +if (pm_op_started (dev)) +return 0 ; + +if (dev->power.async_suspend) { +get_device (dev); +async_schedule (async_suspend, dev); +return 0 ; + } + +device_pm_wait_for_slaves (dev); +return __device_suspend (dev, pm_transition); +} + +/** * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. * @state: PM transition of the system being carried out. */
@@ -964,27 +1160,33 @@ static int dpm_suspend(pm_message_t statINIT_LIST_HEAD (&list);mutex_lock (&dpm_list_mtx); + pm_transition = state;while (!list_empty (&dpm_list)) {struct device *dev =to_device (dpm_list.prev);get_device (dev); + dev->power.status =DPM_OFF ;mutex_unlock (&dpm_list_mtx); - error =device_suspend (dev, state); + error =device_suspend (dev);mutex_lock (&dpm_list_mtx);if (error) {pm_dev_err (dev, state,"" , error); + dev->power.status =DPM_SUSPENDING ;put_device (dev);break ; } - dev->power.status =DPM_OFF ;if (!list_empty (&dev->power.entry))list_move (&dev->power.entry, &list);put_device (dev); + error =atomic_read (&async_error); +if (error) +break ; }list_splice (&list, dpm_list.prev);mutex_unlock (&dpm_list_mtx); +dpm_synchronize ();return error; }
@@ -1043,6 +1245,7 @@ static int dpm_prepare(pm_message_t statINIT_LIST_HEAD (&list);mutex_lock (&dpm_list_mtx); transition_started = true; +atomic_set (&async_error,0 );while (!list_empty (&dpm_list)) {struct device *dev =to_device (dpm_list.next);
PATCH 3/6 PM: Asynchronous suspend of devices
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Extend the approach used in the previous patch to the suspend part of the PM core.
Asynchronous suspend is slightly more complicated, because if any of the suspend callbacks executed asynchronously returns error code, the entire suspend has to be terminated and rolled back. Apart from this, it's completely analogous to the asynchronous resume.
--- drivers/base/power/main.c | 225 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 214 insertions(+), 11 deletions(-)
drivers/base/power/main.c +214 -11
@@ -240,6 +240,15 @@ static void device_pm_wait_for_masters(s }/** +* device_pm_wait_for_slaves - Wait for all slaves of given device. +* @slave: Device to wait for the slaves of. +*/ +static void device_pm_wait_for_slaves (struct device *master) +{ +device_for_each_slave (master, master, device_pm_wait_fn); +} + +/** * device_pm_check - Check the power.op_complete flag of given device. * @dev: Device to check. */
@@ -273,6 +282,15 @@ static int device_pm_check_masters(struc }/** +* device_pm_check_slaves - Check power.op_complete for slaves of a device. +* @slave: Device to check the slaves of. +*/ +static int device_pm_check_slaves (struct device *master) +{ +return device_for_each_slave (master,NULL , device_pm_check_fn); +} + +/** * pm_op - Execute the PM operation appropriate for given PM event. * @dev: Device to handle. * @ops: PM operations to choose from.
@@ -832,6 +850,8 @@ EXPORT_SYMBOL_GPL(dpm_resume_end);/*------------------------- Suspend routines -------------------------*/ +static atomic_t async_error; +/** * resume_event - Return a "resume" message for given "suspend" sleep state. * @sleep_state: PM message representing a sleep state.
@@ -861,21 +881,100 @@ static pm_message_t resume_event(pm_mess* The driver of @dev will not receive interrupts while this function is being * executed. */ -static int device_suspend_noirq (struct device *dev,pm_message_t state) +static int __device_suspend_noirq (struct device *dev,pm_message_t state) {int error =0 ; -if (!dev->bus) -return 0 ; - -if (dev->bus->pm) { +if (dev->bus && dev->bus->pm) {pm_dev_dbg (dev, state,"LATE " ); error =pm_noirq_op (dev, dev->bus->pm, state); } + + dev->power.op_complete = true; +wake_up_all (&dev->power.wait_queue); +return error; }/** +* async_device_suspend_noirq - Wrapper of __device_suspend_noirq(). +* @dev: Device to suspend. +* +* Execute __device_suspend_noirq() for given device unless async_error is +* set and if that returns error code, copy it to async_error and change the +* PM status of @dev to DPM_OFF. +*/ +static void async_device_suspend_noirq (struct device *dev) +{ +int error =atomic_read (&async_error); + +if (error) +return ; +pm_dev_dbg (dev, pm_transition,"async LATE " ); + error =__device_suspend_noirq (dev, pm_transition); +if (!error) +return ; +pm_dev_err (dev, pm_transition," async LATE" , error); + dev->power.status =DPM_OFF ; +atomic_set (&async_error, error); +} + +/** +* async_suspend_noirq - Execute "late" suspend callbacks asynchronously. +* @data: Pointer to the first device to suspend. +* @cookie: Ignored. +* +* The execution of this function is scheduled with async_schedule(), so it runs +* in its own kernel thread. It first calls the "late" suspend callback for the +* device passed to it as @data. Next, it walks dpm_list looking for devices +* that can be suspended without waiting for their "slaves". If such a device +* is found, its "late" suspend callback is run. +*/ +static void async_suspend_noirq (void *data,async_cookie_t cookie) +{ +struct device *dev = (struct device *)data; + +device_pm_wait_for_slaves (dev); +async_device_suspend_noirq (dev); + +list_for_each_entry_continue_reverse (dev, &dpm_list, power.entry) { +if (!dev->power.async_suspend) +continue ; + +if (device_pm_check_slaves (dev)) +continue ; + +if (pm_op_started (dev)) +continue ; + +pm_dev_dbg (dev, pm_transition,"out of order LATE " ); +async_device_suspend_noirq (dev); + } +} + +/** +* device_suspend_noirq - Execute or schedule "late" suspend callback. +* @dev: Device to suspend. +* +* If @dev can be resumed asynchronously, schedule the execution of +* async_suspend_noirq() for it. Otherwise, execute its "late" suspend callback +* directly. +*/ +static int device_suspend_noirq (struct device *dev) +{ +if (pm_op_started (dev)) +return 0 ; + +if (dev->power.async_suspend) { +async_schedule (async_suspend_noirq, dev); +return 0 ; + } + +device_pm_wait_for_slaves (dev); +return __device_suspend_noirq (dev, pm_transition); +} + +/** * dpm_suspend_noirq - Execute "late suspend" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. *
@@ -889,14 +988,20 @@ int dpm_suspend_noirq(pm_message_t statesuspend_device_irqs ();mutex_lock (&dpm_list_mtx); + pm_transition = state;list_for_each_entry_reverse (dev, &dpm_list, power.entry) { - error =device_suspend_noirq (dev, state); + dev->power.status =DPM_OFF_IRQ ; + error =device_suspend_noirq (dev);if (error) { -pm_dev_err (dev, state," late" , error); +pm_dev_err (dev, state," LATE" , error); + dev->power.status =DPM_OFF ;break ; } - dev->power.status =DPM_OFF_IRQ ; + error =atomic_read (&async_error); +if (error) +break ; } +dpm_synchronize_noirq ();mutex_unlock (&dpm_list_mtx);if (error)dpm_resume_noirq (resume_event (state));
@@ -909,7 +1014,7 @@ EXPORT_SYMBOL_GPL(dpm_suspend_noirq);* @dev: Device to handle. * @state: PM transition of the system being carried out. */ -static int device_suspend (struct device *dev,pm_message_t state) +static int __device_suspend (struct device *dev,pm_message_t state) {int error =0 ;
@@ -949,11 +1054,102 @@ static int device_suspend(struct device } End:up (&dev->sem); + dev->power.op_complete = true; +wake_up_all (&dev->power.wait_queue);return error; }/** +* async_device_suspend - Wrapper of __device_suspend(). +* @dev: Device to suspend. +* +* Execute __device_suspend() for given device unless async_error is set and if +* that returns error code, copy it to async_error and change the PM status of +* @dev to DPM_SUSPENDING. +*/ +static void async_device_suspend (struct device *dev) +{ +int error =atomic_read (&async_error); + +if (error) +return ; +pm_dev_dbg (dev, pm_transition,"async " ); + error =__device_suspend (dev, pm_transition); +if (!error) +return ; +pm_dev_err (dev, pm_transition," async" , error); +mutex_lock (&dpm_list_mtx); + dev->power.status =DPM_SUSPENDING ; +atomic_set (&async_error, error); +mutex_unlock (&dpm_list_mtx); +} + +/** +* async_suspend - Execute suspend callbacks asynchronously. +* @data: Pointer to the first device to resume. +* @cookie: Ignored. +* +* The execution of this function is scheduled with async_schedule(), so it runs +* in its own kernel thread. It first calls the suspend callbacks for the +* device passed to it as @data. Next, it walks dpm_list looking for devices +* that can be suspended without waiting for their "slaves". If such a device +* is found, its suspend callbacks are run. +*/ +static void async_suspend (void *data,async_cookie_t cookie) +{ +struct device *dev = (struct device *)data; + +device_pm_wait_for_slaves (dev); + + repeat: +async_device_suspend (dev); +put_device (dev); + +mutex_lock (&dpm_list_mtx); +if (dev->power.status >=DPM_OFF ) + dev =to_device (dpm_list.prev); +list_for_each_entry_continue_reverse (dev, &dpm_list, power.entry) { +if (!dev->power.async_suspend) +continue ; + +if (device_pm_check_slaves (dev)) +continue ; + +if (pm_op_started (dev)) +continue ; + +get_device (dev); +mutex_unlock (&dpm_list_mtx); +pm_dev_dbg (dev, pm_transition,"out of order " ); + goto repeat; + } +mutex_unlock (&dpm_list_mtx); +} + +/** +* device_suspend - Execute or schedule suspend callbacks for given device. +* @dev: Device to suspend. +* +* If @dev can be suspended asynchronously, schedule the execution of +* async_suspend() for it. Otherwise, execute its suspend callbacks directly. +*/ +static int device_suspend (struct device *dev) +{ +if (pm_op_started (dev)) +return 0 ; + +if (dev->power.async_suspend) { +get_device (dev); +async_schedule (async_suspend, dev); +return 0 ; + } + +device_pm_wait_for_slaves (dev); +return __device_suspend (dev, pm_transition); +} + +/** * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. * @state: PM transition of the system being carried out. */
@@ -964,27 +1160,33 @@ static int dpm_suspend(pm_message_t statINIT_LIST_HEAD (&list);mutex_lock (&dpm_list_mtx); + pm_transition = state;while (!list_empty (&dpm_list)) {struct device *dev =to_device (dpm_list.prev);get_device (dev); + dev->power.status =DPM_OFF ;mutex_unlock (&dpm_list_mtx); - error =device_suspend (dev, state); + error =device_suspend (dev);mutex_lock (&dpm_list_mtx);if (error) {pm_dev_err (dev, state,"" , error); + dev->power.status =DPM_SUSPENDING ;put_device (dev);break ; } - dev->power.status =DPM_OFF ;if (!list_empty (&dev->power.entry))list_move (&dev->power.entry, &list);put_device (dev); + error =atomic_read (&async_error); +if (error) +break ; }list_splice (&list, dpm_list.prev);mutex_unlock (&dpm_list_mtx); +dpm_synchronize ();return error; }
@@ -1043,6 +1245,7 @@ static int dpm_prepare(pm_message_t statINIT_LIST_HEAD (&list);mutex_lock (&dpm_list_mtx); transition_started = true; +atomic_set (&async_error,0 );while (!list_empty (&dpm_list)) {struct device *dev =to_device (dpm_list.next);
PATCH 4/6 PM: Allow PCI devices to suspend/resume asynchronously
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Set async_suspend for all PCI devices and PCIe port services, so that they can be suspended and resumed asynchronously with other devices they don't depend on in a known way (i.e. devices which are not their parents or children and to which they are not connected via struct pm_link objects).
--- drivers/pci/pci.c | 1 + drivers/pci/pcie/portdrv_core.c | 1 + 2 files changed, 2 insertions(+)
drivers/pci/pci.c +1 -0
@@ -1360,6 +1360,7 @@ void pci_pm_init(struct pci_dev *dev)int pm;u16 pmc; +device_enable_async_suspend (&dev->dev, true); dev->pm_cap =0 ;/* find PCI PM capability in list */
drivers/pci/pcie/portdrv_core.c +1 -0
@@ -280,6 +280,7 @@ static void pcie_device_init(struct pci_dev_set_name (device,"%s:pcie%02x" ,pci_name (parent),get_descriptor_id (port_type, service_type)); device->parent = &parent->dev; +device_enable_async_suspend (device, true); }/**
PATCH 4/6 PM: Allow PCI devices to suspend/resume asynchronously
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Set async_suspend for all PCI devices and PCIe port services, so that they can be suspended and resumed asynchronously with other devices they don't depend on in a known way (i.e. devices which are not their parents or children and to which they are not connected via struct pm_link objects).
--- drivers/pci/pci.c | 1 + drivers/pci/pcie/portdrv_core.c | 1 + 2 files changed, 2 insertions(+)
drivers/pci/pci.c +1 -0
@@ -1360,6 +1360,7 @@ void pci_pm_init(struct pci_dev *dev)int pm;u16 pmc; +device_enable_async_suspend (&dev->dev, true); dev->pm_cap =0 ;/* find PCI PM capability in list */
drivers/pci/pcie/portdrv_core.c +1 -0
@@ -280,6 +280,7 @@ static void pcie_device_init(struct pci_dev_set_name (device,"%s:pcie%02x" ,pci_name (parent),get_descriptor_id (port_type, service_type)); device->parent = &parent->dev; +device_enable_async_suspend (device, true); }/**
PATCH 5/6 PM: Allow ACPI devices to suspend/resume asynchronously
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Set async_suspend for all ACPI devices, so that they can be suspended and resumed asynchronously with other devices they don't depend on in a known way (i.e. devices which are not their parents or children and to which they are not connected via struct pm_link objects).
--- drivers/acpi/scan.c | 1 + 1 file changed, 1 insertion(+)
drivers/acpi/scan.c +1 -0
@@ -539,6 +539,7 @@ static int acpi_device_register(struct a dev_name(&device->dev)); device->removal_type =ACPI_BUS_REMOVAL_NORMAL ; +device_enable_async_suspend (&device->dev, true);return 0 ; end:mutex_lock (&acpi_device_lock);
PATCH 5/6 PM: Allow ACPI devices to suspend/resume asynchronously
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Set async_suspend for all ACPI devices, so that they can be suspended and resumed asynchronously with other devices they don't depend on in a known way (i.e. devices which are not their parents or children and to which they are not connected via struct pm_link objects).
--- drivers/acpi/scan.c | 1 + 1 file changed, 1 insertion(+)
drivers/acpi/scan.c +1 -0
@@ -539,6 +539,7 @@ static int acpi_device_register(struct a dev_name(&device->dev)); device->removal_type =ACPI_BUS_REMOVAL_NORMAL ; +device_enable_async_suspend (&device->dev, true);return 0 ; end:mutex_lock (&acpi_device_lock);
PATCH 6/6 PM: Allow serio input devices to suspend/resume asynchronously
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Set async_suspend for all serio input devices, so that they can be suspended and resumed asynchronously with other devices they don't depend on in a known way (i.e. devices which are not their parents or children and to which they are not connected via struct pm_link objects).
--- drivers/input/serio/serio.c | 1 + 1 file changed, 1 insertion(+)
drivers/input/serio/serio.c +1 -0
@@ -576,6 +576,7 @@ static void serio_add_port(struct serioprintk (KERN_ERR "serio: sysfs_create_group() failed for %s (%s), error: %d\n" , serio->phys, serio->name, error); +device_enable_async_suspend (&serio->dev, true); } }
PATCH 6/6 PM: Allow serio input devices to suspend/resume asynchronously
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Set async_suspend for all serio input devices, so that they can be suspended and resumed asynchronously with other devices they don't depend on in a known way (i.e. devices which are not their parents or children and to which they are not connected via struct pm_link objects).
--- drivers/input/serio/serio.c | 1 + 1 file changed, 1 insertion(+)
drivers/input/serio/serio.c +1 -0
@@ -576,6 +576,7 @@ static void serio_add_port(struct serioprintk (KERN_ERR "serio: sysfs_create_group() failed for %s (%s), error: %d\n" , serio->phys, serio->name, error); +device_enable_async_suspend (&serio->dev, true); } }
Dmitry Torokhov
maintainerRe: [PATCH 6/6] PM: Allow serio input devices to suspend/resume asynchronously
Quoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Set
async_suspendfor all serio input devices, so that they can be suspended and resumed asynchronously with other devices they don't depend on in a known way (i.e. devices which are not their parents or children and to which they are not connected via structpm_linkobjects).
As I mentioned not yet, please - i8042 may be unhappy if you pound all its ports at once. I should have a solution for it though.
Signature · 1 line
Dmitry
Re: [PATCH 6/6] PM: Allow serio input devices to suspend/resume asynchronously
Quoted 10 lines · Dmitry Torokhov
Quoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Set
async_suspendfor all serio input devices, so that they can be suspended and resumed asynchronously with other devices they don't depend on in a known way (i.e. devices which are not their parents or children and to which they are not connected via structpm_linkobjects).
Quoted 2 lines
As I mentioned not yet, please - i8042 may be unhappy if you pound all its ports at once. I should have a solution for it though.
OK, I'll drop this patch.
Thanks,
Rafael
Re: [PATCH 6/6] PM: Allow serio input devices to suspend/resume asynchronously
In reply to Dmitry TorokhovQuoted 10 lines · Dmitry Torokhov
Quoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Set
async_suspendfor all serio input devices, so that they can be suspended and resumed asynchronously with other devices they don't depend on in a known way (i.e. devices which are not their parents or children and to which they are not connected via structpm_linkobjects).
Quoted 2 lines
As I mentioned not yet, please - i8042 may be unhappy if you pound all its ports at once. I should have a solution for it though.
OK, I'll drop this patch.
Thanks,
Rafael
Dmitry Torokhov
maintainerRe: [PATCH 6/6] PM: Allow serio input devices to suspend/resume asynchronously
In reply to Rafael J. WysockiQuoted 8 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Set
async_suspendfor all serio input devices, so that they can be suspended and resumed asynchronously with other devices they don't depend on in a known way (i.e. devices which are not their parents or children and to which they are not connected via structpm_linkobjects).
As I mentioned not yet, please - i8042 may be unhappy if you pound all its ports at once. I should have a solution for it though.
Signature · 1 line
Dmitry
Quoted 9 lines · Rafael J. Wysocki
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
The idea is explained in the changelogs of the first two patches.
Comments welcome.
Forgot to say, the patchset is on top of the linux-next branch of suspend-2.6.
Thanks,
Rafael
Quoted 7 lines · Rafael J. Wysocki
Quoted 6 lines · Rafael J. Wysocki
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
Quoted 5 lines
Quoted 3 lines
The idea is explained in the changelogs of the first two patches.
Comments welcome.
Forgot to say, the patchset is on top of the linux-next branch of suspend-2.6.
I have two more patches in this series. The first one introduces a sysfs switch allowing the user space to block the async suspend/resume. The second one gives the user space access to the power.async_suspend flag of devices through sysfs.
The patches will follow in the next messages.
Thanks,
Rafael
Quoted 7 lines · Rafael J. Wysocki
Quoted 6 lines · Rafael J. Wysocki
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
Quoted 5 lines
Quoted 3 lines
The idea is explained in the changelogs of the first two patches.
Comments welcome.
Forgot to say, the patchset is on top of the linux-next branch of suspend-2.6.
I have two more patches in this series. The first one introduces a sysfs switch allowing the user space to block the async suspend/resume. The second one gives the user space access to the power.async_suspend flag of devices through sysfs.
The patches will follow in the next messages.
Thanks,
Rafael
PATCH 1/1 PM: Add a switch for disabling/enabling asynchronous suspend/resume
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute kernel/power/pm_async allowing the user space to disable and enable async suspend/resume of devices.
--- drivers/base/power/main.c | 13 +++++++------ drivers/base/power/power.h | 6 +++--- kernel/power/main.c | 28 +++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 10 deletions(-)
kernel/power/main.c +27 -1
@@ -44,6 +44,29 @@ int pm_notifier_call_chain(unsigned long == NOTIFY_BAD) ? -EINVAL : 0; } +/* If set, devices may be suspended and resumed asynchronously. */ +int pm_async_enabled =1 ; + +static ssize_t pm_async_show (struct kobject *kobj,struct kobj_attribute *attr, +char *buf) +{ +return sprintf (buf,"%d\n" , pm_async_enabled); +} + +static ssize_t pm_async_store (struct kobject *kobj,struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int val; + +if (sscanf (buf,"%d" , &val) ==1 ) { + pm_async_enabled = !!val; +return n; + } +return -EINVAL ; +} + +power_attr (pm_async); +#ifdef CONFIG_PM_DEBUG int pm_test_level =TEST_NONE ;
@@ -208,9 +231,12 @@ static struct attribute * g[] = {#ifdef CONFIG_PM_TRACE &pm_trace_attr.attr,#endif -#if defined(CONFIG_PM_SLEEP ) && defined(CONFIG_PM_DEBUG ) +#ifdef CONFIG_PM_SLEEP + &pm_async_attr.attr, +#ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif +#endif NULL, };
drivers/base/power/main.c +7 -6
@@ -235,7 +235,7 @@ static int device_pm_wait_fn(struct devi*/ static void device_pm_wait_for_masters (struct device *slave) { -if (!pm_trace_enabled) +if (pm_async_enabled && !pm_trace_enabled)device_for_each_master (slave, slave, device_pm_wait_fn); }
@@ -245,7 +245,8 @@ static void device_pm_wait_for_masters(s*/ static void device_pm_wait_for_slaves (struct device *master) { -device_for_each_slave (master, master, device_pm_wait_fn); +if (pm_async_enabled) +device_for_each_slave (master, master, device_pm_wait_fn); }/**
@@ -560,7 +561,7 @@ static int device_resume_noirq(struct deif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend && !pm_trace_enabled) { +if (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {async_schedule (async_resume_noirq, dev);return 0 ; }
@@ -719,7 +720,7 @@ static int device_resume(struct device *if (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend && !pm_trace_enabled) { +if (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_resume, dev);return 0 ;
@@ -965,7 +966,7 @@ static int device_suspend_noirq(struct dif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend) { +if (pm_async_enabled && dev->power.async_suspend) {async_schedule (async_suspend_noirq, dev);return 0 ; }
@@ -1139,7 +1140,7 @@ static int device_suspend(struct deviceif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend) { +if (pm_async_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_suspend, dev);return 0 ;
drivers/base/power/power.h +3 -3
@@ -16,10 +16,10 @@ static inline void pm_runtime_remove(str#ifdef CONFIG_PM_SLEEP -/* -* main.c -*/ +/* kernel/power/main.c */ +extern int pm_async_enabled; +/* drivers/base/power/main.c */ extern struct list_head dpm_list;/* The active device list */ static inline struct device *to_device (struct list_head *entry)
Dmitry Torokhov
maintainerRe: [PATCH 7] PM: Add a switch for disabling/enabling asynchronous suspend/resume
Quoted 25 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute
kernel/power/pm_asyncallowing the user space to disable and enable async suspend/resume of devices.Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 13 +++++++------ drivers/base/power/power.h | 6 +++--- kernel/power/main.c | 28 +++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 10 deletions(-)Index:
linux-2.6/kernel/power/main.c
===================================================================kernel/power/main.c +5
-0@@ -44,6 +44,29 @@ int pm_notifier_call_chain(unsigned long == NOTIFY_BAD) ? -EINVAL : 0; } +/* If set, devices may be suspended and resumed asynchronously. */ +int pm_async_enabled =1 ; + +static ssize_t pm_async_show (struct kobject *kobj,struct kobj_attribute *attr, +char *buf)
kernel/power/main.c int pm_notifier_call_chain(unsigned long · line 61
Quoted 10 lines
kernel/power/main.c +10
-0+{ +return sprintf (buf,"%d\n" , pm_async_enabled); +} + +static ssize_t pm_async_store (struct kobject *kobj,struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int val; + +if (sscanf (buf,"%d" , &val) ==1 ) {
I prefer more vigorous restrictions on the sysfs interfaces. Maybe:
if (strict_strtoul(buf, 10, &val) || val > 1) return -EINVAL; pm_async_enabled = val;
?
Signature · 1 line
Dmitry
Dmitry Torokhov
maintainerRe: [PATCH 7] PM: Add a switch for disabling/enabling asynchronous suspend/resume
In reply to Rafael J. WysockiQuoted 25 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute
kernel/power/pm_asyncallowing the user space to disable and enable async suspend/resume of devices.Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 13 +++++++------ drivers/base/power/power.h | 6 +++--- kernel/power/main.c | 28 +++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 10 deletions(-)Index:
linux-2.6/kernel/power/main.c
===================================================================kernel/power/main.c +5
-0@@ -44,6 +44,29 @@ int pm_notifier_call_chain(unsigned long == NOTIFY_BAD) ? -EINVAL : 0; } +/* If set, devices may be suspended and resumed asynchronously. */ +int pm_async_enabled =1 ; + +static ssize_t pm_async_show (struct kobject *kobj,struct kobj_attribute *attr, +char *buf)
kernel/power/main.c int pm_notifier_call_chain(unsigned long · line 61
Quoted 10 lines
kernel/power/main.c +10
-0+{ +return sprintf (buf,"%d\n" , pm_async_enabled); +} + +static ssize_t pm_async_store (struct kobject *kobj,struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int val; + +if (sscanf (buf,"%d" , &val) ==1 ) {
I prefer more vigorous restrictions on the sysfs interfaces. Maybe:
if (strict_strtoul(buf, 10, &val) || val > 1) return -EINVAL; pm_async_enabled = val;
?
Signature · 1 line
Dmitry
PATCH 1/1 PM: Add a switch for disabling/enabling asynchronous suspend/resume
Quoted 39 lines · Dmitry Torokhov
Quoted 25 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute
kernel/power/pm_asyncallowing the user space to disable and enable async suspend/resume of devices.Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 13 +++++++------ drivers/base/power/power.h | 6 +++--- kernel/power/main.c | 28 +++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 10 deletions(-)Index:
linux-2.6/kernel/power/main.c
===================================================================kernel/power/main.c +5
-0@@ -44,6 +44,29 @@ int pm_notifier_call_chain(unsigned long == NOTIFY_BAD) ? -EINVAL : 0; } +/* If set, devices may be suspended and resumed asynchronously. */ +int pm_async_enabled =1 ; + +static ssize_t pm_async_show (struct kobject *kobj,struct kobj_attribute *attr, +char *buf)kernel/power/main.c int pm_notifier_call_chain(unsigned long · line 61
Quoted 10 lines
kernel/power/main.c +10
-0+{ +return sprintf (buf,"%d\n" , pm_async_enabled); +} + +static ssize_t pm_async_store (struct kobject *kobj,struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int val; + +if (sscanf (buf,"%d" , &val) ==1 ) {I prefer more vigorous restrictions on the sysfs interfaces. Maybe:
Quoted 6 lines
if (strict_strtoul(buf, 10, &val) || val > 1) return -EINVAL; pm_async_enabled = val;?
OK
Updated patch below.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Add a switch for disabling/enabling asynchronous suspend/resume
Add sysfs attribute kernel/power/pm_async allowing the user space to disable and enable async suspend/resume of devices.
--- drivers/base/power/main.c | 13 +++++++------ drivers/base/power/power.h | 6 +++--- kernel/power/main.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 10 deletions(-)
kernel/power/main.c +30 -1
@@ -44,6 +44,32 @@ int pm_notifier_call_chain(unsigned long == NOTIFY_BAD) ? -EINVAL : 0; } +/* If set, devices may be suspended and resumed asynchronously. */ +int pm_async_enabled =1 ; + +static ssize_t pm_async_show (struct kobject *kobj,struct kobj_attribute *attr, +char *buf) +{ +return sprintf (buf,"%d\n" , pm_async_enabled); +} + +static ssize_t pm_async_store (struct kobject *kobj,struct kobj_attribute *attr, +const char *buf,size_t n) +{ +unsigned long val; + +if (strict_strtoul (buf,10 , &val)) +return -EINVAL ; + +if (val >1 ) +return -EINVAL ; + + pm_async_enabled = val; +return n; +} + +power_attr (pm_async); +#ifdef CONFIG_PM_DEBUG int pm_test_level =TEST_NONE ;
@@ -208,9 +234,12 @@ static struct attribute * g[] = {#ifdef CONFIG_PM_TRACE &pm_trace_attr.attr,#endif -#if defined(CONFIG_PM_SLEEP ) && defined(CONFIG_PM_DEBUG ) +#ifdef CONFIG_PM_SLEEP + &pm_async_attr.attr, +#ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif +#endif NULL, };
drivers/base/power/main.c +7 -6
@@ -235,7 +235,7 @@ static int device_pm_wait_fn(struct devi*/ static void device_pm_wait_for_masters (struct device *slave) { -if (!pm_trace_enabled) +if (pm_async_enabled && !pm_trace_enabled)device_for_each_master (slave, slave, device_pm_wait_fn); }
@@ -245,7 +245,8 @@ static void device_pm_wait_for_masters(s*/ static void device_pm_wait_for_slaves (struct device *master) { -device_for_each_slave (master, master, device_pm_wait_fn); +if (pm_async_enabled) +device_for_each_slave (master, master, device_pm_wait_fn); }/**
@@ -560,7 +561,7 @@ static int device_resume_noirq(struct deif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend && !pm_trace_enabled) { +if (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {async_schedule (async_resume_noirq, dev);return 0 ; }
@@ -719,7 +720,7 @@ static int device_resume(struct device *if (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend && !pm_trace_enabled) { +if (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_resume, dev);return 0 ;
@@ -965,7 +966,7 @@ static int device_suspend_noirq(struct dif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend) { +if (pm_async_enabled && dev->power.async_suspend) {async_schedule (async_suspend_noirq, dev);return 0 ; }
@@ -1139,7 +1140,7 @@ static int device_suspend(struct deviceif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend) { +if (pm_async_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_suspend, dev);return 0 ;
drivers/base/power/power.h +3 -3
@@ -16,10 +16,10 @@ static inline void pm_runtime_remove(str#ifdef CONFIG_PM_SLEEP -/* -* main.c -*/ +/* kernel/power/main.c */ +extern int pm_async_enabled; +/* drivers/base/power/main.c */ extern struct list_head dpm_list;/* The active device list */ static inline struct device *to_device (struct list_head *entry)
Dmitry Torokhov
maintainerRe: [PATCH 7 updated] PM: Add a switch for disabling/enabling asynchronous suspend/resume
drivers/base/power/power.h static inline void pm_runtime_remove(str · line 20
Quoted 3 lines · Rafael J. Wysocki
drivers/base/power/power.h +2
-0+/* kernel/power/main.c */ +extern int pm_async_enabled;
Sorry, forgot about this in the first mail - why don't we make it a bool?
Signature · 1 line
Dmitry
Dmitry Torokhov
maintainerRe: [PATCH 7 updated] PM: Add a switch for disabling/enabling asynchronous suspend/resume
In reply to Rafael J. Wysockidrivers/base/power/power.h static inline void pm_runtime_remove(str · line 20
Quoted 3 lines · Rafael J. Wysocki
drivers/base/power/power.h +2
-0+/* kernel/power/main.c */ +extern int pm_async_enabled;
Sorry, forgot about this in the first mail - why don't we make it a bool?
Signature · 1 line
Dmitry
Re: [PATCH 7 updated] PM: Add a switch for disabling/enabling asynchronous suspend/resume
Quoted 7 lines · Dmitry Torokhov
drivers/base/power/power.h static inline void pm_runtime_remove(str · line 20
Quoted 3 lines · Rafael J. Wysocki
drivers/base/power/power.h +2
-0+/* kernel/power/main.c */ +extern int pm_async_enabled;Sorry, forgot about this in the first mail - why don't we make it a bool?
I didn't do that because of the
sprintf(buf, "%d\n", pm_async_enabled)
in pm_async_show(), where it clearly is treated as an int.
Thanks,
Rafael
Re: [PATCH 7 updated] PM: Add a switch for disabling/enabling asynchronous suspend/resume
In reply to Dmitry TorokhovQuoted 7 lines · Dmitry Torokhov
drivers/base/power/power.h static inline void pm_runtime_remove(str · line 20
Quoted 3 lines · Rafael J. Wysocki
drivers/base/power/power.h +2
-0+/* kernel/power/main.c */ +extern int pm_async_enabled;Sorry, forgot about this in the first mail - why don't we make it a bool?
I didn't do that because of the
sprintf(buf, "%d\n", pm_async_enabled)
in pm_async_show(), where it clearly is treated as an int.
Thanks,
Rafael
PATCH 1/1 PM: Add a switch for disabling/enabling asynchronous suspend/resume
In reply to Dmitry TorokhovQuoted 39 lines · Dmitry Torokhov
Quoted 25 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute
kernel/power/pm_asyncallowing the user space to disable and enable async suspend/resume of devices.Signed-off-by Rafael J. Wysocki <[email protected]>--- drivers/base/power/main.c | 13 +++++++------ drivers/base/power/power.h | 6 +++--- kernel/power/main.c | 28 +++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 10 deletions(-)Index:
linux-2.6/kernel/power/main.c
===================================================================kernel/power/main.c +5
-0@@ -44,6 +44,29 @@ int pm_notifier_call_chain(unsigned long == NOTIFY_BAD) ? -EINVAL : 0; } +/* If set, devices may be suspended and resumed asynchronously. */ +int pm_async_enabled =1 ; + +static ssize_t pm_async_show (struct kobject *kobj,struct kobj_attribute *attr, +char *buf)kernel/power/main.c int pm_notifier_call_chain(unsigned long · line 61
Quoted 10 lines
kernel/power/main.c +10
-0+{ +return sprintf (buf,"%d\n" , pm_async_enabled); +} + +static ssize_t pm_async_store (struct kobject *kobj,struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int val; + +if (sscanf (buf,"%d" , &val) ==1 ) {I prefer more vigorous restrictions on the sysfs interfaces. Maybe:
Quoted 6 lines
if (strict_strtoul(buf, 10, &val) || val > 1) return -EINVAL; pm_async_enabled = val;?
OK
Updated patch below.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Add a switch for disabling/enabling asynchronous suspend/resume
Add sysfs attribute kernel/power/pm_async allowing the user space to disable and enable async suspend/resume of devices.
--- drivers/base/power/main.c | 13 +++++++------ drivers/base/power/power.h | 6 +++--- kernel/power/main.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 10 deletions(-)
kernel/power/main.c +30 -1
@@ -44,6 +44,32 @@ int pm_notifier_call_chain(unsigned long == NOTIFY_BAD) ? -EINVAL : 0; } +/* If set, devices may be suspended and resumed asynchronously. */ +int pm_async_enabled =1 ; + +static ssize_t pm_async_show (struct kobject *kobj,struct kobj_attribute *attr, +char *buf) +{ +return sprintf (buf,"%d\n" , pm_async_enabled); +} + +static ssize_t pm_async_store (struct kobject *kobj,struct kobj_attribute *attr, +const char *buf,size_t n) +{ +unsigned long val; + +if (strict_strtoul (buf,10 , &val)) +return -EINVAL ; + +if (val >1 ) +return -EINVAL ; + + pm_async_enabled = val; +return n; +} + +power_attr (pm_async); +#ifdef CONFIG_PM_DEBUG int pm_test_level =TEST_NONE ;
@@ -208,9 +234,12 @@ static struct attribute * g[] = {#ifdef CONFIG_PM_TRACE &pm_trace_attr.attr,#endif -#if defined(CONFIG_PM_SLEEP ) && defined(CONFIG_PM_DEBUG ) +#ifdef CONFIG_PM_SLEEP + &pm_async_attr.attr, +#ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif +#endif NULL, };
drivers/base/power/main.c +7 -6
@@ -235,7 +235,7 @@ static int device_pm_wait_fn(struct devi*/ static void device_pm_wait_for_masters (struct device *slave) { -if (!pm_trace_enabled) +if (pm_async_enabled && !pm_trace_enabled)device_for_each_master (slave, slave, device_pm_wait_fn); }
@@ -245,7 +245,8 @@ static void device_pm_wait_for_masters(s*/ static void device_pm_wait_for_slaves (struct device *master) { -device_for_each_slave (master, master, device_pm_wait_fn); +if (pm_async_enabled) +device_for_each_slave (master, master, device_pm_wait_fn); }/**
@@ -560,7 +561,7 @@ static int device_resume_noirq(struct deif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend && !pm_trace_enabled) { +if (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {async_schedule (async_resume_noirq, dev);return 0 ; }
@@ -719,7 +720,7 @@ static int device_resume(struct device *if (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend && !pm_trace_enabled) { +if (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_resume, dev);return 0 ;
@@ -965,7 +966,7 @@ static int device_suspend_noirq(struct dif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend) { +if (pm_async_enabled && dev->power.async_suspend) {async_schedule (async_suspend_noirq, dev);return 0 ; }
@@ -1139,7 +1140,7 @@ static int device_suspend(struct deviceif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend) { +if (pm_async_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_suspend, dev);return 0 ;
drivers/base/power/power.h +3 -3
@@ -16,10 +16,10 @@ static inline void pm_runtime_remove(str#ifdef CONFIG_PM_SLEEP -/* -* main.c -*/ +/* kernel/power/main.c */ +extern int pm_async_enabled; +/* drivers/base/power/main.c */ extern struct list_head dpm_list;/* The active device list */ static inline struct device *to_device (struct list_head *entry)
PATCH 1/1 PM: Add a switch for disabling/enabling asynchronous suspend/resume
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Add sysfs attribute kernel/power/pm_async allowing the user space to disable and enable async suspend/resume of devices.
--- drivers/base/power/main.c | 13 +++++++------ drivers/base/power/power.h | 6 +++--- kernel/power/main.c | 28 +++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 10 deletions(-)
kernel/power/main.c +27 -1
@@ -44,6 +44,29 @@ int pm_notifier_call_chain(unsigned long == NOTIFY_BAD) ? -EINVAL : 0; } +/* If set, devices may be suspended and resumed asynchronously. */ +int pm_async_enabled =1 ; + +static ssize_t pm_async_show (struct kobject *kobj,struct kobj_attribute *attr, +char *buf) +{ +return sprintf (buf,"%d\n" , pm_async_enabled); +} + +static ssize_t pm_async_store (struct kobject *kobj,struct kobj_attribute *attr, +const char *buf,size_t n) +{ +int val; + +if (sscanf (buf,"%d" , &val) ==1 ) { + pm_async_enabled = !!val; +return n; + } +return -EINVAL ; +} + +power_attr (pm_async); +#ifdef CONFIG_PM_DEBUG int pm_test_level =TEST_NONE ;
@@ -208,9 +231,12 @@ static struct attribute * g[] = {#ifdef CONFIG_PM_TRACE &pm_trace_attr.attr,#endif -#if defined(CONFIG_PM_SLEEP ) && defined(CONFIG_PM_DEBUG ) +#ifdef CONFIG_PM_SLEEP + &pm_async_attr.attr, +#ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif +#endif NULL, };
drivers/base/power/main.c +7 -6
@@ -235,7 +235,7 @@ static int device_pm_wait_fn(struct devi*/ static void device_pm_wait_for_masters (struct device *slave) { -if (!pm_trace_enabled) +if (pm_async_enabled && !pm_trace_enabled)device_for_each_master (slave, slave, device_pm_wait_fn); }
@@ -245,7 +245,8 @@ static void device_pm_wait_for_masters(s*/ static void device_pm_wait_for_slaves (struct device *master) { -device_for_each_slave (master, master, device_pm_wait_fn); +if (pm_async_enabled) +device_for_each_slave (master, master, device_pm_wait_fn); }/**
@@ -560,7 +561,7 @@ static int device_resume_noirq(struct deif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend && !pm_trace_enabled) { +if (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {async_schedule (async_resume_noirq, dev);return 0 ; }
@@ -719,7 +720,7 @@ static int device_resume(struct device *if (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend && !pm_trace_enabled) { +if (pm_async_enabled && !pm_trace_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_resume, dev);return 0 ;
@@ -965,7 +966,7 @@ static int device_suspend_noirq(struct dif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend) { +if (pm_async_enabled && dev->power.async_suspend) {async_schedule (async_suspend_noirq, dev);return 0 ; }
@@ -1139,7 +1140,7 @@ static int device_suspend(struct deviceif (pm_op_started (dev))return 0 ; -if (dev->power.async_suspend) { +if (pm_async_enabled && dev->power.async_suspend) {get_device (dev);async_schedule (async_suspend, dev);return 0 ;
drivers/base/power/power.h +3 -3
@@ -16,10 +16,10 @@ static inline void pm_runtime_remove(str#ifdef CONFIG_PM_SLEEP -/* -* main.c -*/ +/* kernel/power/main.c */ +extern int pm_async_enabled; +/* drivers/base/power/main.c */ extern struct list_head dpm_list;/* The active device list */ static inline struct device *to_device (struct list_head *entry)
PATCH 1/1 PM: Allow user space to change the power.async_suspend flag of devices
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if necessary.
--- drivers/base/power/sysfs.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 5 ++++ 2 files changed, 52 insertions(+)
drivers/base/power/sysfs.c +47 -0
@@ -38,6 +38,22 @@* wakeup events internally (unless they are disabled), keeping * their hardware in low power modes whenever they're unused. This * saves runtime power, without requiring system-wide sleep states. +* +* async - Report/change current async suspend setting for the device +* +* If set, the PM core will attempt to suspend and resume the device during +* system power transitions (e.g. suspend to RAM, hibernation) in parallel +* with other devices it doesn't appear to depend on (to the PM core's +* knowledge). +* +* + "enabled\n" to permit the asynchronous suspend/resume of the device +* + "disabled\n" to forbid it +* +* NOTE: It generally is unsafe to permit the asynchronous suspend/resume +* of a device unless it is certain that all of the PM dependencies of the +* device are known to the PM core. However, for some devices this +* attribute is set to "enabled" by the kernel and in that cases it should +* be safe to leave the default value. */ static const char enabled[] ="enabled" ;
@@ -77,9 +93,40 @@ wake_store(struct device * dev, struct d static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); +#ifdef CONFIG_PM_SLEEP +static ssize_t async_show (struct device *dev,struct device_attribute *attr, +char *buf) +{ +return sprintf (buf,"%s\n" , +device_async_suspend_enabled (dev) ? enabled : disabled); +} + +static ssize_t async_store (struct device *dev,struct device_attribute *attr, +const char *buf,size_t n) +{ +char *cp; +int len = n; + + cp =memchr (buf,'\n' , n); +if (cp) + len = cp - buf; +if (len ==sizeof enabled -1 &&strncmp (buf, enabled, len) ==0 ) +device_enable_async_suspend (dev, true); +else if (len ==sizeof disabled -1 &&strncmp (buf, disabled, len) ==0 ) +device_enable_async_suspend (dev, false); +else +return -EINVAL ; +return n; +} + +static DEVICE_ATTR(async, 0644, async_show, async_store); +#endif /* CONFIG_PM_SLEEP */ static struct attribute * power_attrs[] = { &dev_attr_wakeup.attr, +#ifdef CONFIG_PM_SLEEP + &dev_attr_async.attr, +#endifNULL , };static struct attribute_group pm_attr_group = {
include/linux/device.h +5 -0
@@ -478,6 +478,11 @@ static inline void device_enable_async_s dev->power.async_suspend = enable; } +static inline bool device_async_suspend_enabled (struct device *dev) +{ +return !!dev->power.async_suspend; +} +void driver_init (void );/*
Dmitry Torokhov
maintainerRe: [PATCH 8] PM: Allow user space to change the power.async_suspend flag of devices
Hi Rafael,
Quoted 6 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if necessary.
I don't think we should let users meddle with individual devices; one global flag should be enough to work around "broken-at-the-moment" issues.
Signature · 1 line
Dmitry
Re: [PATCH 8] PM: Allow user space to change the power.async_suspend flag of devices
Quoted 5 lines · Dmitry Torokhov
Hi Rafael,
Quoted 2 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Quoted 8 lines
Quoted 4 lines
Add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if necessary.
I don't think we should let users meddle with individual devices; one global flag should be enough to work around "broken-at-the-moment" issues.
Well, this patch is handy for testing if anyone is interested, so I posted it.
It isn't exactly safe, though, you're right.
Thanks,
Rafael
Re: [PATCH 8] PM: Allow user space to change the power.async_suspend flag of devices
Quoted 11 lines · Rafael J. Wysocki
Quoted 10 lines · Dmitry Torokhov
Hi Rafael,
Quoted 6 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if necessary.
Quoted 7 lines
Quoted 3 lines
I don't think we should let users meddle with individual devices; one global flag should be enough to work around "broken-at-the-moment" issues.
Well, this patch is handy for testing if anyone is interested, so I posted it.
It isn't exactly safe, though, you're right.
I guess it would be acceptable to let the users disable it per-driver... but if we can get without that, it would be better.
(it is obviously ok for debugging).
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [PATCH 8] PM: Allow user space to change the power.async_suspend flag of devices
In reply to Rafael J. WysockiQuoted 11 lines · Rafael J. Wysocki
Quoted 10 lines · Dmitry Torokhov
Hi Rafael,
Quoted 6 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if necessary.
Quoted 7 lines
Quoted 3 lines
I don't think we should let users meddle with individual devices; one global flag should be enough to work around "broken-at-the-moment" issues.
Well, this patch is handy for testing if anyone is interested, so I posted it.
It isn't exactly safe, though, you're right.
I guess it would be acceptable to let the users disable it per-driver... but if we can get without that, it would be better.
(it is obviously ok for debugging).
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
PATCH 1/1 PM: Allow user space to change the power.async_suspend flag of devices
Quoted 16 lines · Pavel Machek
Quoted 15 lines · Rafael J. Wysocki
Quoted 13 lines · Dmitry Torokhov
Hi Rafael,
Quoted 6 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if necessary.
I don't think we should let users meddle with individual devices; one global flag should be enough to work around "broken-at-the-moment" issues.
Quoted 8 lines
Quoted 3 lines
Well, this patch is handy for testing if anyone is interested, so I posted it.
It isn't exactly safe, though, you're right.
I guess it would be acceptable to let the users disable it per-driver... but if we can get without that, it would be better.
(it is obviously ok for debugging).
Hmm. What about the appended patch, then?
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Add facility for advanced testing of async suspend/resume
Add configuration switch CONFIG_PM_ADVANCED_DEBUG for compiling in extra PM debugging/testing code allowing one to access some PM-related attributes of devices from the user space via sysfs.
If CONFIG_PM_ADVANCED_DEBUG is set, add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if desired.
--- drivers/base/power/sysfs.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 5 ++++ kernel/power/Kconfig | 14 +++++++++++++ 3 files changed, 66 insertions(+)
drivers/base/power/sysfs.c +47 -0
@@ -38,6 +38,22 @@* wakeup events internally (unless they are disabled), keeping * their hardware in low power modes whenever they're unused. This * saves runtime power, without requiring system-wide sleep states. +* +* async - Report/change current async suspend setting for the device +* +* If set, the PM core will attempt to suspend and resume the device during +* system power transitions (e.g. suspend to RAM, hibernation) in parallel +* with other devices it doesn't appear to depend on (to the PM core's +* knowledge). +* +* + "enabled\n" to permit the asynchronous suspend/resume of the device +* + "disabled\n" to forbid it +* +* NOTE: It generally is unsafe to permit the asynchronous suspend/resume +* of a device unless it is certain that all of the PM dependencies of the +* device are known to the PM core. However, for some devices this +* attribute is set to "enabled" by the kernel and in that cases it should +* be safe to leave the default value. */ static const char enabled[] ="enabled" ;
@@ -77,9 +93,40 @@ wake_store(struct device * dev, struct d static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); +#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG +static ssize_t async_show (struct device *dev,struct device_attribute *attr, +char *buf) +{ +return sprintf (buf,"%s\n" , +device_async_suspend_enabled (dev) ? enabled : disabled); +} + +static ssize_t async_store (struct device *dev,struct device_attribute *attr, +const char *buf,size_t n) +{ +char *cp; +int len = n; + + cp =memchr (buf,'\n' , n); +if (cp) + len = cp - buf; +if (len ==sizeof enabled -1 &&strncmp (buf, enabled, len) ==0 ) +device_enable_async_suspend (dev, true); +else if (len ==sizeof disabled -1 &&strncmp (buf, disabled, len) ==0 ) +device_enable_async_suspend (dev, false); +else +return -EINVAL ; +return n; +} + +static DEVICE_ATTR(async, 0644, async_show, async_store); +#endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */ static struct attribute * power_attrs[] = { &dev_attr_wakeup.attr, +#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG + &dev_attr_async.attr, +#endifNULL , };static struct attribute_group pm_attr_group = {
include/linux/device.h +5 -0
@@ -478,6 +478,11 @@ static inline void device_enable_async_s dev->power.async_suspend = enable; } +static inline bool device_async_suspend_enabled (struct device *dev) +{ +return !!dev->power.async_suspend; +} +void driver_init (void );/*
kernel/power/Kconfig +14 -0
@@ -27,6 +27,15 @@ config PM_DEBUG code. This is helpful when debugging and reporting PM bugs, like suspend support. +config PM_ADVANCED_DEBUG + bool "Extra PM attributes in sysfs for low-level debugging/testing" + depends on PM_DEBUG + default n + ---help--- + Add extra sysfs attributes allowing one to access some Power Management + fields of device objects from user space. If you are not a kernel + developer interested in debugging/testing Power Management, say "no". + config PM_VERBOSE bool "Verbose Power Management debugging" depends on PM_DEBUG
@@ -85,6 +94,11 @@ config PM_SLEEP depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE default y +config PM_SLEEP_ADVANCED_DEBUG + bool + depends on PM_ADVANCED_DEBUG + default n + config SUSPEND bool "Suspend to RAM and standby" depends on PM && ARCH_SUSPEND_POSSIBLE
PATCH 1/1 PM: Allow user space to change the power.async_suspend flag of devices
In reply to Pavel MachekQuoted 16 lines · Pavel Machek
Quoted 15 lines · Rafael J. Wysocki
Quoted 13 lines · Dmitry Torokhov
Hi Rafael,
Quoted 6 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if necessary.
I don't think we should let users meddle with individual devices; one global flag should be enough to work around "broken-at-the-moment" issues.
Quoted 8 lines
Quoted 3 lines
Well, this patch is handy for testing if anyone is interested, so I posted it.
It isn't exactly safe, though, you're right.
I guess it would be acceptable to let the users disable it per-driver... but if we can get without that, it would be better.
(it is obviously ok for debugging).
Hmm. What about the appended patch, then?
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Add facility for advanced testing of async suspend/resume
Add configuration switch CONFIG_PM_ADVANCED_DEBUG for compiling in extra PM debugging/testing code allowing one to access some PM-related attributes of devices from the user space via sysfs.
If CONFIG_PM_ADVANCED_DEBUG is set, add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if desired.
--- drivers/base/power/sysfs.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 5 ++++ kernel/power/Kconfig | 14 +++++++++++++ 3 files changed, 66 insertions(+)
drivers/base/power/sysfs.c +47 -0
@@ -38,6 +38,22 @@* wakeup events internally (unless they are disabled), keeping * their hardware in low power modes whenever they're unused. This * saves runtime power, without requiring system-wide sleep states. +* +* async - Report/change current async suspend setting for the device +* +* If set, the PM core will attempt to suspend and resume the device during +* system power transitions (e.g. suspend to RAM, hibernation) in parallel +* with other devices it doesn't appear to depend on (to the PM core's +* knowledge). +* +* + "enabled\n" to permit the asynchronous suspend/resume of the device +* + "disabled\n" to forbid it +* +* NOTE: It generally is unsafe to permit the asynchronous suspend/resume +* of a device unless it is certain that all of the PM dependencies of the +* device are known to the PM core. However, for some devices this +* attribute is set to "enabled" by the kernel and in that cases it should +* be safe to leave the default value. */ static const char enabled[] ="enabled" ;
@@ -77,9 +93,40 @@ wake_store(struct device * dev, struct d static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); +#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG +static ssize_t async_show (struct device *dev,struct device_attribute *attr, +char *buf) +{ +return sprintf (buf,"%s\n" , +device_async_suspend_enabled (dev) ? enabled : disabled); +} + +static ssize_t async_store (struct device *dev,struct device_attribute *attr, +const char *buf,size_t n) +{ +char *cp; +int len = n; + + cp =memchr (buf,'\n' , n); +if (cp) + len = cp - buf; +if (len ==sizeof enabled -1 &&strncmp (buf, enabled, len) ==0 ) +device_enable_async_suspend (dev, true); +else if (len ==sizeof disabled -1 &&strncmp (buf, disabled, len) ==0 ) +device_enable_async_suspend (dev, false); +else +return -EINVAL ; +return n; +} + +static DEVICE_ATTR(async, 0644, async_show, async_store); +#endif /* CONFIG_PM_SLEEP_ADVANCED_DEBUG */ static struct attribute * power_attrs[] = { &dev_attr_wakeup.attr, +#ifdef CONFIG_PM_SLEEP_ADVANCED_DEBUG + &dev_attr_async.attr, +#endifNULL , };static struct attribute_group pm_attr_group = {
include/linux/device.h +5 -0
@@ -478,6 +478,11 @@ static inline void device_enable_async_s dev->power.async_suspend = enable; } +static inline bool device_async_suspend_enabled (struct device *dev) +{ +return !!dev->power.async_suspend; +} +void driver_init (void );/*
kernel/power/Kconfig +14 -0
@@ -27,6 +27,15 @@ config PM_DEBUG code. This is helpful when debugging and reporting PM bugs, like suspend support. +config PM_ADVANCED_DEBUG + bool "Extra PM attributes in sysfs for low-level debugging/testing" + depends on PM_DEBUG + default n + ---help--- + Add extra sysfs attributes allowing one to access some Power Management + fields of device objects from user space. If you are not a kernel + developer interested in debugging/testing Power Management, say "no". + config PM_VERBOSE bool "Verbose Power Management debugging" depends on PM_DEBUG
@@ -85,6 +94,11 @@ config PM_SLEEP depends on SUSPEND || HIBERNATION || XEN_SAVE_RESTORE default y +config PM_SLEEP_ADVANCED_DEBUG + bool + depends on PM_ADVANCED_DEBUG + default n + config SUSPEND bool "Suspend to RAM and standby" depends on PM && ARCH_SUSPEND_POSSIBLE
Re: [PATCH 8] PM: Allow user space to change the power.async_suspend flag of devices
In reply to Dmitry TorokhovQuoted 5 lines · Dmitry Torokhov
Hi Rafael,
Quoted 2 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Quoted 8 lines
Quoted 4 lines
Add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if necessary.
I don't think we should let users meddle with individual devices; one global flag should be enough to work around "broken-at-the-moment" issues.
Well, this patch is handy for testing if anyone is interested, so I posted it.
It isn't exactly safe, though, you're right.
Thanks,
Rafael
Dmitry Torokhov
maintainerRe: [PATCH 8] PM: Allow user space to change the power.async_suspend flag of devices
In reply to Rafael J. WysockiHi Rafael,
Quoted 6 lines · Rafael J. Wysocki
From: Rafael J. Wysocki <[email protected]>
Add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if necessary.
I don't think we should let users meddle with individual devices; one global flag should be enough to work around "broken-at-the-moment" issues.
Signature · 1 line
Dmitry
PATCH 1/1 PM: Allow user space to change the power.async_suspend flag of devices
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Add sysfs attribute power/async for every device allowing the user space to access the device's power.async_suspend flag and modify it, if necessary.
--- drivers/base/power/sysfs.c | 47 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 5 ++++ 2 files changed, 52 insertions(+)
drivers/base/power/sysfs.c +47 -0
@@ -38,6 +38,22 @@* wakeup events internally (unless they are disabled), keeping * their hardware in low power modes whenever they're unused. This * saves runtime power, without requiring system-wide sleep states. +* +* async - Report/change current async suspend setting for the device +* +* If set, the PM core will attempt to suspend and resume the device during +* system power transitions (e.g. suspend to RAM, hibernation) in parallel +* with other devices it doesn't appear to depend on (to the PM core's +* knowledge). +* +* + "enabled\n" to permit the asynchronous suspend/resume of the device +* + "disabled\n" to forbid it +* +* NOTE: It generally is unsafe to permit the asynchronous suspend/resume +* of a device unless it is certain that all of the PM dependencies of the +* device are known to the PM core. However, for some devices this +* attribute is set to "enabled" by the kernel and in that cases it should +* be safe to leave the default value. */ static const char enabled[] ="enabled" ;
@@ -77,9 +93,40 @@ wake_store(struct device * dev, struct d static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); +#ifdef CONFIG_PM_SLEEP +static ssize_t async_show (struct device *dev,struct device_attribute *attr, +char *buf) +{ +return sprintf (buf,"%s\n" , +device_async_suspend_enabled (dev) ? enabled : disabled); +} + +static ssize_t async_store (struct device *dev,struct device_attribute *attr, +const char *buf,size_t n) +{ +char *cp; +int len = n; + + cp =memchr (buf,'\n' , n); +if (cp) + len = cp - buf; +if (len ==sizeof enabled -1 &&strncmp (buf, enabled, len) ==0 ) +device_enable_async_suspend (dev, true); +else if (len ==sizeof disabled -1 &&strncmp (buf, disabled, len) ==0 ) +device_enable_async_suspend (dev, false); +else +return -EINVAL ; +return n; +} + +static DEVICE_ATTR(async, 0644, async_show, async_store); +#endif /* CONFIG_PM_SLEEP */ static struct attribute * power_attrs[] = { &dev_attr_wakeup.attr, +#ifdef CONFIG_PM_SLEEP + &dev_attr_async.attr, +#endifNULL , };static struct attribute_group pm_attr_group = {
include/linux/device.h +5 -0
@@ -478,6 +478,11 @@ static inline void device_enable_async_s dev->power.async_suspend = enable; } +static inline bool device_async_suspend_enabled (struct device *dev) +{ +return !!dev->power.async_suspend; +} +void driver_init (void );/*
Quoted 9 lines · Rafael J. Wysocki
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
The idea is explained in the changelogs of the first two patches.
Comments welcome.
Forgot to say, the patchset is on top of the linux-next branch of suspend-2.6.
Thanks,
Rafael
Quoted 9 lines · Rafael J. Wysocki
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
The idea is explained in the changelogs of the first two patches.
Comments welcome.
I've been terribly busy and haven't had a chance to look at this. The earlier version seemed to have a bunch of mutual-exclusion issues; are they resolved now? There were also some problems involving unsafe iteration over the dpm_list -- remember that devices can be unregistered at any time, not just while they are suspending or resuming.
When a device finishes, instead of having the async thread look for another device to work on, I think it would be better to have the thread check the dependents of the current device. Those which are now ready can be added to a "device-ready" list, which is used to direct the actions of new async threads.
Alan Stern
Quoted 8 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
Quoted 7 lines
Quoted 3 lines
The idea is explained in the changelogs of the first two patches.
Comments welcome.
I've been terribly busy and haven't had a chance to look at this. The earlier version seemed to have a bunch of mutual-exclusion issues; are they resolved now?
Please be more specific.
Quoted 3 lines
There were also some problems involving unsafe iteration over the
dpm_list-- remember that devices can be unregistered at any time, not just while they are suspending or resuming.
Not at the time we're holding dpm_list_mtx, though.
I don't think there are any unsafe iterations over dpm_list in the patches.
Quoted 5 lines
When a device finishes, instead of having the async thread look for another device to work on, I think it would be better to have the thread check the dependents of the current device. Those which are now ready can be added to a "device-ready" list, which is used to direct the actions of new async threads.
Actually, I wanted to avoid adding such a list, because that would be duplicating of the async framework's actions, to some extent. Yes, we can duplicate the async framework just for the suspend/resume needs. No, I don't think it's worth it.
BTW, the patches have been tested on a dual-core box and I haven't seen any problems with them so far. Also, the testing shows that the waiting for devices with unsatisfied dependencies is almost eliminated (on my test box it happens 4-8 times during the entire suspend-resume cycle).
Thanks,
Rafael
Quoted 8 lines · Alan Stern
Quoted 6 lines · Rafael J. Wysocki
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
Quoted 7 lines
Quoted 3 lines
The idea is explained in the changelogs of the first two patches.
Comments welcome.
I've been terribly busy and haven't had a chance to look at this. The earlier version seemed to have a bunch of mutual-exclusion issues; are they resolved now?
Please be more specific.
Quoted 3 lines
There were also some problems involving unsafe iteration over the
dpm_list-- remember that devices can be unregistered at any time, not just while they are suspending or resuming.
Not at the time we're holding dpm_list_mtx, though.
I don't think there are any unsafe iterations over dpm_list in the patches.
Quoted 5 lines
When a device finishes, instead of having the async thread look for another device to work on, I think it would be better to have the thread check the dependents of the current device. Those which are now ready can be added to a "device-ready" list, which is used to direct the actions of new async threads.
Actually, I wanted to avoid adding such a list, because that would be duplicating of the async framework's actions, to some extent. Yes, we can duplicate the async framework just for the suspend/resume needs. No, I don't think it's worth it.
BTW, the patches have been tested on a dual-core box and I haven't seen any problems with them so far. Also, the testing shows that the waiting for devices with unsatisfied dependencies is almost eliminated (on my test box it happens 4-8 times during the entire suspend-resume cycle).
Thanks,
Rafael
Quoted 9 lines · Rafael J. Wysocki
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
The idea is explained in the changelogs of the first two patches.
Comments welcome.
I've been terribly busy and haven't had a chance to look at this. The earlier version seemed to have a bunch of mutual-exclusion issues; are they resolved now? There were also some problems involving unsafe iteration over the dpm_list -- remember that devices can be unregistered at any time, not just while they are suspending or resuming.
When a device finishes, instead of having the async thread look for another device to work on, I think it would be better to have the thread check the dependents of the current device. Those which are now ready can be added to a "device-ready" list, which is used to direct the actions of new async threads.
Alan Stern
PATCH 1/1 PM: Measure device suspend and resume times (was: Re: [PATCH 0/6] PM: Asynchronous suspend and resume of devices)
In reply to Rafael J. WysockiQuoted 9 lines · Rafael J. Wysocki
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
The idea is explained in the changelogs of the first two patches.
Comments welcome.
I have one more patch that adds time measurements to the code in drivers/base/power/main.c, so that one can easily see how much these operations take.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure device suspend and resume times
Measure and print the time of suspending and resuming all devices.
--- drivers/base/power/main.c | 56 +++++++++++++++++++++++++++++++++++++++++++++- include/linux/pm.h | 3 ++ kernel/power/swsusp.c | 6 ---- 3 files changed, 59 insertions(+), 6 deletions(-)
kernel/power/swsusp.c +1 -5
@@ -169,14 +169,10 @@ int swsusp_swap_in_use(void)void swsusp_show_speed (struct timeval *start,struct timeval *stop,unsigned nr_pages,char *msg) { -s64 elapsed_centisecs64; -int centisecs; +int centisecs =pm_time_elapsed (start, stop);int k;int kps; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); - centisecs = elapsed_centisecs64;if (centisecs ==0 ) centisecs =1 ;/* avoid div-by-zero */ k = nr_pages * (PAGE_SIZE /1024 );
drivers/base/power/main.c +55 -1
127 lines not on this page · raw message
include/linux/pm.h +3 -0
@@ -505,6 +505,9 @@ extern int sysdev_suspend(pm_message_t sextern int dpm_suspend_noirq (pm_message_t state);extern int dpm_suspend_start (pm_message_t state); +struct timeval ; +extern int pm_time_elapsed (struct timeval *start,struct timeval *stop); +extern void __suspend_report_result (const char *function,void *fn,int ret);#define suspend_report_result (fn, ret) \
PATCH 1/1 PM: Measure device suspend and resume times (was: Re: [PATCH 0/6] PM: Asynchronous suspend and resume of devices)
In reply to Rafael J. WysockiQuoted 9 lines · Rafael J. Wysocki
Hi,
The following patches introduce a mechanism allowing us to execute device drivers' suspend and resume callbacks asynchronously during system sleep transitions, such as suspend to RAM.
The idea is explained in the changelogs of the first two patches.
Comments welcome.
I have one more patch that adds time measurements to the code in drivers/base/power/main.c, so that one can easily see how much these operations take.
Thanks,
Rafael
---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Measure device suspend and resume times
Measure and print the time of suspending and resuming all devices.
--- drivers/base/power/main.c | 56 +++++++++++++++++++++++++++++++++++++++++++++- include/linux/pm.h | 3 ++ kernel/power/swsusp.c | 6 ---- 3 files changed, 59 insertions(+), 6 deletions(-)
kernel/power/swsusp.c +1 -5
@@ -169,14 +169,10 @@ int swsusp_swap_in_use(void)void swsusp_show_speed (struct timeval *start,struct timeval *stop,unsigned nr_pages,char *msg) { -s64 elapsed_centisecs64; -int centisecs; +int centisecs =pm_time_elapsed (start, stop);int k;int kps; - elapsed_centisecs64 =timeval_to_ns (stop) -timeval_to_ns (start); -do_div (elapsed_centisecs64,NSEC_PER_SEC /100 ); - centisecs = elapsed_centisecs64;if (centisecs ==0 ) centisecs =1 ;/* avoid div-by-zero */ k = nr_pages * (PAGE_SIZE /1024 );
drivers/base/power/main.c +55 -1
127 lines not on this page · raw message
include/linux/pm.h +3 -0
@@ -505,6 +505,9 @@ extern int sysdev_suspend(pm_message_t sextern int dpm_suspend_noirq (pm_message_t state);extern int dpm_suspend_start (pm_message_t state); +struct timeval ; +extern int pm_time_elapsed (struct timeval *start,struct timeval *stop); +extern void __suspend_report_result (const char *function,void *fn,int ret);#define suspend_report_result (fn, ret) \