Skip to contentSkip to conversation

media: uvcvideo: Fix driver reference counting

PATCH In review Philipp Zabel 2 participants 2 messages May 21, 2018 – May 22, 2018

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 media: uvcvideo: Fix driver reference counting

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")
---
 drivers/media/usb/uvc/uvc_driver.c | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)
drivers/media/usb/uvc/uvc_driver.c +2 -9
index 2469b49b2b30..8e138201330f 100644
@@ -1871,13 +1871,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;
@@ -1887,8 +1880,6 @@ static void uvc_unregister_video(struct uvc_device *dev)
 
 		uvc_debugfs_cleanup_stream(stream);
 	}
-
-	kref_put(&dev->ref, uvc_delete);
 }
 
 int uvc_register_video_device(struct uvc_device *dev,
@@ -2184,6 +2175,7 @@ static int uvc_probe(struct usb_interface *intf,
 
 error:
 	uvc_unregister_video(dev);
+	kref_put(&dev->ref, uvc_delete);
 	return -ENODEV;
 }
 
@@ -2201,6 +2193,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)
Signature · 1 line
2.17.0
maintainer Reviewed-by on v1 1/1
#1 lore raw

Hi Philipp,

Thank you for the patch.

Quoted 7 lines · On Monday, 21 May 2018 13:24:58 EEST Philipp Zabel wrote:

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.

Good catch !

Quoted 3 lines
Fixes 9d15cd958c17 ("media: uvcvideo: Convert from using an atomic variable

to a reference count")

This is a serious issue so I'd like to get the patch merged in v4.18, but it could be argued that it's getting late for that, especially given that the bug has been there since v4.14.

Mauro, would you be OK merging this in v4.18 ? If so could you pick it up, or would you like me to send a pull request ?

Quoted 49 lines
---
 drivers/media/usb/uvc/uvc_driver.c | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)
(quoted diff) +0 -0

b/drivers/media/usb/uvc/uvc_driver.c index 2469b49b2b30..8e138201330f 100644

drivers/media/usb/uvc/uvc_driver.c +0 -9
@@ -1871,13 +1871,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;
@@ -1887,8 +1880,6 @@ static void uvc_unregister_video(struct uvc_device
 *dev)
 
 		uvc_debugfs_cleanup_stream(stream);
 	}
-
-	kref_put(&dev->ref, uvc_delete);
 }
 

int uvc_register_video_device(struct uvc_device *dev,

drivers/media/usb/uvc/uvc_driver.c +2 -0
@@ -2184,6 +2175,7 @@ static int uvc_probe(struct usb_interface *intf,
 
 error:
 	uvc_unregister_video(dev);
+	kref_put(&dev->ref, uvc_delete);
 	return -ENODEV;
 }
 
@@ -2201,6 +2193,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)
Signature · 3 lines
Regards,

Laurent Pinchart