diff options
author | Ian Armstrong <ian@iarmst.demon.co.uk> | 2009-12-21 20:59:26 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-02-26 13:10:41 -0500 |
commit | b6e436b263b35476da4be06e0719cb1d5c8f8eed (patch) | |
tree | b6055dce972162efde4d3d2748ca7096ee1d68d4 /drivers/media/video/ivtv | |
parent | 0726681a7080cef6a9fd414894690fac16418772 (diff) |
V4L/DVB: ivtv: Fix race condition for queued udma transfers
There are several DMA related interrupts which wake up the dma_waitq. The udma
routines use this queue while they wait for their transfer to complete. When
woken, the udma routine will check the IVTV_F_I_UDMA_PENDING & IVTV_F_I_UDMA
flags to see if the transfer is still queued or has finished. However, a small
window exists between the IVTV_F_I_UDMA_PENDING flag being cleared and the
IVTV_F_I_UDMA flag being set. Given that the completion of an unrelated DMA
transfer may wake up the udma routine, it's possible for this check to fail
and the udma routine will start unmapping pages when the transfer has only
just started. The result of this is unpredictable.
This fix simply delays the clearing of the IVTV_F_I_UDMA_PENDING flag until
after IVTV_F_I_UDMA has been set.
Signed-off-by: Ian Armstrong <ian@iarmst.demon.co.uk>
Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/ivtv')
-rw-r--r-- | drivers/media/video/ivtv/ivtv-irq.c | 5 | ||||
-rw-r--r-- | drivers/media/video/ivtv/ivtv-udma.c | 1 |
2 files changed, 4 insertions, 2 deletions
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index cd9db0bf33bf..ee0cdded69f7 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c | |||
@@ -940,9 +940,10 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id) | |||
940 | ivtv_dma_enc_start(s); | 940 | ivtv_dma_enc_start(s); |
941 | break; | 941 | break; |
942 | } | 942 | } |
943 | if (i == IVTV_MAX_STREAMS && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) { | 943 | |
944 | if (i == IVTV_MAX_STREAMS && | ||
945 | test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) | ||
944 | ivtv_udma_start(itv); | 946 | ivtv_udma_start(itv); |
945 | } | ||
946 | } | 947 | } |
947 | 948 | ||
948 | if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) { | 949 | if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) { |
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c index d07ad6c39024..1daf1dd65bf7 100644 --- a/drivers/media/video/ivtv/ivtv-udma.c +++ b/drivers/media/video/ivtv/ivtv-udma.c | |||
@@ -213,6 +213,7 @@ void ivtv_udma_start(struct ivtv *itv) | |||
213 | write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); | 213 | write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); |
214 | set_bit(IVTV_F_I_DMA, &itv->i_flags); | 214 | set_bit(IVTV_F_I_DMA, &itv->i_flags); |
215 | set_bit(IVTV_F_I_UDMA, &itv->i_flags); | 215 | set_bit(IVTV_F_I_UDMA, &itv->i_flags); |
216 | clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags); | ||
216 | } | 217 | } |
217 | 218 | ||
218 | void ivtv_udma_prepare(struct ivtv *itv) | 219 | void ivtv_udma_prepare(struct ivtv *itv) |