From 6675bc056790b403d198a173498d377187754142 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Thu, 25 Nov 2010 23:41:19 +0100 Subject: PM / Runtime: Fix comments to match runtime callback code Commit 05aa55dddb9ee4045c320661068bea78dad6a6e5 changed routines to succeed if the driver handler is not defined. Comments were not updated. Signed-off-by: Aaro Koskinen Signed-off-by: Rafael J. Wysocki --- drivers/base/power/generic_ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index 81f2c84697f4..3d2c3500069a 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -39,7 +39,7 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_idle); * * If PM operations are defined for the @dev's driver and they include * ->runtime_suspend(), execute it and return its error code. Otherwise, - * return -EINVAL. + * return 0. */ int pm_generic_runtime_suspend(struct device *dev) { @@ -58,7 +58,7 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend); * * If PM operations are defined for the @dev's driver and they include * ->runtime_resume(), execute it and return its error code. Otherwise, - * return -EINVAL. + * return 0. */ int pm_generic_runtime_resume(struct device *dev) { -- cgit v1.2.3 From 133f1128b2bf178a1976b17c54bd14ce6feb90bf Mon Sep 17 00:00:00 2001 From: Tracey Dent Date: Thu, 25 Nov 2010 23:41:29 +0100 Subject: PM: Use proper ccflag flag in kernel/power/Makefile Use the ccflags-$ flag instead of EXTRA_CFLAGS because EXTRA_CFLAGS is deprecated and should now be switched. According to (documentation/kbuild/makefiles.txt). Signed-off-by: Tracey Dent Signed-off-by: Rafael J. Wysocki --- kernel/power/Makefile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kernel/power/Makefile b/kernel/power/Makefile index f9063c6b185d..b75597235d85 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -1,7 +1,4 @@ - -ifeq ($(CONFIG_PM_DEBUG),y) -EXTRA_CFLAGS += -DDEBUG -endif +ccflags-$(CONFIG_PM_DEBUG) := -DDEBUG obj-$(CONFIG_PM) += main.o obj-$(CONFIG_PM_SLEEP) += console.o -- cgit v1.2.3 From 8cfe400ca54fd1ed96f962bea5f7e20b09b6d69f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 26 Nov 2010 23:07:27 +0100 Subject: Freezer: Fix a race during freezing of TASK_STOPPED tasks After calling freeze_task(), try_to_freeze_tasks() see whether the task is stopped or traced and if so, considers it to be frozen; however, nothing guarantees that either the task being frozen sees TIF_FREEZE or the freezer sees TASK_STOPPED -> TASK_RUNNING transition. The task being frozen may wake up and not see TIF_FREEZE while the freezer fails to notice the transition and believes the task is still stopped. This patch fixes the race by making freeze_task() always go through fake_signal_wake_up() for applicable tasks. The function goes through the target task's scheduler lock and thus guarantees that either the target sees TIF_FREEZE or try_to_freeze_task() sees TASK_RUNNING. Signed-off-by: Tejun Heo Signed-off-by: Rafael J. Wysocki --- kernel/freezer.c | 9 +++++++-- kernel/power/process.c | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/kernel/freezer.c b/kernel/freezer.c index bd1d42b17cb2..66ecd2ead215 100644 --- a/kernel/freezer.c +++ b/kernel/freezer.c @@ -104,8 +104,13 @@ bool freeze_task(struct task_struct *p, bool sig_only) } if (should_send_signal(p)) { - if (!signal_pending(p)) - fake_signal_wake_up(p); + fake_signal_wake_up(p); + /* + * fake_signal_wake_up() goes through p's scheduler + * lock and guarantees that TASK_STOPPED/TRACED -> + * TASK_RUNNING transition can't race with task state + * testing in try_to_freeze_tasks(). + */ } else if (sig_only) { return false; } else { diff --git a/kernel/power/process.c b/kernel/power/process.c index e50b4c1b2a0f..eb2c88a9e562 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -64,6 +64,12 @@ static int try_to_freeze_tasks(bool sig_only) * perturb a task in TASK_STOPPED or TASK_TRACED. * It is "frozen enough". If the task does wake * up, it will immediately call try_to_freeze. + * + * Because freeze_task() goes through p's + * scheduler lock after setting TIF_FREEZE, it's + * guaranteed that either we see TASK_RUNNING or + * try_to_stop() after schedule() in ptrace/signal + * stop sees TIF_FREEZE. */ if (!task_is_stopped_or_traced(p) && !freezer_should_skip(p)) -- cgit v1.2.3 From 5729c63a51f0f8a351e0f1dc7b3250ebac12c309 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Fri, 26 Nov 2010 23:07:48 +0100 Subject: PM / Hibernate: hibernation_ops->leave should be checked too Because hibernate calls hibernation_ops->leave() without checking whether hibernation_ops->leave is NULL or not, hiberantion_set_ops should WARN_ON if hibernation_ops->leave is NULL. This patch added one more condition to check hibernation_ops->leave. Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- kernel/power/hibernate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 048d0b514831..ab2836c25038 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -62,7 +62,7 @@ void hibernation_set_ops(struct platform_hibernation_ops *ops) { if (ops && !(ops->begin && ops->end && ops->pre_snapshot && ops->prepare && ops->finish && ops->enter && ops->pre_restore - && ops->restore_cleanup)) { + && ops->restore_cleanup && ops->leave)) { WARN_ON(1); return; } -- cgit v1.2.3 From 5262a47502adcfc3a64403120768f528418a3b79 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Fri, 26 Nov 2010 23:07:56 +0100 Subject: PM / Hibernate: When failed, in_suspend should be reset When hibernation failed due to an error in swsusp_write() called by hibernate(), it skips calling "power_down()" and returns. When hibernate() is called again (probably after fixing up so that swsusp_write() wouldn't fail again), before "in_suspend = 1" of create_image is called, in_suspend should be 0. However, because hibernate() did not reset "in_suspend" after a failure, it's already 1. This patch fixes such inconsistency of "in_suspend" value. Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Rafael J. Wysocki --- kernel/power/hibernate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index ab2836c25038..c9a98beffee4 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -647,6 +647,7 @@ int hibernate(void) swsusp_free(); if (!error) power_down(); + in_suspend = 0; pm_restore_gfp_mask(); } else { pr_debug("PM: Image restored successfully.\n"); -- cgit v1.2.3 From c7b61de5b7b17f0df34dc7d2f8b9576f8bd36fce Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 1 Dec 2010 00:14:42 +0100 Subject: PM / Runtime: Add synchronous runtime interface for interrupt handlers (v3) This patch (as1431c) makes the synchronous runtime-PM interface suitable for use in interrupt handlers. Subsystems can call the new pm_runtime_irq_safe() function to tell the PM core that a device's runtime_suspend and runtime_resume callbacks should be invoked with interrupts disabled and the spinlock held. This permits the pm_runtime_get_sync() and the new pm_runtime_put_sync_suspend() routines to be called from within interrupt handlers. When a device is declared irq-safe in this way, the PM core increments the parent's usage count, so the parent will never be runtime suspended. This prevents difficult situations in which an irq-safe device can't resume because it is forced to wait for its non-irq-safe parent. Signed-off-by: Alan Stern Signed-off-by: Rafael J. Wysocki --- Documentation/power/runtime_pm.txt | 31 +++++++++++++++++++++++++ drivers/base/power/runtime.c | 47 ++++++++++++++++++++++++++++++-------- include/linux/pm.h | 1 + include/linux/pm_runtime.h | 7 ++++++ 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 41cc7b30d7dd..ffe55ffa540a 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt @@ -50,6 +50,15 @@ type's callbacks are not defined) of given device. The bus type, device type and device class callbacks are referred to as subsystem-level callbacks in what follows. +By default, the callbacks are always invoked in process context with interrupts +enabled. However, subsystems can use the pm_runtime_irq_safe() helper function +to tell the PM core that a device's ->runtime_suspend() and ->runtime_resume() +callbacks should be invoked in atomic context with interrupts disabled +(->runtime_idle() is still invoked the default way). This implies that these +callback routines must not block or sleep, but it also means that the +synchronous helper functions listed at the end of Section 4 can be used within +an interrupt handler or in an atomic context. + The subsystem-level suspend callback is _entirely_ _responsible_ for handling the suspend of the device as appropriate, which may, but need not include executing the device driver's own ->runtime_suspend() callback (from the @@ -237,6 +246,10 @@ defined in include/linux/pm.h: Section 8); it may be modified only by the pm_runtime_no_callbacks() helper function + unsigned int irq_safe; + - indicates that the ->runtime_suspend() and ->runtime_resume() callbacks + will be invoked with the spinlock held and interrupts disabled + unsigned int use_autosuspend; - indicates that the device's driver supports delayed autosuspend (see Section 9); it may be modified only by the @@ -344,6 +357,10 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: - decrement the device's usage counter; if the result is 0 then run pm_runtime_idle(dev) and return its result + int pm_runtime_put_sync_suspend(struct device *dev); + - decrement the device's usage counter; if the result is 0 then run + pm_runtime_suspend(dev) and return its result + int pm_runtime_put_sync_autosuspend(struct device *dev); - decrement the device's usage counter; if the result is 0 then run pm_runtime_autosuspend(dev) and return its result @@ -397,6 +414,11 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: PM attributes from /sys/devices/.../power (or prevent them from being added when the device is registered) + void pm_runtime_irq_safe(struct device *dev); + - set the power.irq_safe flag for the device, causing the runtime-PM + suspend and resume callbacks (but not the idle callback) to be invoked + with interrupts disabled + void pm_runtime_mark_last_busy(struct device *dev); - set the power.last_busy field to the current time @@ -438,6 +460,15 @@ pm_runtime_suspended() pm_runtime_mark_last_busy() pm_runtime_autosuspend_expiration() +If pm_runtime_irq_safe() has been called for a device then the following helper +functions may also be used in interrupt context: + +pm_runtime_suspend() +pm_runtime_autosuspend() +pm_runtime_resume() +pm_runtime_get_sync() +pm_runtime_put_sync_suspend() + 5. Run-time PM Initialization, Device Probing and Removal Initially, the run-time PM is disabled for all devices, which means that the diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 02c652be83e7..656493a5e073 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -250,13 +250,16 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) if (!cb) return -ENOSYS; - spin_unlock_irq(&dev->power.lock); + if (dev->power.irq_safe) { + retval = cb(dev); + } else { + spin_unlock_irq(&dev->power.lock); - retval = cb(dev); + retval = cb(dev); - spin_lock_irq(&dev->power.lock); + spin_lock_irq(&dev->power.lock); + } dev->power.runtime_error = retval; - return retval; } @@ -404,7 +407,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) goto out; } - if (parent && !parent->power.ignore_children) { + if (parent && !parent->power.ignore_children && !dev->power.irq_safe) { spin_unlock_irq(&dev->power.lock); pm_request_idle(parent); @@ -527,10 +530,13 @@ static int rpm_resume(struct device *dev, int rpmflags) if (!parent && dev->parent) { /* - * Increment the parent's resume counter and resume it if - * necessary. + * Increment the parent's usage counter and resume it if + * necessary. Not needed if dev is irq-safe; then the + * parent is permanently resumed. */ parent = dev->parent; + if (dev->power.irq_safe) + goto skip_parent; spin_unlock(&dev->power.lock); pm_runtime_get_noresume(parent); @@ -553,6 +559,7 @@ static int rpm_resume(struct device *dev, int rpmflags) goto out; goto repeat; } + skip_parent: if (dev->power.no_callbacks) goto no_callback; /* Assume success. */ @@ -584,7 +591,7 @@ static int rpm_resume(struct device *dev, int rpmflags) rpm_idle(dev, RPM_ASYNC); out: - if (parent) { + if (parent && !dev->power.irq_safe) { spin_unlock_irq(&dev->power.lock); pm_runtime_put(parent); @@ -1065,7 +1072,6 @@ EXPORT_SYMBOL_GPL(pm_runtime_allow); * Set the power.no_callbacks flag, which tells the PM core that this * device is power-managed through its parent and has no run-time PM * callbacks of its own. The run-time sysfs attributes will be removed. - * */ void pm_runtime_no_callbacks(struct device *dev) { @@ -1077,6 +1083,27 @@ void pm_runtime_no_callbacks(struct device *dev) } EXPORT_SYMBOL_GPL(pm_runtime_no_callbacks); +/** + * pm_runtime_irq_safe - Leave interrupts disabled during callbacks. + * @dev: Device to handle + * + * Set the power.irq_safe flag, which tells the PM core that the + * ->runtime_suspend() and ->runtime_resume() callbacks for this device should + * always be invoked with the spinlock held and interrupts disabled. It also + * causes the parent's usage counter to be permanently incremented, preventing + * the parent from runtime suspending -- otherwise an irq-safe child might have + * to wait for a non-irq-safe parent. + */ +void pm_runtime_irq_safe(struct device *dev) +{ + if (dev->parent) + pm_runtime_get_sync(dev->parent); + spin_lock_irq(&dev->power.lock); + dev->power.irq_safe = 1; + spin_unlock_irq(&dev->power.lock); +} +EXPORT_SYMBOL_GPL(pm_runtime_irq_safe); + /** * update_autosuspend - Handle a change to a device's autosuspend settings. * @dev: Device to handle. @@ -1199,4 +1226,6 @@ void pm_runtime_remove(struct device *dev) /* Change the status back to 'suspended' to match the initial status. */ if (dev->power.runtime_status == RPM_ACTIVE) pm_runtime_set_suspended(dev); + if (dev->power.irq_safe && dev->parent) + pm_runtime_put_sync(dev->parent); } diff --git a/include/linux/pm.h b/include/linux/pm.h index 40f3f45702ba..61f2066e6852 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -486,6 +486,7 @@ struct dev_pm_info { unsigned int run_wake:1; unsigned int runtime_auto:1; unsigned int no_callbacks:1; + unsigned int irq_safe:1; unsigned int use_autosuspend:1; unsigned int timer_autosuspends:1; enum rpm_request request; diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index d19f1cca7f74..e9cc049ccb62 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -40,6 +40,7 @@ extern int pm_generic_runtime_idle(struct device *dev); extern int pm_generic_runtime_suspend(struct device *dev); extern int pm_generic_runtime_resume(struct device *dev); extern void pm_runtime_no_callbacks(struct device *dev); +extern void pm_runtime_irq_safe(struct device *dev); extern void __pm_runtime_use_autosuspend(struct device *dev, bool use); extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay); extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev); @@ -124,6 +125,7 @@ static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } static inline void pm_runtime_no_callbacks(struct device *dev) {} +static inline void pm_runtime_irq_safe(struct device *dev) {} static inline void pm_runtime_mark_last_busy(struct device *dev) {} static inline void __pm_runtime_use_autosuspend(struct device *dev, @@ -196,6 +198,11 @@ static inline int pm_runtime_put_sync(struct device *dev) return __pm_runtime_idle(dev, RPM_GET_PUT); } +static inline int pm_runtime_put_sync_suspend(struct device *dev) +{ + return __pm_runtime_suspend(dev, RPM_GET_PUT); +} + static inline int pm_runtime_put_sync_autosuspend(struct device *dev) { return __pm_runtime_suspend(dev, RPM_GET_PUT | RPM_AUTO); -- cgit v1.2.3 From 7ac4dcabdb482d4e74c9d36782d00bc6c4c01619 Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Wed, 1 Dec 2010 00:14:55 +0100 Subject: PM: Fix references to basic-pm-debugging.txt in drivers-testing.txt basic-pm-debugging.txt is located in Documentation/power/ not Documents/power/. Change the references in Documentation/power/drivers-testing.txt to reflect the location. Signed-off-by: Jon Mason Signed-off-by: Rafael J. Wysocki --- Documentation/power/drivers-testing.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/power/drivers-testing.txt b/Documentation/power/drivers-testing.txt index 7f7a737f7f9f..638afdf4d6b8 100644 --- a/Documentation/power/drivers-testing.txt +++ b/Documentation/power/drivers-testing.txt @@ -23,10 +23,10 @@ Once you have resolved the suspend/resume-related problems with your test system without the new driver, you are ready to test it: a) Build the driver as a module, load it and try the test modes of hibernation - (see: Documents/power/basic-pm-debugging.txt, 1). + (see: Documentation/power/basic-pm-debugging.txt, 1). b) Load the driver and attempt to hibernate in the "reboot", "shutdown" and - "platform" modes (see: Documents/power/basic-pm-debugging.txt, 1). + "platform" modes (see: Documentation/power/basic-pm-debugging.txt, 1). c) Compile the driver directly into the kernel and try the test modes of hibernation. @@ -34,12 +34,12 @@ c) Compile the driver directly into the kernel and try the test modes of d) Attempt to hibernate with the driver compiled directly into the kernel in the "reboot", "shutdown" and "platform" modes. -e) Try the test modes of suspend (see: Documents/power/basic-pm-debugging.txt, +e) Try the test modes of suspend (see: Documentation/power/basic-pm-debugging.txt, 2). [As far as the STR tests are concerned, it should not matter whether or not the driver is built as a module.] f) Attempt to suspend to RAM using the s2ram tool with the driver loaded - (see: Documents/power/basic-pm-debugging.txt, 2). + (see: Documentation/power/basic-pm-debugging.txt, 2). Each of the above tests should be repeated several times and the STD tests should be mixed with the STR tests. If any of them fails, the driver cannot be -- cgit v1.2.3 From 1e75227ef0571031cd18536ab768ee35667ec5b9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Dec 2010 22:58:05 +0100 Subject: PM: Prevent dpm_prepare() from returning errors unnecessarily Currently dpm_prepare() returns error code if it finds that a device being suspended has a pending runtime resume request. However, it should not do that if the checking for wakeup events is not enabled. On the other hand, if the checking for wakeup events is enabled, it can return error when a wakeup event is detected, regardless of its source. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index ead3e79d6fcf..4747a1e8b44a 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "../base.h" #include "power.h" @@ -1052,8 +1053,10 @@ static int dpm_prepare(pm_message_t state) mutex_unlock(&dpm_list_mtx); pm_runtime_get_noresume(dev); - if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) { - /* Wake-up requested during system sleep transition. */ + if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) + pm_wakeup_event(dev, 0); + + if (!pm_check_wakeup_events()) { pm_runtime_put_sync(dev); error = -EBUSY; } else { @@ -1068,8 +1071,8 @@ static int dpm_prepare(pm_message_t state) error = 0; continue; } - printk(KERN_ERR "PM: Failed to prepare device %s " - "for power transition: error %d\n", + printk(KERN_INFO "PM: Device %s not prepared " + "for power transition: code %d\n", kobject_name(&dev->kobj), error); put_device(dev); break; -- cgit v1.2.3 From a2867e08c8e3bdbc00caf56bc3bdde19ccc058e3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Dec 2010 22:58:31 +0100 Subject: PM / Wakeup: Replace pm_check_wakeup_events() with pm_wakeup_pending() To avoid confusion with the meaning and return value of pm_check_wakeup_events() replace it with pm_wakeup_pending() that will work the other way around (ie. return true when system-wide power transition should be aborted). Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 2 +- drivers/base/power/wakeup.c | 20 ++++++++++---------- include/linux/suspend.h | 4 ++-- kernel/power/hibernate.c | 4 ++-- kernel/power/process.c | 2 +- kernel/power/suspend.c | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 4747a1e8b44a..8a5258339ca2 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1056,7 +1056,7 @@ static int dpm_prepare(pm_message_t state) if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) pm_wakeup_event(dev, 0); - if (!pm_check_wakeup_events()) { + if (pm_wakeup_pending()) { pm_runtime_put_sync(dev); error = -EBUSY; } else { diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 71c5528e1c35..8ec406d8f548 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -542,26 +542,26 @@ static void pm_wakeup_update_hit_counts(void) } /** - * pm_check_wakeup_events - Check for new wakeup events. + * pm_wakeup_pending - Check if power transition in progress should be aborted. * * Compare the current number of registered wakeup events with its preserved - * value from the past to check if new wakeup events have been registered since - * the old value was stored. Check if the current number of wakeup events being - * processed is zero. + * value from the past and return true if new wakeup events have been registered + * since the old value was stored. Also return true if the current number of + * wakeup events being processed is different from zero. */ -bool pm_check_wakeup_events(void) +bool pm_wakeup_pending(void) { unsigned long flags; - bool ret = true; + bool ret = false; spin_lock_irqsave(&events_lock, flags); if (events_check_enabled) { - ret = ((unsigned int)atomic_read(&event_count) == saved_count) - && !atomic_read(&events_in_progress); - events_check_enabled = ret; + ret = ((unsigned int)atomic_read(&event_count) != saved_count) + || atomic_read(&events_in_progress); + events_check_enabled = !ret; } spin_unlock_irqrestore(&events_lock, flags); - if (!ret) + if (ret) pm_wakeup_update_hit_counts(); return ret; } diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 26697514c5ec..144b34be5c32 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -292,7 +292,7 @@ extern int unregister_pm_notifier(struct notifier_block *nb); /* drivers/base/power/wakeup.c */ extern bool events_check_enabled; -extern bool pm_check_wakeup_events(void); +extern bool pm_wakeup_pending(void); extern bool pm_get_wakeup_count(unsigned int *count); extern bool pm_save_wakeup_count(unsigned int count); #else /* !CONFIG_PM_SLEEP */ @@ -309,7 +309,7 @@ static inline int unregister_pm_notifier(struct notifier_block *nb) #define pm_notifier(fn, pri) do { (void)(fn); } while (0) -static inline bool pm_check_wakeup_events(void) { return true; } +static inline bool pm_wakeup_pending(void) { return false; } #endif /* !CONFIG_PM_SLEEP */ extern struct mutex pm_mutex; diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index c9a98beffee4..870f72bc72ae 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -278,7 +278,7 @@ static int create_image(int platform_mode) goto Enable_irqs; } - if (hibernation_test(TEST_CORE) || !pm_check_wakeup_events()) + if (hibernation_test(TEST_CORE) || pm_wakeup_pending()) goto Power_up; in_suspend = 1; @@ -516,7 +516,7 @@ int hibernation_platform_enter(void) local_irq_disable(); sysdev_suspend(PMSG_HIBERNATE); - if (!pm_check_wakeup_events()) { + if (pm_wakeup_pending()) { error = -EAGAIN; goto Power_up; } diff --git a/kernel/power/process.c b/kernel/power/process.c index eb2c88a9e562..d6d2a10320e0 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -85,7 +85,7 @@ static int try_to_freeze_tasks(bool sig_only) if (!todo || time_after(jiffies, end_time)) break; - if (!pm_check_wakeup_events()) { + if (pm_wakeup_pending()) { wakeup = true; break; } diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index ecf770509d0d..5e644e3a6bf3 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -163,7 +163,7 @@ static int suspend_enter(suspend_state_t state) error = sysdev_suspend(PMSG_SUSPEND); if (!error) { - if (!suspend_test(TEST_CORE) && pm_check_wakeup_events()) { + if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) { error = suspend_ops->enter(state); events_check_enabled = false; } -- cgit v1.2.3 From d83f905e126f8cbc5e4addc5d1a64aea785b732e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Dec 2010 23:14:26 +0100 Subject: PM: Use pm_wakeup_pending() in __device_suspend() Before starting to suspend a device in __device_suspend() check if there's a request to abort the power transition and return -EBUSY in that case. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 8a5258339ca2..fb4ca2870081 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -877,6 +877,11 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (async_error) goto End; + if (pm_wakeup_pending()) { + async_error = -EBUSY; + goto End; + } + if (dev->class) { if (dev->class->pm) { pm_dev_dbg(dev, state, "class "); -- cgit v1.2.3 From 2cbb3ce1ad19e66858a4284dd6c4bb958162c483 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 15 Dec 2010 00:17:29 +0100 Subject: PM: Avoid compiler warning in pm_noirq_op() The compiler complains that calltime may be uninitialized in pm_noirq_op(), so add extra initialization for that variable to avoid the warning. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index fb4ca2870081..e6d628012654 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -304,7 +304,7 @@ static int pm_noirq_op(struct device *dev, pm_message_t state) { int error = 0; - ktime_t calltime, delta, rettime; + ktime_t calltime = ktime_set(0, 0), delta, rettime; if (initcall_debug) { pr_info("calling %s+ @ %i, parent: %s\n", -- cgit v1.2.3 From 8a43a9ab7b329aa8590f8a064df9bf8c80987507 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 16 Dec 2010 00:50:30 +0100 Subject: PM: Use a different list of devices for each stage of device suspend Instead of keeping all devices in the same list during system suspend and resume, regardless of what suspend-resume callbacks have been executed for them already, use separate lists of devices that have had their ->prepare(), ->suspend() and ->suspend_noirq() callbacks executed. This will allow us to simplify the core device suspend and resume routines. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 53 +++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index e6d628012654..b711867fa58e 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -42,6 +42,9 @@ */ LIST_HEAD(dpm_list); +LIST_HEAD(dpm_prepared_list); +LIST_HEAD(dpm_suspended_list); +LIST_HEAD(dpm_noirq_list); static DEFINE_MUTEX(dpm_list_mtx); static pm_message_t pm_transition; @@ -476,14 +479,12 @@ End: */ void dpm_resume_noirq(pm_message_t state) { - struct list_head list; ktime_t starttime = ktime_get(); - INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); transition_started = false; - while (!list_empty(&dpm_list)) { - struct device *dev = to_device(dpm_list.next); + while (!list_empty(&dpm_noirq_list)) { + struct device *dev = to_device(dpm_noirq_list.next); get_device(dev); if (dev->power.status > DPM_OFF) { @@ -499,10 +500,9 @@ void dpm_resume_noirq(pm_message_t state) pm_dev_err(dev, state, " early", error); } if (!list_empty(&dev->power.entry)) - list_move_tail(&dev->power.entry, &list); + list_move_tail(&dev->power.entry, &dpm_suspended_list); put_device(dev); } - list_splice(&list, &dpm_list); mutex_unlock(&dpm_list_mtx); dpm_show_time(starttime, state, "early"); resume_device_irqs(); @@ -611,16 +611,14 @@ static bool is_async(struct device *dev) */ static void dpm_resume(pm_message_t state) { - struct list_head list; struct device *dev; ktime_t starttime = ktime_get(); - INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); pm_transition = state; async_error = 0; - list_for_each_entry(dev, &dpm_list, power.entry) { + list_for_each_entry(dev, &dpm_suspended_list, power.entry) { if (dev->power.status < DPM_OFF) continue; @@ -631,8 +629,8 @@ static void dpm_resume(pm_message_t state) } } - while (!list_empty(&dpm_list)) { - dev = to_device(dpm_list.next); + while (!list_empty(&dpm_suspended_list)) { + dev = to_device(dpm_suspended_list.next); get_device(dev); if (dev->power.status >= DPM_OFF && !is_async(dev)) { int error; @@ -644,15 +642,11 @@ static void dpm_resume(pm_message_t state) mutex_lock(&dpm_list_mtx); if (error) pm_dev_err(dev, state, "", error); - } else if (dev->power.status == DPM_SUSPENDING) { - /* Allow new children of the device to be registered */ - dev->power.status = DPM_RESUMING; } if (!list_empty(&dev->power.entry)) - list_move_tail(&dev->power.entry, &list); + list_move_tail(&dev->power.entry, &dpm_prepared_list); put_device(dev); } - list_splice(&list, &dpm_list); mutex_unlock(&dpm_list_mtx); async_synchronize_full(); dpm_show_time(starttime, state, NULL); @@ -699,8 +693,8 @@ static void dpm_complete(pm_message_t state) INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); transition_started = false; - while (!list_empty(&dpm_list)) { - struct device *dev = to_device(dpm_list.prev); + while (!list_empty(&dpm_prepared_list)) { + struct device *dev = to_device(dpm_prepared_list.prev); get_device(dev); if (dev->power.status > DPM_ON) { @@ -803,15 +797,13 @@ End: */ int dpm_suspend_noirq(pm_message_t state) { - struct list_head list; ktime_t starttime = ktime_get(); int error = 0; - INIT_LIST_HEAD(&list); suspend_device_irqs(); mutex_lock(&dpm_list_mtx); - while (!list_empty(&dpm_list)) { - struct device *dev = to_device(dpm_list.prev); + while (!list_empty(&dpm_suspended_list)) { + struct device *dev = to_device(dpm_suspended_list.prev); get_device(dev); mutex_unlock(&dpm_list_mtx); @@ -826,10 +818,9 @@ int dpm_suspend_noirq(pm_message_t state) } dev->power.status = DPM_OFF_IRQ; if (!list_empty(&dev->power.entry)) - list_move(&dev->power.entry, &list); + list_move(&dev->power.entry, &dpm_noirq_list); put_device(dev); } - list_splice_tail(&list, &dpm_list); mutex_unlock(&dpm_list_mtx); if (error) dpm_resume_noirq(resume_event(state)); @@ -957,16 +948,14 @@ static int device_suspend(struct device *dev) */ static int dpm_suspend(pm_message_t state) { - struct list_head list; ktime_t starttime = ktime_get(); int error = 0; - INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); pm_transition = state; async_error = 0; - while (!list_empty(&dpm_list)) { - struct device *dev = to_device(dpm_list.prev); + while (!list_empty(&dpm_prepared_list)) { + struct device *dev = to_device(dpm_prepared_list.prev); get_device(dev); mutex_unlock(&dpm_list_mtx); @@ -980,12 +969,11 @@ static int dpm_suspend(pm_message_t state) break; } if (!list_empty(&dev->power.entry)) - list_move(&dev->power.entry, &list); + list_move(&dev->power.entry, &dpm_suspended_list); put_device(dev); if (async_error) break; } - list_splice(&list, dpm_list.prev); mutex_unlock(&dpm_list_mtx); async_synchronize_full(); if (!error) @@ -1044,10 +1032,8 @@ static int device_prepare(struct device *dev, pm_message_t state) */ static int dpm_prepare(pm_message_t state) { - struct list_head list; int error = 0; - INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); transition_started = true; while (!list_empty(&dpm_list)) { @@ -1084,10 +1070,9 @@ static int dpm_prepare(pm_message_t state) } dev->power.status = DPM_SUSPENDING; if (!list_empty(&dev->power.entry)) - list_move_tail(&dev->power.entry, &list); + list_move_tail(&dev->power.entry, &dpm_prepared_list); put_device(dev); } - list_splice(&list, &dpm_list); mutex_unlock(&dpm_list_mtx); return error; } -- cgit v1.2.3 From 5b219a51fdceaf76e0e18da57c7efb9e5586e567 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 16 Dec 2010 00:51:08 +0100 Subject: PM: Remove redundant checks from core device resume routines Since a separate list of devices is used to link devices that have completed each stage of suspend (or resume), it is not necessary to check dev->power.status in the core device resume routines any more. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index b711867fa58e..bb5c8cb64174 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -485,22 +485,18 @@ void dpm_resume_noirq(pm_message_t state) transition_started = false; while (!list_empty(&dpm_noirq_list)) { struct device *dev = to_device(dpm_noirq_list.next); + int error; get_device(dev); - if (dev->power.status > DPM_OFF) { - int error; - - dev->power.status = DPM_OFF; - mutex_unlock(&dpm_list_mtx); + dev->power.status = DPM_OFF; + list_move_tail(&dev->power.entry, &dpm_suspended_list); + mutex_unlock(&dpm_list_mtx); - error = device_resume_noirq(dev, state); + error = device_resume_noirq(dev, state); + if (error) + pm_dev_err(dev, state, " early", error); - mutex_lock(&dpm_list_mtx); - if (error) - pm_dev_err(dev, state, " early", error); - } - if (!list_empty(&dev->power.entry)) - list_move_tail(&dev->power.entry, &dpm_suspended_list); + mutex_lock(&dpm_list_mtx); put_device(dev); } mutex_unlock(&dpm_list_mtx); @@ -619,9 +615,6 @@ static void dpm_resume(pm_message_t state) async_error = 0; list_for_each_entry(dev, &dpm_suspended_list, power.entry) { - if (dev->power.status < DPM_OFF) - continue; - INIT_COMPLETION(dev->power.completion); if (is_async(dev)) { get_device(dev); @@ -632,16 +625,16 @@ static void dpm_resume(pm_message_t state) while (!list_empty(&dpm_suspended_list)) { dev = to_device(dpm_suspended_list.next); get_device(dev); - if (dev->power.status >= DPM_OFF && !is_async(dev)) { + if (!is_async(dev)) { int error; mutex_unlock(&dpm_list_mtx); error = device_resume(dev, state, false); - - mutex_lock(&dpm_list_mtx); if (error) pm_dev_err(dev, state, "", error); + + mutex_lock(&dpm_list_mtx); } if (!list_empty(&dev->power.entry)) list_move_tail(&dev->power.entry, &dpm_prepared_list); @@ -697,17 +690,14 @@ static void dpm_complete(pm_message_t state) struct device *dev = to_device(dpm_prepared_list.prev); get_device(dev); - if (dev->power.status > DPM_ON) { - dev->power.status = DPM_ON; - mutex_unlock(&dpm_list_mtx); + dev->power.status = DPM_ON; + list_move(&dev->power.entry, &list); + mutex_unlock(&dpm_list_mtx); - device_complete(dev, state); - pm_runtime_put_sync(dev); + device_complete(dev, state); + pm_runtime_put_sync(dev); - mutex_lock(&dpm_list_mtx); - } - if (!list_empty(&dev->power.entry)) - list_move(&dev->power.entry, &list); + mutex_lock(&dpm_list_mtx); put_device(dev); } list_splice(&list, &dpm_list); -- cgit v1.2.3 From b8c76f6aed0ab7df73a6410f3f82de2c831bb144 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 16 Dec 2010 00:51:21 +0100 Subject: PM: Replace the device power.status field with a bit field The device power.status field is too complicated for its purpose (storing the information about whether or not the device is in the "active" state from the PM core's point of view), so replace it with a bit field and modify all of its users accordingly. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 17 +++++------------ drivers/usb/core/driver.c | 7 +++---- include/linux/device.h | 4 ++-- include/linux/pm.h | 43 ++----------------------------------------- 4 files changed, 12 insertions(+), 59 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index bb5c8cb64174..a90480baa850 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -63,7 +63,7 @@ static int async_error; */ void device_pm_init(struct device *dev) { - dev->power.status = DPM_ON; + dev->power.in_suspend = false; init_completion(&dev->power.completion); complete_all(&dev->power.completion); dev->power.wakeup = NULL; @@ -98,7 +98,7 @@ void device_pm_add(struct device *dev) kobject_name(&dev->kobj)); mutex_lock(&dpm_list_mtx); if (dev->parent) { - if (dev->parent->power.status >= DPM_SUSPENDING) + if (dev->parent->power.in_suspend) dev_warn(dev, "parent %s should not be sleeping\n", dev_name(dev->parent)); } else if (transition_started) { @@ -488,7 +488,6 @@ void dpm_resume_noirq(pm_message_t state) int error; get_device(dev); - dev->power.status = DPM_OFF; list_move_tail(&dev->power.entry, &dpm_suspended_list); mutex_unlock(&dpm_list_mtx); @@ -541,7 +540,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) dpm_wait(dev->parent, async); device_lock(dev); - dev->power.status = DPM_RESUMING; + dev->power.in_suspend = false; if (dev->bus) { if (dev->bus->pm) { @@ -690,7 +689,7 @@ static void dpm_complete(pm_message_t state) struct device *dev = to_device(dpm_prepared_list.prev); get_device(dev); - dev->power.status = DPM_ON; + dev->power.in_suspend = false; list_move(&dev->power.entry, &list); mutex_unlock(&dpm_list_mtx); @@ -806,7 +805,6 @@ int dpm_suspend_noirq(pm_message_t state) put_device(dev); break; } - dev->power.status = DPM_OFF_IRQ; if (!list_empty(&dev->power.entry)) list_move(&dev->power.entry, &dpm_noirq_list); put_device(dev); @@ -894,9 +892,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) } } - if (!error) - dev->power.status = DPM_OFF; - End: device_unlock(dev); complete_all(&dev->power.completion); @@ -1030,7 +1025,6 @@ static int dpm_prepare(pm_message_t state) struct device *dev = to_device(dpm_list.next); get_device(dev); - dev->power.status = DPM_PREPARING; mutex_unlock(&dpm_list_mtx); pm_runtime_get_noresume(dev); @@ -1046,7 +1040,6 @@ static int dpm_prepare(pm_message_t state) mutex_lock(&dpm_list_mtx); if (error) { - dev->power.status = DPM_ON; if (error == -EAGAIN) { put_device(dev); error = 0; @@ -1058,7 +1051,7 @@ static int dpm_prepare(pm_message_t state) put_device(dev); break; } - dev->power.status = DPM_SUSPENDING; + dev->power.in_suspend = true; if (!list_empty(&dev->power.entry)) list_move_tail(&dev->power.entry, &dpm_prepared_list); put_device(dev); diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index c0e60fbcb048..4ec50224ee86 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -376,7 +376,7 @@ static int usb_unbind_interface(struct device *dev) * Just re-enable it without affecting the endpoint toggles. */ usb_enable_interface(udev, intf, false); - } else if (!error && intf->dev.power.status == DPM_ON) { + } else if (!error && !intf->dev.power.in_suspend) { r = usb_set_interface(udev, intf->altsetting[0]. desc.bInterfaceNumber, 0); if (r < 0) @@ -961,7 +961,7 @@ void usb_rebind_intf(struct usb_interface *intf) } /* Try to rebind the interface */ - if (intf->dev.power.status == DPM_ON) { + if (!intf->dev.power.in_suspend) { intf->needs_binding = 0; rc = device_attach(&intf->dev); if (rc < 0) @@ -1108,8 +1108,7 @@ static int usb_resume_interface(struct usb_device *udev, if (intf->condition == USB_INTERFACE_UNBOUND) { /* Carry out a deferred switch to altsetting 0 */ - if (intf->needs_altsetting0 && - intf->dev.power.status == DPM_ON) { + if (intf->needs_altsetting0 && !intf->dev.power.in_suspend) { usb_set_interface(udev, intf->altsetting[0]. desc.bInterfaceNumber, 0); intf->needs_altsetting0 = 0; diff --git a/include/linux/device.h b/include/linux/device.h index dd4895313468..45bc8c1669d2 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -508,13 +508,13 @@ static inline int device_is_registered(struct device *dev) static inline void device_enable_async_suspend(struct device *dev) { - if (dev->power.status == DPM_ON) + if (!dev->power.in_suspend) dev->power.async_suspend = true; } static inline void device_disable_async_suspend(struct device *dev) { - if (dev->power.status == DPM_ON) + if (!dev->power.in_suspend) dev->power.async_suspend = false; } diff --git a/include/linux/pm.h b/include/linux/pm.h index 61f2066e6852..c1756dfeb8c5 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -366,45 +366,6 @@ extern struct dev_pm_ops generic_subsys_pm_ops; #define PMSG_AUTO_RESUME ((struct pm_message) \ { .event = PM_EVENT_AUTO_RESUME, }) -/** - * Device power management states - * - * These state labels are used internally by the PM core to indicate the current - * status of a device with respect to the PM core operations. - * - * DPM_ON Device is regarded as operational. Set this way - * initially and when ->complete() is about to be called. - * Also set when ->prepare() fails. - * - * DPM_PREPARING Device is going to be prepared for a PM transition. Set - * when ->prepare() is about to be called. - * - * DPM_RESUMING Device is going to be resumed. Set when ->resume(), - * ->thaw(), or ->restore() is about to be called. - * - * DPM_SUSPENDING Device has been prepared for a power transition. Set - * when ->prepare() has just succeeded. - * - * DPM_OFF Device is regarded as inactive. Set immediately after - * ->suspend(), ->freeze(), or ->poweroff() has succeeded. - * Also set when ->resume()_noirq, ->thaw_noirq(), or - * ->restore_noirq() is about to be called. - * - * DPM_OFF_IRQ Device is in a "deep sleep". Set immediately after - * ->suspend_noirq(), ->freeze_noirq(), or - * ->poweroff_noirq() has just succeeded. - */ - -enum dpm_state { - DPM_INVALID, - DPM_ON, - DPM_PREPARING, - DPM_RESUMING, - DPM_SUSPENDING, - DPM_OFF, - DPM_OFF_IRQ, -}; - /** * Device run-time power management status. * @@ -463,8 +424,8 @@ struct wakeup_source; struct dev_pm_info { pm_message_t power_state; unsigned int can_wakeup:1; - unsigned async_suspend:1; - enum dpm_state status; /* Owned by the PM core */ + unsigned int async_suspend:1; + unsigned int in_suspend:1; /* Owned by the PM core */ spinlock_t lock; #ifdef CONFIG_PM_SLEEP struct list_head entry; -- cgit v1.2.3 From b64959e6158d6dcb640fc22d7f43b94ad1c91135 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 16 Dec 2010 17:11:45 +0100 Subject: PM: Permit registration of parentless devices during system suspend The registration of a new parentless device during system suspend will not lead to any complications affecting the PM core (the device will be effectively seen after the subsequent resume has completed), so remove the code used for detection of such events. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index a90480baa850..11fe6ed75278 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -49,12 +49,6 @@ LIST_HEAD(dpm_noirq_list); static DEFINE_MUTEX(dpm_list_mtx); static pm_message_t pm_transition; -/* - * Set once the preparation of devices for a PM transition has started, reset - * before starting to resume devices. Protected by dpm_list_mtx. - */ -static bool transition_started; - static int async_error; /** @@ -97,19 +91,9 @@ void device_pm_add(struct device *dev) dev->bus ? dev->bus->name : "No Bus", kobject_name(&dev->kobj)); mutex_lock(&dpm_list_mtx); - if (dev->parent) { - if (dev->parent->power.in_suspend) - dev_warn(dev, "parent %s should not be sleeping\n", - dev_name(dev->parent)); - } else if (transition_started) { - /* - * We refuse to register parentless devices while a PM - * transition is in progress in order to avoid leaving them - * unhandled down the road - */ - dev_WARN(dev, "Parentless device registered during a PM transaction\n"); - } - + if (dev->parent && dev->parent->power.in_suspend) + dev_warn(dev, "parent %s should not be sleeping\n", + dev_name(dev->parent)); list_add_tail(&dev->power.entry, &dpm_list); mutex_unlock(&dpm_list_mtx); } @@ -482,7 +466,6 @@ void dpm_resume_noirq(pm_message_t state) ktime_t starttime = ktime_get(); mutex_lock(&dpm_list_mtx); - transition_started = false; while (!list_empty(&dpm_noirq_list)) { struct device *dev = to_device(dpm_noirq_list.next); int error; @@ -684,7 +667,6 @@ static void dpm_complete(pm_message_t state) INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); - transition_started = false; while (!list_empty(&dpm_prepared_list)) { struct device *dev = to_device(dpm_prepared_list.prev); @@ -1020,7 +1002,6 @@ static int dpm_prepare(pm_message_t state) int error = 0; mutex_lock(&dpm_list_mtx); - transition_started = true; while (!list_empty(&dpm_list)) { struct device *dev = to_device(dpm_list.next); -- cgit v1.2.3 From 5c1a07ab3e78ef68fc9ccf419c969e8ed88d7cb6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 24 Dec 2010 15:03:34 +0100 Subject: PM: Use dev_name() in core device suspend and resume routines Use dev_name() wherever applicable in drivers/base/power/main.c. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 11fe6ed75278..2a52270aeb30 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -88,8 +88,7 @@ void device_pm_unlock(void) void 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)); + dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); mutex_lock(&dpm_list_mtx); if (dev->parent && dev->parent->power.in_suspend) dev_warn(dev, "parent %s should not be sleeping\n", @@ -105,8 +104,7 @@ void device_pm_add(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)); + dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); complete_all(&dev->power.completion); mutex_lock(&dpm_list_mtx); list_del_init(&dev->power.entry); @@ -123,10 +121,8 @@ void device_pm_remove(struct device *dev) void device_pm_move_before(struct device *deva, struct device *devb) { pr_debug("PM: Moving %s:%s before %s:%s\n", - deva->bus ? deva->bus->name : "No Bus", - kobject_name(&deva->kobj), - devb->bus ? devb->bus->name : "No Bus", - kobject_name(&devb->kobj)); + deva->bus ? deva->bus->name : "No Bus", dev_name(deva), + devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); /* Delete deva from dpm_list and reinsert before devb. */ list_move_tail(&deva->power.entry, &devb->power.entry); } @@ -139,10 +135,8 @@ void device_pm_move_before(struct device *deva, struct device *devb) void device_pm_move_after(struct device *deva, struct device *devb) { pr_debug("PM: Moving %s:%s after %s:%s\n", - deva->bus ? deva->bus->name : "No Bus", - kobject_name(&deva->kobj), - devb->bus ? devb->bus->name : "No Bus", - kobject_name(&devb->kobj)); + deva->bus ? deva->bus->name : "No Bus", dev_name(deva), + devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); /* Delete deva from dpm_list and reinsert after devb. */ list_move(&deva->power.entry, &devb->power.entry); } @@ -154,8 +148,7 @@ void device_pm_move_after(struct device *deva, struct device *devb) void device_pm_move_last(struct device *dev) { pr_debug("PM: Moving %s:%s to end of list\n", - dev->bus ? dev->bus->name : "No Bus", - kobject_name(&dev->kobj)); + dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); list_move_tail(&dev->power.entry, &dpm_list); } @@ -393,7 +386,7 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info, int error) { printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n", - kobject_name(&dev->kobj), pm_verb(state.event), info, error); + dev_name(dev), pm_verb(state.event), info, error); } static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) @@ -1028,7 +1021,7 @@ static int dpm_prepare(pm_message_t state) } printk(KERN_INFO "PM: Device %s not prepared " "for power transition: code %d\n", - kobject_name(&dev->kobj), error); + dev_name(dev), error); put_device(dev); break; } -- cgit v1.2.3 From 4b31db8a16fa0d4d6a0fa42d044e7a4f4dad3641 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 24 Dec 2010 15:04:06 +0100 Subject: PM / Runtime: Generic resume shouldn't set RPM_ACTIVE unconditionally The __pm_generic_resume() function changes the given device's runtime PM status to RPM_ACTIVE if its driver's callback returns 0, but it only should do that if the rumtime PM is enabled for the device. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/generic_ops.c | 2 +- include/linux/pm_runtime.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index 3d2c3500069a..42f97f925629 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -185,7 +185,7 @@ static int __pm_generic_resume(struct device *dev, int event) return 0; ret = callback(dev); - if (!ret) { + if (!ret && pm_runtime_enabled(dev)) { pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index e9cc049ccb62..d34f067e2a7f 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -82,6 +82,11 @@ static inline bool pm_runtime_suspended(struct device *dev) && !dev->power.disable_depth; } +static inline bool pm_runtime_enabled(struct device *dev) +{ + return !dev->power.disable_depth; +} + static inline void pm_runtime_mark_last_busy(struct device *dev) { ACCESS_ONCE(dev->power.last_busy) = jiffies; @@ -120,6 +125,7 @@ static inline void pm_runtime_put_noidle(struct device *dev) {} static inline bool device_run_wake(struct device *dev) { return false; } static inline void device_set_run_wake(struct device *dev, bool enable) {} static inline bool pm_runtime_suspended(struct device *dev) { return false; } +static inline bool pm_runtime_enabled(struct device *dev) { return false; } static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } -- cgit v1.2.3 From 62bcb91573425975d6ad2389d7ab1d8feca88ab4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 24 Dec 2010 15:04:41 +0100 Subject: PM: Prototype the pm_generic_ operations The pm_generic_ operations are all exported but are not prototyped in any header file for direct use. Do so. [rjw: Added extern.] Signed-off-by: Mark Brown Signed-off-by: Rafael J. Wysocki --- include/linux/pm.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/pm.h b/include/linux/pm.h index c1756dfeb8c5..dd9c7ab38270 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -572,4 +572,11 @@ extern unsigned int pm_flags; #define PM_APM 1 #define PM_ACPI 2 +extern int pm_generic_suspend(struct device *dev); +extern int pm_generic_resume(struct device *dev); +extern int pm_generic_freeze(struct device *dev); +extern int pm_generic_thaw(struct device *dev); +extern int pm_generic_restore(struct device *dev); +extern int pm_generic_poweroff(struct device *dev); + #endif /* _LINUX_PM_H */ -- cgit v1.2.3 From 3ae22e8c8ac39daf88ae32f047fb23825be7c646 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 25 Dec 2010 15:32:27 +0100 Subject: spi / PM: Support dev_pm_ops Allow SPI drivers to use runtime PM and other dev_pm_ops features by implementing dev_pm_ops for the bus. The existing bus specific suspend and resume functions will be called if a driver does not provide dev_pm_ops allowing for transition to the new model. Signed-off-by: Mark Brown Acked-by: Grant Likely Signed-off-by: Rafael J. Wysocki --- drivers/spi/spi.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b02d0cbce890..34bb17f03019 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -28,6 +28,7 @@ #include #include #include +#include static void spidev_release(struct device *dev) { @@ -100,9 +101,8 @@ static int spi_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } -#ifdef CONFIG_PM - -static int spi_suspend(struct device *dev, pm_message_t message) +#ifdef CONFIG_PM_SLEEP +static int spi_legacy_suspend(struct device *dev, pm_message_t message) { int value = 0; struct spi_driver *drv = to_spi_driver(dev->driver); @@ -117,7 +117,7 @@ static int spi_suspend(struct device *dev, pm_message_t message) return value; } -static int spi_resume(struct device *dev) +static int spi_legacy_resume(struct device *dev) { int value = 0; struct spi_driver *drv = to_spi_driver(dev->driver); @@ -132,18 +132,94 @@ static int spi_resume(struct device *dev) return value; } +static int spi_pm_suspend(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_suspend(dev); + else + return spi_legacy_suspend(dev, PMSG_SUSPEND); +} + +static int spi_pm_resume(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_resume(dev); + else + return spi_legacy_resume(dev); +} + +static int spi_pm_freeze(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_freeze(dev); + else + return spi_legacy_suspend(dev, PMSG_FREEZE); +} + +static int spi_pm_thaw(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_thaw(dev); + else + return spi_legacy_resume(dev); +} + +static int spi_pm_poweroff(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_poweroff(dev); + else + return spi_legacy_suspend(dev, PMSG_HIBERNATE); +} + +static int spi_pm_restore(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_restore(dev); + else + return spi_legacy_resume(dev); +} #else -#define spi_suspend NULL -#define spi_resume NULL +#define spi_pm_suspend NULL +#define spi_pm_resume NULL +#define spi_pm_freeze NULL +#define spi_pm_thaw NULL +#define spi_pm_poweroff NULL +#define spi_pm_restore NULL #endif +static const struct dev_pm_ops spi_pm = { + .suspend = spi_pm_suspend, + .resume = spi_pm_resume, + .freeze = spi_pm_freeze, + .thaw = spi_pm_thaw, + .poweroff = spi_pm_poweroff, + .restore = spi_pm_restore, + SET_RUNTIME_PM_OPS( + pm_generic_runtime_suspend, + pm_generic_runtime_resume, + pm_generic_runtime_idle + ) +}; + struct bus_type spi_bus_type = { .name = "spi", .dev_attrs = spi_dev_attrs, .match = spi_match_device, .uevent = spi_uevent, - .suspend = spi_suspend, - .resume = spi_resume, + .pm = &spi_pm, }; EXPORT_SYMBOL_GPL(spi_bus_type); -- cgit v1.2.3