summaryrefslogtreecommitdiff
path: root/drivers/isdn/mISDN/timerdev.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2013-04-16 00:31:13 +0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-04-29 23:41:47 +0400
commitc08c464d6f4136d9e48ffa23c0bcd93442343b2a (patch)
tree988674bee82e2e7f94a258066899e2e0408cf3eb /drivers/isdn/mISDN/timerdev.c
parent03feee373f05d5c500dd6198015de83005df902c (diff)
downloadlinux-c08c464d6f4136d9e48ffa23c0bcd93442343b2a.tar.xz
mISDN: fix the races with timers going off just as they are deleted
timer callback in timerdev.c both accesses struct mISDNtimer it's called for *and* moves it to dev->expired. We need del_timer_sync(), or we risk kfree() freeing it right under dev_expire_timer() *and* dev->expired getting corrupted. 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, 12 insertions, 2 deletions
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
index 1094667d8f31..5a1a5cadc766 100644
--- a/drivers/isdn/mISDN/timerdev.c
+++ b/drivers/isdn/mISDN/timerdev.c
@@ -72,14 +72,24 @@ static int
mISDN_close(struct inode *ino, struct file *filep)
{
struct mISDNtimerdev *dev = filep->private_data;
+ struct list_head *list = &dev->pending;
struct mISDNtimer *timer, *next;
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
- list_for_each_entry_safe(timer, next, &dev->pending, list) {
- del_timer(&timer->tl);
+
+ spin_lock_irq(&dev->lock);
+ while (!list_empty(list)) {
+ timer = list_first_entry(list, struct mISDNtimer, list);
+ spin_unlock_irq(&dev->lock);
+ del_timer_sync(&timer->tl);
+ spin_lock_irq(&dev->lock);
+ /* it might have been moved to ->expired */
+ list_del(&timer->list);
kfree(timer);
}
+ spin_unlock_irq(&dev->lock);
+
list_for_each_entry_safe(timer, next, &dev->expired, list) {
kfree(timer);
}