diff options
-rw-r--r-- | drivers/dma/virt-dma.c | 46 | ||||
-rw-r--r-- | drivers/dma/virt-dma.h | 12 |
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 | } |
40 | EXPORT_SYMBOL_GPL(vchan_tx_submit); | 40 | EXPORT_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 | */ | ||
52 | int 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 | } | ||
67 | EXPORT_SYMBOL_GPL(vchan_tx_desc_free); | ||
68 | |||
42 | struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc, | 69 | struct 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 | } |
104 | EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list); | 137 | EXPORT_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) | |||
134 | static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc, | 142 | static 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 | ||
142 | static inline void vchan_free_chan_resources(struct virt_dma_chan *vc) | 151 | static 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); |