Hi Jayakrishnan,

Thank you for the patch.

On Thursday 27 October 2011 16:05:28 Jayakrishnan wrote:
>   This patch is a fix for uvcvideo crash observed on device hotplug. If
> the camera is removed when streaming is going on, close(fd) call by the
> streaming application could lead to a crash.
> 
> The issue is that memory allocated to instances of uvc_streaming
> structure is freed in the handler for device disconnect notification.

I'm surprised by that. When the device is disconnected the USB subsystem will 
call the driver's disconnection handler, uvc_disconnect(). This in turn calls 
uvc_unregister_video(), which calls video_unregister_device().

video_unregister_device() will call the uvcvideo release handler 
(uvc_release()) back when the last reference to the video device is released. 
This happens when all file handles are closed, so the uvc_streaming instance 
should not get freed until all uvcvideo file handles are closed by 
applications.

Could you elaborate on how you produced the crash, and send me a backtrace ?

> But these instances will be accessed when device is closed. The
> instances of data structure is protected by reference count to avoid
> this crash. Stream count is removed to avoid redundancy.
> 
> This logic is quite similar to what was present in uvcvideo earlier.
> Commit 716fdee110ceb816cca8c46c0890d08c5a1addb9 had removed kref.

The kref was required back then as the videodev was racy. This has been fixed 
in videodev a while ago, so the kref got removed from uvcvideo.

> Signed-off-by: Jayakrishnan Memana <jayakrishnan.mem...@maxim-ic.com>
> ---
> 
> diff --git a/drivers/media/video/uvc/uvc_driver.c
> b/drivers/media/video/uvc/uvc_driver.c
> index 656d4c9..af50624 100644
> --- a/drivers/media/video/uvc/uvc_driver.c
> +++ b/drivers/media/video/uvc/uvc_driver.c
> @@ -1593,8 +1593,9 @@ static int uvc_scan_device(struct uvc_device *dev)
>    * already been canceled by the USB core. There is no need to kill the
>    * interrupt URB manually.
>    */
> -static void uvc_delete(struct uvc_device *dev)
> +void uvc_delete(struct kref *kref)
>   {
> +    struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
>       struct list_head *p, *n;
> 
>       usb_put_intf(dev->intf);
> @@ -1648,11 +1649,8 @@ static void uvc_release(struct video_device *vdev)
>       struct uvc_streaming *stream = video_get_drvdata(vdev);
>       struct uvc_device *dev = stream->dev;
> 
> -    /* Decrement the registered streams count and delete the device when
> it -     * reaches zero.
> -     */
> -    if (atomic_dec_and_test(&dev->nstreams))
> -        uvc_delete(dev);
> +    /* Delete the device when all references are removed */
> +    kref_put(&dev->kref, uvc_delete);
>   }
> 
>   /*
> @@ -1664,10 +1662,9 @@ static void uvc_unregister_video(struct
> uvc_device *dev)
> 
>       /* Unregistering all video devices might result in uvc_delete() being
>        * called from inside the loop if there's no open file handle. To
> avoid
> -     * that, increment the stream count before iterating over the streams
> -     * and decrement it when done.
> +     * that, take a reference now and remove it after the loop
>        */
> -    atomic_inc(&dev->nstreams);
> +    kref_get(&dev->kref);
> 
>       list_for_each_entry(stream, &dev->streams, list) {
>           if (stream->vdev == NULL)
> @@ -1677,11 +1674,10 @@ static void uvc_unregister_video(struct
> uvc_device *dev)
>           stream->vdev = NULL;
>       }
> 
> -    /* Decrement the stream count and call uvc_delete explicitly if there
> -     * are no stream left.
> +    /* Remove the reference and delete the device if all references are
> +     * removed
>        */
> -    if (atomic_dec_and_test(&dev->nstreams))
> -        uvc_delete(dev);
> +    kref_put(&dev->kref, uvc_delete);
>   }
> 
>   static int uvc_register_video(struct uvc_device *dev,
> @@ -1732,7 +1728,6 @@ static int uvc_register_video(struct uvc_device *dev,
>           return ret;
>       }
> 
> -    atomic_inc(&dev->nstreams);
>       return 0;
>   }
> 
> @@ -1816,9 +1811,9 @@ static int uvc_probe(struct usb_interface *intf,
>       INIT_LIST_HEAD(&dev->entities);
>       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);
> +    kref_init(&dev->kref);
> 
>       dev->udev = usb_get_dev(udev);
>       dev->intf = usb_get_intf(intf);
> diff --git a/drivers/media/video/uvc/uvc_v4l2.c
> b/drivers/media/video/uvc/uvc_v4l2.c
> index dadf11f..0c21a56 100644
> --- a/drivers/media/video/uvc/uvc_v4l2.c
> +++ b/drivers/media/video/uvc/uvc_v4l2.c
> @@ -500,6 +500,8 @@ static int uvc_v4l2_open(struct file *file)
>       handle->state = UVC_HANDLE_PASSIVE;
>       file->private_data = handle;
> 
> +    kref_get(&stream->dev->kref);
> +
>       return 0;
>   }
> 
> @@ -528,6 +530,7 @@ static int uvc_v4l2_release(struct file *file)
>           uvc_status_stop(stream->dev);
> 
>       usb_autopm_put_interface(stream->dev->intf);
> +    kref_put(&stream->dev->kref, uvc_delete);
>       return 0;
>   }
> 
> diff --git a/drivers/media/video/uvc/uvcvideo.h
> b/drivers/media/video/uvc/uvcvideo.h
> index 4c1392e..0925642 100644
> --- a/drivers/media/video/uvc/uvcvideo.h
> +++ b/drivers/media/video/uvc/uvcvideo.h
> @@ -423,6 +423,7 @@ struct uvc_device {
>       char name[32];
> 
>       enum uvc_device_state state;
> +    struct kref kref;
>       atomic_t users;
>       atomic_t nmappings;
> 
> @@ -439,7 +440,6 @@ struct uvc_device {
> 
>       /* Video Streaming interfaces */
>       struct list_head streams;
> -    atomic_t nstreams;
> 
>       /* Status Interrupt Endpoint */
>       struct usb_host_endpoint *int_ep;
> @@ -509,6 +509,7 @@ extern unsigned int uvc_timeout_param;
> 
>   /* Core driver */
>   extern struct uvc_driver uvc_driver;
> +extern void uvc_delete(struct kref *kref);
> 
>   extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int
> id);

-- 
Regards,

Laurent Pinchart
_______________________________________________
Linux-uvc-devel mailing list
Linux-uvc-devel@lists.berlios.de
https://lists.berlios.de/mailman/listinfo/linux-uvc-devel

Reply via email to