freezer: fix wait_event_freezable/__thaw_task races
authorOleg Nesterov <oleg@redhat.com>
Wed, 23 Nov 2011 17:28:17 +0000 (09:28 -0800)
committerTejun Heo <tj@kernel.org>
Wed, 23 Nov 2011 17:28:17 +0000 (09:28 -0800)
wait_event_freezable() and friends stop the waiting if try_to_freeze()
fails. This is not right, we can race with __thaw_task() and in this
case

- wait_event_freezable() returns the wrong ERESTARTSYS

- wait_event_freezable_timeout() can return the positive
  value while condition == F

Change the code to always check __retval/condition before return.

Note: with or without this patch the timeout logic looks strange,
probably we should recalc timeout if try_to_freeze() returns T.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Tejun Heo <tj@kernel.org>
include/linux/freezer.h

index a33550fc05c51e41239066c30a3f23d917e285ee..09570ac22be6063d2efe1cd1e11f8aa39ab02cff 100644 (file)
@@ -122,28 +122,30 @@ static inline int freezer_should_skip(struct task_struct *p)
 #define wait_event_freezable(wq, condition)                            \
 ({                                                                     \
        int __retval;                                                   \
-       do {                                                            \
+       for (;;) {                                                      \
                __retval = wait_event_interruptible(wq,                 \
                                (condition) || freezing(current));      \
-               if (__retval && !freezing(current))                     \
+               if (__retval || (condition))                            \
                        break;                                          \
-               else if (!(condition))                                  \
-                       __retval = -ERESTARTSYS;                        \
-       } while (try_to_freeze());                                      \
+               try_to_freeze();                                        \
+       }                                                               \
        __retval;                                                       \
 })
 
-
 #define wait_event_freezable_timeout(wq, condition, timeout)           \
 ({                                                                     \
        long __retval = timeout;                                        \
-       do {                                                            \
+       for (;;) {                                                      \
                __retval = wait_event_interruptible_timeout(wq,         \
                                (condition) || freezing(current),       \
                                __retval);                              \
-       } while (try_to_freeze());                                      \
+               if (__retval <= 0 || (condition))                       \
+                       break;                                          \
+               try_to_freeze();                                        \
+       }                                                               \
        __retval;                                                       \
 })
+
 #else /* !CONFIG_FREEZER */
 static inline bool frozen(struct task_struct *p) { return false; }
 static inline bool freezing(struct task_struct *p) { return false; }