diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-15 17:04:04 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-04-29 15:41:48 -0400 |
commit | 1678ec00a632f8b9204e28e5c506128881171604 (patch) | |
tree | 2e693539d928e501e91ddfedf90ea780e9e2bea2 /drivers | |
parent | 1b1089561ce596a4032ba1039365090304db1cfd (diff) | |
download | lwn-1678ec00a632f8b9204e28e5c506128881171604.tar.gz lwn-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')
-rw-r--r-- | drivers/isdn/mISDN/timerdev.c | 14 |
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; } |