0001
0002
0003
0004
0005
0006 #include <linux/kernel.h>
0007 #include <linux/interrupt.h>
0008 #include <linux/module.h>
0009 #include <linux/dma-mapping.h>
0010 #include <linux/raid/pq.h>
0011 #include <linux/async_tx.h>
0012 #include <linux/gfp.h>
0013
0014
0015
0016
0017
0018 static struct page *pq_scribble_page;
0019
0020
0021
0022
0023
0024
0025
0026 #define P(b, d) (b[d-2])
0027 #define Q(b, d) (b[d-1])
0028
0029 #define MAX_DISKS 255
0030
0031
0032
0033
0034 static __async_inline struct dma_async_tx_descriptor *
0035 do_async_gen_syndrome(struct dma_chan *chan,
0036 const unsigned char *scfs, int disks,
0037 struct dmaengine_unmap_data *unmap,
0038 enum dma_ctrl_flags dma_flags,
0039 struct async_submit_ctl *submit)
0040 {
0041 struct dma_async_tx_descriptor *tx = NULL;
0042 struct dma_device *dma = chan->device;
0043 enum async_tx_flags flags_orig = submit->flags;
0044 dma_async_tx_callback cb_fn_orig = submit->cb_fn;
0045 dma_async_tx_callback cb_param_orig = submit->cb_param;
0046 int src_cnt = disks - 2;
0047 unsigned short pq_src_cnt;
0048 dma_addr_t dma_dest[2];
0049 int src_off = 0;
0050
0051 while (src_cnt > 0) {
0052 submit->flags = flags_orig;
0053 pq_src_cnt = min(src_cnt, dma_maxpq(dma, dma_flags));
0054
0055
0056
0057
0058 if (src_cnt > pq_src_cnt) {
0059 submit->flags &= ~ASYNC_TX_ACK;
0060 submit->flags |= ASYNC_TX_FENCE;
0061 submit->cb_fn = NULL;
0062 submit->cb_param = NULL;
0063 } else {
0064 submit->cb_fn = cb_fn_orig;
0065 submit->cb_param = cb_param_orig;
0066 if (cb_fn_orig)
0067 dma_flags |= DMA_PREP_INTERRUPT;
0068 }
0069 if (submit->flags & ASYNC_TX_FENCE)
0070 dma_flags |= DMA_PREP_FENCE;
0071
0072
0073
0074
0075 for (;;) {
0076 dma_dest[0] = unmap->addr[disks - 2];
0077 dma_dest[1] = unmap->addr[disks - 1];
0078 tx = dma->device_prep_dma_pq(chan, dma_dest,
0079 &unmap->addr[src_off],
0080 pq_src_cnt,
0081 &scfs[src_off], unmap->len,
0082 dma_flags);
0083 if (likely(tx))
0084 break;
0085 async_tx_quiesce(&submit->depend_tx);
0086 dma_async_issue_pending(chan);
0087 }
0088
0089 dma_set_unmap(tx, unmap);
0090 async_tx_submit(chan, tx, submit);
0091 submit->depend_tx = tx;
0092
0093
0094 src_cnt -= pq_src_cnt;
0095 src_off += pq_src_cnt;
0096
0097 dma_flags |= DMA_PREP_CONTINUE;
0098 }
0099
0100 return tx;
0101 }
0102
0103
0104
0105
0106 static void
0107 do_sync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks,
0108 size_t len, struct async_submit_ctl *submit)
0109 {
0110 void **srcs;
0111 int i;
0112 int start = -1, stop = disks - 3;
0113
0114 if (submit->scribble)
0115 srcs = submit->scribble;
0116 else
0117 srcs = (void **) blocks;
0118
0119 for (i = 0; i < disks; i++) {
0120 if (blocks[i] == NULL) {
0121 BUG_ON(i > disks - 3);
0122 srcs[i] = (void*)raid6_empty_zero_page;
0123 } else {
0124 srcs[i] = page_address(blocks[i]) + offsets[i];
0125
0126 if (i < disks - 2) {
0127 stop = i;
0128 if (start == -1)
0129 start = i;
0130 }
0131 }
0132 }
0133 if (submit->flags & ASYNC_TX_PQ_XOR_DST) {
0134 BUG_ON(!raid6_call.xor_syndrome);
0135 if (start >= 0)
0136 raid6_call.xor_syndrome(disks, start, stop, len, srcs);
0137 } else
0138 raid6_call.gen_syndrome(disks, len, srcs);
0139 async_tx_sync_epilog(submit);
0140 }
0141
0142 static inline bool
0143 is_dma_pq_aligned_offs(struct dma_device *dev, unsigned int *offs,
0144 int src_cnt, size_t len)
0145 {
0146 int i;
0147
0148 for (i = 0; i < src_cnt; i++) {
0149 if (!is_dma_pq_aligned(dev, offs[i], 0, len))
0150 return false;
0151 }
0152 return true;
0153 }
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176 struct dma_async_tx_descriptor *
0177 async_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks,
0178 size_t len, struct async_submit_ctl *submit)
0179 {
0180 int src_cnt = disks - 2;
0181 struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ,
0182 &P(blocks, disks), 2,
0183 blocks, src_cnt, len);
0184 struct dma_device *device = chan ? chan->device : NULL;
0185 struct dmaengine_unmap_data *unmap = NULL;
0186
0187 BUG_ON(disks > MAX_DISKS || !(P(blocks, disks) || Q(blocks, disks)));
0188
0189 if (device)
0190 unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOWAIT);
0191
0192
0193 if (unmap && !(submit->flags & ASYNC_TX_PQ_XOR_DST) &&
0194 (src_cnt <= dma_maxpq(device, 0) ||
0195 dma_maxpq(device, DMA_PREP_CONTINUE) > 0) &&
0196 is_dma_pq_aligned_offs(device, offsets, disks, len)) {
0197 struct dma_async_tx_descriptor *tx;
0198 enum dma_ctrl_flags dma_flags = 0;
0199 unsigned char coefs[MAX_DISKS];
0200 int i, j;
0201
0202
0203 pr_debug("%s: (async) disks: %d len: %zu\n",
0204 __func__, disks, len);
0205
0206
0207
0208
0209 unmap->len = len;
0210 for (i = 0, j = 0; i < src_cnt; i++) {
0211 if (blocks[i] == NULL)
0212 continue;
0213 unmap->addr[j] = dma_map_page(device->dev, blocks[i],
0214 offsets[i], len, DMA_TO_DEVICE);
0215 coefs[j] = raid6_gfexp[i];
0216 unmap->to_cnt++;
0217 j++;
0218 }
0219
0220
0221
0222
0223
0224 unmap->bidi_cnt++;
0225 if (P(blocks, disks))
0226 unmap->addr[j++] = dma_map_page(device->dev, P(blocks, disks),
0227 P(offsets, disks),
0228 len, DMA_BIDIRECTIONAL);
0229 else {
0230 unmap->addr[j++] = 0;
0231 dma_flags |= DMA_PREP_PQ_DISABLE_P;
0232 }
0233
0234 unmap->bidi_cnt++;
0235 if (Q(blocks, disks))
0236 unmap->addr[j++] = dma_map_page(device->dev, Q(blocks, disks),
0237 Q(offsets, disks),
0238 len, DMA_BIDIRECTIONAL);
0239 else {
0240 unmap->addr[j++] = 0;
0241 dma_flags |= DMA_PREP_PQ_DISABLE_Q;
0242 }
0243
0244 tx = do_async_gen_syndrome(chan, coefs, j, unmap, dma_flags, submit);
0245 dmaengine_unmap_put(unmap);
0246 return tx;
0247 }
0248
0249 dmaengine_unmap_put(unmap);
0250
0251
0252 pr_debug("%s: (sync) disks: %d len: %zu\n", __func__, disks, len);
0253
0254
0255 async_tx_quiesce(&submit->depend_tx);
0256
0257 if (!P(blocks, disks)) {
0258 P(blocks, disks) = pq_scribble_page;
0259 P(offsets, disks) = 0;
0260 }
0261 if (!Q(blocks, disks)) {
0262 Q(blocks, disks) = pq_scribble_page;
0263 Q(offsets, disks) = 0;
0264 }
0265 do_sync_gen_syndrome(blocks, offsets, disks, len, submit);
0266
0267 return NULL;
0268 }
0269 EXPORT_SYMBOL_GPL(async_gen_syndrome);
0270
0271 static inline struct dma_chan *
0272 pq_val_chan(struct async_submit_ctl *submit, struct page **blocks, int disks, size_t len)
0273 {
0274 #ifdef CONFIG_ASYNC_TX_DISABLE_PQ_VAL_DMA
0275 return NULL;
0276 #endif
0277 return async_tx_find_channel(submit, DMA_PQ_VAL, NULL, 0, blocks,
0278 disks, len);
0279 }
0280
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297 struct dma_async_tx_descriptor *
0298 async_syndrome_val(struct page **blocks, unsigned int *offsets, int disks,
0299 size_t len, enum sum_check_flags *pqres, struct page *spare,
0300 unsigned int s_off, struct async_submit_ctl *submit)
0301 {
0302 struct dma_chan *chan = pq_val_chan(submit, blocks, disks, len);
0303 struct dma_device *device = chan ? chan->device : NULL;
0304 struct dma_async_tx_descriptor *tx;
0305 unsigned char coefs[MAX_DISKS];
0306 enum dma_ctrl_flags dma_flags = submit->cb_fn ? DMA_PREP_INTERRUPT : 0;
0307 struct dmaengine_unmap_data *unmap = NULL;
0308
0309 BUG_ON(disks < 4 || disks > MAX_DISKS);
0310
0311 if (device)
0312 unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOWAIT);
0313
0314 if (unmap && disks <= dma_maxpq(device, 0) &&
0315 is_dma_pq_aligned_offs(device, offsets, disks, len)) {
0316 struct device *dev = device->dev;
0317 dma_addr_t pq[2];
0318 int i, j = 0, src_cnt = 0;
0319
0320 pr_debug("%s: (async) disks: %d len: %zu\n",
0321 __func__, disks, len);
0322
0323 unmap->len = len;
0324 for (i = 0; i < disks-2; i++)
0325 if (likely(blocks[i])) {
0326 unmap->addr[j] = dma_map_page(dev, blocks[i],
0327 offsets[i], len,
0328 DMA_TO_DEVICE);
0329 coefs[j] = raid6_gfexp[i];
0330 unmap->to_cnt++;
0331 src_cnt++;
0332 j++;
0333 }
0334
0335 if (!P(blocks, disks)) {
0336 pq[0] = 0;
0337 dma_flags |= DMA_PREP_PQ_DISABLE_P;
0338 } else {
0339 pq[0] = dma_map_page(dev, P(blocks, disks),
0340 P(offsets, disks), len,
0341 DMA_TO_DEVICE);
0342 unmap->addr[j++] = pq[0];
0343 unmap->to_cnt++;
0344 }
0345 if (!Q(blocks, disks)) {
0346 pq[1] = 0;
0347 dma_flags |= DMA_PREP_PQ_DISABLE_Q;
0348 } else {
0349 pq[1] = dma_map_page(dev, Q(blocks, disks),
0350 Q(offsets, disks), len,
0351 DMA_TO_DEVICE);
0352 unmap->addr[j++] = pq[1];
0353 unmap->to_cnt++;
0354 }
0355
0356 if (submit->flags & ASYNC_TX_FENCE)
0357 dma_flags |= DMA_PREP_FENCE;
0358 for (;;) {
0359 tx = device->device_prep_dma_pq_val(chan, pq,
0360 unmap->addr,
0361 src_cnt,
0362 coefs,
0363 len, pqres,
0364 dma_flags);
0365 if (likely(tx))
0366 break;
0367 async_tx_quiesce(&submit->depend_tx);
0368 dma_async_issue_pending(chan);
0369 }
0370
0371 dma_set_unmap(tx, unmap);
0372 async_tx_submit(chan, tx, submit);
0373 } else {
0374 struct page *p_src = P(blocks, disks);
0375 unsigned int p_off = P(offsets, disks);
0376 struct page *q_src = Q(blocks, disks);
0377 unsigned int q_off = Q(offsets, disks);
0378 enum async_tx_flags flags_orig = submit->flags;
0379 dma_async_tx_callback cb_fn_orig = submit->cb_fn;
0380 void *scribble = submit->scribble;
0381 void *cb_param_orig = submit->cb_param;
0382 void *p, *q, *s;
0383
0384 pr_debug("%s: (sync) disks: %d len: %zu\n",
0385 __func__, disks, len);
0386
0387
0388
0389
0390 BUG_ON(!spare || !scribble);
0391
0392
0393 async_tx_quiesce(&submit->depend_tx);
0394
0395
0396
0397
0398 tx = NULL;
0399 *pqres = 0;
0400 if (p_src) {
0401 init_async_submit(submit, ASYNC_TX_XOR_ZERO_DST, NULL,
0402 NULL, NULL, scribble);
0403 tx = async_xor_offs(spare, s_off,
0404 blocks, offsets, disks-2, len, submit);
0405 async_tx_quiesce(&tx);
0406 p = page_address(p_src) + p_off;
0407 s = page_address(spare) + s_off;
0408 *pqres |= !!memcmp(p, s, len) << SUM_CHECK_P;
0409 }
0410
0411 if (q_src) {
0412 P(blocks, disks) = NULL;
0413 Q(blocks, disks) = spare;
0414 Q(offsets, disks) = s_off;
0415 init_async_submit(submit, 0, NULL, NULL, NULL, scribble);
0416 tx = async_gen_syndrome(blocks, offsets, disks,
0417 len, submit);
0418 async_tx_quiesce(&tx);
0419 q = page_address(q_src) + q_off;
0420 s = page_address(spare) + s_off;
0421 *pqres |= !!memcmp(q, s, len) << SUM_CHECK_Q;
0422 }
0423
0424
0425 P(blocks, disks) = p_src;
0426 P(offsets, disks) = p_off;
0427 Q(blocks, disks) = q_src;
0428 Q(offsets, disks) = q_off;
0429
0430 submit->cb_fn = cb_fn_orig;
0431 submit->cb_param = cb_param_orig;
0432 submit->flags = flags_orig;
0433 async_tx_sync_epilog(submit);
0434 tx = NULL;
0435 }
0436 dmaengine_unmap_put(unmap);
0437
0438 return tx;
0439 }
0440 EXPORT_SYMBOL_GPL(async_syndrome_val);
0441
0442 static int __init async_pq_init(void)
0443 {
0444 pq_scribble_page = alloc_page(GFP_KERNEL);
0445
0446 if (pq_scribble_page)
0447 return 0;
0448
0449 pr_err("%s: failed to allocate required spare page\n", __func__);
0450
0451 return -ENOMEM;
0452 }
0453
0454 static void __exit async_pq_exit(void)
0455 {
0456 __free_page(pq_scribble_page);
0457 }
0458
0459 module_init(async_pq_init);
0460 module_exit(async_pq_exit);
0461
0462 MODULE_DESCRIPTION("asynchronous raid6 syndrome generation/validation");
0463 MODULE_LICENSE("GPL");