diff options
Diffstat (limited to 'kernel/power/process.c')
| -rw-r--r-- | kernel/power/process.c | 197 | 
1 files changed, 118 insertions, 79 deletions
diff --git a/kernel/power/process.c b/kernel/power/process.c index e50b4c1b2a0..4ee194eb524 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -16,62 +16,46 @@  #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; -static inline int freezeable(struct task_struct * p) -{ -	if ((p == current) || -	    (p->flags & PF_NOFREEZE) || -	    (p->exit_state != 0)) -		return 0; -	return 1; -} - -static int try_to_freeze_tasks(bool sig_only) +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_csecs64; -	unsigned int elapsed_csecs; +	u64 elapsed_msecs64; +	unsigned int elapsed_msecs;  	bool wakeup = false; +	int sleep_usecs = USEC_PER_MSEC;  	do_gettimeofday(&start); -	end_time = jiffies + TIMEOUT; +	end_time = jiffies + msecs_to_jiffies(freeze_timeout_msecs); -	if (!sig_only) +	if (!user_only)  		freeze_workqueues_begin();  	while (true) {  		todo = 0;  		read_lock(&tasklist_lock);  		do_each_thread(g, p) { -			if (frozen(p) || !freezeable(p)) -				continue; - -			if (!freeze_task(p, sig_only)) +			if (p == current || !freeze_task(p))  				continue; -			/* -			 * Now that we've done set_freeze_flag, don't -			 * perturb a task in TASK_STOPPED or TASK_TRACED. -			 * It is "frozen enough".  If the task does wake -			 * up, it will immediately call try_to_freeze. -			 */ -			if (!task_is_stopped_or_traced(p) && -			    !freezer_should_skip(p)) +			if (!freezer_should_skip(p))  				todo++;  		} while_each_thread(g, p);  		read_unlock(&tasklist_lock); -		if (!sig_only) { +		if (!user_only) {  			wq_busy = freeze_workqueues_busy();  			todo += wq_busy;  		} @@ -79,111 +63,166 @@ static int try_to_freeze_tasks(bool sig_only)  		if (!todo || time_after(jiffies, end_time))  			break; -		if (!pm_check_wakeup_events()) { +		if (pm_wakeup_pending()) {  			wakeup = true;  			break;  		}  		/*  		 * We need to retry, but first give the freezing tasks some -		 * time to enter the regrigerator. +		 * 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) { -		/* 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. -		 */  		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); -		thaw_workqueues(); - -		read_lock(&tasklist_lock); -		do_each_thread(g, p) { -			task_lock(p); -			if (!wakeup && freezing(p) && !freezer_should_skip(p)) -				sched_show_task(p); -			cancel_freezing(p); -			task_unlock(p); -		} while_each_thread(g, p); -		read_unlock(&tasklist_lock); +		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.%02d seconds) ", elapsed_csecs / 100, -			elapsed_csecs % 100); +		printk("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000, +			elapsed_msecs % 1000);  	}  	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. + * + * On success, returns 0.  On failure, -errno and system is fully thawed.   */  int freeze_processes(void)  {  	int error; +	error = __usermodehelper_disable(UMH_FREEZING); +	if (error) +		return error; + +	/* Make sure this task doesn't get frozen */ +	current->flags |= PF_SUSPEND_TASK; + +	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()); +  	if (error) -		goto Exit; -	printk("done.\n"); +		thaw_processes(); +	return error; +} + +/** + * 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) +{ +	int error;  	printk("Freezing remaining freezable tasks ... "); +	pm_nosig_freezing = true;  	error = try_to_freeze_tasks(false); -	if (error) -		goto Exit; -	printk("done."); +	if (!error) +		printk("done."); -	oom_killer_disable(); - Exit: -	BUG_ON(in_atomic());  	printk("\n"); +	BUG_ON(in_atomic()); +	if (error) +		thaw_kernel_threads();  	return error;  } -static void thaw_tasks(bool nosig_only) +void thaw_processes(void)  {  	struct task_struct *g, *p; +	struct task_struct *curr = current; -	read_lock(&tasklist_lock); -	do_each_thread(g, p) { -		if (!freezeable(p)) -			continue; +	trace_suspend_resume(TPS("thaw_processes"), 0, true); +	if (pm_freezing) +		atomic_dec(&system_freezing_cnt); +	pm_freezing = false; +	pm_nosig_freezing = false; -		if (nosig_only && should_send_signal(p)) -			continue; +	oom_killer_enable(); -		if (cgroup_freezing_or_frozen(p)) -			continue; +	printk("Restarting tasks ... "); + +	__usermodehelper_set_disable_depth(UMH_FREEZING); +	thaw_workqueues(); -		thaw_process(p); +	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);  } -void thaw_processes(void) +void thaw_kernel_threads(void)  { -	oom_killer_enable(); +	struct task_struct *g, *p; + +	pm_nosig_freezing = false; +	printk("Restarting kernel threads ... "); -	printk("Restarting tasks ... ");  	thaw_workqueues(); -	thaw_tasks(true); -	thaw_tasks(false); + +	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");  } -  | 
