summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorTodd Poynor <toddpoynor@google.com>2011-08-25 19:29:45 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:39:03 -0800
commitdfd3a1b588094da9140cdfb80cf9005bfe41b99e (patch)
tree0c7b9fe4caec0047358a8f680004fa71bf0dedfb /kernel
parent5e0d0a23f7a54f41162a995b140ef5746907329e (diff)
PM: Backoff suspend if repeated attempts fail
Change-Id: I32289676d95a307ea3aa5e78f6c126ca979c0fec Signed-off-by: Todd Poynor <toddpoynor@google.com>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/power/wakelock.c39
1 files changed, 35 insertions, 4 deletions
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