Skip to content

Commit b81ac43

Browse files
Dan Vacuragregkh
authored andcommitted
usb: gadget: uvc: allow for application to cleanly shutdown
Several types of kernel panics can occur due to timing during the uvc gadget removal. This appears to be a problem with gadget resources being managed by both the client application's v4l2 open/close and the UDC gadget bind/unbind. Since the concept of USB_GADGET_DELAYED_STATUS doesn't exist for unbind, add a wait to allow for the application to close out. Some examples of the panics that can occur are: <1>[ 1147.652313] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000028 <4>[ 1147.652510] Call trace: <4>[ 1147.652514] usb_gadget_disconnect+0x74/0x1f0 <4>[ 1147.652516] usb_gadget_deactivate+0x38/0x168 <4>[ 1147.652520] usb_function_deactivate+0x54/0x90 <4>[ 1147.652524] uvc_function_disconnect+0x14/0x38 <4>[ 1147.652527] uvc_v4l2_release+0x34/0xa0 <4>[ 1147.652537] __fput+0xdc/0x2c0 <4>[ 1147.652540] ____fput+0x10/0x1c <4>[ 1147.652545] task_work_run+0xe4/0x12c <4>[ 1147.652549] do_notify_resume+0x108/0x168 <1>[ 282.950561][ T1472] Unable to handle kernel NULL pointer dereference at virtual address 00000000000005b8 <6>[ 282.953111][ T1472] Call trace: <6>[ 282.953121][ T1472] usb_function_deactivate+0x54/0xd4 <6>[ 282.953134][ T1472] uvc_v4l2_release+0xac/0x1e4 <6>[ 282.953145][ T1472] v4l2_release+0x134/0x1f0 <6>[ 282.953167][ T1472] __fput+0xf4/0x428 <6>[ 282.953178][ T1472] ____fput+0x14/0x24 <6>[ 282.953193][ T1472] task_work_run+0xac/0x130 <3>[ 213.410077][ T29] configfs-gadget gadget: uvc: Failed to queue request (-108). <1>[ 213.410116][ T29] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000003 <6>[ 213.413460][ T29] Call trace: <6>[ 213.413474][ T29] uvcg_video_pump+0x1f0/0x384 <6>[ 213.413489][ T29] process_one_work+0x2a4/0x544 <6>[ 213.413502][ T29] worker_thread+0x350/0x784 <6>[ 213.413515][ T29] kthread+0x2ac/0x320 <6>[ 213.413528][ T29] ret_from_fork+0x10/0x30 Signed-off-by: Dan Vacura <[email protected]> Cc: stable <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent bbc126a commit b81ac43

File tree

3 files changed

+29
-1
lines changed

3 files changed

+29
-1
lines changed

drivers/usb/gadget/function/f_uvc.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,13 +890,37 @@ static void uvc_function_unbind(struct usb_configuration *c,
890890
{
891891
struct usb_composite_dev *cdev = c->cdev;
892892
struct uvc_device *uvc = to_uvc(f);
893+
long wait_ret = 1;
893894

894895
uvcg_info(f, "%s()\n", __func__);
895896

897+
/* If we know we're connected via v4l2, then there should be a cleanup
898+
* of the device from userspace either via UVC_EVENT_DISCONNECT or
899+
* though the video device removal uevent. Allow some time for the
900+
* application to close out before things get deleted.
901+
*/
902+
if (uvc->func_connected) {
903+
uvcg_dbg(f, "waiting for clean disconnect\n");
904+
wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue,
905+
uvc->func_connected == false, msecs_to_jiffies(500));
906+
uvcg_dbg(f, "done waiting with ret: %ld\n", wait_ret);
907+
}
908+
896909
device_remove_file(&uvc->vdev.dev, &dev_attr_function_name);
897910
video_unregister_device(&uvc->vdev);
898911
v4l2_device_unregister(&uvc->v4l2_dev);
899912

913+
if (uvc->func_connected) {
914+
/* Wait for the release to occur to ensure there are no longer any
915+
* pending operations that may cause panics when resources are cleaned
916+
* up.
917+
*/
918+
uvcg_warn(f, "%s no clean disconnect, wait for release\n", __func__);
919+
wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue,
920+
uvc->func_connected == false, msecs_to_jiffies(1000));
921+
uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret);
922+
}
923+
900924
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
901925
kfree(uvc->control_buf);
902926

@@ -915,6 +939,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
915939

916940
mutex_init(&uvc->video.mutex);
917941
uvc->state = UVC_STATE_DISCONNECTED;
942+
init_waitqueue_head(&uvc->func_connected_queue);
918943
opts = fi_to_f_uvc_opts(fi);
919944

920945
mutex_lock(&opts->lock);

drivers/usb/gadget/function/uvc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/spinlock.h>
1515
#include <linux/usb/composite.h>
1616
#include <linux/videodev2.h>
17+
#include <linux/wait.h>
1718

1819
#include <media/v4l2-device.h>
1920
#include <media/v4l2-dev.h>
@@ -129,6 +130,7 @@ struct uvc_device {
129130
struct usb_function func;
130131
struct uvc_video video;
131132
bool func_connected;
133+
wait_queue_head_t func_connected_queue;
132134

133135
/* Descriptors */
134136
struct {

drivers/usb/gadget/function/uvc_v4l2.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,11 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
253253

254254
static void uvc_v4l2_disable(struct uvc_device *uvc)
255255
{
256-
uvc->func_connected = false;
257256
uvc_function_disconnect(uvc);
258257
uvcg_video_enable(&uvc->video, 0);
259258
uvcg_free_buffers(&uvc->video.queue);
259+
uvc->func_connected = false;
260+
wake_up_interruptible(&uvc->func_connected_queue);
260261
}
261262

262263
static int

0 commit comments

Comments
 (0)