219 lines · 4.7 KB · C
RawDownload
/* * drivers/power/process.c - Functions for starting/stopping processes on  *                           suspend transitions. * * Originally from swsusp. */  #undef DEBUG #include <linux/smp_lock.h>#include <linux/interrupt.h>#include <linux/suspend.h>#include <linux/module.h>#include <linux/syscalls.h>#include <linux/freezer.h> /*  * Timeout for stopping processes */#define TIMEOUT	(20 * HZ) #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))		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(&current->sighand->siglock);	recalc_sigpending(); /* We sent fake signal, clean it up */	spin_unlock_irq(&current->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)) {		rmb();		if (!frozen(p)) {			if (p->state == TASK_STOPPED)				force_sig_specific(SIGSTOP, p); 			freeze(p);			spin_lock_irqsave(&p->sighand->siglock, flags);			signal_wake_up(p, p->state == TASK_STOPPED);			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){	struct task_struct *g, *p;	unsigned long end_time;	unsigned int todo; 	end_time = jiffies + TIMEOUT;	do {		todo = 0;		read_lock(&tasklist_lock);		do_each_thread(g, p) {			if (!freezeable(p))				continue; 			if (frozen(p))				continue; 			if (p->state == TASK_TRACED && frozen(p->parent)) {				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++;		} while_each_thread(g, p);		read_unlock(&tasklist_lock);		yield();			/* Yield is okay here */		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.		 */		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; 			if (freezeable(p) && !frozen(p))				printk(KERN_ERR " %s\n", p->comm); 			cancel_freezing(p);		} while_each_thread(g, p);		read_unlock(&tasklist_lock);	} 	return todo;} /** *	freeze_processes - tell processes to enter the refrigerator * *	Returns 0 on success, or the number of processes that didn't freeze, *	although they were told to. */int freeze_processes(void){	unsigned int nr_unfrozen; 	printk("Stopping tasks ... ");	nr_unfrozen = try_to_freeze_tasks(FREEZER_USER_SPACE);	if (nr_unfrozen)		return nr_unfrozen; 	sys_sync();	nr_unfrozen = try_to_freeze_tasks(FREEZER_KERNEL_THREADS);	if (nr_unfrozen)		return nr_unfrozen; 	printk("done.\n");	BUG_ON(in_atomic());	return 0;} static void thaw_tasks(int thaw_user_space){	struct task_struct *g, *p; 	read_lock(&tasklist_lock);	do_each_thread(g, p) {		if (!freezeable(p))			continue; 		if (is_user_space(p) == !thaw_user_space)			continue; 		if (!thaw_process(p))			printk(KERN_WARNING " Strange, %s not stopped\n",				p->comm );	} while_each_thread(g, p);	read_unlock(&tasklist_lock);} void thaw_processes(void){	printk("Restarting tasks ... ");	thaw_tasks(FREEZER_KERNEL_THREADS);	thaw_tasks(FREEZER_USER_SPACE);	schedule();	printk("done.\n");} EXPORT_SYMBOL(refrigerator);