diff options
Diffstat (limited to 'kernel/freezer.c')
-rw-r--r-- | kernel/freezer.c | 42 |
1 files changed, 24 insertions, 18 deletions
diff --git a/kernel/freezer.c b/kernel/freezer.c index 4130e48649bb..a8822be43da0 100644 --- a/kernel/freezer.c +++ b/kernel/freezer.c | |||
@@ -22,14 +22,19 @@ bool __refrigerator(bool check_kthr_stop) | |||
22 | bool was_frozen = false; | 22 | bool was_frozen = false; |
23 | long save; | 23 | long save; |
24 | 24 | ||
25 | /* | ||
26 | * Enter FROZEN. If NOFREEZE, schedule immediate thawing by | ||
27 | * clearing freezing. | ||
28 | */ | ||
25 | spin_lock_irq(&freezer_lock); | 29 | spin_lock_irq(&freezer_lock); |
30 | repeat: | ||
26 | if (!freezing(current)) { | 31 | if (!freezing(current)) { |
27 | spin_unlock_irq(&freezer_lock); | 32 | spin_unlock_irq(&freezer_lock); |
28 | return was_frozen; | 33 | return was_frozen; |
29 | } | 34 | } |
30 | if (!(current->flags & PF_NOFREEZE)) | 35 | if (current->flags & PF_NOFREEZE) |
31 | current->flags |= PF_FROZEN; | 36 | clear_freeze_flag(current); |
32 | clear_freeze_flag(current); | 37 | current->flags |= PF_FROZEN; |
33 | spin_unlock_irq(&freezer_lock); | 38 | spin_unlock_irq(&freezer_lock); |
34 | 39 | ||
35 | save = current->state; | 40 | save = current->state; |
@@ -44,7 +49,7 @@ bool __refrigerator(bool check_kthr_stop) | |||
44 | 49 | ||
45 | for (;;) { | 50 | for (;;) { |
46 | set_current_state(TASK_UNINTERRUPTIBLE); | 51 | set_current_state(TASK_UNINTERRUPTIBLE); |
47 | if (!frozen(current) || | 52 | if (!freezing(current) || |
48 | (check_kthr_stop && kthread_should_stop())) | 53 | (check_kthr_stop && kthread_should_stop())) |
49 | break; | 54 | break; |
50 | was_frozen = true; | 55 | was_frozen = true; |
@@ -54,6 +59,13 @@ bool __refrigerator(bool check_kthr_stop) | |||
54 | /* Remove the accounting blocker */ | 59 | /* Remove the accounting blocker */ |
55 | current->flags &= ~PF_FREEZING; | 60 | current->flags &= ~PF_FREEZING; |
56 | 61 | ||
62 | /* leave FROZEN */ | ||
63 | spin_lock_irq(&freezer_lock); | ||
64 | if (freezing(current)) | ||
65 | goto repeat; | ||
66 | current->flags &= ~PF_FROZEN; | ||
67 | spin_unlock_irq(&freezer_lock); | ||
68 | |||
57 | pr_debug("%s left refrigerator\n", current->comm); | 69 | pr_debug("%s left refrigerator\n", current->comm); |
58 | 70 | ||
59 | /* | 71 | /* |
@@ -137,25 +149,19 @@ void cancel_freezing(struct task_struct *p) | |||
137 | spin_unlock_irqrestore(&freezer_lock, flags); | 149 | spin_unlock_irqrestore(&freezer_lock, flags); |
138 | } | 150 | } |
139 | 151 | ||
140 | /* | ||
141 | * Wake up a frozen task | ||
142 | * | ||
143 | * task_lock() is needed to prevent the race with refrigerator() which may | ||
144 | * occur if the freezing of tasks fails. Namely, without the lock, if the | ||
145 | * freezing of tasks failed, thaw_tasks() might have run before a task in | ||
146 | * refrigerator() could call frozen_process(), in which case the task would be | ||
147 | * frozen and no one would thaw it. | ||
148 | */ | ||
149 | void __thaw_task(struct task_struct *p) | 152 | void __thaw_task(struct task_struct *p) |
150 | { | 153 | { |
151 | unsigned long flags; | 154 | unsigned long flags; |
152 | 155 | ||
156 | /* | ||
157 | * Clear freezing and kick @p if FROZEN. Clearing is guaranteed to | ||
158 | * be visible to @p as waking up implies wmb. Waking up inside | ||
159 | * freezer_lock also prevents wakeups from leaking outside | ||
160 | * refrigerator. | ||
161 | */ | ||
153 | spin_lock_irqsave(&freezer_lock, flags); | 162 | spin_lock_irqsave(&freezer_lock, flags); |
154 | if (frozen(p)) { | 163 | clear_freeze_flag(p); |
155 | p->flags &= ~PF_FROZEN; | 164 | if (frozen(p)) |
156 | wake_up_process(p); | 165 | wake_up_process(p); |
157 | } else { | ||
158 | clear_freeze_flag(p); | ||
159 | } | ||
160 | spin_unlock_irqrestore(&freezer_lock, flags); | 166 | spin_unlock_irqrestore(&freezer_lock, flags); |
161 | } | 167 | } |