aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/freezer.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2011-11-21 15:32:24 -0500
committerTejun Heo <tj@kernel.org>2011-11-21 15:32:24 -0500
commit0c9af09262864a2744091ee94c98c4a8fd60c98b (patch)
treee7dacbb7f370e0d7c515ed743a2c5b5179a1de5e /kernel/freezer.c
parent6cd8dedcdd8e8de01391a7cf25f0b2afeb24f8f4 (diff)
freezer: use dedicated lock instead of task_lock() + memory barrier
Freezer synchronization is needlessly complicated - it's by no means a hot path and the priority is staying unintrusive and safe. This patch makes it simply use a dedicated lock instead of piggy-backing on task_lock() and playing with memory barriers. On the failure path of try_to_freeze_tasks(), locking is moved from it to cancel_freezing(). This makes the frozen() test racy but the race here is a non-issue as the warning is printed for tasks which failed to enter frozen for 20 seconds and race on PF_FROZEN at the last moment doesn't change anything. This simplifies freezer implementation and eases further changes including some race fixes. Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'kernel/freezer.c')
-rw-r--r--kernel/freezer.c84
1 files changed, 37 insertions, 47 deletions
diff --git a/kernel/freezer.c b/kernel/freezer.c
index c851d588e29f..4130e48649bb 100644
--- a/kernel/freezer.c
+++ b/kernel/freezer.c
@@ -11,17 +11,8 @@
11#include <linux/freezer.h> 11#include <linux/freezer.h>
12#include <linux/kthread.h> 12#include <linux/kthread.h>
13 13
14/* 14/* protects freezing and frozen transitions */
15 * freezing is complete, mark current process as frozen 15static DEFINE_SPINLOCK(freezer_lock);
16 */
17static inline void frozen_process(void)
18{
19 if (!unlikely(current->flags & PF_NOFREEZE)) {
20 current->flags |= PF_FROZEN;
21 smp_wmb();
22 }
23 clear_freeze_flag(current);
24}
25 16
26/* Refrigerator is place where frozen processes are stored :-). */ 17/* Refrigerator is place where frozen processes are stored :-). */
27bool __refrigerator(bool check_kthr_stop) 18bool __refrigerator(bool check_kthr_stop)
@@ -31,14 +22,16 @@ bool __refrigerator(bool check_kthr_stop)
31 bool was_frozen = false; 22 bool was_frozen = false;
32 long save; 23 long save;
33 24
34 task_lock(current); 25 spin_lock_irq(&freezer_lock);
35 if (freezing(current)) { 26 if (!freezing(current)) {
36 frozen_process(); 27 spin_unlock_irq(&freezer_lock);
37 task_unlock(current);
38 } else {
39 task_unlock(current);
40 return was_frozen; 28 return was_frozen;
41 } 29 }
30 if (!(current->flags & PF_NOFREEZE))
31 current->flags |= PF_FROZEN;
32 clear_freeze_flag(current);
33 spin_unlock_irq(&freezer_lock);
34
42 save = current->state; 35 save = current->state;
43 pr_debug("%s entered refrigerator\n", current->comm); 36 pr_debug("%s entered refrigerator\n", current->comm);
44 37
@@ -99,21 +92,18 @@ static void fake_signal_wake_up(struct task_struct *p)
99 */ 92 */
100bool freeze_task(struct task_struct *p, bool sig_only) 93bool freeze_task(struct task_struct *p, bool sig_only)
101{ 94{
102 /* 95 unsigned long flags;
103 * We first check if the task is freezing and next if it has already 96 bool ret = false;
104 * been frozen to avoid the race with frozen_process() which first marks 97
105 * the task as frozen and next clears its TIF_FREEZE. 98 spin_lock_irqsave(&freezer_lock, flags);
106 */ 99
107 if (!freezing(p)) { 100 if (sig_only && !should_send_signal(p))
108 smp_rmb(); 101 goto out_unlock;
109 if (frozen(p)) 102
110 return false; 103 if (frozen(p))
111 104 goto out_unlock;
112 if (!sig_only || should_send_signal(p)) 105
113 set_freeze_flag(p); 106 set_freeze_flag(p);
114 else
115 return false;
116 }
117 107
118 if (should_send_signal(p)) { 108 if (should_send_signal(p)) {
119 fake_signal_wake_up(p); 109 fake_signal_wake_up(p);
@@ -123,26 +113,28 @@ bool freeze_task(struct task_struct *p, bool sig_only)
123 * TASK_RUNNING transition can't race with task state 113 * TASK_RUNNING transition can't race with task state
124 * testing in try_to_freeze_tasks(). 114 * testing in try_to_freeze_tasks().
125 */ 115 */
126 } else if (sig_only) {
127 return false;
128 } else { 116 } else {
129 wake_up_state(p, TASK_INTERRUPTIBLE); 117 wake_up_state(p, TASK_INTERRUPTIBLE);
130 } 118 }
131 119 ret = true;
132 return true; 120out_unlock:
121 spin_unlock_irqrestore(&freezer_lock, flags);
122 return ret;
133} 123}
134 124
135void cancel_freezing(struct task_struct *p) 125void cancel_freezing(struct task_struct *p)
136{ 126{
137 unsigned long flags; 127 unsigned long flags;
138 128
129 spin_lock_irqsave(&freezer_lock, flags);
139 if (freezing(p)) { 130 if (freezing(p)) {
140 pr_debug(" clean up: %s\n", p->comm); 131 pr_debug(" clean up: %s\n", p->comm);
141 clear_freeze_flag(p); 132 clear_freeze_flag(p);
142 spin_lock_irqsave(&p->sighand->siglock, flags); 133 spin_lock(&p->sighand->siglock);
143 recalc_sigpending_and_wake(p); 134 recalc_sigpending_and_wake(p);
144 spin_unlock_irqrestore(&p->sighand->siglock, flags); 135 spin_unlock(&p->sighand->siglock);
145 } 136 }
137 spin_unlock_irqrestore(&freezer_lock, flags);
146} 138}
147 139
148/* 140/*
@@ -156,16 +148,14 @@ void cancel_freezing(struct task_struct *p)
156 */ 148 */
157void __thaw_task(struct task_struct *p) 149void __thaw_task(struct task_struct *p)
158{ 150{
159 bool was_frozen; 151 unsigned long flags;
160 152
161 task_lock(p); 153 spin_lock_irqsave(&freezer_lock, flags);
162 was_frozen = frozen(p); 154 if (frozen(p)) {
163 if (was_frozen)
164 p->flags &= ~PF_FROZEN; 155 p->flags &= ~PF_FROZEN;
165 else
166 clear_freeze_flag(p);
167 task_unlock(p);
168
169 if (was_frozen)
170 wake_up_process(p); 156 wake_up_process(p);
157 } else {
158 clear_freeze_flag(p);
159 }
160 spin_unlock_irqrestore(&freezer_lock, flags);
171} 161}