diff options
Diffstat (limited to 'kernel/power/process.c')
| -rw-r--r-- | kernel/power/process.c | 314 |
1 files changed, 163 insertions, 151 deletions
diff --git a/kernel/power/process.c b/kernel/power/process.c index 99eeb119b06..4ee194eb524 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -8,209 +8,221 @@ #undef DEBUG -#include <linux/smp_lock.h> #include <linux/interrupt.h> +#include <linux/oom.h> #include <linux/suspend.h> #include <linux/module.h> #include <linux/syscalls.h> #include <linux/freezer.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/kmod.h> +#include <trace/events/power.h> /* * Timeout for stopping processes */ -#define TIMEOUT (20 * HZ) +unsigned int __read_mostly freeze_timeout_msecs = 20 * MSEC_PER_SEC; -#define FREEZER_KERNEL_THREADS 0 -#define FREEZER_USER_SPACE 1 - -static inline int freezeable(struct task_struct * p) -{ - if ((p == current) || - (p->flags & PF_NOFREEZE) || - (p->exit_state == EXIT_ZOMBIE) || - (p->exit_state == EXIT_DEAD) || - (p->state == TASK_STOPPED)) - return 0; - return 1; -} - -/* Refrigerator is place where frozen processes are stored :-). */ -void refrigerator(void) -{ - /* Hmm, should we be allowed to suspend when there are realtime - processes around? */ - long save; - save = current->state; - pr_debug("%s entered refrigerator\n", current->comm); - - frozen_process(current); - spin_lock_irq(¤t->sighand->siglock); - recalc_sigpending(); /* We sent fake signal, clean it up */ - spin_unlock_irq(¤t->sighand->siglock); - - while (frozen(current)) { - current->state = TASK_UNINTERRUPTIBLE; - schedule(); - } - pr_debug("%s left refrigerator\n", current->comm); - current->state = save; -} - -static inline void freeze_process(struct task_struct *p) -{ - unsigned long flags; - - if (!freezing(p)) { - freeze(p); - spin_lock_irqsave(&p->sighand->siglock, flags); - signal_wake_up(p, 0); - spin_unlock_irqrestore(&p->sighand->siglock, flags); - } -} - -static void cancel_freezing(struct task_struct *p) -{ - unsigned long flags; - - if (freezing(p)) { - pr_debug(" clean up: %s\n", p->comm); - do_not_freeze(p); - spin_lock_irqsave(&p->sighand->siglock, flags); - recalc_sigpending_tsk(p); - spin_unlock_irqrestore(&p->sighand->siglock, flags); - } -} - -static inline int is_user_space(struct task_struct *p) -{ - return p->mm && !(p->flags & PF_BORROWED_MM); -} - -static unsigned int try_to_freeze_tasks(int freeze_user_space) +static int try_to_freeze_tasks(bool user_only) { struct task_struct *g, *p; unsigned long end_time; unsigned int todo; + bool wq_busy = false; + struct timeval start, end; + u64 elapsed_msecs64; + unsigned int elapsed_msecs; + bool wakeup = false; + int sleep_usecs = USEC_PER_MSEC; - end_time = jiffies + TIMEOUT; - do { + do_gettimeofday(&start); + + end_time = jiffies + msecs_to_jiffies(freeze_timeout_msecs); + + if (!user_only) + freeze_workqueues_begin(); + + while (true) { todo = 0; read_lock(&tasklist_lock); do_each_thread(g, p) { - if (!freezeable(p)) - continue; - - if (frozen(p)) + if (p == current || !freeze_task(p)) continue; - if (p->state == TASK_TRACED && - (frozen(p->parent) || - p->parent->state == TASK_STOPPED)) { - cancel_freezing(p); - continue; - } - if (is_user_space(p)) { - if (!freeze_user_space) - continue; - - /* Freeze the task unless there is a vfork - * completion pending - */ - if (!p->vfork_done) - freeze_process(p); - } else { - if (freeze_user_space) - continue; - - freeze_process(p); - } - todo++; + if (!freezer_should_skip(p)) + todo++; } while_each_thread(g, p); read_unlock(&tasklist_lock); - yield(); /* Yield is okay here */ - if (todo && time_after(jiffies, end_time)) + + if (!user_only) { + wq_busy = freeze_workqueues_busy(); + todo += wq_busy; + } + + if (!todo || time_after(jiffies, end_time)) break; - } while (todo); - if (todo) { - /* This does not unfreeze processes that are already frozen - * (we have slightly ugly calling convention in that respect, - * and caller must call thaw_processes() if something fails), - * but it cleans up leftover PF_FREEZE requests. + if (pm_wakeup_pending()) { + wakeup = true; + break; + } + + /* + * We need to retry, but first give the freezing tasks some + * time to enter the refrigerator. Start with an initial + * 1 ms sleep followed by exponential backoff until 8 ms. */ - printk("\n"); - printk(KERN_ERR "Stopping %s timed out after %d seconds " - "(%d tasks refusing to freeze):\n", - freeze_user_space ? "user space processes" : - "kernel threads", - TIMEOUT / HZ, todo); - read_lock(&tasklist_lock); - do_each_thread(g, p) { - if (is_user_space(p) == !freeze_user_space) - continue; + usleep_range(sleep_usecs / 2, sleep_usecs); + if (sleep_usecs < 8 * USEC_PER_MSEC) + sleep_usecs *= 2; + } - if (freezeable(p) && !frozen(p)) - printk(KERN_ERR " %s\n", p->comm); + do_gettimeofday(&end); + elapsed_msecs64 = timeval_to_ns(&end) - timeval_to_ns(&start); + do_div(elapsed_msecs64, NSEC_PER_MSEC); + elapsed_msecs = elapsed_msecs64; - cancel_freezing(p); - } while_each_thread(g, p); - read_unlock(&tasklist_lock); + if (todo) { + printk("\n"); + printk(KERN_ERR "Freezing of tasks %s after %d.%03d seconds " + "(%d tasks refusing to freeze, wq_busy=%d):\n", + wakeup ? "aborted" : "failed", + elapsed_msecs / 1000, elapsed_msecs % 1000, + todo - wq_busy, wq_busy); + + if (!wakeup) { + read_lock(&tasklist_lock); + do_each_thread(g, p) { + if (p != current && !freezer_should_skip(p) + && freezing(p) && !frozen(p)) + sched_show_task(p); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); + } + } else { + printk("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000, + elapsed_msecs % 1000); } - return todo; + return todo ? -EBUSY : 0; } /** - * freeze_processes - tell processes to enter the refrigerator + * freeze_processes - Signal user space processes to enter the refrigerator. + * The current thread will not be frozen. The same process that calls + * freeze_processes must later call thaw_processes. * - * Returns 0 on success, or the number of processes that didn't freeze, - * although they were told to. + * On success, returns 0. On failure, -errno and system is fully thawed. */ int freeze_processes(void) { - unsigned int nr_unfrozen; + int error; - printk("Stopping tasks ... "); - nr_unfrozen = try_to_freeze_tasks(FREEZER_USER_SPACE); - if (nr_unfrozen) - return nr_unfrozen; + error = __usermodehelper_disable(UMH_FREEZING); + if (error) + return error; - sys_sync(); - nr_unfrozen = try_to_freeze_tasks(FREEZER_KERNEL_THREADS); - if (nr_unfrozen) - return nr_unfrozen; + /* Make sure this task doesn't get frozen */ + current->flags |= PF_SUSPEND_TASK; - printk("done.\n"); + if (!pm_freezing) + atomic_inc(&system_freezing_cnt); + + printk("Freezing user space processes ... "); + pm_freezing = true; + error = try_to_freeze_tasks(true); + if (!error) { + printk("done."); + __usermodehelper_set_disable_depth(UMH_DISABLED); + oom_killer_disable(); + } + printk("\n"); BUG_ON(in_atomic()); - return 0; + + if (error) + thaw_processes(); + return error; } -static void thaw_tasks(int thaw_user_space) +/** + * freeze_kernel_threads - Make freezable kernel threads go to the refrigerator. + * + * On success, returns 0. On failure, -errno and only the kernel threads are + * thawed, so as to give a chance to the caller to do additional cleanups + * (if any) before thawing the userspace tasks. So, it is the responsibility + * of the caller to thaw the userspace tasks, when the time is right. + */ +int freeze_kernel_threads(void) { - struct task_struct *g, *p; + int error; - read_lock(&tasklist_lock); - do_each_thread(g, p) { - if (!freezeable(p)) - continue; + printk("Freezing remaining freezable tasks ... "); + pm_nosig_freezing = true; + error = try_to_freeze_tasks(false); + if (!error) + printk("done."); - if (is_user_space(p) == !thaw_user_space) - continue; + printk("\n"); + BUG_ON(in_atomic()); - if (!thaw_process(p)) - printk(KERN_WARNING " Strange, %s not stopped\n", - p->comm ); - } while_each_thread(g, p); - read_unlock(&tasklist_lock); + if (error) + thaw_kernel_threads(); + return error; } void thaw_processes(void) { + struct task_struct *g, *p; + struct task_struct *curr = current; + + trace_suspend_resume(TPS("thaw_processes"), 0, true); + if (pm_freezing) + atomic_dec(&system_freezing_cnt); + pm_freezing = false; + pm_nosig_freezing = false; + + oom_killer_enable(); + printk("Restarting tasks ... "); - thaw_tasks(FREEZER_KERNEL_THREADS); - thaw_tasks(FREEZER_USER_SPACE); + + __usermodehelper_set_disable_depth(UMH_FREEZING); + thaw_workqueues(); + + read_lock(&tasklist_lock); + do_each_thread(g, p) { + /* No other threads should have PF_SUSPEND_TASK set */ + WARN_ON((p != curr) && (p->flags & PF_SUSPEND_TASK)); + __thaw_task(p); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); + + WARN_ON(!(curr->flags & PF_SUSPEND_TASK)); + curr->flags &= ~PF_SUSPEND_TASK; + + usermodehelper_enable(); + schedule(); printk("done.\n"); + trace_suspend_resume(TPS("thaw_processes"), 0, false); } -EXPORT_SYMBOL(refrigerator); +void thaw_kernel_threads(void) +{ + struct task_struct *g, *p; + + pm_nosig_freezing = false; + printk("Restarting kernel threads ... "); + + thaw_workqueues(); + + read_lock(&tasklist_lock); + do_each_thread(g, p) { + if (p->flags & (PF_KTHREAD | PF_WQ_WORKER)) + __thaw_task(p); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); + + schedule(); + printk("done.\n"); +} |
