1*0a82a623SDan Williams /* 2*0a82a623SDan Williams * Asynchronous RAID-6 recovery calculations ASYNC_TX API. 3*0a82a623SDan Williams * Copyright(c) 2009 Intel Corporation 4*0a82a623SDan Williams * 5*0a82a623SDan Williams * based on raid6recov.c: 6*0a82a623SDan Williams * Copyright 2002 H. Peter Anvin 7*0a82a623SDan Williams * 8*0a82a623SDan Williams * This program is free software; you can redistribute it and/or modify it 9*0a82a623SDan Williams * under the terms of the GNU General Public License as published by the Free 10*0a82a623SDan Williams * Software Foundation; either version 2 of the License, or (at your option) 11*0a82a623SDan Williams * any later version. 12*0a82a623SDan Williams * 13*0a82a623SDan Williams * This program is distributed in the hope that it will be useful, but WITHOUT 14*0a82a623SDan Williams * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15*0a82a623SDan Williams * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16*0a82a623SDan Williams * more details. 17*0a82a623SDan Williams * 18*0a82a623SDan Williams * You should have received a copy of the GNU General Public License along with 19*0a82a623SDan Williams * this program; if not, write to the Free Software Foundation, Inc., 51 20*0a82a623SDan Williams * Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 21*0a82a623SDan Williams * 22*0a82a623SDan Williams */ 23*0a82a623SDan Williams #include <linux/kernel.h> 24*0a82a623SDan Williams #include <linux/interrupt.h> 25*0a82a623SDan Williams #include <linux/dma-mapping.h> 26*0a82a623SDan Williams #include <linux/raid/pq.h> 27*0a82a623SDan Williams #include <linux/async_tx.h> 28*0a82a623SDan Williams 29*0a82a623SDan Williams static struct dma_async_tx_descriptor * 30*0a82a623SDan Williams async_sum_product(struct page *dest, struct page **srcs, unsigned char *coef, 31*0a82a623SDan Williams size_t len, struct async_submit_ctl *submit) 32*0a82a623SDan Williams { 33*0a82a623SDan Williams struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ, 34*0a82a623SDan Williams &dest, 1, srcs, 2, len); 35*0a82a623SDan Williams struct dma_device *dma = chan ? chan->device : NULL; 36*0a82a623SDan Williams const u8 *amul, *bmul; 37*0a82a623SDan Williams u8 ax, bx; 38*0a82a623SDan Williams u8 *a, *b, *c; 39*0a82a623SDan Williams 40*0a82a623SDan Williams if (dma) { 41*0a82a623SDan Williams dma_addr_t dma_dest[2]; 42*0a82a623SDan Williams dma_addr_t dma_src[2]; 43*0a82a623SDan Williams struct device *dev = dma->dev; 44*0a82a623SDan Williams struct dma_async_tx_descriptor *tx; 45*0a82a623SDan Williams enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P; 46*0a82a623SDan Williams 47*0a82a623SDan Williams dma_dest[1] = dma_map_page(dev, dest, 0, len, DMA_BIDIRECTIONAL); 48*0a82a623SDan Williams dma_src[0] = dma_map_page(dev, srcs[0], 0, len, DMA_TO_DEVICE); 49*0a82a623SDan Williams dma_src[1] = dma_map_page(dev, srcs[1], 0, len, DMA_TO_DEVICE); 50*0a82a623SDan Williams tx = dma->device_prep_dma_pq(chan, dma_dest, dma_src, 2, coef, 51*0a82a623SDan Williams len, dma_flags); 52*0a82a623SDan Williams if (tx) { 53*0a82a623SDan Williams async_tx_submit(chan, tx, submit); 54*0a82a623SDan Williams return tx; 55*0a82a623SDan Williams } 56*0a82a623SDan Williams } 57*0a82a623SDan Williams 58*0a82a623SDan Williams /* run the operation synchronously */ 59*0a82a623SDan Williams async_tx_quiesce(&submit->depend_tx); 60*0a82a623SDan Williams amul = raid6_gfmul[coef[0]]; 61*0a82a623SDan Williams bmul = raid6_gfmul[coef[1]]; 62*0a82a623SDan Williams a = page_address(srcs[0]); 63*0a82a623SDan Williams b = page_address(srcs[1]); 64*0a82a623SDan Williams c = page_address(dest); 65*0a82a623SDan Williams 66*0a82a623SDan Williams while (len--) { 67*0a82a623SDan Williams ax = amul[*a++]; 68*0a82a623SDan Williams bx = bmul[*b++]; 69*0a82a623SDan Williams *c++ = ax ^ bx; 70*0a82a623SDan Williams } 71*0a82a623SDan Williams 72*0a82a623SDan Williams return NULL; 73*0a82a623SDan Williams } 74*0a82a623SDan Williams 75*0a82a623SDan Williams static struct dma_async_tx_descriptor * 76*0a82a623SDan Williams async_mult(struct page *dest, struct page *src, u8 coef, size_t len, 77*0a82a623SDan Williams struct async_submit_ctl *submit) 78*0a82a623SDan Williams { 79*0a82a623SDan Williams struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ, 80*0a82a623SDan Williams &dest, 1, &src, 1, len); 81*0a82a623SDan Williams struct dma_device *dma = chan ? chan->device : NULL; 82*0a82a623SDan Williams const u8 *qmul; /* Q multiplier table */ 83*0a82a623SDan Williams u8 *d, *s; 84*0a82a623SDan Williams 85*0a82a623SDan Williams if (dma) { 86*0a82a623SDan Williams dma_addr_t dma_dest[2]; 87*0a82a623SDan Williams dma_addr_t dma_src[1]; 88*0a82a623SDan Williams struct device *dev = dma->dev; 89*0a82a623SDan Williams struct dma_async_tx_descriptor *tx; 90*0a82a623SDan Williams enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P; 91*0a82a623SDan Williams 92*0a82a623SDan Williams dma_dest[1] = dma_map_page(dev, dest, 0, len, DMA_BIDIRECTIONAL); 93*0a82a623SDan Williams dma_src[0] = dma_map_page(dev, src, 0, len, DMA_TO_DEVICE); 94*0a82a623SDan Williams tx = dma->device_prep_dma_pq(chan, dma_dest, dma_src, 1, &coef, 95*0a82a623SDan Williams len, dma_flags); 96*0a82a623SDan Williams if (tx) { 97*0a82a623SDan Williams async_tx_submit(chan, tx, submit); 98*0a82a623SDan Williams return tx; 99*0a82a623SDan Williams } 100*0a82a623SDan Williams } 101*0a82a623SDan Williams 102*0a82a623SDan Williams /* no channel available, or failed to allocate a descriptor, so 103*0a82a623SDan Williams * perform the operation synchronously 104*0a82a623SDan Williams */ 105*0a82a623SDan Williams async_tx_quiesce(&submit->depend_tx); 106*0a82a623SDan Williams qmul = raid6_gfmul[coef]; 107*0a82a623SDan Williams d = page_address(dest); 108*0a82a623SDan Williams s = page_address(src); 109*0a82a623SDan Williams 110*0a82a623SDan Williams while (len--) 111*0a82a623SDan Williams *d++ = qmul[*s++]; 112*0a82a623SDan Williams 113*0a82a623SDan Williams return NULL; 114*0a82a623SDan Williams } 115*0a82a623SDan Williams 116*0a82a623SDan Williams static struct dma_async_tx_descriptor * 117*0a82a623SDan Williams __2data_recov_4(size_t bytes, int faila, int failb, struct page **blocks, 118*0a82a623SDan Williams struct async_submit_ctl *submit) 119*0a82a623SDan Williams { 120*0a82a623SDan Williams struct dma_async_tx_descriptor *tx = NULL; 121*0a82a623SDan Williams struct page *p, *q, *a, *b; 122*0a82a623SDan Williams struct page *srcs[2]; 123*0a82a623SDan Williams unsigned char coef[2]; 124*0a82a623SDan Williams enum async_tx_flags flags = submit->flags; 125*0a82a623SDan Williams dma_async_tx_callback cb_fn = submit->cb_fn; 126*0a82a623SDan Williams void *cb_param = submit->cb_param; 127*0a82a623SDan Williams void *scribble = submit->scribble; 128*0a82a623SDan Williams 129*0a82a623SDan Williams p = blocks[4-2]; 130*0a82a623SDan Williams q = blocks[4-1]; 131*0a82a623SDan Williams 132*0a82a623SDan Williams a = blocks[faila]; 133*0a82a623SDan Williams b = blocks[failb]; 134*0a82a623SDan Williams 135*0a82a623SDan Williams /* in the 4 disk case P + Pxy == P and Q + Qxy == Q */ 136*0a82a623SDan Williams /* Dx = A*(P+Pxy) + B*(Q+Qxy) */ 137*0a82a623SDan Williams srcs[0] = p; 138*0a82a623SDan Williams srcs[1] = q; 139*0a82a623SDan Williams coef[0] = raid6_gfexi[failb-faila]; 140*0a82a623SDan Williams coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]; 141*0a82a623SDan Williams init_async_submit(submit, 0, tx, NULL, NULL, scribble); 142*0a82a623SDan Williams tx = async_sum_product(b, srcs, coef, bytes, submit); 143*0a82a623SDan Williams 144*0a82a623SDan Williams /* Dy = P+Pxy+Dx */ 145*0a82a623SDan Williams srcs[0] = p; 146*0a82a623SDan Williams srcs[1] = b; 147*0a82a623SDan Williams init_async_submit(submit, flags | ASYNC_TX_XOR_ZERO_DST, tx, cb_fn, 148*0a82a623SDan Williams cb_param, scribble); 149*0a82a623SDan Williams tx = async_xor(a, srcs, 0, 2, bytes, submit); 150*0a82a623SDan Williams 151*0a82a623SDan Williams return tx; 152*0a82a623SDan Williams 153*0a82a623SDan Williams } 154*0a82a623SDan Williams 155*0a82a623SDan Williams static struct dma_async_tx_descriptor * 156*0a82a623SDan Williams __2data_recov_5(size_t bytes, int faila, int failb, struct page **blocks, 157*0a82a623SDan Williams struct async_submit_ctl *submit) 158*0a82a623SDan Williams { 159*0a82a623SDan Williams struct dma_async_tx_descriptor *tx = NULL; 160*0a82a623SDan Williams struct page *p, *q, *g, *dp, *dq; 161*0a82a623SDan Williams struct page *srcs[2]; 162*0a82a623SDan Williams unsigned char coef[2]; 163*0a82a623SDan Williams enum async_tx_flags flags = submit->flags; 164*0a82a623SDan Williams dma_async_tx_callback cb_fn = submit->cb_fn; 165*0a82a623SDan Williams void *cb_param = submit->cb_param; 166*0a82a623SDan Williams void *scribble = submit->scribble; 167*0a82a623SDan Williams int uninitialized_var(good); 168*0a82a623SDan Williams int i; 169*0a82a623SDan Williams 170*0a82a623SDan Williams for (i = 0; i < 3; i++) { 171*0a82a623SDan Williams if (i == faila || i == failb) 172*0a82a623SDan Williams continue; 173*0a82a623SDan Williams else { 174*0a82a623SDan Williams good = i; 175*0a82a623SDan Williams break; 176*0a82a623SDan Williams } 177*0a82a623SDan Williams } 178*0a82a623SDan Williams BUG_ON(i >= 3); 179*0a82a623SDan Williams 180*0a82a623SDan Williams p = blocks[5-2]; 181*0a82a623SDan Williams q = blocks[5-1]; 182*0a82a623SDan Williams g = blocks[good]; 183*0a82a623SDan Williams 184*0a82a623SDan Williams /* Compute syndrome with zero for the missing data pages 185*0a82a623SDan Williams * Use the dead data pages as temporary storage for delta p and 186*0a82a623SDan Williams * delta q 187*0a82a623SDan Williams */ 188*0a82a623SDan Williams dp = blocks[faila]; 189*0a82a623SDan Williams dq = blocks[failb]; 190*0a82a623SDan Williams 191*0a82a623SDan Williams init_async_submit(submit, 0, tx, NULL, NULL, scribble); 192*0a82a623SDan Williams tx = async_memcpy(dp, g, 0, 0, bytes, submit); 193*0a82a623SDan Williams init_async_submit(submit, 0, tx, NULL, NULL, scribble); 194*0a82a623SDan Williams tx = async_mult(dq, g, raid6_gfexp[good], bytes, submit); 195*0a82a623SDan Williams 196*0a82a623SDan Williams /* compute P + Pxy */ 197*0a82a623SDan Williams srcs[0] = dp; 198*0a82a623SDan Williams srcs[1] = p; 199*0a82a623SDan Williams init_async_submit(submit, ASYNC_TX_XOR_DROP_DST, tx, NULL, NULL, 200*0a82a623SDan Williams scribble); 201*0a82a623SDan Williams tx = async_xor(dp, srcs, 0, 2, bytes, submit); 202*0a82a623SDan Williams 203*0a82a623SDan Williams /* compute Q + Qxy */ 204*0a82a623SDan Williams srcs[0] = dq; 205*0a82a623SDan Williams srcs[1] = q; 206*0a82a623SDan Williams init_async_submit(submit, ASYNC_TX_XOR_DROP_DST, tx, NULL, NULL, 207*0a82a623SDan Williams scribble); 208*0a82a623SDan Williams tx = async_xor(dq, srcs, 0, 2, bytes, submit); 209*0a82a623SDan Williams 210*0a82a623SDan Williams /* Dx = A*(P+Pxy) + B*(Q+Qxy) */ 211*0a82a623SDan Williams srcs[0] = dp; 212*0a82a623SDan Williams srcs[1] = dq; 213*0a82a623SDan Williams coef[0] = raid6_gfexi[failb-faila]; 214*0a82a623SDan Williams coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]; 215*0a82a623SDan Williams init_async_submit(submit, 0, tx, NULL, NULL, scribble); 216*0a82a623SDan Williams tx = async_sum_product(dq, srcs, coef, bytes, submit); 217*0a82a623SDan Williams 218*0a82a623SDan Williams /* Dy = P+Pxy+Dx */ 219*0a82a623SDan Williams srcs[0] = dp; 220*0a82a623SDan Williams srcs[1] = dq; 221*0a82a623SDan Williams init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn, 222*0a82a623SDan Williams cb_param, scribble); 223*0a82a623SDan Williams tx = async_xor(dp, srcs, 0, 2, bytes, submit); 224*0a82a623SDan Williams 225*0a82a623SDan Williams return tx; 226*0a82a623SDan Williams } 227*0a82a623SDan Williams 228*0a82a623SDan Williams static struct dma_async_tx_descriptor * 229*0a82a623SDan Williams __2data_recov_n(int disks, size_t bytes, int faila, int failb, 230*0a82a623SDan Williams struct page **blocks, struct async_submit_ctl *submit) 231*0a82a623SDan Williams { 232*0a82a623SDan Williams struct dma_async_tx_descriptor *tx = NULL; 233*0a82a623SDan Williams struct page *p, *q, *dp, *dq; 234*0a82a623SDan Williams struct page *srcs[2]; 235*0a82a623SDan Williams unsigned char coef[2]; 236*0a82a623SDan Williams enum async_tx_flags flags = submit->flags; 237*0a82a623SDan Williams dma_async_tx_callback cb_fn = submit->cb_fn; 238*0a82a623SDan Williams void *cb_param = submit->cb_param; 239*0a82a623SDan Williams void *scribble = submit->scribble; 240*0a82a623SDan Williams 241*0a82a623SDan Williams p = blocks[disks-2]; 242*0a82a623SDan Williams q = blocks[disks-1]; 243*0a82a623SDan Williams 244*0a82a623SDan Williams /* Compute syndrome with zero for the missing data pages 245*0a82a623SDan Williams * Use the dead data pages as temporary storage for 246*0a82a623SDan Williams * delta p and delta q 247*0a82a623SDan Williams */ 248*0a82a623SDan Williams dp = blocks[faila]; 249*0a82a623SDan Williams blocks[faila] = (void *)raid6_empty_zero_page; 250*0a82a623SDan Williams blocks[disks-2] = dp; 251*0a82a623SDan Williams dq = blocks[failb]; 252*0a82a623SDan Williams blocks[failb] = (void *)raid6_empty_zero_page; 253*0a82a623SDan Williams blocks[disks-1] = dq; 254*0a82a623SDan Williams 255*0a82a623SDan Williams init_async_submit(submit, 0, tx, NULL, NULL, scribble); 256*0a82a623SDan Williams tx = async_gen_syndrome(blocks, 0, disks, bytes, submit); 257*0a82a623SDan Williams 258*0a82a623SDan Williams /* Restore pointer table */ 259*0a82a623SDan Williams blocks[faila] = dp; 260*0a82a623SDan Williams blocks[failb] = dq; 261*0a82a623SDan Williams blocks[disks-2] = p; 262*0a82a623SDan Williams blocks[disks-1] = q; 263*0a82a623SDan Williams 264*0a82a623SDan Williams /* compute P + Pxy */ 265*0a82a623SDan Williams srcs[0] = dp; 266*0a82a623SDan Williams srcs[1] = p; 267*0a82a623SDan Williams init_async_submit(submit, ASYNC_TX_XOR_DROP_DST, tx, NULL, NULL, 268*0a82a623SDan Williams scribble); 269*0a82a623SDan Williams tx = async_xor(dp, srcs, 0, 2, bytes, submit); 270*0a82a623SDan Williams 271*0a82a623SDan Williams /* compute Q + Qxy */ 272*0a82a623SDan Williams srcs[0] = dq; 273*0a82a623SDan Williams srcs[1] = q; 274*0a82a623SDan Williams init_async_submit(submit, ASYNC_TX_XOR_DROP_DST, tx, NULL, NULL, 275*0a82a623SDan Williams scribble); 276*0a82a623SDan Williams tx = async_xor(dq, srcs, 0, 2, bytes, submit); 277*0a82a623SDan Williams 278*0a82a623SDan Williams /* Dx = A*(P+Pxy) + B*(Q+Qxy) */ 279*0a82a623SDan Williams srcs[0] = dp; 280*0a82a623SDan Williams srcs[1] = dq; 281*0a82a623SDan Williams coef[0] = raid6_gfexi[failb-faila]; 282*0a82a623SDan Williams coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]; 283*0a82a623SDan Williams init_async_submit(submit, 0, tx, NULL, NULL, scribble); 284*0a82a623SDan Williams tx = async_sum_product(dq, srcs, coef, bytes, submit); 285*0a82a623SDan Williams 286*0a82a623SDan Williams /* Dy = P+Pxy+Dx */ 287*0a82a623SDan Williams srcs[0] = dp; 288*0a82a623SDan Williams srcs[1] = dq; 289*0a82a623SDan Williams init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn, 290*0a82a623SDan Williams cb_param, scribble); 291*0a82a623SDan Williams tx = async_xor(dp, srcs, 0, 2, bytes, submit); 292*0a82a623SDan Williams 293*0a82a623SDan Williams return tx; 294*0a82a623SDan Williams } 295*0a82a623SDan Williams 296*0a82a623SDan Williams /** 297*0a82a623SDan Williams * async_raid6_2data_recov - asynchronously calculate two missing data blocks 298*0a82a623SDan Williams * @disks: number of disks in the RAID-6 array 299*0a82a623SDan Williams * @bytes: block size 300*0a82a623SDan Williams * @faila: first failed drive index 301*0a82a623SDan Williams * @failb: second failed drive index 302*0a82a623SDan Williams * @blocks: array of source pointers where the last two entries are p and q 303*0a82a623SDan Williams * @submit: submission/completion modifiers 304*0a82a623SDan Williams */ 305*0a82a623SDan Williams struct dma_async_tx_descriptor * 306*0a82a623SDan Williams async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb, 307*0a82a623SDan Williams struct page **blocks, struct async_submit_ctl *submit) 308*0a82a623SDan Williams { 309*0a82a623SDan Williams BUG_ON(faila == failb); 310*0a82a623SDan Williams if (failb < faila) 311*0a82a623SDan Williams swap(faila, failb); 312*0a82a623SDan Williams 313*0a82a623SDan Williams pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes); 314*0a82a623SDan Williams 315*0a82a623SDan Williams /* we need to preserve the contents of 'blocks' for the async 316*0a82a623SDan Williams * case, so punt to synchronous if a scribble buffer is not available 317*0a82a623SDan Williams */ 318*0a82a623SDan Williams if (!submit->scribble) { 319*0a82a623SDan Williams void **ptrs = (void **) blocks; 320*0a82a623SDan Williams int i; 321*0a82a623SDan Williams 322*0a82a623SDan Williams async_tx_quiesce(&submit->depend_tx); 323*0a82a623SDan Williams for (i = 0; i < disks; i++) 324*0a82a623SDan Williams ptrs[i] = page_address(blocks[i]); 325*0a82a623SDan Williams 326*0a82a623SDan Williams raid6_2data_recov(disks, bytes, faila, failb, ptrs); 327*0a82a623SDan Williams 328*0a82a623SDan Williams async_tx_sync_epilog(submit); 329*0a82a623SDan Williams 330*0a82a623SDan Williams return NULL; 331*0a82a623SDan Williams } 332*0a82a623SDan Williams 333*0a82a623SDan Williams switch (disks) { 334*0a82a623SDan Williams case 4: 335*0a82a623SDan Williams /* dma devices do not uniformly understand a zero source pq 336*0a82a623SDan Williams * operation (in contrast to the synchronous case), so 337*0a82a623SDan Williams * explicitly handle the 4 disk special case 338*0a82a623SDan Williams */ 339*0a82a623SDan Williams return __2data_recov_4(bytes, faila, failb, blocks, submit); 340*0a82a623SDan Williams case 5: 341*0a82a623SDan Williams /* dma devices do not uniformly understand a single 342*0a82a623SDan Williams * source pq operation (in contrast to the synchronous 343*0a82a623SDan Williams * case), so explicitly handle the 5 disk special case 344*0a82a623SDan Williams */ 345*0a82a623SDan Williams return __2data_recov_5(bytes, faila, failb, blocks, submit); 346*0a82a623SDan Williams default: 347*0a82a623SDan Williams return __2data_recov_n(disks, bytes, faila, failb, blocks, submit); 348*0a82a623SDan Williams } 349*0a82a623SDan Williams } 350*0a82a623SDan Williams EXPORT_SYMBOL_GPL(async_raid6_2data_recov); 351*0a82a623SDan Williams 352*0a82a623SDan Williams /** 353*0a82a623SDan Williams * async_raid6_datap_recov - asynchronously calculate a data and the 'p' block 354*0a82a623SDan Williams * @disks: number of disks in the RAID-6 array 355*0a82a623SDan Williams * @bytes: block size 356*0a82a623SDan Williams * @faila: failed drive index 357*0a82a623SDan Williams * @blocks: array of source pointers where the last two entries are p and q 358*0a82a623SDan Williams * @submit: submission/completion modifiers 359*0a82a623SDan Williams */ 360*0a82a623SDan Williams struct dma_async_tx_descriptor * 361*0a82a623SDan Williams async_raid6_datap_recov(int disks, size_t bytes, int faila, 362*0a82a623SDan Williams struct page **blocks, struct async_submit_ctl *submit) 363*0a82a623SDan Williams { 364*0a82a623SDan Williams struct dma_async_tx_descriptor *tx = NULL; 365*0a82a623SDan Williams struct page *p, *q, *dq; 366*0a82a623SDan Williams u8 coef; 367*0a82a623SDan Williams enum async_tx_flags flags = submit->flags; 368*0a82a623SDan Williams dma_async_tx_callback cb_fn = submit->cb_fn; 369*0a82a623SDan Williams void *cb_param = submit->cb_param; 370*0a82a623SDan Williams void *scribble = submit->scribble; 371*0a82a623SDan Williams struct page *srcs[2]; 372*0a82a623SDan Williams 373*0a82a623SDan Williams pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes); 374*0a82a623SDan Williams 375*0a82a623SDan Williams /* we need to preserve the contents of 'blocks' for the async 376*0a82a623SDan Williams * case, so punt to synchronous if a scribble buffer is not available 377*0a82a623SDan Williams */ 378*0a82a623SDan Williams if (!scribble) { 379*0a82a623SDan Williams void **ptrs = (void **) blocks; 380*0a82a623SDan Williams int i; 381*0a82a623SDan Williams 382*0a82a623SDan Williams async_tx_quiesce(&submit->depend_tx); 383*0a82a623SDan Williams for (i = 0; i < disks; i++) 384*0a82a623SDan Williams ptrs[i] = page_address(blocks[i]); 385*0a82a623SDan Williams 386*0a82a623SDan Williams raid6_datap_recov(disks, bytes, faila, ptrs); 387*0a82a623SDan Williams 388*0a82a623SDan Williams async_tx_sync_epilog(submit); 389*0a82a623SDan Williams 390*0a82a623SDan Williams return NULL; 391*0a82a623SDan Williams } 392*0a82a623SDan Williams 393*0a82a623SDan Williams p = blocks[disks-2]; 394*0a82a623SDan Williams q = blocks[disks-1]; 395*0a82a623SDan Williams 396*0a82a623SDan Williams /* Compute syndrome with zero for the missing data page 397*0a82a623SDan Williams * Use the dead data page as temporary storage for delta q 398*0a82a623SDan Williams */ 399*0a82a623SDan Williams dq = blocks[faila]; 400*0a82a623SDan Williams blocks[faila] = (void *)raid6_empty_zero_page; 401*0a82a623SDan Williams blocks[disks-1] = dq; 402*0a82a623SDan Williams 403*0a82a623SDan Williams /* in the 4 disk case we only need to perform a single source 404*0a82a623SDan Williams * multiplication 405*0a82a623SDan Williams */ 406*0a82a623SDan Williams if (disks == 4) { 407*0a82a623SDan Williams int good = faila == 0 ? 1 : 0; 408*0a82a623SDan Williams struct page *g = blocks[good]; 409*0a82a623SDan Williams 410*0a82a623SDan Williams init_async_submit(submit, 0, tx, NULL, NULL, scribble); 411*0a82a623SDan Williams tx = async_memcpy(p, g, 0, 0, bytes, submit); 412*0a82a623SDan Williams 413*0a82a623SDan Williams init_async_submit(submit, 0, tx, NULL, NULL, scribble); 414*0a82a623SDan Williams tx = async_mult(dq, g, raid6_gfexp[good], bytes, submit); 415*0a82a623SDan Williams } else { 416*0a82a623SDan Williams init_async_submit(submit, 0, tx, NULL, NULL, scribble); 417*0a82a623SDan Williams tx = async_gen_syndrome(blocks, 0, disks, bytes, submit); 418*0a82a623SDan Williams } 419*0a82a623SDan Williams 420*0a82a623SDan Williams /* Restore pointer table */ 421*0a82a623SDan Williams blocks[faila] = dq; 422*0a82a623SDan Williams blocks[disks-1] = q; 423*0a82a623SDan Williams 424*0a82a623SDan Williams /* calculate g^{-faila} */ 425*0a82a623SDan Williams coef = raid6_gfinv[raid6_gfexp[faila]]; 426*0a82a623SDan Williams 427*0a82a623SDan Williams srcs[0] = dq; 428*0a82a623SDan Williams srcs[1] = q; 429*0a82a623SDan Williams init_async_submit(submit, ASYNC_TX_XOR_DROP_DST, tx, NULL, NULL, 430*0a82a623SDan Williams scribble); 431*0a82a623SDan Williams tx = async_xor(dq, srcs, 0, 2, bytes, submit); 432*0a82a623SDan Williams 433*0a82a623SDan Williams init_async_submit(submit, 0, tx, NULL, NULL, scribble); 434*0a82a623SDan Williams tx = async_mult(dq, dq, coef, bytes, submit); 435*0a82a623SDan Williams 436*0a82a623SDan Williams srcs[0] = p; 437*0a82a623SDan Williams srcs[1] = dq; 438*0a82a623SDan Williams init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn, 439*0a82a623SDan Williams cb_param, scribble); 440*0a82a623SDan Williams tx = async_xor(p, srcs, 0, 2, bytes, submit); 441*0a82a623SDan Williams 442*0a82a623SDan Williams return tx; 443*0a82a623SDan Williams } 444*0a82a623SDan Williams EXPORT_SYMBOL_GPL(async_raid6_datap_recov); 445*0a82a623SDan Williams 446*0a82a623SDan Williams MODULE_AUTHOR("Dan Williams <dan.j.williams@intel.com>"); 447*0a82a623SDan Williams MODULE_DESCRIPTION("asynchronous RAID-6 recovery api"); 448*0a82a623SDan Williams MODULE_LICENSE("GPL"); 449