Re: [PATCH v6 1/2] media: uvcvideo: add UVC_QUIRK_CTRL_THROTTLE for fragile USB firmware

Jeffrey Hein
2026-04-09T08:12:48Z

Hi Ricardo,

Agreed -- the error-code query goes through __uvc_query_ctrl() now,
so it gets the 50ms throttle automatically. Dropped in v7.

Thanks,
JP

On Thu, Apr 9, 2026 at 12:57 AM Ricardo Ribalda <ribalda@chromium.org> wrote:
>
> On Thu, 9 Apr 2026 at 09:44, JP Hein <jp@jphein.com> wrote:
> >
> > Some UVC devices have firmware that locks up under sustained rapid
> > USB control transfers, crashing the xHCI host controller and taking
> > all USB devices on the bus with it.
> >
> > The Razer Kiyo Pro (1532:0e05) is the first known example: approximately
> > 25 rapid consecutive control transfers cause the firmware to stall an
> > endpoint. The kernel's standard UVC error recovery (GET_CUR on
> > UVC_VC_REQUEST_ERROR_CODE_CONTROL) then sends a second transfer to the
> > already-stalling device, amplifying the failure into complete firmware
> > lockup and xHCI controller death.
> >
> > Add UVC_QUIRK_CTRL_THROTTLE which:
> > - Rate-limits all USB control transfers to 50ms intervals in
> > __uvc_query_ctrl(), the lowest-level UVC control transfer function,
> > ensuring all callers are throttled including uvc_set_video_ctrl()
> > which bypasses uvc_query_ctrl()
> > - Skips the error-code query after EPIPE to avoid amplifying stalls
> >
> > The 50ms interval was determined experimentally: the device is stable
> > at this rate under sustained operation, while shorter intervals
> > eventually trigger the firmware bug.
> >
> > Signed-off-by: JP Hein <jp@jphein.com>
> > ---
> > drivers/media/usb/uvc/uvc_video.c | 30 ++++++++++++++++++++++++++++++
> > drivers/media/usb/uvc/uvcvideo.h | 3 +++
> > 2 files changed, 33 insertions(+)
> >
> > diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
> > index a5013a7..cee93ac 100644
> > --- a/drivers/media/usb/uvc/uvc_video.c
> > +++ b/drivers/media/usb/uvc/uvc_video.c
> > @@ -36,6 +36,26 @@ static int __uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
> > u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
> > unsigned int pipe;
> >
> > + /*
> > + * Rate-limit control transfers for devices with fragile firmware.
> > + * The Razer Kiyo Pro locks up under sustained rapid control
> > + * transfers (hundreds without delay), crashing the xHCI controller.
> > + * Throttle in this low-level function to cover all callers,
> > + * including uvc_set_video_ctrl() which bypasses uvc_query_ctrl().
> > + */
> > + if (dev->quirks & UVC_QUIRK_CTRL_THROTTLE) {
> > + unsigned long min_interval = msecs_to_jiffies(50);
> > +
> > + if (dev->last_ctrl_jiffies &&
> > + time_before(jiffies,
> > + dev->last_ctrl_jiffies + min_interval)) {
> > + unsigned long wait = dev->last_ctrl_jiffies +
> > + min_interval - jiffies;
> > + msleep(jiffies_to_msecs(wait));
> > + }
> > + dev->last_ctrl_jiffies = jiffies;
> > + }
> > +
> > pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
> > : usb_sndctrlpipe(dev->udev, 0);
> > type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
> > @@ -108,6 +128,16 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
> > return ret < 0 ? ret : -EPIPE;
> > }
> >
> > + /*
> > + * Skip the error code query for devices that crash under load.
> > + * The standard error-code query (GET_CUR on
> > + * UVC_VC_REQUEST_ERROR_CODE_CONTROL) sends a second USB transfer to
> > + * a device that is already stalling, which can amplify the failure
> > + * into a full firmware lockup and xHCI controller death.
> > + */
> > + if (dev->quirks & UVC_QUIRK_CTRL_THROTTLE)
> > + return -EPIPE;
>
> I do not believe this is needed anymore now that the check is in the
> inner funcion
>
> > +
> > /* Reuse data[0] to request the error code. */
> > tmp = *(u8 *)data;
> >
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index 757254f..31f2af5 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -78,6 +78,7 @@
> > #define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000
> > #define UVC_QUIRK_MJPEG_NO_EOF 0x00020000
> > #define UVC_QUIRK_MSXU_META 0x00040000
> > +#define UVC_QUIRK_CTRL_THROTTLE 0x00080000
> >
> > /* Format flags */
> > #define UVC_FMT_FLAG_COMPRESSED 0x00000001
> > @@ -583,6 +584,8 @@ struct uvc_device {
> > struct usb_interface *intf;
> > unsigned long warnings;
> > u32 quirks;
> > + /* UVC control transfer throttling (UVC_QUIRK_CTRL_THROTTLE) */
> > + unsigned long last_ctrl_jiffies;
> > int intfnum;
> > char name[32];
> >
> > --
> > 2.43.0
> >
>
>
> --
> Ricardo Ribalda