diff options
Diffstat (limited to 'crypto/async_tx/async_xor.c')
-rw-r--r-- | crypto/async_tx/async_xor.c | 124 |
1 files changed, 75 insertions, 49 deletions
diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c index 2575f674dcd5..2259a4ff15cb 100644 --- a/crypto/async_tx/async_xor.c +++ b/crypto/async_tx/async_xor.c | |||
@@ -30,35 +30,51 @@ | |||
30 | #include <linux/raid/xor.h> | 30 | #include <linux/raid/xor.h> |
31 | #include <linux/async_tx.h> | 31 | #include <linux/async_tx.h> |
32 | 32 | ||
33 | static void | 33 | /* do_async_xor - dma map the pages and perform the xor with an engine. |
34 | do_async_xor(struct dma_async_tx_descriptor *tx, struct dma_device *device, | 34 | * This routine is marked __always_inline so it can be compiled away |
35 | * when CONFIG_DMA_ENGINE=n | ||
36 | */ | ||
37 | static __always_inline struct dma_async_tx_descriptor * | ||
38 | do_async_xor(struct dma_device *device, | ||
35 | struct dma_chan *chan, struct page *dest, struct page **src_list, | 39 | struct dma_chan *chan, struct page *dest, struct page **src_list, |
36 | unsigned int offset, unsigned int src_cnt, size_t len, | 40 | unsigned int offset, unsigned int src_cnt, size_t len, |
37 | enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, | 41 | enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, |
38 | dma_async_tx_callback cb_fn, void *cb_param) | 42 | dma_async_tx_callback cb_fn, void *cb_param) |
39 | { | 43 | { |
40 | dma_addr_t dma_addr; | 44 | dma_addr_t dma_dest; |
41 | enum dma_data_direction dir; | 45 | dma_addr_t *dma_src = (dma_addr_t *) src_list; |
46 | struct dma_async_tx_descriptor *tx; | ||
42 | int i; | 47 | int i; |
48 | unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0; | ||
43 | 49 | ||
44 | pr_debug("%s: len: %zu\n", __FUNCTION__, len); | 50 | pr_debug("%s: len: %zu\n", __FUNCTION__, len); |
45 | 51 | ||
46 | dir = (flags & ASYNC_TX_ASSUME_COHERENT) ? | 52 | dma_dest = dma_map_page(device->dev, dest, offset, len, |
47 | DMA_NONE : DMA_FROM_DEVICE; | 53 | DMA_FROM_DEVICE); |
48 | |||
49 | dma_addr = dma_map_page(device->dev, dest, offset, len, dir); | ||
50 | tx->tx_set_dest(dma_addr, tx, 0); | ||
51 | |||
52 | dir = (flags & ASYNC_TX_ASSUME_COHERENT) ? | ||
53 | DMA_NONE : DMA_TO_DEVICE; | ||
54 | 54 | ||
55 | for (i = 0; i < src_cnt; i++) { | 55 | for (i = 0; i < src_cnt; i++) |
56 | dma_addr = dma_map_page(device->dev, src_list[i], | 56 | dma_src[i] = dma_map_page(device->dev, src_list[i], offset, |
57 | offset, len, dir); | 57 | len, DMA_TO_DEVICE); |
58 | tx->tx_set_src(dma_addr, tx, i); | 58 | |
59 | /* Since we have clobbered the src_list we are committed | ||
60 | * to doing this asynchronously. Drivers force forward progress | ||
61 | * in case they can not provide a descriptor | ||
62 | */ | ||
63 | tx = device->device_prep_dma_xor(chan, dma_dest, dma_src, src_cnt, len, | ||
64 | dma_prep_flags); | ||
65 | if (!tx) { | ||
66 | if (depend_tx) | ||
67 | dma_wait_for_async_tx(depend_tx); | ||
68 | |||
69 | while (!tx) | ||
70 | tx = device->device_prep_dma_xor(chan, dma_dest, | ||
71 | dma_src, src_cnt, len, | ||
72 | dma_prep_flags); | ||
59 | } | 73 | } |
60 | 74 | ||
61 | async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); | 75 | async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); |
76 | |||
77 | return tx; | ||
62 | } | 78 | } |
63 | 79 | ||
64 | static void | 80 | static void |
@@ -102,7 +118,7 @@ do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset, | |||
102 | * @src_cnt: number of source pages | 118 | * @src_cnt: number of source pages |
103 | * @len: length in bytes | 119 | * @len: length in bytes |
104 | * @flags: ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DEST, | 120 | * @flags: ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DEST, |
105 | * ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK, ASYNC_TX_DEP_ACK | 121 | * ASYNC_TX_ACK, ASYNC_TX_DEP_ACK |
106 | * @depend_tx: xor depends on the result of this transaction. | 122 | * @depend_tx: xor depends on the result of this transaction. |
107 | * @cb_fn: function to call when the xor completes | 123 | * @cb_fn: function to call when the xor completes |
108 | * @cb_param: parameter to pass to the callback routine | 124 | * @cb_param: parameter to pass to the callback routine |
@@ -113,14 +129,16 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset, | |||
113 | struct dma_async_tx_descriptor *depend_tx, | 129 | struct dma_async_tx_descriptor *depend_tx, |
114 | dma_async_tx_callback cb_fn, void *cb_param) | 130 | dma_async_tx_callback cb_fn, void *cb_param) |
115 | { | 131 | { |
116 | struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_XOR); | 132 | struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_XOR, |
133 | &dest, 1, src_list, | ||
134 | src_cnt, len); | ||
117 | struct dma_device *device = chan ? chan->device : NULL; | 135 | struct dma_device *device = chan ? chan->device : NULL; |
118 | struct dma_async_tx_descriptor *tx = NULL; | 136 | struct dma_async_tx_descriptor *tx = NULL; |
119 | dma_async_tx_callback _cb_fn; | 137 | dma_async_tx_callback _cb_fn; |
120 | void *_cb_param; | 138 | void *_cb_param; |
121 | unsigned long local_flags; | 139 | unsigned long local_flags; |
122 | int xor_src_cnt; | 140 | int xor_src_cnt; |
123 | int i = 0, src_off = 0, int_en; | 141 | int i = 0, src_off = 0; |
124 | 142 | ||
125 | BUG_ON(src_cnt <= 1); | 143 | BUG_ON(src_cnt <= 1); |
126 | 144 | ||
@@ -140,20 +158,11 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset, | |||
140 | _cb_param = cb_param; | 158 | _cb_param = cb_param; |
141 | } | 159 | } |
142 | 160 | ||
143 | int_en = _cb_fn ? 1 : 0; | 161 | tx = do_async_xor(device, chan, dest, |
144 | 162 | &src_list[src_off], offset, | |
145 | tx = device->device_prep_dma_xor( | 163 | xor_src_cnt, len, local_flags, |
146 | chan, xor_src_cnt, len, int_en); | 164 | depend_tx, _cb_fn, _cb_param); |
147 | |||
148 | if (tx) { | ||
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, | ||
152 | _cb_param); | ||
153 | } else /* fall through */ | ||
154 | goto xor_sync; | ||
155 | } else { /* run the xor synchronously */ | 165 | } else { /* run the xor synchronously */ |
156 | xor_sync: | ||
157 | /* in the sync case the dest is an implied source | 166 | /* in the sync case the dest is an implied source |
158 | * (assumes the dest is at the src_off index) | 167 | * (assumes the dest is at the src_off index) |
159 | */ | 168 | */ |
@@ -242,7 +251,7 @@ static int page_is_zero(struct page *p, unsigned int offset, size_t len) | |||
242 | * @src_cnt: number of source pages | 251 | * @src_cnt: number of source pages |
243 | * @len: length in bytes | 252 | * @len: length in bytes |
244 | * @result: 0 if sum == 0 else non-zero | 253 | * @result: 0 if sum == 0 else non-zero |
245 | * @flags: ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK, ASYNC_TX_DEP_ACK | 254 | * @flags: ASYNC_TX_ACK, ASYNC_TX_DEP_ACK |
246 | * @depend_tx: xor depends on the result of this transaction. | 255 | * @depend_tx: xor depends on the result of this transaction. |
247 | * @cb_fn: function to call when the xor completes | 256 | * @cb_fn: function to call when the xor completes |
248 | * @cb_param: parameter to pass to the callback routine | 257 | * @cb_param: parameter to pass to the callback routine |
@@ -254,29 +263,36 @@ async_xor_zero_sum(struct page *dest, struct page **src_list, | |||
254 | struct dma_async_tx_descriptor *depend_tx, | 263 | struct dma_async_tx_descriptor *depend_tx, |
255 | dma_async_tx_callback cb_fn, void *cb_param) | 264 | dma_async_tx_callback cb_fn, void *cb_param) |
256 | { | 265 | { |
257 | struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_ZERO_SUM); | 266 | struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_ZERO_SUM, |
267 | &dest, 1, src_list, | ||
268 | src_cnt, len); | ||
258 | struct dma_device *device = chan ? chan->device : NULL; | 269 | struct dma_device *device = chan ? chan->device : NULL; |
259 | int int_en = cb_fn ? 1 : 0; | 270 | struct dma_async_tx_descriptor *tx = NULL; |
260 | struct dma_async_tx_descriptor *tx = device ? | ||
261 | device->device_prep_dma_zero_sum(chan, src_cnt, len, result, | ||
262 | int_en) : NULL; | ||
263 | int i; | ||
264 | 271 | ||
265 | BUG_ON(src_cnt <= 1); | 272 | BUG_ON(src_cnt <= 1); |
266 | 273 | ||
267 | if (tx) { | 274 | if (device) { |
268 | dma_addr_t dma_addr; | 275 | dma_addr_t *dma_src = (dma_addr_t *) src_list; |
269 | enum dma_data_direction dir; | 276 | unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0; |
277 | int i; | ||
270 | 278 | ||
271 | pr_debug("%s: (async) len: %zu\n", __FUNCTION__, len); | 279 | pr_debug("%s: (async) len: %zu\n", __FUNCTION__, len); |
272 | 280 | ||
273 | dir = (flags & ASYNC_TX_ASSUME_COHERENT) ? | 281 | for (i = 0; i < src_cnt; i++) |
274 | DMA_NONE : DMA_TO_DEVICE; | 282 | dma_src[i] = dma_map_page(device->dev, src_list[i], |
275 | 283 | offset, len, DMA_TO_DEVICE); | |
276 | for (i = 0; i < src_cnt; i++) { | 284 | |
277 | dma_addr = dma_map_page(device->dev, src_list[i], | 285 | tx = device->device_prep_dma_zero_sum(chan, dma_src, src_cnt, |
278 | offset, len, dir); | 286 | len, result, |
279 | tx->tx_set_src(dma_addr, tx, i); | 287 | dma_prep_flags); |
288 | if (!tx) { | ||
289 | if (depend_tx) | ||
290 | dma_wait_for_async_tx(depend_tx); | ||
291 | |||
292 | while (!tx) | ||
293 | tx = device->device_prep_dma_zero_sum(chan, | ||
294 | dma_src, src_cnt, len, result, | ||
295 | dma_prep_flags); | ||
280 | } | 296 | } |
281 | 297 | ||
282 | async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); | 298 | async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); |
@@ -311,6 +327,16 @@ EXPORT_SYMBOL_GPL(async_xor_zero_sum); | |||
311 | 327 | ||
312 | static int __init async_xor_init(void) | 328 | static int __init async_xor_init(void) |
313 | { | 329 | { |
330 | #ifdef CONFIG_DMA_ENGINE | ||
331 | /* To conserve stack space the input src_list (array of page pointers) | ||
332 | * is reused to hold the array of dma addresses passed to the driver. | ||
333 | * This conversion is only possible when dma_addr_t is less than the | ||
334 | * the size of a pointer. HIGHMEM64G is known to violate this | ||
335 | * assumption. | ||
336 | */ | ||
337 | BUILD_BUG_ON(sizeof(dma_addr_t) > sizeof(struct page *)); | ||
338 | #endif | ||
339 | |||
314 | return 0; | 340 | return 0; |
315 | } | 341 | } |
316 | 342 | ||