aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/cgroup_freezer.c2
-rw-r--r--kernel/freezer.c42
-rw-r--r--kernel/power/process.c3
3 files changed, 27 insertions, 20 deletions
diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c
index a6d405a86ee0..cd27b0825560 100644
--- a/kernel/cgroup_freezer.c
+++ b/kernel/cgroup_freezer.c
@@ -231,7 +231,7 @@ static void update_if_frozen(struct cgroup *cgroup,
cgroup_iter_start(cgroup, &it);
while ((task = cgroup_iter_next(cgroup, &it))) {
ntotal++;
- if (frozen(task))
+ if (freezing(task) && frozen(task))
nfrozen++;
}
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)
bool was_frozen = false;
long save;
+ /*
+ * Enter FROZEN. If NOFREEZE, schedule immediate thawing by
+ * clearing freezing.
+ */
spin_lock_irq(&freezer_lock);
+repeat:
if (!freezing(current)) {
spin_unlock_irq(&freezer_lock);
return was_frozen;
}
- if (!(current->flags & PF_NOFREEZE))
- current->flags |= PF_FROZEN;
- clear_freeze_flag(current);
+ if (current->flags & PF_NOFREEZE)
+ clear_freeze_flag(current);
+ current->flags |= PF_FROZEN;
spin_unlock_irq(&freezer_lock);
save = current->state;
@@ -44,7 +49,7 @@ bool __refrigerator(bool check_kthr_stop)
for (;;) {
set_current_state(TASK_UNINTERRUPTIBLE);
- if (!frozen(current) ||
+ if (!freezing(current) ||
(check_kthr_stop && kthread_should_stop()))
break;
was_frozen = true;
@@ -54,6 +59,13 @@ bool __refrigerator(bool check_kthr_stop)
/* Remove the accounting blocker */
current->flags &= ~PF_FREEZING;
+ /* leave FROZEN */
+ spin_lock_irq(&freezer_lock);
+ if (freezing(current))
+ goto repeat;
+ current->flags &= ~PF_FROZEN;
+ spin_unlock_irq(&freezer_lock);
+
pr_debug("%s left refrigerator\n", current->comm);
/*
@@ -137,25 +149,19 @@ void cancel_freezing(struct task_struct *p)
spin_unlock_irqrestore(&freezer_lock, flags);
}
-/*
- * Wake up a frozen task
- *
- * task_lock() is needed to prevent the race with refrigerator() which may
- * occur if the freezing of tasks fails. Namely, without the lock, if the
- * freezing of tasks failed, thaw_tasks() might have run before a task in
- * refrigerator() could call frozen_process(), in which case the task would be
- * frozen and no one would thaw it.
- */
void __thaw_task(struct task_struct *p)
{
unsigned long flags;
+ /*
+ * Clear freezing and kick @p if FROZEN. Clearing is guaranteed to
+ * be visible to @p as waking up implies wmb. Waking up inside
+ * freezer_lock also prevents wakeups from leaking outside
+ * refrigerator.
+ */
spin_lock_irqsave(&freezer_lock, flags);
- if (frozen(p)) {
- p->flags &= ~PF_FROZEN;
+ clear_freeze_flag(p);
+ if (frozen(p))
wake_up_process(p);
- } else {
- clear_freeze_flag(p);
- }
spin_unlock_irqrestore(&freezer_lock, flags);
}
diff --git a/kernel/power/process.c b/kernel/power/process.c
index bd420ca48261..e6e2739190b5 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -118,7 +118,8 @@ static int try_to_freeze_tasks(bool sig_only)
read_lock(&tasklist_lock);
do_each_thread(g, p) {
- if (!wakeup && freezing(p) && !freezer_should_skip(p))
+ if (!wakeup && !freezer_should_skip(p) &&
+ freezing(p) && !frozen(p))
sched_show_task(p);
cancel_freezing(p);
} while_each_thread(g, p);