summaryrefslogtreecommitdiff
path: root/drivers/media/usb/uvc/uvc_driver.c
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2013-04-25 22:28:51 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2013-06-08 19:51:16 -0300
commit17706f5653a90ff277b5b36c2eb60ff872df5e7a (patch)
tree110fbcca442fc9801187db3b73de32897fe9b9a0 /drivers/media/usb/uvc/uvc_driver.c
parentc2a273b24f2c82184cc0f04ba3a61da51c84724b (diff)
[media] uvcvideo: Fix open/close race condition
Maintaining the users count using an atomic variable makes sure that access to the counter won't be racy, but doesn't serialize access to the operations protected by the counter. This creates a race condition that could result in the status URB being submitted multiple times. Use a mutex to protect the users count and serialize access to the status start and stop operations. Reported-by: Shawn Nematbakhsh <shawnn@chromium.org> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/usb/uvc/uvc_driver.c')
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c23
1 files changed, 17 insertions, 6 deletions
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index a7e64af5f82e..81695d48c13e 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1836,8 +1836,8 @@ static int uvc_probe(struct usb_interface *intf,
INIT_LIST_HEAD(&dev->chains);
INIT_LIST_HEAD(&dev->streams);
atomic_set(&dev->nstreams, 0);
- atomic_set(&dev->users, 0);
atomic_set(&dev->nmappings, 0);
+ mutex_init(&dev->lock);
dev->udev = usb_get_dev(udev);
dev->intf = usb_get_intf(intf);
@@ -1950,8 +1950,13 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
/* Controls are cached on the fly so they don't need to be saved. */
if (intf->cur_altsetting->desc.bInterfaceSubClass ==
- UVC_SC_VIDEOCONTROL)
- return uvc_status_suspend(dev);
+ UVC_SC_VIDEOCONTROL) {
+ mutex_lock(&dev->lock);
+ if (dev->users)
+ uvc_status_stop(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
list_for_each_entry(stream, &dev->streams, list) {
if (stream->intf == intf)
@@ -1973,14 +1978,20 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
if (intf->cur_altsetting->desc.bInterfaceSubClass ==
UVC_SC_VIDEOCONTROL) {
- if (reset) {
- int ret = uvc_ctrl_resume_device(dev);
+ int ret = 0;
+ if (reset) {
+ ret = uvc_ctrl_resume_device(dev);
if (ret < 0)
return ret;
}
- return uvc_status_resume(dev);
+ mutex_lock(&dev->lock);
+ if (dev->users)
+ ret = uvc_status_start(dev, GFP_NOIO);
+ mutex_unlock(&dev->lock);
+
+ return ret;
}
list_for_each_entry(stream, &dev->streams, list) {