aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@iki.fi>2011-11-26 05:54:08 -0500
committerVinod Koul <vinod.koul@linux.intel.com>2011-12-04 21:46:26 -0500
commit6d0709d2000ae7dbead759715f57ba381b7057bb (patch)
tree29a7dabceb6e8132f8979da33cade6d090c8affc /drivers/dma
parentb62cfc5e0e59d1e0192105144b724b0c332720a0 (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.c60
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,
246static struct ep93xx_dma_desc * 246static struct ep93xx_dma_desc *
247ep93xx_dma_get_active(struct ep93xx_dma_chan *edmac) 247ep93xx_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 */
264static bool ep93xx_dma_advance_active(struct ep93xx_dma_chan *edmac) 267static 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
328static void m2p_fill_desc(struct ep93xx_dma_chan *edmac) 337static 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
492static void m2m_fill_desc(struct ep93xx_dma_chan *edmac) 507static 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)
706static irqreturn_t ep93xx_dma_interrupt(int irq, void *dev_id) 733static 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