2 * Asynchronous RAID-6 recovery calculations ASYNC_TX API.
3 * Copyright(c) 2009 Intel Corporation
5 * based on raid6recov.c:
6 * Copyright 2002 H. Peter Anvin
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)
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
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.
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>
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
)
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
;
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
;
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
,
55 async_tx_submit(chan
, tx
, submit
);
59 /* could not get a descriptor, unmap and fall through to
60 * the synchronous path
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
);
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
);
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
)
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 */
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
;
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
,
108 async_tx_submit(chan
, tx
, submit
);
112 /* could not get a descriptor, unmap and fall through to
113 * the synchronous path
115 dma_unmap_page(dev
, dma_dest
[1], len
, DMA_BIDIRECTIONAL
);
116 dma_unmap_page(dev
, dma_src
[0], len
, DMA_TO_DEVICE
);
119 /* no channel available, or failed to allocate a descriptor, so
120 * perform the operation synchronously
122 async_tx_quiesce(&submit
->depend_tx
);
123 qmul
= raid6_gfmul
[coef
];
124 d
= page_address(dest
);
125 s
= page_address(src
);
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
)
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
;
152 /* in the 4 disk case P + Pxy == P and Q + Qxy == Q */
153 /* Dx = A*(P+Pxy) + B*(Q+Qxy) */
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
);
164 init_async_submit(submit
, flags
| ASYNC_TX_XOR_ZERO_DST
, tx
, cb_fn
,
166 tx
= async_xor(a
, srcs
, 0, 2, bytes
, submit
);
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
)
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
);
187 for (i
= 0; i
< 3; i
++) {
188 if (i
== faila
|| i
== failb
)
201 /* Compute syndrome with zero for the missing data pages
202 * Use the dead data pages as temporary storage for delta p and
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
);
213 /* compute P + Pxy */
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
);
220 /* compute Q + Qxy */
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
);
227 /* Dx = A*(P+Pxy) + B*(Q+Qxy) */
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
);
238 init_async_submit(submit
, flags
| ASYNC_TX_XOR_DROP_DST
, tx
, cb_fn
,
240 tx
= async_xor(dp
, srcs
, 0, 2, bytes
, submit
);
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
)
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
;
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
266 blocks
[faila
] = (void *)raid6_empty_zero_page
;
267 blocks
[disks
-2] = dp
;
269 blocks
[failb
] = (void *)raid6_empty_zero_page
;
270 blocks
[disks
-1] = dq
;
272 init_async_submit(submit
, ASYNC_TX_FENCE
, tx
, NULL
, NULL
, scribble
);
273 tx
= async_gen_syndrome(blocks
, 0, disks
, bytes
, submit
);
275 /* Restore pointer table */
281 /* compute P + Pxy */
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
);
288 /* compute Q + Qxy */
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
);
295 /* Dx = A*(P+Pxy) + B*(Q+Qxy) */
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
);
306 init_async_submit(submit
, flags
| ASYNC_TX_XOR_DROP_DST
, tx
, cb_fn
,
308 tx
= async_xor(dp
, srcs
, 0, 2, bytes
, submit
);
314 * async_raid6_2data_recov - asynchronously calculate two missing data blocks
315 * @disks: number of disks in the RAID-6 array
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
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
)
326 BUG_ON(faila
== failb
);
330 pr_debug("%s: disks: %d len: %zu\n", __func__
, disks
, bytes
);
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
335 if (!submit
->scribble
) {
336 void **ptrs
= (void **) blocks
;
339 async_tx_quiesce(&submit
->depend_tx
);
340 for (i
= 0; i
< disks
; i
++)
341 ptrs
[i
] = page_address(blocks
[i
]);
343 raid6_2data_recov(disks
, bytes
, faila
, failb
, ptrs
);
345 async_tx_sync_epilog(submit
);
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
356 return __2data_recov_4(bytes
, faila
, failb
, blocks
, submit
);
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
362 return __2data_recov_5(bytes
, faila
, failb
, blocks
, submit
);
364 return __2data_recov_n(disks
, bytes
, faila
, failb
, blocks
, submit
);
367 EXPORT_SYMBOL_GPL(async_raid6_2data_recov
);
370 * async_raid6_datap_recov - asynchronously calculate a data and the 'p' block
371 * @disks: number of disks in the RAID-6 array
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
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
)
381 struct dma_async_tx_descriptor
*tx
= NULL
;
382 struct page
*p
, *q
, *dq
;
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];
390 pr_debug("%s: disks: %d len: %zu\n", __func__
, disks
, bytes
);
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
396 void **ptrs
= (void **) blocks
;
399 async_tx_quiesce(&submit
->depend_tx
);
400 for (i
= 0; i
< disks
; i
++)
401 ptrs
[i
] = page_address(blocks
[i
]);
403 raid6_datap_recov(disks
, bytes
, faila
, ptrs
);
405 async_tx_sync_epilog(submit
);
413 /* Compute syndrome with zero for the missing data page
414 * Use the dead data page as temporary storage for delta q
417 blocks
[faila
] = (void *)raid6_empty_zero_page
;
418 blocks
[disks
-1] = dq
;
420 /* in the 4 disk case we only need to perform a single source
424 int good
= faila
== 0 ? 1 : 0;
425 struct page
*g
= blocks
[good
];
427 init_async_submit(submit
, ASYNC_TX_FENCE
, tx
, NULL
, NULL
,
429 tx
= async_memcpy(p
, g
, 0, 0, bytes
, submit
);
431 init_async_submit(submit
, ASYNC_TX_FENCE
, tx
, NULL
, NULL
,
433 tx
= async_mult(dq
, g
, raid6_gfexp
[good
], bytes
, submit
);
435 init_async_submit(submit
, ASYNC_TX_FENCE
, tx
, NULL
, NULL
,
437 tx
= async_gen_syndrome(blocks
, 0, disks
, bytes
, submit
);
440 /* Restore pointer table */
444 /* calculate g^{-faila} */
445 coef
= raid6_gfinv
[raid6_gfexp
[faila
]];
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
);
453 init_async_submit(submit
, ASYNC_TX_FENCE
, tx
, NULL
, NULL
, scribble
);
454 tx
= async_mult(dq
, dq
, coef
, bytes
, submit
);
458 init_async_submit(submit
, flags
| ASYNC_TX_XOR_DROP_DST
, tx
, cb_fn
,
460 tx
= async_xor(p
, srcs
, 0, 2, bytes
, submit
);
464 EXPORT_SYMBOL_GPL(async_raid6_datap_recov
);
466 MODULE_AUTHOR("Dan Williams <dan.j.williams@intel.com>");
467 MODULE_DESCRIPTION("asynchronous RAID-6 recovery api");
468 MODULE_LICENSE("GPL");