diff options
author | Oleg Nesterov <oleg@redhat.com> | 2011-11-23 12:28:17 -0500 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2011-11-23 12:28:17 -0500 |
commit | 24b7ead3fb0bae267c2ee50898eb4c13aedd1e9f (patch) | |
tree | 899a48d29daebb6e2eb9398797304985e5961fc8 /include/linux/freezer.h | |
parent | 34b087e48367c252e343c2f8de65676a78af1e4a (diff) |
freezer: fix wait_event_freezable/__thaw_task races
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>
Diffstat (limited to 'include/linux/freezer.h')
-rw-r--r-- | include/linux/freezer.h | 18 |
1 files changed, 10 insertions, 8 deletions
diff --git a/include/linux/freezer.h b/include/linux/freezer.h index a33550fc05c5..09570ac22be6 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h | |||
@@ -122,28 +122,30 @@ static inline int freezer_should_skip(struct task_struct *p) | |||
122 | #define wait_event_freezable(wq, condition) \ | 122 | #define wait_event_freezable(wq, condition) \ |
123 | ({ \ | 123 | ({ \ |
124 | int __retval; \ | 124 | int __retval; \ |
125 | do { \ | 125 | for (;;) { \ |
126 | __retval = wait_event_interruptible(wq, \ | 126 | __retval = wait_event_interruptible(wq, \ |
127 | (condition) || freezing(current)); \ | 127 | (condition) || freezing(current)); \ |
128 | if (__retval && !freezing(current)) \ | 128 | if (__retval || (condition)) \ |
129 | break; \ | 129 | break; \ |
130 | else if (!(condition)) \ | 130 | try_to_freeze(); \ |
131 | __retval = -ERESTARTSYS; \ | 131 | } \ |
132 | } while (try_to_freeze()); \ | ||
133 | __retval; \ | 132 | __retval; \ |
134 | }) | 133 | }) |
135 | 134 | ||
136 | |||
137 | #define wait_event_freezable_timeout(wq, condition, timeout) \ | 135 | #define wait_event_freezable_timeout(wq, condition, timeout) \ |
138 | ({ \ | 136 | ({ \ |
139 | long __retval = timeout; \ | 137 | long __retval = timeout; \ |
140 | do { \ | 138 | for (;;) { \ |
141 | __retval = wait_event_interruptible_timeout(wq, \ | 139 | __retval = wait_event_interruptible_timeout(wq, \ |
142 | (condition) || freezing(current), \ | 140 | (condition) || freezing(current), \ |
143 | __retval); \ | 141 | __retval); \ |
144 | } while (try_to_freeze()); \ | 142 | if (__retval <= 0 || (condition)) \ |
143 | break; \ | ||
144 | try_to_freeze(); \ | ||
145 | } \ | ||
145 | __retval; \ | 146 | __retval; \ |
146 | }) | 147 | }) |
148 | |||
147 | #else /* !CONFIG_FREEZER */ | 149 | #else /* !CONFIG_FREEZER */ |
148 | static inline bool frozen(struct task_struct *p) { return false; } | 150 | static inline bool frozen(struct task_struct *p) { return false; } |
149 | static inline bool freezing(struct task_struct *p) { return false; } | 151 | static inline bool freezing(struct task_struct *p) { return false; } |