summaryrefslogtreecommitdiffstats
path: root/kernel/power/process.c
blob: 28de118f7a0b047c6c507a8adbf2113ce52bb21d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
 * 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>

/* 
 * Timeout for stopping processes
 */
#define TIMEOUT	(6 * HZ)


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) ||
	    (p->state == TASK_TRACED))
		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);
	printk("=");

	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;
}

/* 0 = success, else # of processes that we failed to stop */
int freeze_processes(void)
{
	int todo;
	unsigned long start_time;
	struct task_struct *g, *p;
	unsigned long flags;

	printk( "Stopping tasks: " );
	start_time = jiffies;
	do {
		todo = 0;
		read_lock(&tasklist_lock);
		do_each_thread(g, p) {
			if (!freezeable(p))
				continue;
			if (frozen(p))
				continue;

			freeze(p);
			spin_lock_irqsave(&p->sighand->siglock, flags);
			signal_wake_up(p, 0);
			spin_unlock_irqrestore(&p->sighand->siglock, flags);
			todo++;
		} while_each_thread(g, p);
		read_unlock(&tasklist_lock);
		yield();			/* Yield is okay here */
		if (todo && time_after(jiffies, start_time + TIMEOUT)) {
			printk( "\n" );
			printk(KERN_ERR " stopping tasks failed (%d tasks remaining)\n", todo );
			break;
		}
	} 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) {
		read_lock(&tasklist_lock);
		do_each_thread(g, p)
			if (freezing(p)) {
				pr_debug("  clean up: %s\n", p->comm);
				p->flags &= ~PF_FREEZE;
				spin_lock_irqsave(&p->sighand->siglock, flags);
				recalc_sigpending_tsk(p);
				spin_unlock_irqrestore(&p->sighand->siglock, flags);
			}
		while_each_thread(g, p);
		read_unlock(&tasklist_lock);
		return todo;
	}

	printk( "|\n" );
	BUG_ON(in_atomic());
	return 0;
}

void thaw_processes(void)
{
	struct task_struct *g, *p;

	printk( "Restarting tasks..." );
	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 );
	} while_each_thread(g, p);

	read_unlock(&tasklist_lock);
	schedule();
	printk( " done\n" );
}

EXPORT_SYMBOL(refrigerator);