media: uvcvideo: Fix driver reference counting
authorPhilipp Zabel <philipp.zabel@gmail.com>
Mon, 21 May 2018 10:24:58 +0000 (06:24 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 4 Nov 2018 13:52:47 +0000 (14:52 +0100)
commit f9ffcb0a21e1fa8e64d09ed613d884e054ae8191 upstream

kref_init initializes the reference count to 1, not 0. This additional
reference is never released since the conversion to reference counters.
As a result, uvc_delete is not called anymore when UVC cameras are
disconnected.
Fix this by adding an additional kref_put in uvc_disconnect and in the
probe error path. This also allows to remove the temporary additional
reference in uvc_unregister_video.

Fixes: 9d15cd958c17 ("media: uvcvideo: Convert from using an atomic variable to a reference count")

Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/media/usb/uvc/uvc_driver.c

index 6d22b22..064d882 100644 (file)
@@ -1865,13 +1865,6 @@ static void uvc_unregister_video(struct uvc_device *dev)
 {
        struct uvc_streaming *stream;
 
-       /* 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 refcount before iterating over the streams and
-        * decrement it when done.
-        */
-       kref_get(&dev->ref);
-
        list_for_each_entry(stream, &dev->streams, list) {
                if (!video_is_registered(&stream->vdev))
                        continue;
@@ -1880,8 +1873,6 @@ static void uvc_unregister_video(struct uvc_device *dev)
 
                uvc_debugfs_cleanup_stream(stream);
        }
-
-       kref_put(&dev->ref, uvc_delete);
 }
 
 static int uvc_register_video(struct uvc_device *dev,
@@ -2129,6 +2120,7 @@ static int uvc_probe(struct usb_interface *intf,
 
 error:
        uvc_unregister_video(dev);
+       kref_put(&dev->ref, uvc_delete);
        return -ENODEV;
 }
 
@@ -2146,6 +2138,7 @@ static void uvc_disconnect(struct usb_interface *intf)
                return;
 
        uvc_unregister_video(dev);
+       kref_put(&dev->ref, uvc_delete);
 }
 
 static int uvc_suspend(struct usb_interface *intf, pm_message_t message)