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

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

 



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