diff options
Diffstat (limited to 'crypto/async_tx/async_raid6_recov.c')
-rw-r--r-- | crypto/async_tx/async_raid6_recov.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c new file mode 100644 index 000000000000..6d73dde4786d --- /dev/null +++ b/crypto/async_tx/async_raid6_recov.c | |||
@@ -0,0 +1,468 @@ | |||
1 | /* | ||
2 | * Asynchronous RAID-6 recovery calculations ASYNC_TX API. | ||
3 | * Copyright(c) 2009 Intel Corporation | ||
4 | * | ||
5 | * based on raid6recov.c: | ||
6 | * Copyright 2002 H. Peter Anvin | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the Free | ||
10 | * Software Foundation; either version 2 of the License, or (at your option) | ||
11 | * any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
16 | * more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License along with | ||
19 | * this program; if not, write to the Free Software Foundation, Inc., 51 | ||
20 | * Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
21 | * | ||
22 | */ | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/dma-mapping.h> | ||
26 | #include <linux/raid/pq.h> | ||
27 | #include <linux/async_tx.h> | ||
28 | |||
29 | static struct dma_async_tx_descriptor * | ||
30 | async_sum_product(struct page *dest, struct page **srcs, unsigned char *coef, | ||
31 | size_t len, struct async_submit_ctl *submit) | ||
32 | { | ||
33 | struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ, | ||
34 | &dest, 1, srcs, 2, len); | ||
35 | struct dma_device *dma = chan ? chan->device : NULL; | ||
36 | const u8 *amul, *bmul; | ||
37 | u8 ax, bx; | ||
38 | u8 *a, *b, *c; | ||
39 | |||
40 | if (dma) { | ||
41 | dma_addr_t dma_dest[2]; | ||
42 | dma_addr_t dma_src[2]; | ||
43 | struct device *dev = dma->dev; | ||
44 | struct dma_async_tx_descriptor *tx; | ||
45 | enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P; | ||
46 | |||
47 | if (submit->flags & ASYNC_TX_FENCE) | ||
48 | dma_flags |= DMA_PREP_FENCE; | ||
49 | dma_dest[1] = dma_map_page(dev, dest, 0, len, DMA_BIDIRECTIONAL); | ||
50 | dma_src[0] = dma_map_page(dev, srcs[0], 0, len, DMA_TO_DEVICE); | ||
51 | dma_src[1] = dma_map_page(dev, srcs[1], 0, len, DMA_TO_DEVICE); | ||
52 | tx = dma->device_prep_dma_pq(chan, dma_dest, dma_src, 2, coef, | ||
53 | len, dma_flags); | ||
54 | if (tx) { | ||
55 | async_tx_submit(chan, tx, submit); | ||
56 | return tx; | ||
57 | } | ||
58 | |||
59 | /* could not get a descriptor, unmap and fall through to | ||
60 | * the synchronous path | ||
61 | */ | ||
62 | dma_unmap_page(dev, dma_dest[1], len, DMA_BIDIRECTIONAL); | ||
63 | dma_unmap_page(dev, dma_src[0], len, DMA_TO_DEVICE); | ||
64 | dma_unmap_page(dev, dma_src[1], len, DMA_TO_DEVICE); | ||
65 | } | ||
66 | |||
67 | /* run the operation synchronously */ | ||
68 | async_tx_quiesce(&submit->depend_tx); | ||
69 | amul = raid6_gfmul[coef[0]]; | ||
70 | bmul = raid6_gfmul[coef[1]]; | ||
71 | a = page_address(srcs[0]); | ||
72 | b = page_address(srcs[1]); | ||
73 | c = page_address(dest); | ||
74 | |||
75 | while (len--) { | ||
76 | ax = amul[*a++]; | ||
77 | bx = bmul[*b++]; | ||
78 | *c++ = ax ^ bx; | ||
79 | } | ||
80 | |||
81 | return NULL; | ||
82 | } | ||
83 | |||
84 | static struct dma_async_tx_descriptor * | ||
85 | async_mult(struct page *dest, struct page *src, u8 coef, size_t len, | ||
86 | struct async_submit_ctl *submit) | ||
87 | { | ||
88 | struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ, | ||
89 | &dest, 1, &src, 1, len); | ||
90 | struct dma_device *dma = chan ? chan->device : NULL; | ||
91 | const u8 *qmul; /* Q multiplier table */ | ||
92 | u8 *d, *s; | ||
93 | |||
94 | if (dma) { | ||
95 | dma_addr_t dma_dest[2]; | ||
96 | dma_addr_t dma_src[1]; | ||
97 | struct device *dev = dma->dev; | ||
98 | struct dma_async_tx_descriptor *tx; | ||
99 | enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P; | ||
100 | |||
101 | if (submit->flags & ASYNC_TX_FENCE) | ||
102 | dma_flags |= DMA_PREP_FENCE; | ||
103 | dma_dest[1] = dma_map_page(dev, dest, 0, len, DMA_BIDIRECTIONAL); | ||
104 | dma_src[0] = dma_map_page(dev, src, 0, len, DMA_TO_DEVICE); | ||
105 | tx = dma->device_prep_dma_pq(chan, dma_dest, dma_src, 1, &coef, | ||
106 | len, dma_flags); | ||
107 | if (tx) { | ||
108 | async_tx_submit(chan, tx, submit); | ||
109 | return tx; | ||
110 | } | ||
111 | |||
112 | /* could not get a descriptor, unmap and fall through to | ||
113 | * the synchronous path | ||
114 | */ | ||
115 | dma_unmap_page(dev, dma_dest[1], len, DMA_BIDIRECTIONAL); | ||
116 | dma_unmap_page(dev, dma_src[0], len, DMA_TO_DEVICE); | ||
117 | } | ||
118 | |||
119 | /* no channel available, or failed to allocate a descriptor, so | ||
120 | * perform the operation synchronously | ||
121 | */ | ||
122 | async_tx_quiesce(&submit->depend_tx); | ||
123 | qmul = raid6_gfmul[coef]; | ||
124 | d = page_address(dest); | ||
125 | s = page_address(src); | ||
126 | |||
127 | while (len--) | ||
128 | *d++ = qmul[*s++]; | ||
129 | |||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | static struct dma_async_tx_descriptor * | ||
134 | __2data_recov_4(size_t bytes, int faila, int failb, struct page **blocks, | ||
135 | struct async_submit_ctl *submit) | ||
136 | { | ||
137 | struct dma_async_tx_descriptor *tx = NULL; | ||
138 | struct page *p, *q, *a, *b; | ||
139 | struct page *srcs[2]; | ||
140 | unsigned char coef[2]; | ||
141 | enum async_tx_flags flags = submit->flags; | ||
142 | dma_async_tx_callback cb_fn = submit->cb_fn; | ||
143 | void *cb_param = submit->cb_param; | ||
144 | void *scribble = submit->scribble; | ||
145 | |||
146 | p = blocks[4-2]; | ||
147 | q = blocks[4-1]; | ||
148 | |||
149 | a = blocks[faila]; | ||
150 | b = blocks[failb]; | ||
151 | |||
152 | /* in the 4 disk case P + Pxy == P and Q + Qxy == Q */ | ||
153 | /* Dx = A*(P+Pxy) + B*(Q+Qxy) */ | ||
154 | srcs[0] = p; | ||
155 | srcs[1] = q; | ||
156 | coef[0] = raid6_gfexi[failb-faila]; | ||
157 | coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]; | ||
158 | init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); | ||
159 | tx = async_sum_product(b, srcs, coef, bytes, submit); | ||
160 | |||
161 | /* Dy = P+Pxy+Dx */ | ||
162 | srcs[0] = p; | ||
163 | srcs[1] = b; | ||
164 | init_async_submit(submit, flags | ASYNC_TX_XOR_ZERO_DST, tx, cb_fn, | ||
165 | cb_param, scribble); | ||
166 | tx = async_xor(a, srcs, 0, 2, bytes, submit); | ||
167 | |||
168 | return tx; | ||
169 | |||
170 | } | ||
171 | |||
172 | static struct dma_async_tx_descriptor * | ||
173 | __2data_recov_5(size_t bytes, int faila, int failb, struct page **blocks, | ||
174 | struct async_submit_ctl *submit) | ||
175 | { | ||
176 | struct dma_async_tx_descriptor *tx = NULL; | ||
177 | struct page *p, *q, *g, *dp, *dq; | ||
178 | struct page *srcs[2]; | ||
179 | unsigned char coef[2]; | ||
180 | enum async_tx_flags flags = submit->flags; | ||
181 | dma_async_tx_callback cb_fn = submit->cb_fn; | ||
182 | void *cb_param = submit->cb_param; | ||
183 | void *scribble = submit->scribble; | ||
184 | int uninitialized_var(good); | ||
185 | int i; | ||
186 | |||
187 | for (i = 0; i < 3; i++) { | ||
188 | if (i == faila || i == failb) | ||
189 | continue; | ||
190 | else { | ||
191 | good = i; | ||
192 | break; | ||
193 | } | ||
194 | } | ||
195 | BUG_ON(i >= 3); | ||
196 | |||
197 | p = blocks[5-2]; | ||
198 | q = blocks[5-1]; | ||
199 | g = blocks[good]; | ||
200 | |||
201 | /* Compute syndrome with zero for the missing data pages | ||
202 | * Use the dead data pages as temporary storage for delta p and | ||
203 | * delta q | ||
204 | */ | ||
205 | dp = blocks[faila]; | ||
206 | dq = blocks[failb]; | ||
207 | |||
208 | init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); | ||
209 | tx = async_memcpy(dp, g, 0, 0, bytes, submit); | ||
210 | init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); | ||
211 | tx = async_mult(dq, g, raid6_gfexp[good], bytes, submit); | ||
212 | |||
213 | /* compute P + Pxy */ | ||
214 | srcs[0] = dp; | ||
215 | srcs[1] = p; | ||
216 | init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, | ||
217 | NULL, NULL, scribble); | ||
218 | tx = async_xor(dp, srcs, 0, 2, bytes, submit); | ||
219 | |||
220 | /* compute Q + Qxy */ | ||
221 | srcs[0] = dq; | ||
222 | srcs[1] = q; | ||
223 | init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, | ||
224 | NULL, NULL, scribble); | ||
225 | tx = async_xor(dq, srcs, 0, 2, bytes, submit); | ||
226 | |||
227 | /* Dx = A*(P+Pxy) + B*(Q+Qxy) */ | ||
228 | srcs[0] = dp; | ||
229 | srcs[1] = dq; | ||
230 | coef[0] = raid6_gfexi[failb-faila]; | ||
231 | coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]; | ||
232 | init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); | ||
233 | tx = async_sum_product(dq, srcs, coef, bytes, submit); | ||
234 | |||
235 | /* Dy = P+Pxy+Dx */ | ||
236 | srcs[0] = dp; | ||
237 | srcs[1] = dq; | ||
238 | init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn, | ||
239 | cb_param, scribble); | ||
240 | tx = async_xor(dp, srcs, 0, 2, bytes, submit); | ||
241 | |||
242 | return tx; | ||
243 | } | ||
244 | |||
245 | static struct dma_async_tx_descriptor * | ||
246 | __2data_recov_n(int disks, size_t bytes, int faila, int failb, | ||
247 | struct page **blocks, struct async_submit_ctl *submit) | ||
248 | { | ||
249 | struct dma_async_tx_descriptor *tx = NULL; | ||
250 | struct page *p, *q, *dp, *dq; | ||
251 | struct page *srcs[2]; | ||
252 | unsigned char coef[2]; | ||
253 | enum async_tx_flags flags = submit->flags; | ||
254 | dma_async_tx_callback cb_fn = submit->cb_fn; | ||
255 | void *cb_param = submit->cb_param; | ||
256 | void *scribble = submit->scribble; | ||
257 | |||
258 | p = blocks[disks-2]; | ||
259 | q = blocks[disks-1]; | ||
260 | |||
261 | /* Compute syndrome with zero for the missing data pages | ||
262 | * Use the dead data pages as temporary storage for | ||
263 | * delta p and delta q | ||
264 | */ | ||
265 | dp = blocks[faila]; | ||
266 | blocks[faila] = (void *)raid6_empty_zero_page; | ||
267 | blocks[disks-2] = dp; | ||
268 | dq = blocks[failb]; | ||
269 | blocks[failb] = (void *)raid6_empty_zero_page; | ||
270 | blocks[disks-1] = dq; | ||
271 | |||
272 | init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); | ||
273 | tx = async_gen_syndrome(blocks, 0, disks, bytes, submit); | ||
274 | |||
275 | /* Restore pointer table */ | ||
276 | blocks[faila] = dp; | ||
277 | blocks[failb] = dq; | ||
278 | blocks[disks-2] = p; | ||
279 | blocks[disks-1] = q; | ||
280 | |||
281 | /* compute P + Pxy */ | ||
282 | srcs[0] = dp; | ||
283 | srcs[1] = p; | ||
284 | init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, | ||
285 | NULL, NULL, scribble); | ||
286 | tx = async_xor(dp, srcs, 0, 2, bytes, submit); | ||
287 | |||
288 | /* compute Q + Qxy */ | ||
289 | srcs[0] = dq; | ||
290 | srcs[1] = q; | ||
291 | init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, | ||
292 | NULL, NULL, scribble); | ||
293 | tx = async_xor(dq, srcs, 0, 2, bytes, submit); | ||
294 | |||
295 | /* Dx = A*(P+Pxy) + B*(Q+Qxy) */ | ||
296 | srcs[0] = dp; | ||
297 | srcs[1] = dq; | ||
298 | coef[0] = raid6_gfexi[failb-faila]; | ||
299 | coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]; | ||
300 | init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); | ||
301 | tx = async_sum_product(dq, srcs, coef, bytes, submit); | ||
302 | |||
303 | /* Dy = P+Pxy+Dx */ | ||
304 | srcs[0] = dp; | ||
305 | srcs[1] = dq; | ||
306 | init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn, | ||
307 | cb_param, scribble); | ||
308 | tx = async_xor(dp, srcs, 0, 2, bytes, submit); | ||
309 | |||
310 | return tx; | ||
311 | } | ||
312 | |||
313 | /** | ||
314 | * async_raid6_2data_recov - asynchronously calculate two missing data blocks | ||
315 | * @disks: number of disks in the RAID-6 array | ||
316 | * @bytes: block size | ||
317 | * @faila: first failed drive index | ||
318 | * @failb: second failed drive index | ||
319 | * @blocks: array of source pointers where the last two entries are p and q | ||
320 | * @submit: submission/completion modifiers | ||
321 | */ | ||
322 | struct dma_async_tx_descriptor * | ||
323 | async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb, | ||
324 | struct page **blocks, struct async_submit_ctl *submit) | ||
325 | { | ||
326 | BUG_ON(faila == failb); | ||
327 | if (failb < faila) | ||
328 | swap(faila, failb); | ||
329 | |||
330 | pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes); | ||
331 | |||
332 | /* we need to preserve the contents of 'blocks' for the async | ||
333 | * case, so punt to synchronous if a scribble buffer is not available | ||
334 | */ | ||
335 | if (!submit->scribble) { | ||
336 | void **ptrs = (void **) blocks; | ||
337 | int i; | ||
338 | |||
339 | async_tx_quiesce(&submit->depend_tx); | ||
340 | for (i = 0; i < disks; i++) | ||
341 | ptrs[i] = page_address(blocks[i]); | ||
342 | |||
343 | raid6_2data_recov(disks, bytes, faila, failb, ptrs); | ||
344 | |||
345 | async_tx_sync_epilog(submit); | ||
346 | |||
347 | return NULL; | ||
348 | } | ||
349 | |||
350 | switch (disks) { | ||
351 | case 4: | ||
352 | /* dma devices do not uniformly understand a zero source pq | ||
353 | * operation (in contrast to the synchronous case), so | ||
354 | * explicitly handle the 4 disk special case | ||
355 | */ | ||
356 | return __2data_recov_4(bytes, faila, failb, blocks, submit); | ||
357 | case 5: | ||
358 | /* dma devices do not uniformly understand a single | ||
359 | * source pq operation (in contrast to the synchronous | ||
360 | * case), so explicitly handle the 5 disk special case | ||
361 | */ | ||
362 | return __2data_recov_5(bytes, faila, failb, blocks, submit); | ||
363 | default: | ||
364 | return __2data_recov_n(disks, bytes, faila, failb, blocks, submit); | ||
365 | } | ||
366 | } | ||
367 | EXPORT_SYMBOL_GPL(async_raid6_2data_recov); | ||
368 | |||
369 | /** | ||
370 | * async_raid6_datap_recov - asynchronously calculate a data and the 'p' block | ||
371 | * @disks: number of disks in the RAID-6 array | ||
372 | * @bytes: block size | ||
373 | * @faila: failed drive index | ||
374 | * @blocks: array of source pointers where the last two entries are p and q | ||
375 | * @submit: submission/completion modifiers | ||
376 | */ | ||
377 | struct dma_async_tx_descriptor * | ||
378 | async_raid6_datap_recov(int disks, size_t bytes, int faila, | ||
379 | struct page **blocks, struct async_submit_ctl *submit) | ||
380 | { | ||
381 | struct dma_async_tx_descriptor *tx = NULL; | ||
382 | struct page *p, *q, *dq; | ||
383 | u8 coef; | ||
384 | enum async_tx_flags flags = submit->flags; | ||
385 | dma_async_tx_callback cb_fn = submit->cb_fn; | ||
386 | void *cb_param = submit->cb_param; | ||
387 | void *scribble = submit->scribble; | ||
388 | struct page *srcs[2]; | ||
389 | |||
390 | pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes); | ||
391 | |||
392 | /* we need to preserve the contents of 'blocks' for the async | ||
393 | * case, so punt to synchronous if a scribble buffer is not available | ||
394 | */ | ||
395 | if (!scribble) { | ||
396 | void **ptrs = (void **) blocks; | ||
397 | int i; | ||
398 | |||
399 | async_tx_quiesce(&submit->depend_tx); | ||
400 | for (i = 0; i < disks; i++) | ||
401 | ptrs[i] = page_address(blocks[i]); | ||
402 | |||
403 | raid6_datap_recov(disks, bytes, faila, ptrs); | ||
404 | |||
405 | async_tx_sync_epilog(submit); | ||
406 | |||
407 | return NULL; | ||
408 | } | ||
409 | |||
410 | p = blocks[disks-2]; | ||
411 | q = blocks[disks-1]; | ||
412 | |||
413 | /* Compute syndrome with zero for the missing data page | ||
414 | * Use the dead data page as temporary storage for delta q | ||
415 | */ | ||
416 | dq = blocks[faila]; | ||
417 | blocks[faila] = (void *)raid6_empty_zero_page; | ||
418 | blocks[disks-1] = dq; | ||
419 | |||
420 | /* in the 4 disk case we only need to perform a single source | ||
421 | * multiplication | ||
422 | */ | ||
423 | if (disks == 4) { | ||
424 | int good = faila == 0 ? 1 : 0; | ||
425 | struct page *g = blocks[good]; | ||
426 | |||
427 | init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, | ||
428 | scribble); | ||
429 | tx = async_memcpy(p, g, 0, 0, bytes, submit); | ||
430 | |||
431 | init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, | ||
432 | scribble); | ||
433 | tx = async_mult(dq, g, raid6_gfexp[good], bytes, submit); | ||
434 | } else { | ||
435 | init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, | ||
436 | scribble); | ||
437 | tx = async_gen_syndrome(blocks, 0, disks, bytes, submit); | ||
438 | } | ||
439 | |||
440 | /* Restore pointer table */ | ||
441 | blocks[faila] = dq; | ||
442 | blocks[disks-1] = q; | ||
443 | |||
444 | /* calculate g^{-faila} */ | ||
445 | coef = raid6_gfinv[raid6_gfexp[faila]]; | ||
446 | |||
447 | srcs[0] = dq; | ||
448 | srcs[1] = q; | ||
449 | init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, | ||
450 | NULL, NULL, scribble); | ||
451 | tx = async_xor(dq, srcs, 0, 2, bytes, submit); | ||
452 | |||
453 | init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); | ||
454 | tx = async_mult(dq, dq, coef, bytes, submit); | ||
455 | |||
456 | srcs[0] = p; | ||
457 | srcs[1] = dq; | ||
458 | init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn, | ||
459 | cb_param, scribble); | ||
460 | tx = async_xor(p, srcs, 0, 2, bytes, submit); | ||
461 | |||
462 | return tx; | ||
463 | } | ||
464 | EXPORT_SYMBOL_GPL(async_raid6_datap_recov); | ||
465 | |||
466 | MODULE_AUTHOR("Dan Williams <dan.j.williams@intel.com>"); | ||
467 | MODULE_DESCRIPTION("asynchronous RAID-6 recovery api"); | ||
468 | MODULE_LICENSE("GPL"); | ||