diff options
author | Mika Westerberg <mika.westerberg@iki.fi> | 2011-11-26 05:54:08 -0500 |
---|---|---|
committer | Vinod Koul <vinod.koul@linux.intel.com> | 2011-12-04 21:46:26 -0500 |
commit | 6d0709d2000ae7dbead759715f57ba381b7057bb (patch) | |
tree | 29a7dabceb6e8132f8979da33cade6d090c8affc /drivers/dma | |
parent | b62cfc5e0e59d1e0192105144b724b0c332720a0 (diff) |
dma/ep93xx_dma: prevent ep93xx_dma_tasklet() to reference an empty list
If dma_terminate_all() is called before the ep93xx_dma_tasklet() gets to run,
it tries to access an empty ->active list which results following OOPS:
Internal error: Oops - undefined instruction: 0 [#1]
CPU: 0 Not tainted (3.2.0-rc1EP-1+ #1008)
PC is at 0xc184c868
LR is at ep93xx_dma_tasklet+0xec/0x164
pc : [<c184c868>] lr : [<c012b528>] psr: 00000013
sp : c02b7e70 ip : ffffffff fp : c02b7ea4
r10: 00000100 r9 : 80000013 r8 : c02b7e50
r7 : c02b7e70 r6 : c02b7ea4 r5 : 000000a4 r4 : c02b7e70
r3 : c02b751d r2 : 8ae34598 r1 : c184c6e0 r0 : c02b7ea4
Flags: nzcv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: c000717f Table: c0004000 DAC: 00000017
Process swapper (pid: 0, stack limit = 0xc02b6270)
Stack: (0xc02b7e70 to 0xc02b8000)
7e60: c02b7ea4 c02b7e70 c0008b64 c02bd5c4
7e80: c02d60e0 00000000 00000000 c02bd44c c02d60e0 00000100 c02b7ec4 c02b7ea8
7ea0: c001c49c c012b44c 00000018 00000001 c02d60e0 c02b6000 c02b7f04 c02b7ec8
7ec0: c001cbc0 c001c3e4 c02b7eec c02b7ed8 00000006 0000000a c02bf674 c02c458c
7ee0: 00000011 00000000 c02b7f7c c0004000 41129200 c02b0c80 c02b7f14 c02b7f08
7f00: c001cdd0 c001cb38 c02b7f34 c02b7f18 c000983c c001cd98 c0009a60 60000013
7f20: fefb0001 c02b7f7c c02b7f44 c02b7f38 c0008190 c0009810 c02b7f9c c02b7f48
7f40: c0008b64 c0008190 c02c2bf8 00000002 c02b7f90 60000013 c02b6000 c02d1504
7f60: c02baa88 c02baa80 c0004000 41129200 c02b0c80 c02b7f9c c02b7fa0 c02b7f90
7f80: c0009a54 c0009a60 60000013 ffffffff c02b7fbc c02b7fa0 c000a03c c0009a40
7fa0: c02b80b0 c02b19dc c02b19d8 c02baa80 c02b7fcc c02b7fc0 c02384e4 c0009fd4
7fc0: c02b7ff4 c02b7fd0 c029d924 c0238494 c029d49c 00000000 00000000 c02b19dc
7fe0: c0007175 c02b803c 00000000 c02b7ff8 c000803c c029d700 00000000 00000000
Backtrace:
[<c012b43c>] (ep93xx_dma_tasklet+0x0/0x164) from [<c001c49c>] (tasklet_action+0xc8/0xdc)
[<c001c3d4>] (tasklet_action+0x0/0xdc) from [<c001cbc0>] (__do_softirq+0x98/0x154)
r7:c02b6000 r6:c02d60e0 r5:00000001 r4:00000018
[<c001cb28>] (__do_softirq+0x0/0x154) from [<c001cdd0>] (irq_exit+0x48/0x50)
[<c001cd88>] (irq_exit+0x0/0x50) from [<c000983c>] (handle_IRQ+0x3c/0x8c)
[<c0009800>] (handle_IRQ+0x0/0x8c) from [<c0008190>] (asm_do_IRQ+0x10/0x14)
r7:c02b7f7c r6:fefb0001 r5:60000013 r4:c0009a60
[<c0008180>] (asm_do_IRQ+0x0/0x14) from [<c0008b64>] (__irq_svc+0x24/0xc0)
Exception stack(0xc02b7f48 to 0xc02b7f90)
7f40: c02c2bf8 00000002 c02b7f90 60000013 c02b6000 c02d1504
7f60: c02baa88 c02baa80 c0004000 41129200 c02b0c80 c02b7f9c c02b7fa0 c02b7f90
7f80: c0009a54 c0009a60 60000013 ffffffff
[<c0009a30>] (default_idle+0x0/0x34) from [<c000a03c>] (cpu_idle+0x78/0xb0)
[<c0009fc4>] (cpu_idle+0x0/0xb0) from [<c02384e4>] (rest_init+0x60/0x78)
r7:c02baa80 r6:c02b19d8 r5:c02b19dc r4:c02b80b0
[<c0238484>] (rest_init+0x0/0x78) from [<c029d924>] (start_kernel+0x234/0x278)
[<c029d6f0>] (start_kernel+0x0/0x278) from [<c000803c>] (0xc000803c)
r5:c02b803c r4:c0007175
Code: 42555300 54535953 643d4d45 65766972 (53007372)
To make the code a bit more robust against things like these, we modify
ep93xx_dma_get_active() to return NULL in case of empty ->active list and make
sure that callers handle this correctly.
Reported-by: Rafal Prylowski <prylowski@metasoft.pl>
Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Signed-off-by: Vinod Koul <vinod.koul@linux.intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/ep93xx_dma.c | 60 |
1 files changed, 48 insertions, 12 deletions
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 526a7424f6a9..59e7a965772b 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c | |||
@@ -246,6 +246,9 @@ static void ep93xx_dma_set_active(struct ep93xx_dma_chan *edmac, | |||
246 | static struct ep93xx_dma_desc * | 246 | static struct ep93xx_dma_desc * |
247 | ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac) | 247 | ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac) |
248 | { | 248 | { |
249 | if (list_empty(&edmac->active)) | ||
250 | return NULL; | ||
251 | |||
249 | return list_first_entry(&edmac->active, struct ep93xx_dma_desc, node); | 252 | return list_first_entry(&edmac->active, struct ep93xx_dma_desc, node); |
250 | } | 253 | } |
251 | 254 | ||
@@ -263,16 +266,22 @@ ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac) | |||
263 | */ | 266 | */ |
264 | static bool ep93xx_dma_advance_active(struct ep93xx_dma_chan *edmac) | 267 | static bool ep93xx_dma_advance_active(struct ep93xx_dma_chan *edmac) |
265 | { | 268 | { |
269 | struct ep93xx_dma_desc *desc; | ||
270 | |||
266 | list_rotate_left(&edmac->active); | 271 | list_rotate_left(&edmac->active); |
267 | 272 | ||
268 | if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) | 273 | if (test_bit(EP93XX_DMA_IS_CYCLIC, &edmac->flags)) |
269 | return true; | 274 | return true; |
270 | 275 | ||
276 | desc = ep93xx_dma_get_active(edmac); | ||
277 | if (!desc) | ||
278 | return false; | ||
279 | |||
271 | /* | 280 | /* |
272 | * If txd.cookie is set it means that we are back in the first | 281 | * If txd.cookie is set it means that we are back in the first |
273 | * descriptor in the chain and hence done with it. | 282 | * descriptor in the chain and hence done with it. |
274 | */ | 283 | */ |
275 | return !ep93xx_dma_get_active(edmac)->txd.cookie; | 284 | return !desc->txd.cookie; |
276 | } | 285 | } |
277 | 286 | ||
278 | /* | 287 | /* |
@@ -327,9 +336,15 @@ static void m2p_hw_shutdown(struct ep93xx_dma_chan *edmac) | |||
327 | 336 | ||
328 | static void m2p_fill_desc(struct ep93xx_dma_chan *edmac) | 337 | static void m2p_fill_desc(struct ep93xx_dma_chan *edmac) |
329 | { | 338 | { |
330 | struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac); | 339 | struct ep93xx_dma_desc *desc; |
331 | u32 bus_addr; | 340 | u32 bus_addr; |
332 | 341 | ||
342 | desc = ep93xx_dma_get_active(edmac); | ||
343 | if (!desc) { | ||
344 | dev_warn(chan2dev(edmac), "M2P: empty descriptor list\n"); | ||
345 | return; | ||
346 | } | ||
347 | |||
333 | if (ep93xx_dma_chan_direction(&edmac->chan) == DMA_MEM_TO_DEV) | 348 | if (ep93xx_dma_chan_direction(&edmac->chan) == DMA_MEM_TO_DEV) |
334 | bus_addr = desc->src_addr; | 349 | bus_addr = desc->src_addr; |
335 | else | 350 | else |
@@ -491,7 +506,13 @@ static void m2m_hw_shutdown(struct ep93xx_dma_chan *edmac) | |||
491 | 506 | ||
492 | static void m2m_fill_desc(struct ep93xx_dma_chan *edmac) | 507 | static void m2m_fill_desc(struct ep93xx_dma_chan *edmac) |
493 | { | 508 | { |
494 | struct ep93xx_dma_desc *desc = ep93xx_dma_get_active(edmac); | 509 | struct ep93xx_dma_desc *desc; |
510 | |||
511 | desc = ep93xx_dma_get_active(edmac); | ||
512 | if (!desc) { | ||
513 | dev_warn(chan2dev(edmac), "M2M: empty descriptor list\n"); | ||
514 | return; | ||
515 | } | ||
495 | 516 | ||
496 | if (edmac->buffer == 0) { | 517 | if (edmac->buffer == 0) { |
497 | writel(desc->src_addr, edmac->regs + M2M_SAR_BASE0); | 518 | writel(desc->src_addr, edmac->regs + M2M_SAR_BASE0); |
@@ -669,24 +690,30 @@ static void ep93xx_dma_tasklet(unsigned long data) | |||
669 | { | 690 | { |
670 | struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data; | 691 | struct ep93xx_dma_chan *edmac = (struct ep93xx_dma_chan *)data; |
671 | struct ep93xx_dma_desc *desc, *d; | 692 | struct ep93xx_dma_desc *desc, *d; |
672 | dma_async_tx_callback callback; | 693 | dma_async_tx_callback callback = NULL; |
673 | void *callback_param; | 694 | void *callback_param = NULL; |
674 | LIST_HEAD(list); | 695 | LIST_HEAD(list); |
675 | 696 | ||
676 | spin_lock_irq(&edmac->lock); | 697 | spin_lock_irq(&edmac->lock); |
698 | /* | ||
699 | * If dma_terminate_all() was called before we get to run, the active | ||
700 | * list has become empty. If that happens we aren't supposed to do | ||
701 | * anything more than call ep93xx_dma_advance_work(). | ||
702 | */ | ||
677 | desc = ep93xx_dma_get_active(edmac); | 703 | desc = ep93xx_dma_get_active(edmac); |
678 | if (desc->complete) { | 704 | if (desc) { |
679 | edmac->last_completed = desc->txd.cookie; | 705 | if (desc->complete) { |
680 | list_splice_init(&edmac->active, &list); | 706 | edmac->last_completed = desc->txd.cookie; |
707 | list_splice_init(&edmac->active, &list); | ||
708 | } | ||
709 | callback = desc->txd.callback; | ||
710 | callback_param = desc->txd.callback_param; | ||
681 | } | 711 | } |
682 | spin_unlock_irq(&edmac->lock); | 712 | spin_unlock_irq(&edmac->lock); |
683 | 713 | ||
684 | /* Pick up the next descriptor from the queue */ | 714 | /* Pick up the next descriptor from the queue */ |
685 | ep93xx_dma_advance_work(edmac); | 715 | ep93xx_dma_advance_work(edmac); |
686 | 716 | ||
687 | callback = desc->txd.callback; | ||
688 | callback_param = desc->txd.callback_param; | ||
689 | |||
690 | /* Now we can release all the chained descriptors */ | 717 | /* Now we can release all the chained descriptors */ |
691 | list_for_each_entry_safe(desc, d, &list, node) { | 718 | list_for_each_entry_safe(desc, d, &list, node) { |
692 | /* | 719 | /* |
@@ -706,13 +733,22 @@ static void ep93xx_dma_tasklet(unsigned long data) | |||
706 | static irqreturn_t ep93xx_dma_interrupt(int irq, void *dev_id) | 733 | static irqreturn_t ep93xx_dma_interrupt(int irq, void *dev_id) |
707 | { | 734 | { |
708 | struct ep93xx_dma_chan *edmac = dev_id; | 735 | struct ep93xx_dma_chan *edmac = dev_id; |
736 | struct ep93xx_dma_desc *desc; | ||
709 | irqreturn_t ret = IRQ_HANDLED; | 737 | irqreturn_t ret = IRQ_HANDLED; |
710 | 738 | ||
711 | spin_lock(&edmac->lock); | 739 | spin_lock(&edmac->lock); |
712 | 740 | ||
741 | desc = ep93xx_dma_get_active(edmac); | ||
742 | if (!desc) { | ||
743 | dev_warn(chan2dev(edmac), | ||
744 | "got interrupt while active list is empty\n"); | ||
745 | spin_unlock(&edmac->lock); | ||
746 | return IRQ_NONE; | ||
747 | } | ||
748 | |||
713 | switch (edmac->edma->hw_interrupt(edmac)) { | 749 | switch (edmac->edma->hw_interrupt(edmac)) { |
714 | case INTERRUPT_DONE: | 750 | case INTERRUPT_DONE: |
715 | ep93xx_dma_get_active(edmac)->complete = true; | 751 | desc->complete = true; |
716 | tasklet_schedule(&edmac->tasklet); | 752 | tasklet_schedule(&edmac->tasklet); |
717 | break; | 753 | break; |
718 | 754 | ||