PM: Backoff suspend if repeated attempts fail
Todd Poynor [Fri, 26 Aug 2011 02:29:45 +0000 (19:29 -0700)]
Change-Id: I32289676d95a307ea3aa5e78f6c126ca979c0fec
Signed-off-by: Todd Poynor <toddpoynor@google.com>

kernel/power/wakelock.c

index 2ee459f..81e1b7c 100644 (file)
@@ -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