diff options
author | Tejun Heo <tj@kernel.org> | 2011-11-21 15:32:24 -0500 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2011-11-21 15:32:24 -0500 |
commit | 0c9af09262864a2744091ee94c98c4a8fd60c98b (patch) | |
tree | e7dacbb7f370e0d7c515ed743a2c5b5179a1de5e /kernel/freezer.c | |
parent | 6cd8dedcdd8e8de01391a7cf25f0b2afeb24f8f4 (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.c | 84 |
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 | 15 | static DEFINE_SPINLOCK(freezer_lock); |
16 | */ | ||
17 | static 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 :-). */ |
27 | bool __refrigerator(bool check_kthr_stop) | 18 | bool __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 | */ |
100 | bool freeze_task(struct task_struct *p, bool sig_only) | 93 | bool 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; | 120 | out_unlock: |
121 | spin_unlock_irqrestore(&freezer_lock, flags); | ||
122 | return ret; | ||
133 | } | 123 | } |
134 | 124 | ||
135 | void cancel_freezing(struct task_struct *p) | 125 | void 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 | */ |
157 | void __thaw_task(struct task_struct *p) | 149 | void __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 | } |