diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-15 16:55:41 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-29 15:41:48 -0400 |
commit | 1b1089561ce596a4032ba1039365090304db1cfd (patch) | |
tree | 5958e757c7d664ea874ab2b11bf231f8f5964cbd | |
parent | c08c464d6f4136d9e48ffa23c0bcd93442343b2a (diff) |
mISDN: fix races between misdn_del_timer() and timer callback
mark the victim with negative ->id if misdn_del_timer() finds it on
the list, have timer callback *not* move ones so marked to dev->expired
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | drivers/isdn/mISDN/timerdev.c | 22 |
1 files changed, 9 insertions, 13 deletions
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c index 5a1a5cadc766..c00546f830db 100644 --- a/drivers/isdn/mISDN/timerdev.c +++ b/drivers/isdn/mISDN/timerdev.c | |||
@@ -163,7 +163,8 @@ dev_expire_timer(unsigned long data) | |||
163 | u_long flags; | 163 | u_long flags; |
164 | 164 | ||
165 | spin_lock_irqsave(&timer->dev->lock, flags); | 165 | spin_lock_irqsave(&timer->dev->lock, flags); |
166 | list_move_tail(&timer->list, &timer->dev->expired); | 166 | if (timer->id >= 0) |
167 | list_move_tail(&timer->list, &timer->dev->expired); | ||
167 | spin_unlock_irqrestore(&timer->dev->lock, flags); | 168 | spin_unlock_irqrestore(&timer->dev->lock, flags); |
168 | wake_up_interruptible(&timer->dev->wait); | 169 | wake_up_interruptible(&timer->dev->wait); |
169 | } | 170 | } |
@@ -203,26 +204,21 @@ misdn_add_timer(struct mISDNtimerdev *dev, int timeout) | |||
203 | static int | 204 | static int |
204 | misdn_del_timer(struct mISDNtimerdev *dev, int id) | 205 | misdn_del_timer(struct mISDNtimerdev *dev, int id) |
205 | { | 206 | { |
206 | u_long flags; | ||
207 | struct mISDNtimer *timer; | 207 | struct mISDNtimer *timer; |
208 | int ret = 0; | ||
209 | 208 | ||
210 | spin_lock_irqsave(&dev->lock, flags); | 209 | spin_lock_irq(&dev->lock); |
211 | list_for_each_entry(timer, &dev->pending, list) { | 210 | list_for_each_entry(timer, &dev->pending, list) { |
212 | if (timer->id == id) { | 211 | if (timer->id == id) { |
213 | list_del_init(&timer->list); | 212 | list_del_init(&timer->list); |
214 | /* RED-PEN AK: race -- timer can be still running on | 213 | timer->id = -1; |
215 | * other CPU. Needs reference count I think | 214 | spin_unlock_irq(&dev->lock); |
216 | */ | 215 | del_timer_sync(&timer->tl); |
217 | del_timer(&timer->tl); | ||
218 | ret = timer->id; | ||
219 | kfree(timer); | 216 | kfree(timer); |
220 | goto unlock; | 217 | return id; |
221 | } | 218 | } |
222 | } | 219 | } |
223 | unlock: | 220 | spin_unlock_irq(&dev->lock); |
224 | spin_unlock_irqrestore(&dev->lock, flags); | 221 | return 0; |
225 | return ret; | ||
226 | } | 222 | } |
227 | 223 | ||
228 | static long | 224 | static long |