diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2009-08-25 10:46:51 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-09-18 23:18:51 -0400 |
commit | cca0e54905259a456d97652d4f1e2fe8b188b6ad (patch) | |
tree | 82a0af7fadc341e4955628f45c2110439c9701e8 | |
parent | fa48984e36ee73e964eeb994a45de6525114e871 (diff) |
V4L/DVB (12520): sh-mobile-ceu-camera: do not wait for interrupt when releasing buffers
Patch
[PATCH] video: use videobuf_waiton() in sh_mobile_ceu free_buffer()
was not quite correct. It closed a race, but introduced a potential
lock-up, if for some reason an interrupt does not come. This has been
observed in tests with tw9910. This patch safely dequeues buffers without
waiting for their completion. It also moves a buffer state assignment
under a spinlock to make it atomic with queuing of the buffer.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/sh_mobile_ceu_camera.c | 27 |
1 files changed, 27 insertions, 0 deletions
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 4c4b60c32263..c0dc4a1e8e52 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c | |||
@@ -307,6 +307,27 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, | |||
307 | static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq, | 307 | static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq, |
308 | struct videobuf_buffer *vb) | 308 | struct videobuf_buffer *vb) |
309 | { | 309 | { |
310 | struct soc_camera_device *icd = vq->priv_data; | ||
311 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | ||
312 | struct sh_mobile_ceu_dev *pcdev = ici->priv; | ||
313 | unsigned long flags; | ||
314 | |||
315 | spin_lock_irqsave(&pcdev->lock, flags); | ||
316 | |||
317 | if (pcdev->active == vb) { | ||
318 | /* disable capture (release DMA buffer), reset */ | ||
319 | ceu_write(pcdev, CAPSR, 1 << 16); | ||
320 | pcdev->active = NULL; | ||
321 | } | ||
322 | |||
323 | if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) && | ||
324 | !list_empty(&vb->queue)) { | ||
325 | vb->state = VIDEOBUF_ERROR; | ||
326 | list_del_init(&vb->queue); | ||
327 | } | ||
328 | |||
329 | spin_unlock_irqrestore(&pcdev->lock, flags); | ||
330 | |||
310 | free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb)); | 331 | free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb)); |
311 | } | 332 | } |
312 | 333 | ||
@@ -326,6 +347,10 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) | |||
326 | spin_lock_irqsave(&pcdev->lock, flags); | 347 | spin_lock_irqsave(&pcdev->lock, flags); |
327 | 348 | ||
328 | vb = pcdev->active; | 349 | vb = pcdev->active; |
350 | if (!vb) | ||
351 | /* Stale interrupt from a released buffer */ | ||
352 | goto out; | ||
353 | |||
329 | list_del_init(&vb->queue); | 354 | list_del_init(&vb->queue); |
330 | 355 | ||
331 | if (!list_empty(&pcdev->capture)) | 356 | if (!list_empty(&pcdev->capture)) |
@@ -340,6 +365,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) | |||
340 | do_gettimeofday(&vb->ts); | 365 | do_gettimeofday(&vb->ts); |
341 | vb->field_count++; | 366 | vb->field_count++; |
342 | wake_up(&vb->done); | 367 | wake_up(&vb->done); |
368 | |||
369 | out: | ||
343 | spin_unlock_irqrestore(&pcdev->lock, flags); | 370 | spin_unlock_irqrestore(&pcdev->lock, flags); |
344 | 371 | ||
345 | return IRQ_HANDLED; | 372 | return IRQ_HANDLED; |