Merge branch 'linux-linaro-lsk' into linux-linaro-lsk-android
[firefly-linux-kernel-4.4.55.git] / kernel / power / process.c
index 1b212bee1510cb22b6e1df1c2ba00fff6b6cf4e6..6ff55e749197d3aaca24c806caab42d683085ce3 100644 (file)
@@ -30,9 +30,10 @@ static int try_to_freeze_tasks(bool user_only)
        unsigned int todo;
        bool wq_busy = false;
        struct timeval start, end;
-       u64 elapsed_csecs64;
-       unsigned int elapsed_csecs;
+       u64 elapsed_msecs64;
+       unsigned int elapsed_msecs;
        bool wakeup = false;
+       int sleep_usecs = USEC_PER_MSEC;
 
        do_gettimeofday(&start);
 
@@ -68,22 +69,25 @@ static int try_to_freeze_tasks(bool user_only)
 
                /*
                 * We need to retry, but first give the freezing tasks some
-                * time to enter the refrigerator.
+                * time to enter the refrigerator.  Start with an initial
+                * 1 ms sleep followed by exponential backoff until 8 ms.
                 */
-               msleep(10);
+               usleep_range(sleep_usecs / 2, sleep_usecs);
+               if (sleep_usecs < 8 * USEC_PER_MSEC)
+                       sleep_usecs *= 2;
        }
 
        do_gettimeofday(&end);
-       elapsed_csecs64 = timeval_to_ns(&end) - timeval_to_ns(&start);
-       do_div(elapsed_csecs64, NSEC_PER_SEC / 100);
-       elapsed_csecs = elapsed_csecs64;
+       elapsed_msecs64 = timeval_to_ns(&end) - timeval_to_ns(&start);
+       do_div(elapsed_msecs64, NSEC_PER_MSEC);
+       elapsed_msecs = elapsed_msecs64;
 
        if (todo) {
                printk("\n");
-               printk(KERN_ERR "Freezing of tasks %s after %d.%02d seconds "
+               printk(KERN_ERR "Freezing of tasks %s after %d.%03d seconds "
                       "(%d tasks refusing to freeze, wq_busy=%d):\n",
                       wakeup ? "aborted" : "failed",
-                      elapsed_csecs / 100, elapsed_csecs % 100,
+                      elapsed_msecs / 1000, elapsed_msecs % 1000,
                       todo - wq_busy, wq_busy);
 
                if (!wakeup) {
@@ -96,13 +100,35 @@ static int try_to_freeze_tasks(bool user_only)
                        read_unlock(&tasklist_lock);
                }
        } else {
-               printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100,
-                       elapsed_csecs % 100);
+               printk("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000,
+                       elapsed_msecs % 1000);
        }
 
        return todo ? -EBUSY : 0;
 }
 
+/*
+ * Returns true if all freezable tasks (except for current) are frozen already
+ */
+static bool check_frozen_processes(void)
+{
+       struct task_struct *g, *p;
+       bool ret = true;
+
+       read_lock(&tasklist_lock);
+       for_each_process_thread(g, p) {
+               if (p != current && !freezer_should_skip(p) &&
+                   !frozen(p)) {
+                       ret = false;
+                       goto done;
+               }
+       }
+done:
+       read_unlock(&tasklist_lock);
+
+       return ret;
+}
+
 /**
  * freeze_processes - Signal user space processes to enter the refrigerator.
  *
@@ -111,6 +137,7 @@ static int try_to_freeze_tasks(bool user_only)
 int freeze_processes(void)
 {
        int error;
+       int oom_kills_saved;
 
        error = __usermodehelper_disable(UMH_FREEZING);
        if (error)
@@ -121,12 +148,27 @@ int freeze_processes(void)
 
        printk("Freezing user space processes ... ");
        pm_freezing = true;
+       oom_kills_saved = oom_kills_count();
        error = try_to_freeze_tasks(true);
        if (!error) {
-               printk("done.");
                __usermodehelper_set_disable_depth(UMH_DISABLED);
                oom_killer_disable();
+
+               /*
+                * There might have been an OOM kill while we were
+                * freezing tasks and the killed task might be still
+                * on the way out so we have to double check for race.
+                */
+               if (oom_kills_count() != oom_kills_saved &&
+                               !check_frozen_processes()) {
+                       __usermodehelper_set_disable_depth(UMH_ENABLED);
+                       printk("OOM in progress.");
+                       error = -EBUSY;
+                       goto done;
+               }
+               printk("done.");
        }
+done:
        printk("\n");
        BUG_ON(in_atomic());