1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#include <linux/kernel.h>
27#include <linux/interrupt.h>
28#include <linux/mm.h>
29#include <linux/dma-mapping.h>
30#include <linux/raid/xor.h>
31#include <linux/async_tx.h>
32
33
34
35
36
37static __always_inline struct dma_async_tx_descriptor *
38do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,
39 unsigned int offset, int src_cnt, size_t len,
40 enum async_tx_flags flags,
41 struct dma_async_tx_descriptor *depend_tx,
42 dma_async_tx_callback cb_fn, void *cb_param)
43{
44 struct dma_device *dma = chan->device;
45 dma_addr_t *dma_src = (dma_addr_t *) src_list;
46 struct dma_async_tx_descriptor *tx = NULL;
47 int src_off = 0;
48 int i;
49 dma_async_tx_callback _cb_fn;
50 void *_cb_param;
51 enum async_tx_flags async_flags;
52 enum dma_ctrl_flags dma_flags;
53 int xor_src_cnt;
54 dma_addr_t dma_dest;
55
56
57 dma_dest = dma_map_page(dma->dev, dest, offset, len, DMA_BIDIRECTIONAL);
58 for (i = 0; i < src_cnt; i++) {
59
60 if (unlikely(src_list[i] == dest)) {
61 dma_src[i] = dma_dest;
62 continue;
63 }
64 dma_src[i] = dma_map_page(dma->dev, src_list[i], offset,
65 len, DMA_TO_DEVICE);
66 }
67
68 while (src_cnt) {
69 async_flags = flags;
70 dma_flags = 0;
71 xor_src_cnt = min(src_cnt, dma->max_xor);
72
73
74
75
76 if (src_cnt > xor_src_cnt) {
77 async_flags &= ~ASYNC_TX_ACK;
78 dma_flags = DMA_COMPL_SKIP_DEST_UNMAP;
79 _cb_fn = NULL;
80 _cb_param = NULL;
81 } else {
82 _cb_fn = cb_fn;
83 _cb_param = cb_param;
84 }
85 if (_cb_fn)
86 dma_flags |= DMA_PREP_INTERRUPT;
87
88
89
90
91
92 tx = dma->device_prep_dma_xor(chan, dma_dest, &dma_src[src_off],
93 xor_src_cnt, len, dma_flags);
94
95 if (unlikely(!tx))
96 async_tx_quiesce(&depend_tx);
97
98
99 while (unlikely(!tx)) {
100 dma_async_issue_pending(chan);
101 tx = dma->device_prep_dma_xor(chan, dma_dest,
102 &dma_src[src_off],
103 xor_src_cnt, len,
104 dma_flags);
105 }
106
107 async_tx_submit(chan, tx, async_flags, depend_tx, _cb_fn,
108 _cb_param);
109
110 depend_tx = tx;
111 flags |= ASYNC_TX_DEP_ACK;
112
113 if (src_cnt > xor_src_cnt) {
114
115 src_cnt -= xor_src_cnt;
116 src_off += xor_src_cnt;
117
118
119 dma_src[--src_off] = dma_dest;
120 src_cnt++;
121 } else
122 break;
123 }
124
125 return tx;
126}
127
128static void
129do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset,
130 int src_cnt, size_t len, enum async_tx_flags flags,
131 dma_async_tx_callback cb_fn, void *cb_param)
132{
133 int i;
134 int xor_src_cnt;
135 int src_off = 0;
136 void *dest_buf;
137 void **srcs = (void **) src_list;
138
139
140 for (i = 0; i < src_cnt; i++)
141 srcs[i] = page_address(src_list[i]) + offset;
142
143
144 dest_buf = page_address(dest) + offset;
145
146 if (flags & ASYNC_TX_XOR_ZERO_DST)
147 memset(dest_buf, 0, len);
148
149 while (src_cnt > 0) {
150
151 xor_src_cnt = min(src_cnt, MAX_XOR_BLOCKS);
152 xor_blocks(xor_src_cnt, len, dest_buf, &srcs[src_off]);
153
154
155 src_cnt -= xor_src_cnt;
156 src_off += xor_src_cnt;
157 }
158
159 async_tx_sync_epilog(cb_fn, cb_param);
160}
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180struct dma_async_tx_descriptor *
181async_xor(struct page *dest, struct page **src_list, unsigned int offset,
182 int src_cnt, size_t len, enum async_tx_flags flags,
183 struct dma_async_tx_descriptor *depend_tx,
184 dma_async_tx_callback cb_fn, void *cb_param)
185{
186 struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_XOR,
187 &dest, 1, src_list,
188 src_cnt, len);
189 BUG_ON(src_cnt <= 1);
190
191 if (chan) {
192
193 pr_debug("%s (async): len: %zu\n", __func__, len);
194
195 return do_async_xor(chan, dest, src_list, offset, src_cnt, len,
196 flags, depend_tx, cb_fn, cb_param);
197 } else {
198
199 pr_debug("%s (sync): len: %zu\n", __func__, len);
200
201
202
203
204 if (flags & ASYNC_TX_XOR_DROP_DST) {
205 src_cnt--;
206 src_list++;
207 }
208
209
210 async_tx_quiesce(&depend_tx);
211
212 do_sync_xor(dest, src_list, offset, src_cnt, len,
213 flags, cb_fn, cb_param);
214
215 return NULL;
216 }
217}
218EXPORT_SYMBOL_GPL(async_xor);
219
220static int page_is_zero(struct page *p, unsigned int offset, size_t len)
221{
222 char *a = page_address(p) + offset;
223 return ((*(u32 *) a) == 0 &&
224 memcmp(a, a + 4, len - 4) == 0);
225}
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241struct dma_async_tx_descriptor *
242async_xor_zero_sum(struct page *dest, struct page **src_list,
243 unsigned int offset, int src_cnt, size_t len,
244 u32 *result, enum async_tx_flags flags,
245 struct dma_async_tx_descriptor *depend_tx,
246 dma_async_tx_callback cb_fn, void *cb_param)
247{
248 struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_ZERO_SUM,
249 &dest, 1, src_list,
250 src_cnt, len);
251 struct dma_device *device = chan ? chan->device : NULL;
252 struct dma_async_tx_descriptor *tx = NULL;
253
254 BUG_ON(src_cnt <= 1);
255
256 if (device && src_cnt <= device->max_xor) {
257 dma_addr_t *dma_src = (dma_addr_t *) src_list;
258 unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0;
259 int i;
260
261 pr_debug("%s: (async) len: %zu\n", __func__, len);
262
263 for (i = 0; i < src_cnt; i++)
264 dma_src[i] = dma_map_page(device->dev, src_list[i],
265 offset, len, DMA_TO_DEVICE);
266
267 tx = device->device_prep_dma_zero_sum(chan, dma_src, src_cnt,
268 len, result,
269 dma_prep_flags);
270 if (unlikely(!tx)) {
271 async_tx_quiesce(&depend_tx);
272
273 while (!tx) {
274 dma_async_issue_pending(chan);
275 tx = device->device_prep_dma_zero_sum(chan,
276 dma_src, src_cnt, len, result,
277 dma_prep_flags);
278 }
279 }
280
281 async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param);
282 } else {
283 unsigned long xor_flags = flags;
284
285 pr_debug("%s: (sync) len: %zu\n", __func__, len);
286
287 xor_flags |= ASYNC_TX_XOR_DROP_DST;
288 xor_flags &= ~ASYNC_TX_ACK;
289
290 tx = async_xor(dest, src_list, offset, src_cnt, len, xor_flags,
291 depend_tx, NULL, NULL);
292
293 async_tx_quiesce(&tx);
294
295 *result = page_is_zero(dest, offset, len) ? 0 : 1;
296
297 async_tx_sync_epilog(cb_fn, cb_param);
298 }
299
300 return tx;
301}
302EXPORT_SYMBOL_GPL(async_xor_zero_sum);
303
304static int __init async_xor_init(void)
305{
306 #ifdef CONFIG_DMA_ENGINE
307
308
309
310
311
312
313 BUILD_BUG_ON(sizeof(dma_addr_t) > sizeof(struct page *));
314 #endif
315
316 return 0;
317}
318
319static void __exit async_xor_exit(void)
320{
321 do { } while (0);
322}
323
324module_init(async_xor_init);
325module_exit(async_xor_exit);
326
327MODULE_AUTHOR("Intel Corporation");
328MODULE_DESCRIPTION("asynchronous xor/xor-zero-sum api");
329MODULE_LICENSE("GPL");
330