--- /dev/null
+From ed0ee0ce0a3224dab5caa088a5f8b6df25924276 Mon Sep 17 00:00:00 2001
+From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Date: Tue, 27 Mar 2012 05:51:00 -0300
+Subject: [media] uvcvideo: Fix race-related crash in uvc_video_clock_update()
+
+From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+
+commit ed0ee0ce0a3224dab5caa088a5f8b6df25924276 upstream.
+
+The driver frees the clock samples buffer before stopping the video
+buffers queue. If a DQBUF call arrives in-between,
+uvc_video_clock_update() will be called with a NULL clock samples
+buffer, leading to a crash. This occurs very frequently when using the
+webcam with the flash browser plugin.
+
+Move clock initialization/cleanup to uvc_video_enable() in order to free
+the clock samples buffer after the queue is stopped. Make sure the clock
+is reset at resume time to avoid miscalculating timestamps.
+
+Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/media/video/uvc/uvc_video.c | 50 +++++++++++++++++++++++-------------
+ 1 file changed, 32 insertions(+), 18 deletions(-)
+
+--- a/drivers/media/video/uvc/uvc_video.c
++++ b/drivers/media/video/uvc/uvc_video.c
+@@ -468,22 +468,30 @@ uvc_video_clock_decode(struct uvc_stream
+ spin_unlock_irqrestore(&stream->clock.lock, flags);
+ }
+
+-static int uvc_video_clock_init(struct uvc_streaming *stream)
++static void uvc_video_clock_reset(struct uvc_streaming *stream)
+ {
+ struct uvc_clock *clock = &stream->clock;
+
+- spin_lock_init(&clock->lock);
+ clock->head = 0;
+ clock->count = 0;
+- clock->size = 32;
+ clock->last_sof = -1;
+ clock->sof_offset = -1;
++}
++
++static int uvc_video_clock_init(struct uvc_streaming *stream)
++{
++ struct uvc_clock *clock = &stream->clock;
++
++ spin_lock_init(&clock->lock);
++ clock->size = 32;
+
+ clock->samples = kmalloc(clock->size * sizeof(*clock->samples),
+ GFP_KERNEL);
+ if (clock->samples == NULL)
+ return -ENOMEM;
+
++ uvc_video_clock_reset(stream);
++
+ return 0;
+ }
+
+@@ -1424,8 +1432,6 @@ static void uvc_uninit_video(struct uvc_
+
+ if (free_buffers)
+ uvc_free_urb_buffers(stream);
+-
+- uvc_video_clock_cleanup(stream);
+ }
+
+ /*
+@@ -1555,10 +1561,6 @@ static int uvc_init_video(struct uvc_str
+
+ uvc_video_stats_start(stream);
+
+- ret = uvc_video_clock_init(stream);
+- if (ret < 0)
+- return ret;
+-
+ if (intf->num_altsetting > 1) {
+ struct usb_host_endpoint *best_ep = NULL;
+ unsigned int best_psize = 3 * 1024;
+@@ -1683,6 +1685,8 @@ int uvc_video_resume(struct uvc_streamin
+
+ stream->frozen = 0;
+
++ uvc_video_clock_reset(stream);
++
+ ret = uvc_commit_video(stream, &stream->ctrl);
+ if (ret < 0) {
+ uvc_queue_enable(&stream->queue, 0);
+@@ -1819,25 +1823,35 @@ int uvc_video_enable(struct uvc_streamin
+ uvc_uninit_video(stream, 1);
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+ uvc_queue_enable(&stream->queue, 0);
++ uvc_video_clock_cleanup(stream);
+ return 0;
+ }
+
+- ret = uvc_queue_enable(&stream->queue, 1);
++ ret = uvc_video_clock_init(stream);
+ if (ret < 0)
+ return ret;
+
++ ret = uvc_queue_enable(&stream->queue, 1);
++ if (ret < 0)
++ goto error_queue;
++
+ /* Commit the streaming parameters. */
+ ret = uvc_commit_video(stream, &stream->ctrl);
+- if (ret < 0) {
+- uvc_queue_enable(&stream->queue, 0);
+- return ret;
+- }
++ if (ret < 0)
++ goto error_commit;
+
+ ret = uvc_init_video(stream, GFP_KERNEL);
+- if (ret < 0) {
+- usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+- uvc_queue_enable(&stream->queue, 0);
+- }
++ if (ret < 0)
++ goto error_video;
++
++ return 0;
++
++error_video:
++ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
++error_commit:
++ uvc_queue_enable(&stream->queue, 0);
++error_queue:
++ uvc_video_clock_cleanup(stream);
+
+ return ret;
+ }