summary refs log tree commit diff
path: root/drivers/isdn/mISDN/timerdev.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2013-04-15 17:04:04 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-04-29 15:41:48 -0400
commit1678ec00a632f8b9204e28e5c506128881171604 (patch)
tree2e693539d928e501e91ddfedf90ea780e9e2bea2 /drivers/isdn/mISDN/timerdev.c
parent1b1089561ce596a4032ba1039365090304db1cfd (diff)
downloadlinux-1678ec00a632f8b9204e28e5c506128881171604.tar.gz
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/mISDN/timerdev.c')
-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;
 }