diff options
| -rw-r--r-- | crypto/async_tx/async_xor.c | 244 |
1 files changed, 113 insertions, 131 deletions
diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c index 3a0dddca5a10..1fcf45ac81ec 100644 --- a/crypto/async_tx/async_xor.c +++ b/crypto/async_tx/async_xor.c | |||
| @@ -35,72 +35,118 @@ | |||
| 35 | * when CONFIG_DMA_ENGINE=n | 35 | * when CONFIG_DMA_ENGINE=n |
| 36 | */ | 36 | */ |
| 37 | static __always_inline struct dma_async_tx_descriptor * | 37 | static __always_inline struct dma_async_tx_descriptor * |
| 38 | do_async_xor(struct dma_device *device, | 38 | do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list, |
| 39 | struct dma_chan *chan, struct page *dest, struct page **src_list, | 39 | unsigned int offset, int src_cnt, size_t len, |
| 40 | unsigned int offset, unsigned int src_cnt, size_t len, | 40 | enum async_tx_flags flags, |
| 41 | enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, | 41 | struct dma_async_tx_descriptor *depend_tx, |
| 42 | dma_async_tx_callback cb_fn, void *cb_param) | 42 | dma_async_tx_callback cb_fn, void *cb_param) |
| 43 | { | 43 | { |
| 44 | dma_addr_t dma_dest; | 44 | struct dma_device *dma = chan->device; |
| 45 | dma_addr_t *dma_src = (dma_addr_t *) src_list; | 45 | dma_addr_t *dma_src = (dma_addr_t *) src_list; |
| 46 | struct dma_async_tx_descriptor *tx; | 46 | struct dma_async_tx_descriptor *tx = NULL; |
| 47 | int src_off = 0; | ||
| 47 | int i; | 48 | int i; |
| 48 | unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0; | 49 | dma_async_tx_callback _cb_fn; |
| 49 | 50 | void *_cb_param; | |
| 50 | pr_debug("%s: len: %zu\n", __func__, len); | 51 | enum async_tx_flags async_flags; |
| 51 | 52 | enum dma_ctrl_flags dma_flags; | |
| 52 | dma_dest = dma_map_page(device->dev, dest, offset, len, | 53 | int xor_src_cnt; |
| 53 | DMA_FROM_DEVICE); | 54 | dma_addr_t dma_dest; |
| 54 | 55 | ||
| 56 | dma_dest = dma_map_page(dma->dev, dest, offset, len, DMA_FROM_DEVICE); | ||
| 55 | for (i = 0; i < src_cnt; i++) | 57 | for (i = 0; i < src_cnt; i++) |
| 56 | dma_src[i] = dma_map_page(device->dev, src_list[i], offset, | 58 | dma_src[i] = dma_map_page(dma->dev, src_list[i], offset, |
| 57 | len, DMA_TO_DEVICE); | 59 | len, DMA_TO_DEVICE); |
| 58 | 60 | ||
| 59 | /* Since we have clobbered the src_list we are committed | 61 | while (src_cnt) { |
| 60 | * to doing this asynchronously. Drivers force forward progress | 62 | async_flags = flags; |
| 61 | * in case they can not provide a descriptor | 63 | dma_flags = 0; |
| 62 | */ | 64 | xor_src_cnt = min(src_cnt, dma->max_xor); |
| 63 | tx = device->device_prep_dma_xor(chan, dma_dest, dma_src, src_cnt, len, | 65 | /* if we are submitting additional xors, leave the chain open, |
| 64 | dma_prep_flags); | 66 | * clear the callback parameters, and leave the destination |
| 65 | if (!tx) { | 67 | * buffer mapped |
| 66 | if (depend_tx) | 68 | */ |
| 69 | if (src_cnt > xor_src_cnt) { | ||
| 70 | async_flags &= ~ASYNC_TX_ACK; | ||
| 71 | dma_flags = DMA_COMPL_SKIP_DEST_UNMAP; | ||
| 72 | _cb_fn = NULL; | ||
| 73 | _cb_param = NULL; | ||
| 74 | } else { | ||
| 75 | _cb_fn = cb_fn; | ||
| 76 | _cb_param = cb_param; | ||
| 77 | } | ||
| 78 | if (_cb_fn) | ||
| 79 | dma_flags |= DMA_PREP_INTERRUPT; | ||
| 80 | |||
| 81 | /* Since we have clobbered the src_list we are committed | ||
| 82 | * to doing this asynchronously. Drivers force forward progress | ||
| 83 | * in case they can not provide a descriptor | ||
| 84 | */ | ||
| 85 | tx = dma->device_prep_dma_xor(chan, dma_dest, &dma_src[src_off], | ||
| 86 | xor_src_cnt, len, dma_flags); | ||
| 87 | |||
| 88 | if (unlikely(!tx && depend_tx)) | ||
| 67 | dma_wait_for_async_tx(depend_tx); | 89 | dma_wait_for_async_tx(depend_tx); |
| 68 | 90 | ||
| 69 | while (!tx) | 91 | /* spin wait for the preceeding transactions to complete */ |
| 70 | tx = device->device_prep_dma_xor(chan, dma_dest, | 92 | while (unlikely(!tx)) |
| 71 | dma_src, src_cnt, len, | 93 | tx = dma->device_prep_dma_xor(chan, dma_dest, |
| 72 | dma_prep_flags); | 94 | &dma_src[src_off], |
| 73 | } | 95 | xor_src_cnt, len, |
| 96 | dma_flags); | ||
| 97 | |||
| 98 | async_tx_submit(chan, tx, async_flags, depend_tx, _cb_fn, | ||
| 99 | _cb_param); | ||
| 100 | |||
| 101 | depend_tx = tx; | ||
| 102 | flags |= ASYNC_TX_DEP_ACK; | ||
| 103 | |||
| 104 | if (src_cnt > xor_src_cnt) { | ||
| 105 | /* drop completed sources */ | ||
| 106 | src_cnt -= xor_src_cnt; | ||
| 107 | src_off += xor_src_cnt; | ||
| 74 | 108 | ||
| 75 | async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); | 109 | /* use the intermediate result a source */ |
| 110 | dma_src[--src_off] = dma_dest; | ||
| 111 | src_cnt++; | ||
| 112 | } else | ||
| 113 | break; | ||
| 114 | } | ||
| 76 | 115 | ||
| 77 | return tx; | 116 | return tx; |
| 78 | } | 117 | } |
| 79 | 118 | ||
| 80 | static void | 119 | static void |
| 81 | do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset, | 120 | do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset, |
| 82 | unsigned int src_cnt, size_t len, enum async_tx_flags flags, | 121 | int src_cnt, size_t len, enum async_tx_flags flags, |
| 83 | struct dma_async_tx_descriptor *depend_tx, | 122 | struct dma_async_tx_descriptor *depend_tx, |
| 84 | dma_async_tx_callback cb_fn, void *cb_param) | 123 | dma_async_tx_callback cb_fn, void *cb_param) |
| 85 | { | 124 | { |
| 86 | void *_dest; | ||
| 87 | int i; | 125 | int i; |
| 88 | 126 | int xor_src_cnt; | |
| 89 | pr_debug("%s: len: %zu\n", __func__, len); | 127 | int src_off = 0; |
| 128 | void *dest_buf; | ||
| 129 | void **srcs = (void **) src_list; | ||
| 90 | 130 | ||
| 91 | /* reuse the 'src_list' array to convert to buffer pointers */ | 131 | /* reuse the 'src_list' array to convert to buffer pointers */ |
| 92 | for (i = 0; i < src_cnt; i++) | 132 | for (i = 0; i < src_cnt; i++) |
| 93 | src_list[i] = (struct page *) | 133 | srcs[i] = page_address(src_list[i]) + offset; |
| 94 | (page_address(src_list[i]) + offset); | ||
| 95 | 134 | ||
| 96 | /* set destination address */ | 135 | /* set destination address */ |
| 97 | _dest = page_address(dest) + offset; | 136 | dest_buf = page_address(dest) + offset; |
| 98 | 137 | ||
| 99 | if (flags & ASYNC_TX_XOR_ZERO_DST) | 138 | if (flags & ASYNC_TX_XOR_ZERO_DST) |
| 100 | memset(_dest, 0, len); | 139 | memset(dest_buf, 0, len); |
| 140 | |||
| 141 | while (src_cnt > 0) { | ||
| 142 | /* process up to 'MAX_XOR_BLOCKS' sources */ | ||
| 143 | xor_src_cnt = min(src_cnt, MAX_XOR_BLOCKS); | ||
| 144 | xor_blocks(xor_src_cnt, len, dest_buf, &srcs[src_off]); | ||
| 101 | 145 | ||
| 102 | xor_blocks(src_cnt, len, _dest, | 146 | /* drop completed sources */ |
| 103 | (void **) src_list); | 147 | src_cnt -= xor_src_cnt; |
| 148 | src_off += xor_src_cnt; | ||
| 149 | } | ||
| 104 | 150 | ||
| 105 | async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); | 151 | async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); |
| 106 | } | 152 | } |
| @@ -132,106 +178,42 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset, | |||
| 132 | struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_XOR, | 178 | struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_XOR, |
| 133 | &dest, 1, src_list, | 179 | &dest, 1, src_list, |
| 134 | src_cnt, len); | 180 | src_cnt, len); |
| 135 | struct dma_device *device = chan ? chan->device : NULL; | ||
| 136 | struct dma_async_tx_descriptor *tx = NULL; | ||
| 137 | dma_async_tx_callback _cb_fn; | ||
| 138 | void *_cb_param; | ||
| 139 | unsigned long local_flags; | ||
| 140 | int xor_src_cnt; | ||
| 141 | int i = 0, src_off = 0; | ||
| 142 | |||
| 143 | BUG_ON(src_cnt <= 1); | 181 | BUG_ON(src_cnt <= 1); |
| 144 | 182 | ||
| 145 | while (src_cnt) { | 183 | if (chan) { |
| 146 | local_flags = flags; | 184 | /* run the xor asynchronously */ |
| 147 | if (device) { /* run the xor asynchronously */ | 185 | pr_debug("%s (async): len: %zu\n", __func__, len); |
| 148 | xor_src_cnt = min(src_cnt, device->max_xor); | ||
| 149 | /* if we are submitting additional xors | ||
| 150 | * only set the callback on the last transaction | ||
| 151 | */ | ||
| 152 | if (src_cnt > xor_src_cnt) { | ||
| 153 | local_flags &= ~ASYNC_TX_ACK; | ||
| 154 | _cb_fn = NULL; | ||
| 155 | _cb_param = NULL; | ||
| 156 | } else { | ||
| 157 | _cb_fn = cb_fn; | ||
| 158 | _cb_param = cb_param; | ||
| 159 | } | ||
| 160 | |||
| 161 | tx = do_async_xor(device, chan, dest, | ||
| 162 | &src_list[src_off], offset, | ||
| 163 | xor_src_cnt, len, local_flags, | ||
| 164 | depend_tx, _cb_fn, _cb_param); | ||
| 165 | } else { /* run the xor synchronously */ | ||
| 166 | /* in the sync case the dest is an implied source | ||
| 167 | * (assumes the dest is at the src_off index) | ||
| 168 | */ | ||
| 169 | if (flags & ASYNC_TX_XOR_DROP_DST) { | ||
| 170 | src_cnt--; | ||
| 171 | src_off++; | ||
| 172 | } | ||
| 173 | |||
| 174 | /* process up to 'MAX_XOR_BLOCKS' sources */ | ||
| 175 | xor_src_cnt = min(src_cnt, MAX_XOR_BLOCKS); | ||
| 176 | 186 | ||
| 177 | /* if we are submitting additional xors | 187 | return do_async_xor(chan, dest, src_list, offset, src_cnt, len, |
| 178 | * only set the callback on the last transaction | 188 | flags, depend_tx, cb_fn, cb_param); |
| 179 | */ | 189 | } else { |
| 180 | if (src_cnt > xor_src_cnt) { | 190 | /* run the xor synchronously */ |
| 181 | local_flags &= ~ASYNC_TX_ACK; | 191 | pr_debug("%s (sync): len: %zu\n", __func__, len); |
| 182 | _cb_fn = NULL; | ||
| 183 | _cb_param = NULL; | ||
| 184 | } else { | ||
| 185 | _cb_fn = cb_fn; | ||
| 186 | _cb_param = cb_param; | ||
| 187 | } | ||
| 188 | |||
| 189 | /* wait for any prerequisite operations */ | ||
| 190 | if (depend_tx) { | ||
| 191 | /* if ack is already set then we cannot be sure | ||
| 192 | * we are referring to the correct operation | ||
| 193 | */ | ||
| 194 | BUG_ON(async_tx_test_ack(depend_tx)); | ||
| 195 | if (dma_wait_for_async_tx(depend_tx) == | ||
| 196 | DMA_ERROR) | ||
| 197 | panic("%s: DMA_ERROR waiting for " | ||
| 198 | "depend_tx\n", | ||
| 199 | __func__); | ||
| 200 | } | ||
| 201 | |||
| 202 | do_sync_xor(dest, &src_list[src_off], offset, | ||
| 203 | xor_src_cnt, len, local_flags, depend_tx, | ||
| 204 | _cb_fn, _cb_param); | ||
| 205 | } | ||
| 206 | 192 | ||
| 207 | /* the previous tx is hidden from the client, | 193 | /* in the sync case the dest is an implied source |
| 208 | * so ack it | 194 | * (assumes the dest is the first source) |
| 209 | */ | 195 | */ |
| 210 | if (i && depend_tx) | 196 | if (flags & ASYNC_TX_XOR_DROP_DST) { |
| 211 | async_tx_ack(depend_tx); | 197 | src_cnt--; |
| 212 | 198 | src_list++; | |
| 213 | depend_tx = tx; | 199 | } |
| 214 | 200 | ||
| 215 | if (src_cnt > xor_src_cnt) { | 201 | /* wait for any prerequisite operations */ |
| 216 | /* drop completed sources */ | 202 | if (depend_tx) { |
| 217 | src_cnt -= xor_src_cnt; | 203 | /* if ack is already set then we cannot be sure |
| 218 | src_off += xor_src_cnt; | 204 | * we are referring to the correct operation |
| 205 | */ | ||
| 206 | BUG_ON(async_tx_test_ack(depend_tx)); | ||
| 207 | if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) | ||
| 208 | panic("%s: DMA_ERROR waiting for depend_tx\n", | ||
| 209 | __func__); | ||
| 210 | } | ||
| 219 | 211 | ||
| 220 | /* unconditionally preserve the destination */ | 212 | do_sync_xor(dest, src_list, offset, src_cnt, len, |
| 221 | flags &= ~ASYNC_TX_XOR_ZERO_DST; | 213 | flags, depend_tx, cb_fn, cb_param); |
| 222 | 214 | ||
| 223 | /* use the intermediate result a source, but remember | 215 | return NULL; |
| 224 | * it's dropped, because it's implied, in the sync case | ||
| 225 | */ | ||
| 226 | src_list[--src_off] = dest; | ||
| 227 | src_cnt++; | ||
| 228 | flags |= ASYNC_TX_XOR_DROP_DST; | ||
| 229 | } else | ||
| 230 | src_cnt = 0; | ||
| 231 | i++; | ||
| 232 | } | 216 | } |
| 233 | |||
| 234 | return tx; | ||
| 235 | } | 217 | } |
| 236 | EXPORT_SYMBOL_GPL(async_xor); | 218 | EXPORT_SYMBOL_GPL(async_xor); |
| 237 | 219 | ||
