From a1703895092bd7582e171b2cb3440e52d705663e Mon Sep 17 00:00:00 2001 From: Shaun Ruffell Date: Thu, 4 Nov 2010 16:40:44 +0000 Subject: [PATCH] dahdi: Close race between checking IOMUX condition and sleeping. wake_up_interruptible() will change the state of waiting tasks back to TASK_RUNNING, therefore, we want to ensure we set TASK_INTERRUPTIBLE before checking the conditions that we're going to sleep on. In practice, this closes a small window were the caller may be put to sleep when the condition is true, and have to wait for another event to come in order to wake from the sleep. Signed-off-by: Shaun Ruffell Acked-by: Tzafrir Cohen git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@9467 a0bf4364-ded3-4de4-8d8a-66a801d63aff --- drivers/dahdi/dahdi-base.c | 79 +++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c index 4886671..122c655 100644 --- a/drivers/dahdi/dahdi-base.c +++ b/drivers/dahdi/dahdi-base.c @@ -4941,67 +4941,76 @@ static int dahdi_ioctl_getconf(struct file *file, unsigned long data) /** * dahdi_ioctl_iomux() - Wait for *something* to happen. * + * This is now basically like the wait_event_interruptible function, but with + * a much more involved wait condition. */ static int dahdi_ioctl_iomux(struct file *file, unsigned long data) { struct dahdi_chan *const chan = chan_from_file(file); unsigned long flags; int ret; + DEFINE_WAIT(wait); if (!chan) return -EINVAL; - get_user(chan->iomask, (int __user *)data); /* save mask */ - if (!chan->iomask)return(-EINVAL); /* cant wait for nothing */ - for(;;) /* loop forever */ - { - /* has to have SOME mask */ - ret = 0; /* start with empty return value */ + get_user(chan->iomask, (int __user *)data); + if (!chan->iomask) + return -EINVAL; + + while (1) { + + ret = 0; + prepare_to_wait(&chan->eventbufq, &wait, TASK_INTERRUPTIBLE); + spin_lock_irqsave(&chan->lock, flags); - /* if looking for read */ - if (chan->iomask & DAHDI_IOMUX_READ) - { + /* if looking for read */ + if (chan->iomask & DAHDI_IOMUX_READ) { /* if read available */ if ((chan->outreadbuf > -1) && !chan->rxdisable) ret |= DAHDI_IOMUX_READ; - } - /* if looking for write avail */ - if (chan->iomask & DAHDI_IOMUX_WRITE) - { + } + + /* if looking for write avail */ + if (chan->iomask & DAHDI_IOMUX_WRITE) { if (chan->inwritebuf > -1) ret |= DAHDI_IOMUX_WRITE; - } - /* if looking for write empty */ - if (chan->iomask & DAHDI_IOMUX_WRITEEMPTY) - { - /* if everything empty -- be sure the transmitter is enabled */ + } + /* if looking for write empty */ + if (chan->iomask & DAHDI_IOMUX_WRITEEMPTY) { + /* if everything empty -- be sure the transmitter is + * enabled */ chan->txdisable = 0; if (chan->outwritebuf < 0) ret |= DAHDI_IOMUX_WRITEEMPTY; - } - /* if looking for signalling event */ - if (chan->iomask & DAHDI_IOMUX_SIGEVENT) - { - /* if event */ + } + /* if looking for signalling event */ + if (chan->iomask & DAHDI_IOMUX_SIGEVENT) { + /* if event */ if (chan->eventinidx != chan->eventoutidx) ret |= DAHDI_IOMUX_SIGEVENT; - } + } spin_unlock_irqrestore(&chan->lock, flags); - /* if something to return, or not to wait */ - if (ret || (chan->iomask & DAHDI_IOMUX_NOWAIT)) - { - /* set return value */ + + /* if something to return, or not to wait */ + if (ret || (chan->iomask & DAHDI_IOMUX_NOWAIT)) { + /* set return value */ put_user(ret, (int __user *)data); break; /* get out of loop */ - } + } - interruptible_sleep_on(&chan->eventbufq); - if (signal_pending(current)) - return -ERESTARTSYS; - } - /* clear IO MUX mask */ + if (!signal_pending(current)) { + schedule(); + continue; + } + + ret = -ERESTARTSYS; + break; + } + + finish_wait(&chan->eventbufq, &wait); chan->iomask = 0; - return 0; + return ret; } static int