aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
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:41:21 -0400
commit70a207ad4db2f0c60308b3f32086263c438c67a3 (patch)
tree875fbbdfe57a044516f722766e4185d9fcc05938 /drivers
parent270e779036ff144d6c6904ce9480f0d70ff93e86 (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')
-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;