diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-09-08 19:49:32 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-09-08 19:49:32 -0400 |
| commit | e4e436e0bd480668834fe6849a52c5397b7be4fb (patch) | |
| tree | c2beb41548c9879c55811e8e027c9c89d23e3fdc | |
| parent | 9ba365438a532436ecd96a089fb29b01516bed33 (diff) | |
| parent | 82babbb361f207a80cffa8ac34c2b6a0b62acc88 (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.c | 42 |
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 */ | ||
| 647 | static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc) | 650 | static 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: | |||
| 803 | static void d40_term_all(struct d40_chan *d40c) | 808 | static 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); |
