]> git.karo-electronics.de Git - karo-tx-linux.git/blob - kernel/freezer.c
Merge branch 'bkl-removal' of git://git.lwn.net/linux-2.6
[karo-tx-linux.git] / kernel / freezer.c
1 /*
2  * kernel/freezer.c - Function to freeze a process
3  *
4  * Originally from kernel/power/process.c
5  */
6
7 #include <linux/interrupt.h>
8 #include <linux/suspend.h>
9 #include <linux/module.h>
10 #include <linux/syscalls.h>
11 #include <linux/freezer.h>
12
13 /*
14  * freezing is complete, mark current process as frozen
15  */
16 static inline void frozen_process(void)
17 {
18         if (!unlikely(current->flags & PF_NOFREEZE)) {
19                 current->flags |= PF_FROZEN;
20                 wmb();
21         }
22         clear_freeze_flag(current);
23 }
24
25 /* Refrigerator is place where frozen processes are stored :-). */
26 void refrigerator(void)
27 {
28         /* Hmm, should we be allowed to suspend when there are realtime
29            processes around? */
30         long save;
31
32         task_lock(current);
33         if (freezing(current)) {
34                 frozen_process();
35                 task_unlock(current);
36         } else {
37                 task_unlock(current);
38                 return;
39         }
40         save = current->state;
41         pr_debug("%s entered refrigerator\n", current->comm);
42
43         spin_lock_irq(&current->sighand->siglock);
44         recalc_sigpending(); /* We sent fake signal, clean it up */
45         spin_unlock_irq(&current->sighand->siglock);
46
47         for (;;) {
48                 set_current_state(TASK_UNINTERRUPTIBLE);
49                 if (!frozen(current))
50                         break;
51                 schedule();
52         }
53         pr_debug("%s left refrigerator\n", current->comm);
54         __set_current_state(save);
55 }
56 EXPORT_SYMBOL(refrigerator);
57
58 static void fake_signal_wake_up(struct task_struct *p)
59 {
60         unsigned long flags;
61
62         spin_lock_irqsave(&p->sighand->siglock, flags);
63         signal_wake_up(p, 0);
64         spin_unlock_irqrestore(&p->sighand->siglock, flags);
65 }
66
67 /**
68  *      freeze_task - send a freeze request to given task
69  *      @p: task to send the request to
70  *      @sig_only: if set, the request will only be sent if the task has the
71  *              PF_FREEZER_NOSIG flag unset
72  *      Return value: 'false', if @sig_only is set and the task has
73  *              PF_FREEZER_NOSIG set or the task is frozen, 'true', otherwise
74  *
75  *      The freeze request is sent by setting the tasks's TIF_FREEZE flag and
76  *      either sending a fake signal to it or waking it up, depending on whether
77  *      or not it has PF_FREEZER_NOSIG set.  If @sig_only is set and the task
78  *      has PF_FREEZER_NOSIG set (ie. it is a typical kernel thread), its
79  *      TIF_FREEZE flag will not be set.
80  */
81 bool freeze_task(struct task_struct *p, bool sig_only)
82 {
83         /*
84          * We first check if the task is freezing and next if it has already
85          * been frozen to avoid the race with frozen_process() which first marks
86          * the task as frozen and next clears its TIF_FREEZE.
87          */
88         if (!freezing(p)) {
89                 rmb();
90                 if (frozen(p))
91                         return false;
92
93                 if (!sig_only || should_send_signal(p))
94                         set_freeze_flag(p);
95                 else
96                         return false;
97         }
98
99         if (should_send_signal(p)) {
100                 if (!signal_pending(p))
101                         fake_signal_wake_up(p);
102         } else if (sig_only) {
103                 return false;
104         } else {
105                 wake_up_state(p, TASK_INTERRUPTIBLE);
106         }
107
108         return true;
109 }
110
111 void cancel_freezing(struct task_struct *p)
112 {
113         unsigned long flags;
114
115         if (freezing(p)) {
116                 pr_debug("  clean up: %s\n", p->comm);
117                 clear_freeze_flag(p);
118                 spin_lock_irqsave(&p->sighand->siglock, flags);
119                 recalc_sigpending_and_wake(p);
120                 spin_unlock_irqrestore(&p->sighand->siglock, flags);
121         }
122 }
123
124 /*
125  * Wake up a frozen process
126  *
127  * task_lock() is needed to prevent the race with refrigerator() which may
128  * occur if the freezing of tasks fails.  Namely, without the lock, if the
129  * freezing of tasks failed, thaw_tasks() might have run before a task in
130  * refrigerator() could call frozen_process(), in which case the task would be
131  * frozen and no one would thaw it.
132  */
133 int __thaw_process(struct task_struct *p)
134 {
135         if (frozen(p)) {
136                 p->flags &= ~PF_FROZEN;
137                 return 1;
138         }
139         clear_freeze_flag(p);
140         return 0;
141 }
142
143 int thaw_process(struct task_struct *p)
144 {
145         task_lock(p);
146         if (__thaw_process(p) == 1) {
147                 task_unlock(p);
148                 wake_up_process(p);
149                 return 1;
150         }
151         task_unlock(p);
152         return 0;
153 }
154 EXPORT_SYMBOL(thaw_process);