From b6ec94520c5b944da9e045e173ddb453ecf42da3 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Wed, 6 May 2015 15:26:56 -0700 Subject: PM / wakeup: validate wakeup source before activating it. A rogue wakeup source not registered in wakeup_sources list is not visible from wakeup_sources_stats_show. Check if the wakeup source is registered properly by looking at the timer struct. Signed-off-by: Jin Qian Signed-off-by: Rafael J. Wysocki --- drivers/base/power/wakeup.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 77262009f89d..7b5ad9a5a6b6 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -351,6 +351,20 @@ int device_set_wakeup_enable(struct device *dev, bool enable) } EXPORT_SYMBOL_GPL(device_set_wakeup_enable); +/** + * wakeup_source_not_registered - validate the given wakeup source. + * @ws: Wakeup source to be validated. + */ +static bool wakeup_source_not_registered(struct wakeup_source *ws) +{ + /* + * Use timer struct to check if the given source is initialized + * by wakeup_source_add. + */ + return ws->timer.function != pm_wakeup_timer_fn || + ws->timer.data != (unsigned long)ws; +} + /* * The functions below use the observation that each wakeup event starts a * period in which the system should not be suspended. The moment this period @@ -391,6 +405,10 @@ static void wakeup_source_activate(struct wakeup_source *ws) { unsigned int cec; + if (WARN_ONCE(wakeup_source_not_registered(ws), + "unregistered wakeup source\n")) + return; + /* * active wakeup source should bring the system * out of PM_SUSPEND_FREEZE state -- cgit v1.2.3 From 258d2a1095eebd1d061f84925b05f0d13bd5fcad Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 27 Apr 2015 21:24:31 +0300 Subject: bus: omap_l3_noc: add missed callbacks for suspend-to-disk Add missed callbacks needed for proper supporting of suspend-to-disk by using recently introduced macro SET_NOIRQ_SYSTEM_SLEEP_PM_OPS. Signed-off-by: Grygorii Strashko Acked-by: Nishanth Menon Acked-by: Santosh Shilimkar Reviewed-by: Ulf Hansson Acked-by: Pavel Machek Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/bus/omap_l3_noc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 11f7982cbdb3..6ae38848719e 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -300,7 +300,7 @@ static int omap_l3_probe(struct platform_device *pdev) return ret; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /** * l3_resume_noirq() - resume function for l3_noc @@ -346,7 +346,7 @@ static int l3_resume_noirq(struct device *dev) } static const struct dev_pm_ops l3_dev_pm_ops = { - .resume_noirq = l3_resume_noirq, + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, l3_resume_noirq) }; #define L3_DEV_PM_OPS (&l3_dev_pm_ops) -- cgit v1.2.3 From 084609bf727981c7a2e6e69aefe0052c9d793300 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 24 Apr 2015 14:57:10 +0300 Subject: leds / PM: fix hibernation on arm when gpio-led used with CPU led trigger Setting a dev_pm_ops suspend/resume pair of callbacks but not a set of hibernation callbacks means those pm functions will not be called upon hibernation - that leads to system crash on ARM during freezing if gpio-led is used in combination with CPU led trigger. It may happen after freeze_noirq stage (GPIO is suspended) and before syscore_suspend stage (CPU led trigger is suspended) - usually when disable_nonboot_cpus() is called. Log: PM: noirq freeze of devices complete after 1.425 msecs Disabling non-boot CPUs ... ^ system may crash or stuck here with message (TI AM572x) WARNING: CPU: 0 PID: 3100 at drivers/bus/omap_l3_noc.c:148 l3_interrupt_handler+0x22c/0x370() 44000000.ocp:L3 Custom Error: MASTER MPU TARGET L4_PER1_P3 (Idle): Data Access in Supervisor mode during Functional access CPU1: shutdown ^ or here Fix this by using SIMPLE_DEV_PM_OPS, which appropriately assigns the suspend and hibernation callbacks and move led_suspend/led_resume under CONFIG_PM_SLEEP to avoid build warnings. Fixes: 73e1ab41a80d (leds: Convert led class driver from legacy pm ops to dev_pm_ops) Signed-off-by: Grygorii Strashko Acked-by: Jacek Anaszewski Cc: 3.11+ # 3.11+ Signed-off-by: Rafael J. Wysocki --- drivers/leds/led-class.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 728681debdbe..7fb2a19ac649 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -187,6 +187,7 @@ void led_classdev_resume(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_classdev_resume); +#ifdef CONFIG_PM_SLEEP static int led_suspend(struct device *dev) { struct led_classdev *led_cdev = dev_get_drvdata(dev); @@ -206,11 +207,9 @@ static int led_resume(struct device *dev) return 0; } +#endif -static const struct dev_pm_ops leds_class_dev_pm_ops = { - .suspend = led_suspend, - .resume = led_resume, -}; +static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume); static int match_name(struct device *dev, const void *data) { -- cgit v1.2.3 From 87e9b9f1d86c2ee9a10c2a4186a72d0af4cc963e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 May 2015 01:38:15 +0200 Subject: PM / sleep: Make suspend-to-idle-specific code depend on CONFIG_SUSPEND Since idle_should_freeze() is defined to always return 'false' for CONFIG_SUSPEND unset, all of the code depending on it in cpuidle_idle_call() is not necessary in that case. Make that code depend on CONFIG_SUSPEND too to avoid building it when it is not going to be used. Signed-off-by: Rafael J. Wysocki Acked-by: Thomas Gleixner --- drivers/cpuidle/cpuidle.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 61c417b9e53f..71459f546145 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -92,6 +92,7 @@ static int find_deepest_state(struct cpuidle_driver *drv, return ret; } +#ifdef CONFIG_SUSPEND /** * cpuidle_find_deepest_state - Find the deepest available idle state. * @drv: cpuidle driver for the given CPU. @@ -145,6 +146,7 @@ int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev) return index; } +#endif /* CONFIG_SUSPEND */ /** * cpuidle_enter_state - enter the state and update stats -- cgit v1.2.3 From 7f436055cb0c0e17c430fb81197b42e20c2e812c Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 15 May 2015 18:10:37 -0700 Subject: PM / wakeup: add a dummy wakeup_source to record statistics After a wakeup_source is destroyed, we lost all information such as how long this wakeup_source has been active. Add a dummy wakeup_source to record such info. Signed-off-by: Jin Qian Signed-off-by: Rafael J. Wysocki --- drivers/base/power/wakeup.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 7b5ad9a5a6b6..87c2603b9327 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -56,6 +56,11 @@ static LIST_HEAD(wakeup_sources); static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue); +static struct wakeup_source deleted_ws = { + .name = "deleted", + .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock), +}; + /** * wakeup_source_prepare - Prepare a new wakeup source for initialization. * @ws: Wakeup source to prepare. @@ -107,6 +112,34 @@ void wakeup_source_drop(struct wakeup_source *ws) } EXPORT_SYMBOL_GPL(wakeup_source_drop); +/* + * Record wakeup_source statistics being deleted into a dummy wakeup_source. + */ +static void wakeup_source_record(struct wakeup_source *ws) +{ + unsigned long flags; + + spin_lock_irqsave(&deleted_ws.lock, flags); + + if (ws->event_count) { + deleted_ws.total_time = + ktime_add(deleted_ws.total_time, ws->total_time); + deleted_ws.prevent_sleep_time = + ktime_add(deleted_ws.prevent_sleep_time, + ws->prevent_sleep_time); + deleted_ws.max_time = + ktime_compare(deleted_ws.max_time, ws->max_time) > 0 ? + deleted_ws.max_time : ws->max_time; + deleted_ws.event_count += ws->event_count; + deleted_ws.active_count += ws->active_count; + deleted_ws.relax_count += ws->relax_count; + deleted_ws.expire_count += ws->expire_count; + deleted_ws.wakeup_count += ws->wakeup_count; + } + + spin_unlock_irqrestore(&deleted_ws.lock, flags); +} + /** * wakeup_source_destroy - Destroy a struct wakeup_source object. * @ws: Wakeup source to destroy. @@ -119,6 +152,7 @@ void wakeup_source_destroy(struct wakeup_source *ws) return; wakeup_source_drop(ws); + wakeup_source_record(ws); kfree(ws->name); kfree(ws); } @@ -912,6 +946,8 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) print_wakeup_source_stats(m, ws); rcu_read_unlock(); + print_wakeup_source_stats(m, &deleted_ws); + return 0; } -- cgit v1.2.3 From 56f487c78015936097474fd89b2ccb229d500d0f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 13 May 2015 16:36:32 -0700 Subject: PM / Runtime: Update last_busy in rpm_resume If we don't update last_busy in rpm_resume, devices can go back to sleep immediately after resume. This happens at least in cases where the device has been powered off and does not have any interrupt pending until there's something in the FIFO. Signed-off-by: Tony Lindgren Signed-off-by: Rafael J. Wysocki --- drivers/base/power/runtime.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 5070c4fe8542..4ffe4a2add76 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -741,6 +741,7 @@ static int rpm_resume(struct device *dev, int rpmflags) } else { no_callback: __update_runtime_status(dev, RPM_ACTIVE); + pm_runtime_mark_last_busy(dev); if (parent) atomic_inc(&parent->power.child_count); } -- cgit v1.2.3 From 32e8d689dc12e29fcb6ba9c65a33473d0cbdfec8 Mon Sep 17 00:00:00 2001 From: Todd E Brandt Date: Thu, 28 May 2015 12:55:53 -0700 Subject: PM / sleep: trace_device_pm_callback coverage in dpm_prepare/complete Move the trace_device_pm_callback locations for dpm_prepare and dpm_complete to encompass the attempt to capture the device mutex prior to callback. This is needed by analyze_suspend to identify gaps in the trace output caused by the delay in locking the mutex for a device. Signed-off-by: Todd Brandt Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 3d874eca7104..5528e59ae788 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -920,9 +920,7 @@ static void device_complete(struct device *dev, pm_message_t state) if (callback) { pm_dev_dbg(dev, state, info); - trace_device_pm_callback_start(dev, info, state.event); callback(dev); - trace_device_pm_callback_end(dev, 0); } device_unlock(dev); @@ -954,7 +952,9 @@ void dpm_complete(pm_message_t state) list_move(&dev->power.entry, &list); mutex_unlock(&dpm_list_mtx); + trace_device_pm_callback_start(dev, "", state.event); device_complete(dev, state); + trace_device_pm_callback_end(dev, 0); mutex_lock(&dpm_list_mtx); put_device(dev); @@ -1585,11 +1585,8 @@ static int device_prepare(struct device *dev, pm_message_t state) callback = dev->driver->pm->prepare; } - if (callback) { - trace_device_pm_callback_start(dev, info, state.event); + if (callback) ret = callback(dev); - trace_device_pm_callback_end(dev, ret); - } device_unlock(dev); @@ -1631,7 +1628,9 @@ int dpm_prepare(pm_message_t state) get_device(dev); mutex_unlock(&dpm_list_mtx); + trace_device_pm_callback_start(dev, "", state.event); error = device_prepare(dev, state); + trace_device_pm_callback_end(dev, error); mutex_lock(&dpm_list_mtx); if (error) { -- cgit v1.2.3