freezer: make freezing indicate freeze condition in effect
[cascardo/linux.git] / kernel / freezer.c
index 4130e48..a8822be 100644 (file)
@@ -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);
 }