summaryrefslogtreecommitdiffstats
path: root/drivers/isdn
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2013-04-15 23:04:04 +0200
committerAl Viro <viro@zeniv.linux.org.uk>2013-04-29 21:41:48 +0200
commit1678ec00a632f8b9204e28e5c506128881171604 (patch)
tree2e693539d928e501e91ddfedf90ea780e9e2bea2 /drivers/isdn
parentmISDN: fix races between misdn_del_timer() and timer callback (diff)
downloadlinux-1678ec00a632f8b9204e28e5c506128881171604.tar.xz
linux-1678ec00a632f8b9204e28e5c506128881171604.zip
mISDN: fix misdn_add_timer()/misdn_del_timer() race
do add_timer() *before* unlocking dev->lock, or unpleasant things can happen if misdn_del_timer() on another CPU finds the sucker, calls del_timer_sync() (which does nothing, since we hadn't started the timer yet) and frees it, just as we get around to add_timer()... Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'drivers/isdn')
-rw-r--r--drivers/isdn/mISDN/timerdev.c14
1 files changed, 5 insertions, 9 deletions
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
index c00546f830db..ddb8adcd5fbb 100644
--- a/drivers/isdn/mISDN/timerdev.c
+++ b/drivers/isdn/mISDN/timerdev.c
@@ -173,7 +173,6 @@ static int
misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
{
int id;
- u_long flags;
struct mISDNtimer *timer;
if (!timeout) {
@@ -184,19 +183,16 @@ misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
if (!timer)
return -ENOMEM;
- spin_lock_irqsave(&dev->lock, flags);
- timer->id = dev->next_id++;
+ timer->dev = dev;
+ setup_timer(&timer->tl, dev_expire_timer, (long)timer);
+ spin_lock_irq(&dev->lock);
+ id = timer->id = dev->next_id++;
if (dev->next_id < 0)
dev->next_id = 1;
list_add_tail(&timer->list, &dev->pending);
- spin_unlock_irqrestore(&dev->lock, flags);
- timer->dev = dev;
- timer->tl.data = (long)timer;
- timer->tl.function = dev_expire_timer;
- init_timer(&timer->tl);
timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
add_timer(&timer->tl);
- id = timer->id;
+ spin_unlock_irq(&dev->lock);
}
return id;
}