+++ /dev/null
-From 619d9b710cf06f7a00a17120ca92333684ac45a8 Mon Sep 17 00:00:00 2001
-From: Ricardo Ribalda <ribalda@chromium.org>
-Date: Thu, 5 Jan 2023 15:31:29 +0100
-Subject: media: uvcvideo: Fix race condition with usb_kill_urb
-
-From: Ricardo Ribalda <ribalda@chromium.org>
-
-commit 619d9b710cf06f7a00a17120ca92333684ac45a8 upstream.
-
-usb_kill_urb warranties that all the handlers are finished when it
-returns, but does not protect against threads that might be handling
-asynchronously the urb.
-
-For UVC, the function uvc_ctrl_status_event_async() takes care of
-control changes asynchronously.
-
-If the code is executed in the following order:
-
-CPU 0 CPU 1
-===== =====
-uvc_status_complete()
- uvc_status_stop()
-uvc_ctrl_status_event_work()
- uvc_status_start() -> FAIL
-
-Then uvc_status_start will keep failing and this error will be shown:
-
-<4>[ 5.540139] URB 0000000000000000 submitted while active
-drivers/usb/core/urb.c:378 usb_submit_urb+0x4c3/0x528
-
-Let's improve the current situation, by not re-submiting the urb if
-we are stopping the status event. Also process the queued work
-(if any) during stop.
-
-CPU 0 CPU 1
-===== =====
-uvc_status_complete()
- uvc_status_stop()
- uvc_status_start()
-uvc_ctrl_status_event_work() -> FAIL
-
-Hopefully, with the usb layer protection this should be enough to cover
-all the cases.
-
-Cc: stable@vger.kernel.org
-Fixes: e5225c820c05 ("media: uvcvideo: Send a control event when a Control Change interrupt arrives")
-Reviewed-by: Yunke Cao <yunkec@chromium.org>
-Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
-Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- drivers/media/usb/uvc/uvc_ctrl.c | 5 +++++
- drivers/media/usb/uvc/uvc_status.c | 37 +++++++++++++++++++++++++++++++++++++
- drivers/media/usb/uvc/uvcvideo.h | 1 +
- 3 files changed, 43 insertions(+)
-
---- a/drivers/media/usb/uvc/uvc_ctrl.c
-+++ b/drivers/media/usb/uvc/uvc_ctrl.c
-@@ -11,6 +11,7 @@
- *
- */
-
-+#include <asm/barrier.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/module.h>
-@@ -1318,6 +1319,10 @@ static void uvc_ctrl_status_event_work(s
-
- mutex_unlock(&chain->ctrl_mutex);
-
-+ /* The barrier is needed to synchronize with uvc_status_stop(). */
-+ if (smp_load_acquire(&dev->flush_status))
-+ return;
-+
- /* Resubmit the URB. */
- w->urb->interval = dev->int_ep->desc.bInterval;
- ret = usb_submit_urb(w->urb, GFP_KERNEL);
---- a/drivers/media/usb/uvc/uvc_status.c
-+++ b/drivers/media/usb/uvc/uvc_status.c
-@@ -11,6 +11,7 @@
- *
- */
-
-+#include <asm/barrier.h>
- #include <linux/kernel.h>
- #include <linux/input.h>
- #include <linux/slab.h>
-@@ -314,5 +315,41 @@ int uvc_status_start(struct uvc_device *
-
- void uvc_status_stop(struct uvc_device *dev)
- {
-+ struct uvc_ctrl_work *w = &dev->async_ctrl;
-+
-+ /*
-+ * Prevent the asynchronous control handler from requeing the URB. The
-+ * barrier is needed so the flush_status change is visible to other
-+ * CPUs running the asynchronous handler before usb_kill_urb() is
-+ * called below.
-+ */
-+ smp_store_release(&dev->flush_status, true);
-+
-+ /*
-+ * Cancel any pending asynchronous work. If any status event was queued,
-+ * process it synchronously.
-+ */
-+ if (cancel_work_sync(&w->work))
-+ uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
-+
-+ /* Kill the urb. */
- usb_kill_urb(dev->int_urb);
-+
-+ /*
-+ * The URB completion handler may have queued asynchronous work. This
-+ * won't resubmit the URB as flush_status is set, but it needs to be
-+ * cancelled before returning or it could then race with a future
-+ * uvc_status_start() call.
-+ */
-+ if (cancel_work_sync(&w->work))
-+ uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
-+
-+ /*
-+ * From this point, there are no events on the queue and the status URB
-+ * is dead. No events will be queued until uvc_status_start() is called.
-+ * The barrier is needed to make sure that flush_status is visible to
-+ * uvc_ctrl_status_event_work() when uvc_status_start() will be called
-+ * again.
-+ */
-+ smp_store_release(&dev->flush_status, false);
- }
---- a/drivers/media/usb/uvc/uvcvideo.h
-+++ b/drivers/media/usb/uvc/uvcvideo.h
-@@ -603,6 +603,7 @@ struct uvc_device {
- /* Status Interrupt Endpoint */
- struct usb_host_endpoint *int_ep;
- struct urb *int_urb;
-+ bool flush_status;
- u8 *status;
- struct input_dev *input;
- char input_phys[64];