summaryrefslogtreecommitdiffstats
path: root/drivers/dma/virt-dma.c
diff options
context:
space:
mode:
authorRobert Jarzmik <robert.jarzmik@free.fr>2015-10-13 15:54:28 -0400
committerVinod Koul <vinod.koul@intel.com>2015-11-15 22:02:16 -0500
commit13bb26ae8850ede9cfb5ba20e646fe08e23aca97 (patch)
treedc3038dcd1135bd00bb7e2e2d4529a81d944d6de /drivers/dma/virt-dma.c
parent8005c49d9aea74d382f474ce11afbbc7d7130bec (diff)
dmaengine: virt-dma: don't always free descriptor upon completion
This patch attempts to enhance the case of a transfer submitted multiple times, and where the cost of creating the descriptors chain is not negligible. This happens with big video buffers (several megabytes, ie. several thousands of linked descriptors in one scatter-gather list). In these cases, a video driver would want to do : - tx = dmaengine_prep_slave_sg() - dma_engine_submit(tx); - dma_async_issue_pending() - wait for video completion - read video data (or not, skipping a frame is also possible) - dma_engine_submit(tx) => here, the descriptors chain recalculation will take time => the dma coherent allocation over and over might create holes in the dma pool, which is counter-productive. - dma_async_issue_pending() - etc ... In order to cope with this case, virt-dma is modified to prevent freeing the descriptors upon completion if DMA_CTRL_REUSE flag is set in the transfer. This patch is a respin of the former DMA_CTRL_ACK approach, which was reverted due to a regression in audio drivers. Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma/virt-dma.c')
-rw-r--r--drivers/dma/virt-dma.c46
1 files changed, 40 insertions, 6 deletions
diff --git a/drivers/dma/virt-dma.c b/drivers/dma/virt-dma.c
index 6f80432a3f0a..a35c211857dd 100644
--- a/drivers/dma/virt-dma.c
+++ b/drivers/dma/virt-dma.c
@@ -29,7 +29,7 @@ dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
29 spin_lock_irqsave(&vc->lock, flags); 29 spin_lock_irqsave(&vc->lock, flags);
30 cookie = dma_cookie_assign(tx); 30 cookie = dma_cookie_assign(tx);
31 31
32 list_add_tail(&vd->node, &vc->desc_submitted); 32 list_move_tail(&vd->node, &vc->desc_submitted);
33 spin_unlock_irqrestore(&vc->lock, flags); 33 spin_unlock_irqrestore(&vc->lock, flags);
34 34
35 dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n", 35 dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
@@ -39,6 +39,33 @@ dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
39} 39}
40EXPORT_SYMBOL_GPL(vchan_tx_submit); 40EXPORT_SYMBOL_GPL(vchan_tx_submit);
41 41
42/**
43 * vchan_tx_desc_free - free a reusable descriptor
44 * @tx: the transfer
45 *
46 * This function frees a previously allocated reusable descriptor. The only
47 * other way is to clear the DMA_CTRL_REUSE flag and submit one last time the
48 * transfer.
49 *
50 * Returns 0 upon success
51 */
52int vchan_tx_desc_free(struct dma_async_tx_descriptor *tx)
53{
54 struct virt_dma_chan *vc = to_virt_chan(tx->chan);
55 struct virt_dma_desc *vd = to_virt_desc(tx);
56 unsigned long flags;
57
58 spin_lock_irqsave(&vc->lock, flags);
59 list_del(&vd->node);
60 spin_unlock_irqrestore(&vc->lock, flags);
61
62 dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: freeing\n",
63 vc, vd, vd->tx.cookie);
64 vc->desc_free(vd);
65 return 0;
66}
67EXPORT_SYMBOL_GPL(vchan_tx_desc_free);
68
42struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc, 69struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc,
43 dma_cookie_t cookie) 70 dma_cookie_t cookie)
44{ 71{
@@ -83,8 +110,10 @@ static void vchan_complete(unsigned long arg)
83 cb_data = vd->tx.callback_param; 110 cb_data = vd->tx.callback_param;
84 111
85 list_del(&vd->node); 112 list_del(&vd->node);
86 113 if (dmaengine_desc_test_reuse(&vd->tx))
87 vc->desc_free(vd); 114 list_add(&vd->node, &vc->desc_allocated);
115 else
116 vc->desc_free(vd);
88 117
89 if (cb) 118 if (cb)
90 cb(cb_data); 119 cb(cb_data);
@@ -96,9 +125,13 @@ void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
96 while (!list_empty(head)) { 125 while (!list_empty(head)) {
97 struct virt_dma_desc *vd = list_first_entry(head, 126 struct virt_dma_desc *vd = list_first_entry(head,
98 struct virt_dma_desc, node); 127 struct virt_dma_desc, node);
99 list_del(&vd->node); 128 if (dmaengine_desc_test_reuse(&vd->tx)) {
100 dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd); 129 list_move_tail(&vd->node, &vc->desc_allocated);
101 vc->desc_free(vd); 130 } else {
131 dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
132 list_del(&vd->node);
133 vc->desc_free(vd);
134 }
102 } 135 }
103} 136}
104EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list); 137EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
@@ -108,6 +141,7 @@ void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
108 dma_cookie_init(&vc->chan); 141 dma_cookie_init(&vc->chan);
109 142
110 spin_lock_init(&vc->lock); 143 spin_lock_init(&vc->lock);
144 INIT_LIST_HEAD(&vc->desc_allocated);
111 INIT_LIST_HEAD(&vc->desc_submitted); 145 INIT_LIST_HEAD(&vc->desc_submitted);
112 INIT_LIST_HEAD(&vc->desc_issued); 146 INIT_LIST_HEAD(&vc->desc_issued);
113 INIT_LIST_HEAD(&vc->desc_completed); 147 INIT_LIST_HEAD(&vc->desc_completed);