From 11048dcf333c414f237bb713c422e68f67b115a3 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 23 May 2007 14:19:41 -0700 Subject: Power Management: use mutexes instead of semaphores The Power Management code uses semaphores as mutexes. Use the mutex API instead of the (binary) semaphores. Signed-off-by: Matthias Kaehlcke Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/main.c | 14 ++++++++------ drivers/base/power/power.h | 4 ++-- drivers/base/power/resume.c | 12 ++++++------ drivers/base/power/runtime.c | 12 ++++++------ drivers/base/power/suspend.c | 14 +++++++------- 5 files changed, 29 insertions(+), 27 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 05dc8764e765..7b3cc3c15b9d 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -20,14 +20,16 @@ */ #include +#include + #include "power.h" LIST_HEAD(dpm_active); LIST_HEAD(dpm_off); LIST_HEAD(dpm_off_irq); -DECLARE_MUTEX(dpm_sem); -DECLARE_MUTEX(dpm_list_sem); +DEFINE_MUTEX(dpm_mtx); +DEFINE_MUTEX(dpm_list_mtx); int (*platform_enable_wakeup)(struct device *dev, int is_on); @@ -59,12 +61,12 @@ int device_pm_add(struct device * dev) pr_debug("PM: Adding info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", kobject_name(&dev->kobj)); - down(&dpm_list_sem); + mutex_lock(&dpm_list_mtx); list_add_tail(&dev->power.entry, &dpm_active); device_pm_set_parent(dev, dev->parent); if ((error = dpm_sysfs_add(dev))) list_del(&dev->power.entry); - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); return error; } @@ -73,11 +75,11 @@ void device_pm_remove(struct device * dev) pr_debug("PM: Removing info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", kobject_name(&dev->kobj)); - down(&dpm_list_sem); + mutex_lock(&dpm_list_mtx); dpm_sysfs_remove(dev); put_device(dev->power.pm_parent); list_del_init(&dev->power.entry); - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); } diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index fb3d35a9e101..2760f25b3ac5 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -14,12 +14,12 @@ extern void device_shutdown(void); /* * Used to synchronize global power management operations. */ -extern struct semaphore dpm_sem; +extern struct mutex dpm_mtx; /* * Used to serialize changes to the dpm_* lists. */ -extern struct semaphore dpm_list_sem; +extern struct mutex dpm_list_mtx; /* * The PM lists. diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index a2c64188d713..f6cfea496ea0 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -80,7 +80,7 @@ static int resume_device_early(struct device * dev) */ void dpm_resume(void) { - down(&dpm_list_sem); + mutex_lock(&dpm_list_mtx); while(!list_empty(&dpm_off)) { struct list_head * entry = dpm_off.next; struct device * dev = to_device(entry); @@ -88,13 +88,13 @@ void dpm_resume(void) get_device(dev); list_move_tail(entry, &dpm_active); - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); if (!dev->power.prev_state.event) resume_device(dev); - down(&dpm_list_sem); + mutex_lock(&dpm_list_mtx); put_device(dev); } - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); } @@ -108,9 +108,9 @@ void dpm_resume(void) void device_resume(void) { might_sleep(); - down(&dpm_sem); + mutex_lock(&dpm_mtx); dpm_resume(); - up(&dpm_sem); + mutex_unlock(&dpm_mtx); } EXPORT_SYMBOL_GPL(device_resume); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 96370ec1d673..df6174d85866 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -32,9 +32,9 @@ static void runtime_resume(struct device * dev) void dpm_runtime_resume(struct device * dev) { - down(&dpm_sem); + mutex_lock(&dpm_mtx); runtime_resume(dev); - up(&dpm_sem); + mutex_unlock(&dpm_mtx); } EXPORT_SYMBOL(dpm_runtime_resume); @@ -49,7 +49,7 @@ int dpm_runtime_suspend(struct device * dev, pm_message_t state) { int error = 0; - down(&dpm_sem); + mutex_lock(&dpm_mtx); if (dev->power.power_state.event == state.event) goto Done; @@ -59,7 +59,7 @@ int dpm_runtime_suspend(struct device * dev, pm_message_t state) if (!(error = suspend_device(dev, state))) dev->power.power_state = state; Done: - up(&dpm_sem); + mutex_unlock(&dpm_mtx); return error; } EXPORT_SYMBOL(dpm_runtime_suspend); @@ -78,8 +78,8 @@ EXPORT_SYMBOL(dpm_runtime_suspend); */ void dpm_set_power_state(struct device * dev, pm_message_t state) { - down(&dpm_sem); + mutex_lock(&dpm_mtx); dev->power.power_state = state; - up(&dpm_sem); + mutex_unlock(&dpm_mtx); } #endif /* 0 */ diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 42d2b86ba765..9d6701cd7f10 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -108,7 +108,7 @@ int suspend_device(struct device * dev, pm_message_t state) /* * This is called with interrupts off, only a single CPU - * running. We can't do down() on a semaphore (and we don't + * running. We can't acquire a mutex or semaphore (and we don't * need the protection) */ static int suspend_device_late(struct device *dev, pm_message_t state) @@ -153,18 +153,18 @@ int device_suspend(pm_message_t state) int error = 0; might_sleep(); - down(&dpm_sem); - down(&dpm_list_sem); + mutex_lock(&dpm_mtx); + mutex_lock(&dpm_list_mtx); while (!list_empty(&dpm_active) && error == 0) { struct list_head * entry = dpm_active.prev; struct device * dev = to_device(entry); get_device(dev); - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); error = suspend_device(dev, state); - down(&dpm_list_sem); + mutex_lock(&dpm_list_mtx); /* Check if the device got removed */ if (!list_empty(&dev->power.entry)) { @@ -179,11 +179,11 @@ int device_suspend(pm_message_t state) error == -EAGAIN ? " (please convert to suspend_late)" : ""); put_device(dev); } - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); if (error) dpm_resume(); - up(&dpm_sem); + mutex_unlock(&dpm_mtx); return error; } -- cgit v1.2.2 From 9cddad77574313fcee36c5e60122718daa7c0361 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 13 Jun 2007 15:53:34 +0200 Subject: PM: Remove pm_parent from struct dev_pm_info The pm_parent member of struct dev_pm_info (defined in include/linux/pm.h) is only used to check if the device's parent is in the right state while the device is being suspended or resumed. However, this can be done just as well with the help of the parent pointer in struct device, so pm_parent can be removed along with some code that handles it. Signed-off-by: Rafael J. Wysocki Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/main.c | 30 ++++-------------------------- drivers/base/power/resume.c | 7 +++---- drivers/base/power/suspend.c | 7 +++---- 3 files changed, 10 insertions(+), 34 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 7b3cc3c15b9d..eb9f38d0aa58 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -33,28 +33,7 @@ DEFINE_MUTEX(dpm_list_mtx); int (*platform_enable_wakeup)(struct device *dev, int is_on); - -/** - * device_pm_set_parent - Specify power dependency. - * @dev: Device who needs power. - * @parent: Device that supplies power. - * - * This function is used to manually describe a power-dependency - * relationship. It may be used to specify a transversal relationship - * (where the power supplier is not the physical (or electrical) - * ancestor of a specific device. - * The effect of this is that the supplier will not be powered down - * before the power dependent. - */ - -void device_pm_set_parent(struct device * dev, struct device * parent) -{ - put_device(dev->power.pm_parent); - dev->power.pm_parent = get_device(parent); -} -EXPORT_SYMBOL_GPL(device_pm_set_parent); - -int device_pm_add(struct device * dev) +int device_pm_add(struct device *dev) { int error; @@ -63,21 +42,20 @@ int device_pm_add(struct device * dev) kobject_name(&dev->kobj)); mutex_lock(&dpm_list_mtx); list_add_tail(&dev->power.entry, &dpm_active); - device_pm_set_parent(dev, dev->parent); - if ((error = dpm_sysfs_add(dev))) + error = dpm_sysfs_add(dev); + if (error) list_del(&dev->power.entry); mutex_unlock(&dpm_list_mtx); return error; } -void device_pm_remove(struct device * dev) +void device_pm_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); dpm_sysfs_remove(dev); - put_device(dev->power.pm_parent); list_del_init(&dev->power.entry); mutex_unlock(&dpm_list_mtx); } diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index f6cfea496ea0..99679e7a6cc6 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -29,12 +29,11 @@ int resume_device(struct device * dev) down(&dev->sem); - if (dev->power.pm_parent - && dev->power.pm_parent->power.power_state.event) { + if (dev->parent && dev->parent->power.power_state.event) { dev_err(dev, "PM: resume from %d, parent %s still %d\n", dev->power.power_state.event, - dev->power.pm_parent->bus_id, - dev->power.pm_parent->power.power_state.event); + dev->parent->bus_id, + dev->parent->power.power_state.event); } if (dev->bus && dev->bus->resume) { diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 9d6701cd7f10..19fae88de7b3 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -55,13 +55,12 @@ int suspend_device(struct device * dev, pm_message_t state) dev_dbg(dev, "PM: suspend %d-->%d\n", dev->power.power_state.event, state.event); } - if (dev->power.pm_parent - && dev->power.pm_parent->power.power_state.event) { + if (dev->parent && dev->parent->power.power_state.event) { dev_err(dev, "PM: suspend %d->%d, parent %s already %d\n", dev->power.power_state.event, state.event, - dev->power.pm_parent->bus_id, - dev->power.pm_parent->power.power_state.event); + dev->parent->bus_id, + dev->parent->power.power_state.event); } dev->power.prev_state = dev->power.power_state; -- cgit v1.2.2 From 9e584a4fe58881e2dc8f09bdf8444b199ab5ecaf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 13 Jun 2007 16:19:27 +0200 Subject: PM: Simplify suspend_device Reduce code duplication in drivers/base/suspend.c by introducing a separate function for printing diagnostic messages. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/suspend.c | 49 ++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 19fae88de7b3..af2cedfbc1b4 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -40,6 +40,14 @@ static inline char *suspend_verb(u32 event) } +static void +suspend_device_dbg(struct device *dev, pm_message_t state, char *info) +{ + dev_dbg(dev, "%s%s%s\n", info, suspend_verb(state.event), + ((state.event == PM_EVENT_SUSPEND) && device_may_wakeup(dev)) ? + ", may wakeup" : ""); +} + /** * suspend_device - Save state of one device. * @dev: Device. @@ -66,37 +74,21 @@ int suspend_device(struct device * dev, pm_message_t state) dev->power.prev_state = dev->power.power_state; if (dev->class && dev->class->suspend && !dev->power.power_state.event) { - dev_dbg(dev, "class %s%s\n", - suspend_verb(state.event), - ((state.event == PM_EVENT_SUSPEND) - && device_may_wakeup(dev)) - ? ", may wakeup" - : "" - ); + suspend_device_dbg(dev, state, "class "); error = dev->class->suspend(dev, state); suspend_report_result(dev->class->suspend, error); } - if (!error && dev->type && dev->type->suspend && !dev->power.power_state.event) { - dev_dbg(dev, "%s%s\n", - suspend_verb(state.event), - ((state.event == PM_EVENT_SUSPEND) - && device_may_wakeup(dev)) - ? ", may wakeup" - : "" - ); + if (!error && dev->type && dev->type->suspend + && !dev->power.power_state.event) { + suspend_device_dbg(dev, state, "type "); error = dev->type->suspend(dev, state); suspend_report_result(dev->type->suspend, error); } - if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) { - dev_dbg(dev, "%s%s\n", - suspend_verb(state.event), - ((state.event == PM_EVENT_SUSPEND) - && device_may_wakeup(dev)) - ? ", may wakeup" - : "" - ); + if (!error && dev->bus && dev->bus->suspend + && !dev->power.power_state.event) { + suspend_device_dbg(dev, state, ""); error = dev->bus->suspend(dev, state); suspend_report_result(dev->bus->suspend, error); } @@ -114,14 +106,9 @@ static int suspend_device_late(struct device *dev, pm_message_t state) { int error = 0; - if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) { - dev_dbg(dev, "LATE %s%s\n", - suspend_verb(state.event), - ((state.event == PM_EVENT_SUSPEND) - && device_may_wakeup(dev)) - ? ", may wakeup" - : "" - ); + if (dev->bus && dev->bus->suspend_late + && !dev->power.power_state.event) { + suspend_device_dbg(dev, state, "LATE "); error = dev->bus->suspend_late(dev, state); suspend_report_result(dev->bus->suspend_late, error); } -- cgit v1.2.2 From 515c53576299e32d6bdb6295cfa2fe1307516eb4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 17 Jun 2007 19:48:06 +0200 Subject: PM: Remove prev_state from struct dev_pm_info The prev_state member of struct dev_pm_info (defined in include/linux/pm.h) is only used during a resume to check if the device's state before the suspend was 'off', in which case the device is not resumed. However, in such cases the decision whether or not to resume the device should be made on the driver level and the resume callbacks from the device's bus and class should be executed anyway (the may be needed for some things other than just powering on the device). Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/resume.c | 3 +-- drivers/base/power/suspend.c | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 99679e7a6cc6..0c9610688542 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -88,8 +88,7 @@ void dpm_resume(void) list_move_tail(entry, &dpm_active); mutex_unlock(&dpm_list_mtx); - if (!dev->power.prev_state.event) - resume_device(dev); + resume_device(dev); mutex_lock(&dpm_list_mtx); put_device(dev); } diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index af2cedfbc1b4..5178b0fbd82e 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -71,8 +71,6 @@ int suspend_device(struct device * dev, pm_message_t state) dev->parent->power.power_state.event); } - dev->power.prev_state = dev->power.power_state; - if (dev->class && dev->class->suspend && !dev->power.power_state.event) { suspend_device_dbg(dev, state, "class "); error = dev->class->suspend(dev, state); -- cgit v1.2.2 From 1c3f7d1c79425dd8f627b8da139fd1855f096b6e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 17 Jun 2007 19:49:20 +0200 Subject: PM: Remove power_state.event checks from suspend core code The suspend routines should be called for every device during a system sleep transition, regardless of the device's state, so that drivers can regard these method calls as notifications that the system is about to go to sleep, rather than as directives to put their devices into the 'off' state. This is documented in Documentation/power/devices.txt and is already done in the core resume code, so it seems reasonable to make the core suspend code behave accordingly. Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/suspend.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 5178b0fbd82e..a3ff3944832a 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -71,21 +71,19 @@ int suspend_device(struct device * dev, pm_message_t state) dev->parent->power.power_state.event); } - if (dev->class && dev->class->suspend && !dev->power.power_state.event) { + if (dev->class && dev->class->suspend) { suspend_device_dbg(dev, state, "class "); error = dev->class->suspend(dev, state); suspend_report_result(dev->class->suspend, error); } - if (!error && dev->type && dev->type->suspend - && !dev->power.power_state.event) { + if (!error && dev->type && dev->type->suspend) { suspend_device_dbg(dev, state, "type "); error = dev->type->suspend(dev, state); suspend_report_result(dev->type->suspend, error); } - if (!error && dev->bus && dev->bus->suspend - && !dev->power.power_state.event) { + if (!error && dev->bus && dev->bus->suspend) { suspend_device_dbg(dev, state, ""); error = dev->bus->suspend(dev, state); suspend_report_result(dev->bus->suspend, error); @@ -104,8 +102,7 @@ static int suspend_device_late(struct device *dev, pm_message_t state) { int error = 0; - if (dev->bus && dev->bus->suspend_late - && !dev->power.power_state.event) { + if (dev->bus && dev->bus->suspend_late) { suspend_device_dbg(dev, state, "LATE "); error = dev->bus->suspend_late(dev, state); suspend_report_result(dev->bus->suspend_late, error); -- cgit v1.2.2 From 43a49f8baa6b0e7e5b68ed0ee141e1efacfd9ae6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 17 Jun 2007 19:50:51 +0200 Subject: PM: Do not check parent state in suspend and resume core code The checks if the device's parent is in the right state done in drivers/base/power/suspend.c and drivers/base/power/resume.c serve no particular purpose, since if the parent is in a wrong power state, the device's suspend or resume callbacks are supposed to return an error anyway. Moreover, they are also useless from the sanity checking point of view, because they rely on the code being checked to set dev->parent->power.power_state.event appropriately, which need not happen if that code is buggy. For these reasons they can be removed. Signed-off-by: Rafael J. Wysocki Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/resume.c | 7 ------- drivers/base/power/suspend.c | 7 ------- 2 files changed, 14 deletions(-) (limited to 'drivers/base/power') diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 0c9610688542..00fd84ae6e66 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -29,13 +29,6 @@ int resume_device(struct device * dev) down(&dev->sem); - if (dev->parent && dev->parent->power.power_state.event) { - dev_err(dev, "PM: resume from %d, parent %s still %d\n", - dev->power.power_state.event, - dev->parent->bus_id, - dev->parent->power.power_state.event); - } - if (dev->bus && dev->bus->resume) { dev_dbg(dev,"resuming\n"); error = dev->bus->resume(dev); diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index a3ff3944832a..26df9b231737 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -63,13 +63,6 @@ int suspend_device(struct device * dev, pm_message_t state) dev_dbg(dev, "PM: suspend %d-->%d\n", dev->power.power_state.event, state.event); } - if (dev->parent && dev->parent->power.power_state.event) { - dev_err(dev, - "PM: suspend %d->%d, parent %s already %d\n", - dev->power.power_state.event, state.event, - dev->parent->bus_id, - dev->parent->power.power_state.event); - } if (dev->class && dev->class->suspend) { suspend_device_dbg(dev, state, "class "); -- cgit v1.2.2