diff options
author | Jean-François Moine <moinejf@free.fr> | 2010-07-06 03:16:40 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-08-02 15:42:47 -0400 |
commit | 02bbcb9d863df10b5a4b91ba5b4c76eaf1340883 (patch) | |
tree | 1e19c0d8d0a50e1a34d4b6fa5f5027ee713b9612 /drivers/media/video/gspca | |
parent | 9059cd44403608f6554b37c3b3d5598ded7a3a92 (diff) |
V4L/DVB: gspca - main: Possible race condition in queue management
The problem may occur with SMP:
- a frame is completed at interrupt level (in gspca_frame_add with
packet_type == LAST_PACKET,
- just after clearing the bit V4L2_BUF_FLAG_QUEUED and before setting
the bit V4L2_BUF_FLAG_DONE, on the other processor, the application
tries to requeue the same frame buffer,
- then, the qbuf function succeeds because ALL_FLAGS are not set.
The fix sets and resets the two flags in one instruction.
Reported-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Jean-François Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/gspca')
-rw-r--r-- | drivers/media/video/gspca/gspca.c | 5 |
1 files changed, 3 insertions, 2 deletions
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 8e822ed4435f..2dc7270722f3 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c | |||
@@ -466,8 +466,9 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, | |||
466 | j = gspca_dev->fr_queue[i]; | 466 | j = gspca_dev->fr_queue[i]; |
467 | frame = &gspca_dev->frame[j]; | 467 | frame = &gspca_dev->frame[j]; |
468 | frame->v4l2_buf.bytesused = gspca_dev->image_len; | 468 | frame->v4l2_buf.bytesused = gspca_dev->image_len; |
469 | frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; | 469 | frame->v4l2_buf.flags = (frame->v4l2_buf.flags |
470 | frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; | 470 | | V4L2_BUF_FLAG_DONE) |
471 | & ~V4L2_BUF_FLAG_QUEUED; | ||
471 | wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ | 472 | wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ |
472 | i = (i + 1) % gspca_dev->nframes; | 473 | i = (i + 1) % gspca_dev->nframes; |
473 | gspca_dev->fr_i = i; | 474 | gspca_dev->fr_i = i; |