Skip to content
Snippets Groups Projects
process.c 3.94 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     * 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>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /* 
     * Timeout for stopping processes
     */
    
    #define TIMEOUT	(20 * HZ)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    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))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		return 0;
    	return 1;
    }
    
    /* Refrigerator is place where frozen processes are stored :-). */
    
    void refrigerator(void)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	/* 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);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	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;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		schedule();
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	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);
    	}
    }
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /* 0 = success, else # of processes that we failed to stop */
    int freeze_processes(void)
    {
    
    	int todo, nr_user, user_frozen;
    
    	unsigned long start_time;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct task_struct *g, *p;
    
    	printk("Stopping tasks... ");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	start_time = jiffies;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	do {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		read_lock(&tasklist_lock);
    		do_each_thread(g, p) {
    			if (!freezeable(p))
    				continue;
    
    			if (frozen(p))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				continue;
    
    			if (p->state == TASK_TRACED && frozen(p->parent)) {
    				cancel_freezing(p);
    				continue;
    			}
    
    			if (p->mm && !(p->flags & PF_BORROWED_MM)) {
    				/* The task is a user-space one.
    				 * Freeze it unless there's a vfork completion
    				 * pending
    				 */
    				if (!p->vfork_done)
    					freeze_process(p);
    				nr_user++;
    			} else {
    				/* Freeze only if the user space is frozen */
    				if (user_frozen)
    					freeze_process(p);
    				todo++;
    			}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		} while_each_thread(g, p);
    		read_unlock(&tasklist_lock);
    
    		todo += nr_user;
    		if (!user_frozen && !nr_user) {
    			sys_sync();
    			start_time = jiffies;
    		}
    		user_frozen = !nr_user;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		yield();			/* Yield is okay here */
    
    		if (todo && time_after(jiffies, start_time + TIMEOUT))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	} while(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 (todo) {
    
    		printk("\n");
    		printk(KERN_ERR "Stopping tasks timed out "
    
    			"after %d seconds (%d tasks remaining):\n",
    			TIMEOUT / HZ, todo);
    
    		read_lock(&tasklist_lock);
    
    		do_each_thread(g, p) {
    			if (freezeable(p) && !frozen(p))
    
    				printk(KERN_ERR " %s\n", p->comm);
    
    		} while_each_thread(g, p);
    
    		read_unlock(&tasklist_lock);
    		return todo;
    	}
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	BUG_ON(in_atomic());
    	return 0;
    }
    
    void thaw_processes(void)
    {
    	struct task_struct *g, *p;
    
    
    	printk("Restarting tasks... ");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	read_lock(&tasklist_lock);
    	do_each_thread(g, p) {
    		if (!freezeable(p))
    			continue;
    
    		if (!thaw_process(p))
    
    			printk(KERN_INFO "Strange, %s not stopped\n", p->comm);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	} while_each_thread(g, p);
    
    	read_unlock(&tasklist_lock);
    	schedule();
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    EXPORT_SYMBOL(refrigerator);