From dfd3a1b588094da9140cdfb80cf9005bfe41b99e Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 25 Aug 2011 19:29:45 -0700 Subject: PM: Backoff suspend if repeated attempts fail Change-Id: I32289676d95a307ea3aa5e78f6c126ca979c0fec Signed-off-by: Todd Poynor --- kernel/power/wakelock.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) (limited to 'kernel/power') diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c index 2ee459fe4456..81e1b7c65ca1 100644 --- a/kernel/power/wakelock.c +++ b/kernel/power/wakelock.c @@ -48,6 +48,12 @@ struct workqueue_struct *suspend_work_queue; struct wake_lock main_wake_lock; suspend_state_t requested_suspend_state = PM_SUSPEND_MEM; static struct wake_lock unknown_wakeup; +static struct wake_lock suspend_backoff_lock; + +#define SUSPEND_BACKOFF_THRESHOLD 10 +#define SUSPEND_BACKOFF_INTERVAL 10000 + +static unsigned suspend_short_count; #ifdef CONFIG_WAKELOCK_STAT static struct wake_lock deleted_wake_locks; @@ -255,10 +261,18 @@ long has_wake_lock(int type) return ret; } +static void suspend_backoff(void) +{ + pr_info("suspend: too many immediate wakeups, back off\n"); + wake_lock_timeout(&suspend_backoff_lock, + msecs_to_jiffies(SUSPEND_BACKOFF_INTERVAL)); +} + static void suspend(struct work_struct *work) { int ret; int entry_event_num; + struct timespec ts_entry, ts_exit; if (has_wake_lock(WAKE_LOCK_SUSPEND)) { if (debug_mask & DEBUG_SUSPEND) @@ -270,17 +284,30 @@ static void suspend(struct work_struct *work) sys_sync(); if (debug_mask & DEBUG_SUSPEND) pr_info("suspend: enter suspend\n"); + getnstimeofday(&ts_entry); ret = pm_suspend(requested_suspend_state); + getnstimeofday(&ts_exit); + if (debug_mask & DEBUG_EXIT_SUSPEND) { - struct timespec ts; struct rtc_time tm; - getnstimeofday(&ts); - rtc_time_to_tm(ts.tv_sec, &tm); + rtc_time_to_tm(ts_exit.tv_sec, &tm); pr_info("suspend: exit suspend, ret = %d " "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); + tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec); } + + if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) { + ++suspend_short_count; + + if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) { + suspend_backoff(); + suspend_short_count = 0; + } + } else { + suspend_short_count = 0; + } + if (current_event_num == entry_event_num) { if (debug_mask & DEBUG_SUSPEND) pr_info("suspend: pm_suspend returned with no event\n"); @@ -547,6 +574,8 @@ static int __init wakelocks_init(void) wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main"); wake_lock(&main_wake_lock); wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups"); + wake_lock_init(&suspend_backoff_lock, WAKE_LOCK_SUSPEND, + "suspend_backoff"); ret = platform_device_register(&power_device); if (ret) { @@ -576,6 +605,7 @@ err_suspend_work_queue: err_platform_driver_register: platform_device_unregister(&power_device); err_platform_device_register: + wake_lock_destroy(&suspend_backoff_lock); wake_lock_destroy(&unknown_wakeup); wake_lock_destroy(&main_wake_lock); #ifdef CONFIG_WAKELOCK_STAT @@ -592,6 +622,7 @@ static void __exit wakelocks_exit(void) destroy_workqueue(suspend_work_queue); platform_driver_unregister(&power_driver); platform_device_unregister(&power_device); + wake_lock_destroy(&suspend_backoff_lock); wake_lock_destroy(&unknown_wakeup); wake_lock_destroy(&main_wake_lock); #ifdef CONFIG_WAKELOCK_STAT -- cgit v1.2.3