Re: [PATCH v2] uvcvideo: Fix open/close race condition

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Laurent,

Thanks for the revised patch. I tested and confirmed that it fixes the
issue I reported.

On Wed, May 15, 2013 at 2:58 AM, Laurent Pinchart
<laurent.pinchart@xxxxxxxxxxxxxxxx> wrote:
> Hi Shawn,
>
> Could you please confirm whether this patch fixes the issue you've reported ?
>
> On Tuesday 07 May 2013 13:19:37 Laurent Pinchart wrote:
>> 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@xxxxxxxxxxxx>
>> Signed-off-by: Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>
>> ---
>>  drivers/media/usb/uvc/uvc_driver.c | 23 +++++++++++++++++------
>>  drivers/media/usb/uvc/uvc_status.c | 21 ++-------------------
>>  drivers/media/usb/uvc/uvc_v4l2.c   | 14 ++++++++++----
>>  drivers/media/usb/uvc/uvcvideo.h   |  7 +++----
>>  4 files changed, 32 insertions(+), 33 deletions(-)
>>
>> Changes since v1:
>>
>> - Add a missing return back in the uvc_suspend() function
>>
>> diff --git a/drivers/media/usb/uvc/uvc_driver.c
>> b/drivers/media/usb/uvc/uvc_driver.c index e68fa53..d704be3 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);
>> @@ -1953,8 +1953,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)
>> @@ -1976,14 +1981,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) {
>> diff --git a/drivers/media/usb/uvc/uvc_status.c
>> b/drivers/media/usb/uvc/uvc_status.c index b749277..f552ab9 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)
>>       uvc_input_cleanup(dev);
>>  }
>>
>> -int uvc_status_start(struct uvc_device *dev)
>> +int uvc_status_start(struct uvc_device *dev, gfp_t flags)
>>  {
>>       if (dev->int_urb == NULL)
>>               return 0;
>>
>> -     return usb_submit_urb(dev->int_urb, GFP_KERNEL);
>> +     return usb_submit_urb(dev->int_urb, flags);
>>  }
>>
>>  void uvc_status_stop(struct uvc_device *dev)
>>  {
>>       usb_kill_urb(dev->int_urb);
>>  }
>> -
>> -int uvc_status_suspend(struct uvc_device *dev)
>> -{
>> -     if (atomic_read(&dev->users))
>> -             usb_kill_urb(dev->int_urb);
>> -
>> -     return 0;
>> -}
>> -
>> -int uvc_status_resume(struct uvc_device *dev)
>> -{
>> -     if (dev->int_urb == NULL || atomic_read(&dev->users) == 0)
>> -             return 0;
>> -
>> -     return usb_submit_urb(dev->int_urb, GFP_NOIO);
>> -}
>> -
>> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c
>> b/drivers/media/usb/uvc/uvc_v4l2.c index b2dc326..3afff92 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)
>>               return -ENOMEM;
>>       }
>>
>> -     if (atomic_inc_return(&stream->dev->users) == 1) {
>> -             ret = uvc_status_start(stream->dev);
>> +     mutex_lock(&stream->dev->lock);
>> +     if (stream->dev->users == 0) {
>> +             ret = uvc_status_start(stream->dev, GFP_KERNEL);
>>               if (ret < 0) {
>> -                     atomic_dec(&stream->dev->users);
>> +                     mutex_unlock(&stream->dev->lock);
>>                       usb_autopm_put_interface(stream->dev->intf);
>>                       kfree(handle);
>>                       return ret;
>>               }
>>       }
>>
>> +     stream->dev->users++;
>> +     mutex_unlock(&stream->dev->lock);
>> +
>>       v4l2_fh_init(&handle->vfh, stream->vdev);
>>       v4l2_fh_add(&handle->vfh);
>>       handle->chain = stream->chain;
>> @@ -538,8 +542,10 @@ static int uvc_v4l2_release(struct file *file)
>>       kfree(handle);
>>       file->private_data = NULL;
>>
>> -     if (atomic_dec_return(&stream->dev->users) == 0)
>> +     mutex_lock(&stream->dev->lock);
>> +     if (--stream->dev->users == 0)
>>               uvc_status_stop(stream->dev);
>> +     mutex_unlock(&stream->dev->lock);
>>
>>       usb_autopm_put_interface(stream->dev->intf);
>>       return 0;
>> diff --git a/drivers/media/usb/uvc/uvcvideo.h
>> b/drivers/media/usb/uvc/uvcvideo.h index 9cd584a..eb90a92 100644
>> --- a/drivers/media/usb/uvc/uvcvideo.h
>> +++ b/drivers/media/usb/uvc/uvcvideo.h
>> @@ -515,7 +515,8 @@ struct uvc_device {
>>       char name[32];
>>
>>       enum uvc_device_state state;
>> -     atomic_t users;
>> +     struct mutex lock;              /* Protects users */
>> +     unsigned int users;
>>       atomic_t nmappings;
>>
>>       /* Video control interface */
>> @@ -661,10 +662,8 @@ void uvc_video_clock_update(struct uvc_streaming
>> *stream, /* Status */
>>  extern int uvc_status_init(struct uvc_device *dev);
>>  extern void uvc_status_cleanup(struct uvc_device *dev);
>> -extern int uvc_status_start(struct uvc_device *dev);
>> +extern int uvc_status_start(struct uvc_device *dev, gfp_t flags);
>>  extern void uvc_status_stop(struct uvc_device *dev);
>> -extern int uvc_status_suspend(struct uvc_device *dev);
>> -extern int uvc_status_resume(struct uvc_device *dev);
>>
>>  /* Controls */
>>  extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
> --
> Regards,
>
> Laurent Pinchart
>
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux