2 * xor offload engine api
4 * Copyright © 2006, Intel Corporation.
6 * Dan Williams <dan.j.williams@intel.com>
8 * with architecture considerations by:
9 * Neil Brown <neilb@suse.de>
10 * Jeff Garzik <jeff@garzik.org>
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms and conditions of the GNU General Public License,
14 * version 2, as published by the Free Software Foundation.
16 * This program is distributed in the hope it will be useful, but WITHOUT
17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
21 * You should have received a copy of the GNU General Public License along with
22 * this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <linux/kernel.h>
27 #include <linux/interrupt.h>
29 #include <linux/dma-mapping.h>
30 #include <linux/raid/xor.h>
31 #include <linux/async_tx.h>
34 do_async_xor(struct dma_async_tx_descriptor
*tx
, struct dma_device
*device
,
35 struct dma_chan
*chan
, struct page
*dest
, struct page
**src_list
,
36 unsigned int offset
, unsigned int src_cnt
, size_t len
,
37 enum async_tx_flags flags
, struct dma_async_tx_descriptor
*depend_tx
,
38 dma_async_tx_callback cb_fn
, void *cb_param
)
41 enum dma_data_direction dir
;
44 pr_debug("%s: len: %zu\n", __FUNCTION__
, len
);
46 dir
= (flags
& ASYNC_TX_ASSUME_COHERENT
) ?
47 DMA_NONE
: DMA_FROM_DEVICE
;
49 dma_addr
= dma_map_page(device
->dev
, dest
, offset
, len
, dir
);
50 tx
->tx_set_dest(dma_addr
, tx
, 0);
52 dir
= (flags
& ASYNC_TX_ASSUME_COHERENT
) ?
53 DMA_NONE
: DMA_TO_DEVICE
;
55 for (i
= 0; i
< src_cnt
; i
++) {
56 dma_addr
= dma_map_page(device
->dev
, src_list
[i
],
58 tx
->tx_set_src(dma_addr
, tx
, i
);
61 async_tx_submit(chan
, tx
, flags
, depend_tx
, cb_fn
, cb_param
);
65 do_sync_xor(struct page
*dest
, struct page
**src_list
, unsigned int offset
,
66 unsigned int src_cnt
, size_t len
, enum async_tx_flags flags
,
67 struct dma_async_tx_descriptor
*depend_tx
,
68 dma_async_tx_callback cb_fn
, void *cb_param
)
73 pr_debug("%s: len: %zu\n", __FUNCTION__
, len
);
75 /* reuse the 'src_list' array to convert to buffer pointers */
76 for (i
= 0; i
< src_cnt
; i
++)
77 src_list
[i
] = (struct page
*)
78 (page_address(src_list
[i
]) + offset
);
80 /* set destination address */
81 _dest
= page_address(dest
) + offset
;
83 if (flags
& ASYNC_TX_XOR_ZERO_DST
)
84 memset(_dest
, 0, len
);
86 xor_blocks(src_cnt
, len
, _dest
,
89 async_tx_sync_epilog(flags
, depend_tx
, cb_fn
, cb_param
);
93 * async_xor - attempt to xor a set of blocks with a dma engine.
94 * xor_blocks always uses the dest as a source so the ASYNC_TX_XOR_ZERO_DST
95 * flag must be set to not include dest data in the calculation. The
96 * assumption with dma eninges is that they only use the destination
97 * buffer as a source when it is explicity specified in the source list.
98 * @dest: destination page
99 * @src_list: array of source pages (if the dest is also a source it must be
100 * at index zero). The contents of this array may be overwritten.
101 * @offset: offset in pages to start transaction
102 * @src_cnt: number of source pages
103 * @len: length in bytes
104 * @flags: ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DEST,
105 * ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK, ASYNC_TX_DEP_ACK
106 * @depend_tx: xor depends on the result of this transaction.
107 * @cb_fn: function to call when the xor completes
108 * @cb_param: parameter to pass to the callback routine
110 struct dma_async_tx_descriptor
*
111 async_xor(struct page
*dest
, struct page
**src_list
, unsigned int offset
,
112 int src_cnt
, size_t len
, enum async_tx_flags flags
,
113 struct dma_async_tx_descriptor
*depend_tx
,
114 dma_async_tx_callback cb_fn
, void *cb_param
)
116 struct dma_chan
*chan
= async_tx_find_channel(depend_tx
, DMA_XOR
);
117 struct dma_device
*device
= chan
? chan
->device
: NULL
;
118 struct dma_async_tx_descriptor
*tx
= NULL
;
119 dma_async_tx_callback _cb_fn
;
121 unsigned long local_flags
;
123 int i
= 0, src_off
= 0, int_en
;
125 BUG_ON(src_cnt
<= 1);
129 if (device
) { /* run the xor asynchronously */
130 xor_src_cnt
= min(src_cnt
, device
->max_xor
);
131 /* if we are submitting additional xors
132 * only set the callback on the last transaction
134 if (src_cnt
> xor_src_cnt
) {
135 local_flags
&= ~ASYNC_TX_ACK
;
140 _cb_param
= cb_param
;
143 int_en
= _cb_fn
? 1 : 0;
145 tx
= device
->device_prep_dma_xor(
146 chan
, xor_src_cnt
, len
, int_en
);
149 do_async_xor(tx
, device
, chan
, dest
,
150 &src_list
[src_off
], offset
, xor_src_cnt
, len
,
151 local_flags
, depend_tx
, _cb_fn
,
153 } else /* fall through */
155 } else { /* run the xor synchronously */
157 /* in the sync case the dest is an implied source
158 * (assumes the dest is at the src_off index)
160 if (flags
& ASYNC_TX_XOR_DROP_DST
) {
165 /* process up to 'MAX_XOR_BLOCKS' sources */
166 xor_src_cnt
= min(src_cnt
, MAX_XOR_BLOCKS
);
168 /* if we are submitting additional xors
169 * only set the callback on the last transaction
171 if (src_cnt
> xor_src_cnt
) {
172 local_flags
&= ~ASYNC_TX_ACK
;
177 _cb_param
= cb_param
;
180 /* wait for any prerequisite operations */
182 /* if ack is already set then we cannot be sure
183 * we are referring to the correct operation
185 BUG_ON(depend_tx
->ack
);
186 if (dma_wait_for_async_tx(depend_tx
) ==
188 panic("%s: DMA_ERROR waiting for "
193 do_sync_xor(dest
, &src_list
[src_off
], offset
,
194 xor_src_cnt
, len
, local_flags
, depend_tx
,
198 /* the previous tx is hidden from the client,
202 async_tx_ack(depend_tx
);
206 if (src_cnt
> xor_src_cnt
) {
207 /* drop completed sources */
208 src_cnt
-= xor_src_cnt
;
209 src_off
+= xor_src_cnt
;
211 /* unconditionally preserve the destination */
212 flags
&= ~ASYNC_TX_XOR_ZERO_DST
;
214 /* use the intermediate result a source, but remember
215 * it's dropped, because it's implied, in the sync case
217 src_list
[--src_off
] = dest
;
219 flags
|= ASYNC_TX_XOR_DROP_DST
;
227 EXPORT_SYMBOL_GPL(async_xor
);
229 static int page_is_zero(struct page
*p
, unsigned int offset
, size_t len
)
231 char *a
= page_address(p
) + offset
;
232 return ((*(u32
*) a
) == 0 &&
233 memcmp(a
, a
+ 4, len
- 4) == 0);
237 * async_xor_zero_sum - attempt a xor parity check with a dma engine.
238 * @dest: destination page used if the xor is performed synchronously
239 * @src_list: array of source pages. The dest page must be listed as a source
240 * at index zero. The contents of this array may be overwritten.
241 * @offset: offset in pages to start transaction
242 * @src_cnt: number of source pages
243 * @len: length in bytes
244 * @result: 0 if sum == 0 else non-zero
245 * @flags: ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK, ASYNC_TX_DEP_ACK
246 * @depend_tx: xor depends on the result of this transaction.
247 * @cb_fn: function to call when the xor completes
248 * @cb_param: parameter to pass to the callback routine
250 struct dma_async_tx_descriptor
*
251 async_xor_zero_sum(struct page
*dest
, struct page
**src_list
,
252 unsigned int offset
, int src_cnt
, size_t len
,
253 u32
*result
, enum async_tx_flags flags
,
254 struct dma_async_tx_descriptor
*depend_tx
,
255 dma_async_tx_callback cb_fn
, void *cb_param
)
257 struct dma_chan
*chan
= async_tx_find_channel(depend_tx
, DMA_ZERO_SUM
);
258 struct dma_device
*device
= chan
? chan
->device
: NULL
;
259 int int_en
= cb_fn
? 1 : 0;
260 struct dma_async_tx_descriptor
*tx
= device
?
261 device
->device_prep_dma_zero_sum(chan
, src_cnt
, len
, result
,
265 BUG_ON(src_cnt
<= 1);
269 enum dma_data_direction dir
;
271 pr_debug("%s: (async) len: %zu\n", __FUNCTION__
, len
);
273 dir
= (flags
& ASYNC_TX_ASSUME_COHERENT
) ?
274 DMA_NONE
: DMA_TO_DEVICE
;
276 for (i
= 0; i
< src_cnt
; i
++) {
277 dma_addr
= dma_map_page(device
->dev
, src_list
[i
],
279 tx
->tx_set_src(dma_addr
, tx
, i
);
282 async_tx_submit(chan
, tx
, flags
, depend_tx
, cb_fn
, cb_param
);
284 unsigned long xor_flags
= flags
;
286 pr_debug("%s: (sync) len: %zu\n", __FUNCTION__
, len
);
288 xor_flags
|= ASYNC_TX_XOR_DROP_DST
;
289 xor_flags
&= ~ASYNC_TX_ACK
;
291 tx
= async_xor(dest
, src_list
, offset
, src_cnt
, len
, xor_flags
,
292 depend_tx
, NULL
, NULL
);
295 if (dma_wait_for_async_tx(tx
) == DMA_ERROR
)
296 panic("%s: DMA_ERROR waiting for tx\n",
301 *result
= page_is_zero(dest
, offset
, len
) ? 0 : 1;
305 async_tx_sync_epilog(flags
, depend_tx
, cb_fn
, cb_param
);
310 EXPORT_SYMBOL_GPL(async_xor_zero_sum
);
312 static int __init
async_xor_init(void)
317 static void __exit
async_xor_exit(void)
322 module_init(async_xor_init
);
323 module_exit(async_xor_exit
);
325 MODULE_AUTHOR("Intel Corporation");
326 MODULE_DESCRIPTION("asynchronous xor/xor-zero-sum api");
327 MODULE_LICENSE("GPL");