From: Hans Verkuil Date: Mon, 10 Feb 2014 10:21:36 +0000 (-0300) Subject: upstream: [media] radio-cadet: avoid interruptible_sleep_on race X-Git-Tag: submit/tizen/20141121.110247~1431 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4915abd4f03d2849228710eb3f7eaca11e2b82d3;p=platform%2Fkernel%2Flinux-3.10.git upstream: [media] radio-cadet: avoid interruptible_sleep_on race interruptible_sleep_on is racy and going away. This replaces one use in the radio-cadet driver with a wait_event_interruptible call. Special care was taken that accesses to the rdsin and rdsout indices are always done with dev->lock held. Signed-off-by: Hans Verkuil Acked-by: Arnd Bergmann Signed-off-by: Mauro Carvalho Chehab --- diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index 545c04c..d719e59 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -270,6 +270,16 @@ reset_rds: outb(inb(dev->io + 1) & 0x7f, dev->io + 1); } +static bool cadet_has_rds_data(struct cadet *dev) +{ + bool result; + + mutex_lock(&dev->lock); + result = dev->rdsin != dev->rdsout; + mutex_unlock(&dev->lock); + return result; +} + static void cadet_handler(unsigned long data) { @@ -279,13 +289,12 @@ static void cadet_handler(unsigned long data) if (mutex_trylock(&dev->lock)) { outb(0x3, dev->io); /* Select RDS Decoder Control */ if ((inb(dev->io + 1) & 0x20) != 0) - printk(KERN_CRIT "cadet: RDS fifo overflow\n"); + pr_err("cadet: RDS fifo overflow\n"); outb(0x80, dev->io); /* Select RDS fifo */ + while ((inb(dev->io) & 0x80) != 0) { dev->rdsbuf[dev->rdsin] = inb(dev->io + 1); - if (dev->rdsin + 1 == dev->rdsout) - printk(KERN_WARNING "cadet: RDS buffer overflow\n"); - else + if (dev->rdsin + 1 != dev->rdsout) dev->rdsin++; } mutex_unlock(&dev->lock); @@ -294,7 +303,7 @@ static void cadet_handler(unsigned long data) /* * Service pending read */ - if (dev->rdsin != dev->rdsout) + if (cadet_has_rds_data(dev)) wake_up_interruptible(&dev->read_queue); /* @@ -327,22 +336,21 @@ static ssize_t cadet_read(struct file *file, char __user *data, size_t count, lo mutex_lock(&dev->lock); if (dev->rdsstat == 0) cadet_start_rds(dev); - if (dev->rdsin == dev->rdsout) { - if (file->f_flags & O_NONBLOCK) { - i = -EWOULDBLOCK; - goto unlock; - } - mutex_unlock(&dev->lock); - interruptible_sleep_on(&dev->read_queue); - mutex_lock(&dev->lock); - } + mutex_unlock(&dev->lock); + + if (!cadet_has_rds_data(dev) && (file->f_flags & O_NONBLOCK)) + return -EWOULDBLOCK; + i = wait_event_interruptible(dev->read_queue, cadet_has_rds_data(dev)); + if (i) + return i; + + mutex_lock(&dev->lock); while (i < count && dev->rdsin != dev->rdsout) readbuf[i++] = dev->rdsbuf[dev->rdsout++]; + mutex_unlock(&dev->lock); if (i && copy_to_user(data, readbuf, i)) - i = -EFAULT; -unlock: - mutex_unlock(&dev->lock); + return -EFAULT; return i; } @@ -352,7 +360,7 @@ static int vidioc_querycap(struct file *file, void *priv, { strlcpy(v->driver, "ADS Cadet", sizeof(v->driver)); strlcpy(v->card, "ADS Cadet", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); + strlcpy(v->bus_info, "ISA:radio-cadet", sizeof(v->bus_info)); v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE; v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -491,7 +499,7 @@ static unsigned int cadet_poll(struct file *file, struct poll_table_struct *wait cadet_start_rds(dev); mutex_unlock(&dev->lock); } - if (dev->rdsin != dev->rdsout) + if (cadet_has_rds_data(dev)) res |= POLLIN | POLLRDNORM; return res; }