]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
freezer: restructure __refrigerator()
authorTejun Heo <tj@kernel.org>
Thu, 3 Nov 2011 22:19:28 +0000 (15:19 -0700)
committerTejun Heo <tj@kernel.org>
Thu, 3 Nov 2011 22:19:28 +0000 (15:19 -0700)
If another freeze happens before all tasks leave FROZEN state after
being thawed, the freezer can see the existing FROZEN and consider the
tasks to be frozen but they can clear FROZEN without checking the new
freezing().

Oleg suggested restructuring __refrigerator() such that there's single
condition check section inside freezer_lock and sigpending is cleared
afterwards, which fixes the problem and simplifies the code.
Restructure accordingly.

-v2: Frozen loop exited without releasing freezer_lock.  Fixed.

Signed-off-by: Tejun Heo <tj@kernel.org>
Reported-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Oleg Nesterov <oleg@redhat.com>
Cc: "Rafael J. Wysocki" <rjw@sisk.pl>
kernel/freezer.c

index a95250c8c93d08b0305c9de61d65bf69c4427b67..10f98405ba93335c10a9cba402735d89c23868e7 100644 (file)
@@ -52,39 +52,29 @@ bool __refrigerator(bool check_kthr_stop)
        /* Hmm, should we be allowed to suspend when there are realtime
           processes around? */
        bool was_frozen = false;
-       long save;
+       long save = current->state;
 
-       /*
-        * No point in checking freezing() again - the caller already did.
-        * Proceed to enter FROZEN.
-        */
-       spin_lock_irq(&freezer_lock);
-repeat:
-       current->flags |= PF_FROZEN;
-       spin_unlock_irq(&freezer_lock);
-
-       save = current->state;
        pr_debug("%s entered refrigerator\n", current->comm);
 
-       spin_lock_irq(&current->sighand->siglock);
-       recalc_sigpending(); /* We sent fake signal, clean it up */
-       spin_unlock_irq(&current->sighand->siglock);
-
        for (;;) {
                set_current_state(TASK_UNINTERRUPTIBLE);
+
+               spin_lock_irq(&freezer_lock);
+               current->flags |= PF_FROZEN;
                if (!freezing(current) ||
                    (check_kthr_stop && kthread_should_stop()))
+                       current->flags &= ~PF_FROZEN;
+               spin_unlock_irq(&freezer_lock);
+
+               if (!(current->flags & PF_FROZEN))
                        break;
                was_frozen = true;
                schedule();
        }
 
-       /* leave FROZEN */
-       spin_lock_irq(&freezer_lock);
-       if (freezing(current))
-               goto repeat;
-       current->flags &= ~PF_FROZEN;
-       spin_unlock_irq(&freezer_lock);
+       spin_lock_irq(&current->sighand->siglock);
+       recalc_sigpending(); /* We sent fake signal, clean it up */
+       spin_unlock_irq(&current->sighand->siglock);
 
        pr_debug("%s left refrigerator\n", current->comm);