sh-mobile-ceu-camera: do not wait for interrupt when releasing buffers
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/1 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 <[email protected]>
---
drivers/media/video/sh_mobile_ceu_camera.c +28 -1
@@ -296,8 +306,8 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,dev_dbg (&icd->dev,"%s (vb=0x%p) 0x%08lx %zd\n" , __func__, vb, vb->baddr, vb->bsize); - vb->state =VIDEOBUF_QUEUED ;spin_lock_irqsave (&pcdev->lock, flags); + vb->state =VIDEOBUF_QUEUED ;list_add_tail (&vb->queue, &pcdev->capture);if (!pcdev->active) {
@@ -311,6 +321,27 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,static void sh_mobile_ceu_videobuf_release (struct videobuf_queue *vq,struct videobuf_buffer *vb) { +struct soc_camera_device *icd = vq->priv_data; +struct soc_camera_host *ici =to_soc_camera_host (icd->dev.parent); +struct sh_mobile_ceu_dev *pcdev = ici->priv; +unsigned long flags; + +spin_lock_irqsave (&pcdev->lock, flags); + +if (pcdev->active == vb) { +/* disable capture (release DMA buffer), reset */ +ceu_write (pcdev,CAPSR ,1 <<16 ); + pcdev->active =NULL ; + } + +if ((vb->state ==VIDEOBUF_ACTIVE || vb->state ==VIDEOBUF_QUEUED ) && + !list_empty (&vb->queue)) { + vb->state =VIDEOBUF_ERROR ; +list_del_init (&vb->queue); + } + +spin_unlock_irqrestore (&pcdev->lock, flags); + free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb)); }
@@ -330,6 +361,10 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)spin_lock_irqsave (&pcdev->lock, flags); vb = pcdev->active; +if (!vb) +/* Stale interrupt from a released buffer */ + goto out; +list_del_init (&vb->queue);if (!list_empty (&pcdev->capture))
@@ -344,6 +379,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)do_gettimeofday (&vb->ts); vb->field_count++;wake_up (&vb->done); + +out:spin_unlock_irqrestore (&pcdev->lock, flags);return IRQ_HANDLED ;