aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma
diff options
context:
space:
mode:
authorPer Forlin <per.forlin@linaro.org>2011-08-29 07:33:34 -0400
committerVinod Koul <vinod.koul@intel.com>2011-09-05 07:37:58 -0400
commit7404368c22b4910ab839238e48d96be45180f6fc (patch)
tree3e641c685e8d97376c36b9b90c6717fbce405cf6 /drivers/dma
parent3b3d5b0f855b3eec45a02832e97c3c1890ff8823 (diff)
dmaengine/ste_dma40: fix Oops due to double free of client descriptor
The client list may exist in two lists at the same time. This makes free fail since the same desc is freed multiple times. Remove desc from client list when adding it to the pending queue. Move free of client owned descriptors from free_dma() to terminate_all(). Unable to handle kernel paging request at virtual address 00100104 pgd = dea8c000 [00100104] *pgd=1ea62831, *pte=00000000, *ppte=00000000 Internal error: Oops: 817 [#1] PREEMPT SMP Modules linked in: CPU: 0 Not tainted (3.1.0-rc3+ #58) PC is at d40_free_chan_resources+0x64/0x330 Signed-off-by: Per Forlin <per.forlin@linaro.org> Acked-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/ste_dma40.c22
1 files changed, 12 insertions, 10 deletions
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index 37388d10497a..92ec0a26401a 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -644,8 +644,11 @@ static struct d40_desc *d40_first_active_get(struct d40_chan *d40c)
644 return d; 644 return d;
645} 645}
646 646
647/* remove desc from current queue and add it to the pending_queue */
647static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc) 648static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc)
648{ 649{
650 d40_desc_remove(desc);
651 desc->is_in_client_list = false;
649 list_add_tail(&desc->node, &d40c->pending_queue); 652 list_add_tail(&desc->node, &d40c->pending_queue);
650} 653}
651 654
@@ -803,6 +806,7 @@ done:
803static void d40_term_all(struct d40_chan *d40c) 806static void d40_term_all(struct d40_chan *d40c)
804{ 807{
805 struct d40_desc *d40d; 808 struct d40_desc *d40d;
809 struct d40_desc *_d;
806 810
807 /* Release active descriptors */ 811 /* Release active descriptors */
808 while ((d40d = d40_first_active_get(d40c))) { 812 while ((d40d = d40_first_active_get(d40c))) {
@@ -822,6 +826,14 @@ static void d40_term_all(struct d40_chan *d40c)
822 d40_desc_free(d40c, d40d); 826 d40_desc_free(d40c, d40d);
823 } 827 }
824 828
829 /* Release client owned descriptors */
830 if (!list_empty(&d40c->client))
831 list_for_each_entry_safe(d40d, _d, &d40c->client, node) {
832 d40_desc_remove(d40d);
833 d40_desc_free(d40c, d40d);
834 }
835
836
825 d40c->pending_tx = 0; 837 d40c->pending_tx = 0;
826 d40c->busy = false; 838 d40c->busy = false;
827} 839}
@@ -1594,20 +1606,10 @@ static int d40_free_dma(struct d40_chan *d40c)
1594 u32 event; 1606 u32 event;
1595 struct d40_phy_res *phy = d40c->phy_chan; 1607 struct d40_phy_res *phy = d40c->phy_chan;
1596 bool is_src; 1608 bool is_src;
1597 struct d40_desc *d;
1598 struct d40_desc *_d;
1599
1600 1609
1601 /* Terminate all queued and active transfers */ 1610 /* Terminate all queued and active transfers */
1602 d40_term_all(d40c); 1611 d40_term_all(d40c);
1603 1612
1604 /* Release client owned descriptors */
1605 if (!list_empty(&d40c->client))
1606 list_for_each_entry_safe(d, _d, &d40c->client, node) {
1607 d40_desc_remove(d);
1608 d40_desc_free(d40c, d);
1609 }
1610
1611 if (phy == NULL) { 1613 if (phy == NULL) {
1612 chan_err(d40c, "phy == null\n"); 1614 chan_err(d40c, "phy == null\n");
1613 return -EINVAL; 1615 return -EINVAL;