summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2013-04-16 00:55:41 +0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-04-29 23:41:48 +0400
commit1b1089561ce596a4032ba1039365090304db1cfd (patch)
tree5958e757c7d664ea874ab2b11bf231f8f5964cbd
parentc08c464d6f4136d9e48ffa23c0bcd93442343b2a (diff)
downloadlinux-1b1089561ce596a4032ba1039365090304db1cfd.tar.xz
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.c22
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)
u_long flags;
spin_lock_irqsave(&timer->dev->lock, flags);
- list_move_tail(&timer->list, &timer->dev->expired);
+ if (timer->id >= 0)
+ list_move_tail(&timer->list, &timer->dev->expired);
spin_unlock_irqrestore(&timer->dev->lock, flags);
wake_up_interruptible(&timer->dev->wait);
}
@@ -203,26 +204,21 @@ misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
static int
misdn_del_timer(struct mISDNtimerdev *dev, int id)
{
- u_long flags;
struct mISDNtimer *timer;
- int ret = 0;
- spin_lock_irqsave(&dev->lock, flags);
+ spin_lock_irq(&dev->lock);
list_for_each_entry(timer, &dev->pending, list) {
if (timer->id == id) {
list_del_init(&timer->list);
- /* RED-PEN AK: race -- timer can be still running on
- * other CPU. Needs reference count I think
- */
- del_timer(&timer->tl);
- ret = timer->id;
+ timer->id = -1;
+ spin_unlock_irq(&dev->lock);
+ del_timer_sync(&timer->tl);
kfree(timer);
- goto unlock;
+ return id;
}
}
-unlock:
- spin_unlock_irqrestore(&dev->lock, flags);
- return ret;
+ spin_unlock_irq(&dev->lock);
+ return 0;
}
static long