aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2013-04-25 21:28:51 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2013-06-08 18:51:16 -0400
commit17706f5653a90ff277b5b36c2eb60ff872df5e7a (patch)
tree110fbcca442fc9801187db3b73de32897fe9b9a0
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>
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c23
-rw-r--r--drivers/media/usb/uvc/uvc_status.c21
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c14
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h7
4 files changed, 32 insertions, 33 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,
1836 INIT_LIST_HEAD(&dev->chains); 1836 INIT_LIST_HEAD(&dev->chains);
1837 INIT_LIST_HEAD(&dev->streams); 1837 INIT_LIST_HEAD(&dev->streams);
1838 atomic_set(&dev->nstreams, 0); 1838 atomic_set(&dev->nstreams, 0);
1839 atomic_set(&dev->users, 0);
1840 atomic_set(&dev->nmappings, 0); 1839 atomic_set(&dev->nmappings, 0);
1840 mutex_init(&dev->lock);
1841 1841
1842 dev->udev = usb_get_dev(udev); 1842 dev->udev = usb_get_dev(udev);
1843 dev->intf = usb_get_intf(intf); 1843 dev->intf = usb_get_intf(intf);
@@ -1950,8 +1950,13 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
1950 1950
1951 /* Controls are cached on the fly so they don't need to be saved. */ 1951 /* Controls are cached on the fly so they don't need to be saved. */
1952 if (intf->cur_altsetting->desc.bInterfaceSubClass == 1952 if (intf->cur_altsetting->desc.bInterfaceSubClass ==
1953 UVC_SC_VIDEOCONTROL) 1953 UVC_SC_VIDEOCONTROL) {
1954 return uvc_status_suspend(dev); 1954 mutex_lock(&dev->lock);
1955 if (dev->users)
1956 uvc_status_stop(dev);
1957 mutex_unlock(&dev->lock);
1958 return 0;
1959 }
1955 1960
1956 list_for_each_entry(stream, &dev->streams, list) { 1961 list_for_each_entry(stream, &dev->streams, list) {
1957 if (stream->intf == intf) 1962 if (stream->intf == intf)
@@ -1973,14 +1978,20 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
1973 1978
1974 if (intf->cur_altsetting->desc.bInterfaceSubClass == 1979 if (intf->cur_altsetting->desc.bInterfaceSubClass ==
1975 UVC_SC_VIDEOCONTROL) { 1980 UVC_SC_VIDEOCONTROL) {
1976 if (reset) { 1981 int ret = 0;
1977 int ret = uvc_ctrl_resume_device(dev);
1978 1982
1983 if (reset) {
1984 ret = uvc_ctrl_resume_device(dev);
1979 if (ret < 0) 1985 if (ret < 0)
1980 return ret; 1986 return ret;
1981 } 1987 }
1982 1988
1983 return uvc_status_resume(dev); 1989 mutex_lock(&dev->lock);
1990 if (dev->users)
1991 ret = uvc_status_start(dev, GFP_NOIO);
1992 mutex_unlock(&dev->lock);
1993
1994 return ret;
1984 } 1995 }
1985 1996
1986 list_for_each_entry(stream, &dev->streams, list) { 1997 list_for_each_entry(stream, &dev->streams, list) {
diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index b7492775e6ae..f552ab997956 100644
--- a/drivers/media/usb/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
@@ -206,32 +206,15 @@ void uvc_status_cleanup(struct uvc_device *dev)
206 uvc_input_cleanup(dev); 206 uvc_input_cleanup(dev);
207} 207}
208 208
209int uvc_status_start(struct uvc_device *dev) 209int uvc_status_start(struct uvc_device *dev, gfp_t flags)
210{ 210{
211 if (dev->int_urb == NULL) 211 if (dev->int_urb == NULL)
212 return 0; 212 return 0;
213 213
214 return usb_submit_urb(dev->int_urb, GFP_KERNEL); 214 return usb_submit_urb(dev->int_urb, flags);
215} 215}
216 216
217void uvc_status_stop(struct uvc_device *dev) 217void uvc_status_stop(struct uvc_device *dev)
218{ 218{
219 usb_kill_urb(dev->int_urb); 219 usb_kill_urb(dev->int_urb);
220} 220}
221
222int uvc_status_suspend(struct uvc_device *dev)
223{
224 if (atomic_read(&dev->users))
225 usb_kill_urb(dev->int_urb);
226
227 return 0;
228}
229
230int uvc_status_resume(struct uvc_device *dev)
231{
232 if (dev->int_urb == NULL || atomic_read(&dev->users) == 0)
233 return 0;
234
235 return usb_submit_urb(dev->int_urb, GFP_NOIO);
236}
237
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index b2dc32623a71..3afff92804d3 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -498,16 +498,20 @@ static int uvc_v4l2_open(struct file *file)
498 return -ENOMEM; 498 return -ENOMEM;
499 } 499 }
500 500
501 if (atomic_inc_return(&stream->dev->users) == 1) { 501 mutex_lock(&stream->dev->lock);
502 ret = uvc_status_start(stream->dev); 502 if (stream->dev->users == 0) {
503 ret = uvc_status_start(stream->dev, GFP_KERNEL);
503 if (ret < 0) { 504 if (ret < 0) {
504 atomic_dec(&stream->dev->users); 505 mutex_unlock(&stream->dev->lock);
505 usb_autopm_put_interface(stream->dev->intf); 506 usb_autopm_put_interface(stream->dev->intf);
506 kfree(handle); 507 kfree(handle);
507 return ret; 508 return ret;
508 } 509 }
509 } 510 }
510 511
512 stream->dev->users++;
513 mutex_unlock(&stream->dev->lock);
514
511 v4l2_fh_init(&handle->vfh, stream->vdev); 515 v4l2_fh_init(&handle->vfh, stream->vdev);
512 v4l2_fh_add(&handle->vfh); 516 v4l2_fh_add(&handle->vfh);
513 handle->chain = stream->chain; 517 handle->chain = stream->chain;
@@ -538,8 +542,10 @@ static int uvc_v4l2_release(struct file *file)
538 kfree(handle); 542 kfree(handle);
539 file->private_data = NULL; 543 file->private_data = NULL;
540 544
541 if (atomic_dec_return(&stream->dev->users) == 0) 545 mutex_lock(&stream->dev->lock);
546 if (--stream->dev->users == 0)
542 uvc_status_stop(stream->dev); 547 uvc_status_stop(stream->dev);
548 mutex_unlock(&stream->dev->lock);
543 549
544 usb_autopm_put_interface(stream->dev->intf); 550 usb_autopm_put_interface(stream->dev->intf);
545 return 0; 551 return 0;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index af505fdd9b3f..9e35982d099a 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -514,7 +514,8 @@ struct uvc_device {
514 char name[32]; 514 char name[32];
515 515
516 enum uvc_device_state state; 516 enum uvc_device_state state;
517 atomic_t users; 517 struct mutex lock; /* Protects users */
518 unsigned int users;
518 atomic_t nmappings; 519 atomic_t nmappings;
519 520
520 /* Video control interface */ 521 /* Video control interface */
@@ -660,10 +661,8 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
660/* Status */ 661/* Status */
661extern int uvc_status_init(struct uvc_device *dev); 662extern int uvc_status_init(struct uvc_device *dev);
662extern void uvc_status_cleanup(struct uvc_device *dev); 663extern void uvc_status_cleanup(struct uvc_device *dev);
663extern int uvc_status_start(struct uvc_device *dev); 664extern int uvc_status_start(struct uvc_device *dev, gfp_t flags);
664extern void uvc_status_stop(struct uvc_device *dev); 665extern void uvc_status_stop(struct uvc_device *dev);
665extern int uvc_status_suspend(struct uvc_device *dev);
666extern int uvc_status_resume(struct uvc_device *dev);
667 666
668/* Controls */ 667/* Controls */
669extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; 668extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;