aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-09-08 19:49:32 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-09-08 19:49:32 -0400
commite4e436e0bd480668834fe6849a52c5397b7be4fb (patch)
treec2beb41548c9879c55811e8e027c9c89d23e3fdc
parent9ba365438a532436ecd96a089fb29b01516bed33 (diff)
parent82babbb361f207a80cffa8ac34c2b6a0b62acc88 (diff)
Merge branch 'fixes' of git://git.infradead.org/users/vkoul/slave-dma
* 'fixes' of git://git.infradead.org/users/vkoul/slave-dma: dmaengine/ste_dma40: fix memory leak due to prepared descriptors dmaengine/ste_dma40: fix Oops due to double free of client descriptor dmaengine/ste_dma40: remove duplicate call to d40_pool_lli_free(). dmaengine/ste_dma40: add missing kernel doc for pending_queue
-rw-r--r--drivers/dma/ste_dma40.c42
1 files changed, 29 insertions, 13 deletions
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index cd3a7c726bf8..467e4dcb20a0 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -174,8 +174,10 @@ struct d40_base;
174 * @tasklet: Tasklet that gets scheduled from interrupt context to complete a 174 * @tasklet: Tasklet that gets scheduled from interrupt context to complete a
175 * transfer and call client callback. 175 * transfer and call client callback.
176 * @client: Cliented owned descriptor list. 176 * @client: Cliented owned descriptor list.
177 * @pending_queue: Submitted jobs, to be issued by issue_pending()
177 * @active: Active descriptor. 178 * @active: Active descriptor.
178 * @queue: Queued jobs. 179 * @queue: Queued jobs.
180 * @prepare_queue: Prepared jobs.
179 * @dma_cfg: The client configuration of this dma channel. 181 * @dma_cfg: The client configuration of this dma channel.
180 * @configured: whether the dma_cfg configuration is valid 182 * @configured: whether the dma_cfg configuration is valid
181 * @base: Pointer to the device instance struct. 183 * @base: Pointer to the device instance struct.
@@ -203,6 +205,7 @@ struct d40_chan {
203 struct list_head pending_queue; 205 struct list_head pending_queue;
204 struct list_head active; 206 struct list_head active;
205 struct list_head queue; 207 struct list_head queue;
208 struct list_head prepare_queue;
206 struct stedma40_chan_cfg dma_cfg; 209 struct stedma40_chan_cfg dma_cfg;
207 bool configured; 210 bool configured;
208 struct d40_base *base; 211 struct d40_base *base;
@@ -477,7 +480,6 @@ static struct d40_desc *d40_desc_get(struct d40_chan *d40c)
477 480
478 list_for_each_entry_safe(d, _d, &d40c->client, node) 481 list_for_each_entry_safe(d, _d, &d40c->client, node)
479 if (async_tx_test_ack(&d->txd)) { 482 if (async_tx_test_ack(&d->txd)) {
480 d40_pool_lli_free(d40c, d);
481 d40_desc_remove(d); 483 d40_desc_remove(d);
482 desc = d; 484 desc = d;
483 memset(desc, 0, sizeof(*desc)); 485 memset(desc, 0, sizeof(*desc));
@@ -644,8 +646,11 @@ static struct d40_desc *d40_first_active_get(struct d40_chan *d40c)
644 return d; 646 return d;
645} 647}
646 648
649/* 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) 650static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc)
648{ 651{
652 d40_desc_remove(desc);
653 desc->is_in_client_list = false;
649 list_add_tail(&desc->node, &d40c->pending_queue); 654 list_add_tail(&desc->node, &d40c->pending_queue);
650} 655}
651 656
@@ -803,6 +808,7 @@ done:
803static void d40_term_all(struct d40_chan *d40c) 808static void d40_term_all(struct d40_chan *d40c)
804{ 809{
805 struct d40_desc *d40d; 810 struct d40_desc *d40d;
811 struct d40_desc *_d;
806 812
807 /* Release active descriptors */ 813 /* Release active descriptors */
808 while ((d40d = d40_first_active_get(d40c))) { 814 while ((d40d = d40_first_active_get(d40c))) {
@@ -822,6 +828,21 @@ static void d40_term_all(struct d40_chan *d40c)
822 d40_desc_free(d40c, d40d); 828 d40_desc_free(d40c, d40d);
823 } 829 }
824 830
831 /* Release client owned descriptors */
832 if (!list_empty(&d40c->client))
833 list_for_each_entry_safe(d40d, _d, &d40c->client, node) {
834 d40_desc_remove(d40d);
835 d40_desc_free(d40c, d40d);
836 }
837
838 /* Release descriptors in prepare queue */
839 if (!list_empty(&d40c->prepare_queue))
840 list_for_each_entry_safe(d40d, _d,
841 &d40c->prepare_queue, node) {
842 d40_desc_remove(d40d);
843 d40_desc_free(d40c, d40d);
844 }
845
825 d40c->pending_tx = 0; 846 d40c->pending_tx = 0;
826 d40c->busy = false; 847 d40c->busy = false;
827} 848}
@@ -1208,7 +1229,6 @@ static void dma_tasklet(unsigned long data)
1208 1229
1209 if (!d40d->cyclic) { 1230 if (!d40d->cyclic) {
1210 if (async_tx_test_ack(&d40d->txd)) { 1231 if (async_tx_test_ack(&d40d->txd)) {
1211 d40_pool_lli_free(d40c, d40d);
1212 d40_desc_remove(d40d); 1232 d40_desc_remove(d40d);
1213 d40_desc_free(d40c, d40d); 1233 d40_desc_free(d40c, d40d);
1214 } else { 1234 } else {
@@ -1595,21 +1615,10 @@ static int d40_free_dma(struct d40_chan *d40c)
1595 u32 event; 1615 u32 event;
1596 struct d40_phy_res *phy = d40c->phy_chan; 1616 struct d40_phy_res *phy = d40c->phy_chan;
1597 bool is_src; 1617 bool is_src;
1598 struct d40_desc *d;
1599 struct d40_desc *_d;
1600
1601 1618
1602 /* Terminate all queued and active transfers */ 1619 /* Terminate all queued and active transfers */
1603 d40_term_all(d40c); 1620 d40_term_all(d40c);
1604 1621
1605 /* Release client owned descriptors */
1606 if (!list_empty(&d40c->client))
1607 list_for_each_entry_safe(d, _d, &d40c->client, node) {
1608 d40_pool_lli_free(d40c, d);
1609 d40_desc_remove(d);
1610 d40_desc_free(d40c, d);
1611 }
1612
1613 if (phy == NULL) { 1622 if (phy == NULL) {
1614 chan_err(d40c, "phy == null\n"); 1623 chan_err(d40c, "phy == null\n");
1615 return -EINVAL; 1624 return -EINVAL;
@@ -1911,6 +1920,12 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src,
1911 goto err; 1920 goto err;
1912 } 1921 }
1913 1922
1923 /*
1924 * add descriptor to the prepare queue in order to be able
1925 * to free them later in terminate_all
1926 */
1927 list_add_tail(&desc->node, &chan->prepare_queue);
1928
1914 spin_unlock_irqrestore(&chan->lock, flags); 1929 spin_unlock_irqrestore(&chan->lock, flags);
1915 1930
1916 return &desc->txd; 1931 return &desc->txd;
@@ -2400,6 +2415,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
2400 INIT_LIST_HEAD(&d40c->queue); 2415 INIT_LIST_HEAD(&d40c->queue);
2401 INIT_LIST_HEAD(&d40c->pending_queue); 2416 INIT_LIST_HEAD(&d40c->pending_queue);
2402 INIT_LIST_HEAD(&d40c->client); 2417 INIT_LIST_HEAD(&d40c->client);
2418 INIT_LIST_HEAD(&d40c->prepare_queue);
2403 2419
2404 tasklet_init(&d40c->tasklet, dma_tasklet, 2420 tasklet_init(&d40c->tasklet, dma_tasklet,
2405 (unsigned long) d40c); 2421 (unsigned long) d40c);