aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/dma/virt-dma.c46
-rw-r--r--drivers/dma/virt-dma.h12
2 files changed, 52 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);
diff --git a/drivers/dma/virt-dma.h b/drivers/dma/virt-dma.h
index 2fa47745a41f..bff8c39dd716 100644
--- a/drivers/dma/virt-dma.h
+++ b/drivers/dma/virt-dma.h
@@ -29,6 +29,7 @@ struct virt_dma_chan {
29 spinlock_t lock; 29 spinlock_t lock;
30 30
31 /* protected by vc.lock */ 31 /* protected by vc.lock */
32 struct list_head desc_allocated;
32 struct list_head desc_submitted; 33 struct list_head desc_submitted;
33 struct list_head desc_issued; 34 struct list_head desc_issued;
34 struct list_head desc_completed; 35 struct list_head desc_completed;
@@ -55,10 +56,17 @@ static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan
55 struct virt_dma_desc *vd, unsigned long tx_flags) 56 struct virt_dma_desc *vd, unsigned long tx_flags)
56{ 57{
57 extern dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *); 58 extern dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *);
59 extern int vchan_tx_desc_free(struct dma_async_tx_descriptor *);
60 unsigned long flags;
58 61
59 dma_async_tx_descriptor_init(&vd->tx, &vc->chan); 62 dma_async_tx_descriptor_init(&vd->tx, &vc->chan);
60 vd->tx.flags = tx_flags; 63 vd->tx.flags = tx_flags;
61 vd->tx.tx_submit = vchan_tx_submit; 64 vd->tx.tx_submit = vchan_tx_submit;
65 vd->tx.desc_free = vchan_tx_desc_free;
66
67 spin_lock_irqsave(&vc->lock, flags);
68 list_add_tail(&vd->node, &vc->desc_allocated);
69 spin_unlock_irqrestore(&vc->lock, flags);
62 70
63 return &vd->tx; 71 return &vd->tx;
64} 72}
@@ -134,6 +142,7 @@ static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc)
134static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc, 142static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc,
135 struct list_head *head) 143 struct list_head *head)
136{ 144{
145 list_splice_tail_init(&vc->desc_allocated, head);
137 list_splice_tail_init(&vc->desc_submitted, head); 146 list_splice_tail_init(&vc->desc_submitted, head);
138 list_splice_tail_init(&vc->desc_issued, head); 147 list_splice_tail_init(&vc->desc_issued, head);
139 list_splice_tail_init(&vc->desc_completed, head); 148 list_splice_tail_init(&vc->desc_completed, head);
@@ -141,11 +150,14 @@ static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc,
141 150
142static inline void vchan_free_chan_resources(struct virt_dma_chan *vc) 151static inline void vchan_free_chan_resources(struct virt_dma_chan *vc)
143{ 152{
153 struct virt_dma_desc *vd;
144 unsigned long flags; 154 unsigned long flags;
145 LIST_HEAD(head); 155 LIST_HEAD(head);
146 156
147 spin_lock_irqsave(&vc->lock, flags); 157 spin_lock_irqsave(&vc->lock, flags);
148 vchan_get_all_descriptors(vc, &head); 158 vchan_get_all_descriptors(vc, &head);
159 list_for_each_entry(vd, &head, node)
160 dmaengine_desc_clear_reuse(&vd->tx);
149 spin_unlock_irqrestore(&vc->lock, flags); 161 spin_unlock_irqrestore(&vc->lock, flags);
150 162
151 vchan_dma_desc_free_list(vc, &head); 163 vchan_dma_desc_free_list(vc, &head);