1fd534e9bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 20a82a623SDan Williams /* 30a82a623SDan Williams * Asynchronous RAID-6 recovery calculations ASYNC_TX API. 40a82a623SDan Williams * Copyright(c) 2009 Intel Corporation 50a82a623SDan Williams * 60a82a623SDan Williams * based on raid6recov.c: 70a82a623SDan Williams * Copyright 2002 H. Peter Anvin 80a82a623SDan Williams */ 90a82a623SDan Williams #include <linux/kernel.h> 100a82a623SDan Williams #include <linux/interrupt.h> 114bb33cc8SPaul Gortmaker #include <linux/module.h> 120a82a623SDan Williams #include <linux/dma-mapping.h> 130a82a623SDan Williams #include <linux/raid/pq.h> 140a82a623SDan Williams #include <linux/async_tx.h> 153bbdd498SDan Williams #include <linux/dmaengine.h> 160a82a623SDan Williams 170a82a623SDan Williams static struct dma_async_tx_descriptor * 18*4f86ff55SYufen Yu async_sum_product(struct page *dest, unsigned int d_off, 19*4f86ff55SYufen Yu struct page **srcs, unsigned int *src_offs, unsigned char *coef, 200a82a623SDan Williams size_t len, struct async_submit_ctl *submit) 210a82a623SDan Williams { 220a82a623SDan Williams struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ, 230a82a623SDan Williams &dest, 1, srcs, 2, len); 240a82a623SDan Williams struct dma_device *dma = chan ? chan->device : NULL; 253bbdd498SDan Williams struct dmaengine_unmap_data *unmap = NULL; 260a82a623SDan Williams const u8 *amul, *bmul; 270a82a623SDan Williams u8 ax, bx; 280a82a623SDan Williams u8 *a, *b, *c; 290a82a623SDan Williams 303bbdd498SDan Williams if (dma) 31b02bab6bSNeilBrown unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOWAIT); 323bbdd498SDan Williams 333bbdd498SDan Williams if (unmap) { 340a82a623SDan Williams struct device *dev = dma->dev; 353bbdd498SDan Williams dma_addr_t pq[2]; 360a82a623SDan Williams struct dma_async_tx_descriptor *tx; 370776ae7bSBartlomiej Zolnierkiewicz enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P; 380a82a623SDan Williams 390403e382SDan Williams if (submit->flags & ASYNC_TX_FENCE) 400403e382SDan Williams dma_flags |= DMA_PREP_FENCE; 41*4f86ff55SYufen Yu unmap->addr[0] = dma_map_page(dev, srcs[0], src_offs[0], 42*4f86ff55SYufen Yu len, DMA_TO_DEVICE); 43*4f86ff55SYufen Yu unmap->addr[1] = dma_map_page(dev, srcs[1], src_offs[1], 44*4f86ff55SYufen Yu len, DMA_TO_DEVICE); 453bbdd498SDan Williams unmap->to_cnt = 2; 463bbdd498SDan Williams 47*4f86ff55SYufen Yu unmap->addr[2] = dma_map_page(dev, dest, d_off, 48*4f86ff55SYufen Yu len, DMA_BIDIRECTIONAL); 493bbdd498SDan Williams unmap->bidi_cnt = 1; 503bbdd498SDan Williams /* engine only looks at Q, but expects it to follow P */ 513bbdd498SDan Williams pq[1] = unmap->addr[2]; 523bbdd498SDan Williams 533bbdd498SDan Williams unmap->len = len; 543bbdd498SDan Williams tx = dma->device_prep_dma_pq(chan, pq, unmap->addr, 2, coef, 550a82a623SDan Williams len, dma_flags); 560a82a623SDan Williams if (tx) { 573bbdd498SDan Williams dma_set_unmap(tx, unmap); 580a82a623SDan Williams async_tx_submit(chan, tx, submit); 593bbdd498SDan Williams dmaengine_unmap_put(unmap); 600a82a623SDan Williams return tx; 610a82a623SDan Williams } 621f6672d4SDan Williams 631f6672d4SDan Williams /* could not get a descriptor, unmap and fall through to 641f6672d4SDan Williams * the synchronous path 651f6672d4SDan Williams */ 663bbdd498SDan Williams dmaengine_unmap_put(unmap); 670a82a623SDan Williams } 680a82a623SDan Williams 690a82a623SDan Williams /* run the operation synchronously */ 700a82a623SDan Williams async_tx_quiesce(&submit->depend_tx); 710a82a623SDan Williams amul = raid6_gfmul[coef[0]]; 720a82a623SDan Williams bmul = raid6_gfmul[coef[1]]; 73*4f86ff55SYufen Yu a = page_address(srcs[0]) + src_offs[0]; 74*4f86ff55SYufen Yu b = page_address(srcs[1]) + src_offs[1]; 75*4f86ff55SYufen Yu c = page_address(dest) + d_off; 760a82a623SDan Williams 770a82a623SDan Williams while (len--) { 780a82a623SDan Williams ax = amul[*a++]; 790a82a623SDan Williams bx = bmul[*b++]; 800a82a623SDan Williams *c++ = ax ^ bx; 810a82a623SDan Williams } 820a82a623SDan Williams 830a82a623SDan Williams return NULL; 840a82a623SDan Williams } 850a82a623SDan Williams 860a82a623SDan Williams static struct dma_async_tx_descriptor * 87*4f86ff55SYufen Yu async_mult(struct page *dest, unsigned int d_off, struct page *src, 88*4f86ff55SYufen Yu unsigned int s_off, u8 coef, size_t len, 890a82a623SDan Williams struct async_submit_ctl *submit) 900a82a623SDan Williams { 910a82a623SDan Williams struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ, 920a82a623SDan Williams &dest, 1, &src, 1, len); 930a82a623SDan Williams struct dma_device *dma = chan ? chan->device : NULL; 943bbdd498SDan Williams struct dmaengine_unmap_data *unmap = NULL; 950a82a623SDan Williams const u8 *qmul; /* Q multiplier table */ 960a82a623SDan Williams u8 *d, *s; 970a82a623SDan Williams 983bbdd498SDan Williams if (dma) 99b02bab6bSNeilBrown unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOWAIT); 1003bbdd498SDan Williams 1013bbdd498SDan Williams if (unmap) { 1020a82a623SDan Williams dma_addr_t dma_dest[2]; 1030a82a623SDan Williams struct device *dev = dma->dev; 1040a82a623SDan Williams struct dma_async_tx_descriptor *tx; 1050776ae7bSBartlomiej Zolnierkiewicz enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P; 1060a82a623SDan Williams 1070403e382SDan Williams if (submit->flags & ASYNC_TX_FENCE) 1080403e382SDan Williams dma_flags |= DMA_PREP_FENCE; 109*4f86ff55SYufen Yu unmap->addr[0] = dma_map_page(dev, src, s_off, 110*4f86ff55SYufen Yu len, DMA_TO_DEVICE); 1113bbdd498SDan Williams unmap->to_cnt++; 112*4f86ff55SYufen Yu unmap->addr[1] = dma_map_page(dev, dest, d_off, 113*4f86ff55SYufen Yu len, DMA_BIDIRECTIONAL); 1143bbdd498SDan Williams dma_dest[1] = unmap->addr[1]; 1153bbdd498SDan Williams unmap->bidi_cnt++; 1163bbdd498SDan Williams unmap->len = len; 1173bbdd498SDan Williams 1183bbdd498SDan Williams /* this looks funny, but the engine looks for Q at 1193bbdd498SDan Williams * dma_dest[1] and ignores dma_dest[0] as a dest 1203bbdd498SDan Williams * due to DMA_PREP_PQ_DISABLE_P 1213bbdd498SDan Williams */ 1223bbdd498SDan Williams tx = dma->device_prep_dma_pq(chan, dma_dest, unmap->addr, 1233bbdd498SDan Williams 1, &coef, len, dma_flags); 1243bbdd498SDan Williams 1250a82a623SDan Williams if (tx) { 1263bbdd498SDan Williams dma_set_unmap(tx, unmap); 1273bbdd498SDan Williams dmaengine_unmap_put(unmap); 1280a82a623SDan Williams async_tx_submit(chan, tx, submit); 1290a82a623SDan Williams return tx; 1300a82a623SDan Williams } 1311f6672d4SDan Williams 1321f6672d4SDan Williams /* could not get a descriptor, unmap and fall through to 1331f6672d4SDan Williams * the synchronous path 1341f6672d4SDan Williams */ 1353bbdd498SDan Williams dmaengine_unmap_put(unmap); 1360a82a623SDan Williams } 1370a82a623SDan Williams 1380a82a623SDan Williams /* no channel available, or failed to allocate a descriptor, so 1390a82a623SDan Williams * perform the operation synchronously 1400a82a623SDan Williams */ 1410a82a623SDan Williams async_tx_quiesce(&submit->depend_tx); 1420a82a623SDan Williams qmul = raid6_gfmul[coef]; 143*4f86ff55SYufen Yu d = page_address(dest) + d_off; 144*4f86ff55SYufen Yu s = page_address(src) + s_off; 1450a82a623SDan Williams 1460a82a623SDan Williams while (len--) 1470a82a623SDan Williams *d++ = qmul[*s++]; 1480a82a623SDan Williams 1490a82a623SDan Williams return NULL; 1500a82a623SDan Williams } 1510a82a623SDan Williams 1520a82a623SDan Williams static struct dma_async_tx_descriptor * 153da17bf43SDan Williams __2data_recov_4(int disks, size_t bytes, int faila, int failb, 154*4f86ff55SYufen Yu struct page **blocks, unsigned int *offs, 155*4f86ff55SYufen Yu struct async_submit_ctl *submit) 1560a82a623SDan Williams { 1570a82a623SDan Williams struct dma_async_tx_descriptor *tx = NULL; 1580a82a623SDan Williams struct page *p, *q, *a, *b; 159*4f86ff55SYufen Yu unsigned int p_off, q_off, a_off, b_off; 1600a82a623SDan Williams struct page *srcs[2]; 161*4f86ff55SYufen Yu unsigned int src_offs[2]; 1620a82a623SDan Williams unsigned char coef[2]; 1630a82a623SDan Williams enum async_tx_flags flags = submit->flags; 1640a82a623SDan Williams dma_async_tx_callback cb_fn = submit->cb_fn; 1650a82a623SDan Williams void *cb_param = submit->cb_param; 1660a82a623SDan Williams void *scribble = submit->scribble; 1670a82a623SDan Williams 168da17bf43SDan Williams p = blocks[disks-2]; 169*4f86ff55SYufen Yu p_off = offs[disks-2]; 170da17bf43SDan Williams q = blocks[disks-1]; 171*4f86ff55SYufen Yu q_off = offs[disks-1]; 1720a82a623SDan Williams 1730a82a623SDan Williams a = blocks[faila]; 174*4f86ff55SYufen Yu a_off = offs[faila]; 1750a82a623SDan Williams b = blocks[failb]; 176*4f86ff55SYufen Yu b_off = offs[failb]; 1770a82a623SDan Williams 1780a82a623SDan Williams /* in the 4 disk case P + Pxy == P and Q + Qxy == Q */ 1790a82a623SDan Williams /* Dx = A*(P+Pxy) + B*(Q+Qxy) */ 1800a82a623SDan Williams srcs[0] = p; 181*4f86ff55SYufen Yu src_offs[0] = p_off; 1820a82a623SDan Williams srcs[1] = q; 183*4f86ff55SYufen Yu src_offs[1] = q_off; 1840a82a623SDan Williams coef[0] = raid6_gfexi[failb-faila]; 1850a82a623SDan Williams coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]; 1860403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); 187*4f86ff55SYufen Yu tx = async_sum_product(b, b_off, srcs, src_offs, coef, bytes, submit); 1880a82a623SDan Williams 1890a82a623SDan Williams /* Dy = P+Pxy+Dx */ 1900a82a623SDan Williams srcs[0] = p; 191*4f86ff55SYufen Yu src_offs[0] = p_off; 1920a82a623SDan Williams srcs[1] = b; 193*4f86ff55SYufen Yu src_offs[1] = b_off; 1940a82a623SDan Williams init_async_submit(submit, flags | ASYNC_TX_XOR_ZERO_DST, tx, cb_fn, 1950a82a623SDan Williams cb_param, scribble); 196*4f86ff55SYufen Yu tx = async_xor_offs(a, a_off, srcs, src_offs, 2, bytes, submit); 1970a82a623SDan Williams 1980a82a623SDan Williams return tx; 1990a82a623SDan Williams 2000a82a623SDan Williams } 2010a82a623SDan Williams 2020a82a623SDan Williams static struct dma_async_tx_descriptor * 203da17bf43SDan Williams __2data_recov_5(int disks, size_t bytes, int faila, int failb, 204*4f86ff55SYufen Yu struct page **blocks, unsigned int *offs, 205*4f86ff55SYufen Yu struct async_submit_ctl *submit) 2060a82a623SDan Williams { 2070a82a623SDan Williams struct dma_async_tx_descriptor *tx = NULL; 2080a82a623SDan Williams struct page *p, *q, *g, *dp, *dq; 209*4f86ff55SYufen Yu unsigned int p_off, q_off, g_off, dp_off, dq_off; 2100a82a623SDan Williams struct page *srcs[2]; 211*4f86ff55SYufen Yu unsigned int src_offs[2]; 2120a82a623SDan Williams unsigned char coef[2]; 2130a82a623SDan Williams enum async_tx_flags flags = submit->flags; 2140a82a623SDan Williams dma_async_tx_callback cb_fn = submit->cb_fn; 2150a82a623SDan Williams void *cb_param = submit->cb_param; 2160a82a623SDan Williams void *scribble = submit->scribble; 217da17bf43SDan Williams int good_srcs, good, i; 2180a82a623SDan Williams 219da17bf43SDan Williams good_srcs = 0; 220da17bf43SDan Williams good = -1; 221da17bf43SDan Williams for (i = 0; i < disks-2; i++) { 222da17bf43SDan Williams if (blocks[i] == NULL) 223da17bf43SDan Williams continue; 2240a82a623SDan Williams if (i == faila || i == failb) 2250a82a623SDan Williams continue; 2260a82a623SDan Williams good = i; 227da17bf43SDan Williams good_srcs++; 2280a82a623SDan Williams } 229da17bf43SDan Williams BUG_ON(good_srcs > 1); 2300a82a623SDan Williams 231da17bf43SDan Williams p = blocks[disks-2]; 232*4f86ff55SYufen Yu p_off = offs[disks-2]; 233da17bf43SDan Williams q = blocks[disks-1]; 234*4f86ff55SYufen Yu q_off = offs[disks-1]; 2350a82a623SDan Williams g = blocks[good]; 236*4f86ff55SYufen Yu g_off = offs[good]; 2370a82a623SDan Williams 2380a82a623SDan Williams /* Compute syndrome with zero for the missing data pages 2390a82a623SDan Williams * Use the dead data pages as temporary storage for delta p and 2400a82a623SDan Williams * delta q 2410a82a623SDan Williams */ 2420a82a623SDan Williams dp = blocks[faila]; 243*4f86ff55SYufen Yu dp_off = offs[faila]; 2440a82a623SDan Williams dq = blocks[failb]; 245*4f86ff55SYufen Yu dq_off = offs[failb]; 2460a82a623SDan Williams 2470403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); 248*4f86ff55SYufen Yu tx = async_memcpy(dp, g, dp_off, g_off, bytes, submit); 2490403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); 250*4f86ff55SYufen Yu tx = async_mult(dq, dq_off, g, g_off, 251*4f86ff55SYufen Yu raid6_gfexp[good], bytes, submit); 2520a82a623SDan Williams 2530a82a623SDan Williams /* compute P + Pxy */ 2540a82a623SDan Williams srcs[0] = dp; 255*4f86ff55SYufen Yu src_offs[0] = dp_off; 2560a82a623SDan Williams srcs[1] = p; 257*4f86ff55SYufen Yu src_offs[1] = p_off; 2580403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, 2590403e382SDan Williams NULL, NULL, scribble); 260*4f86ff55SYufen Yu tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit); 2610a82a623SDan Williams 2620a82a623SDan Williams /* compute Q + Qxy */ 2630a82a623SDan Williams srcs[0] = dq; 264*4f86ff55SYufen Yu src_offs[0] = dq_off; 2650a82a623SDan Williams srcs[1] = q; 266*4f86ff55SYufen Yu src_offs[1] = q_off; 2670403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, 2680403e382SDan Williams NULL, NULL, scribble); 269*4f86ff55SYufen Yu tx = async_xor_offs(dq, dq_off, srcs, src_offs, 2, bytes, submit); 2700a82a623SDan Williams 2710a82a623SDan Williams /* Dx = A*(P+Pxy) + B*(Q+Qxy) */ 2720a82a623SDan Williams srcs[0] = dp; 273*4f86ff55SYufen Yu src_offs[0] = dp_off; 2740a82a623SDan Williams srcs[1] = dq; 275*4f86ff55SYufen Yu src_offs[1] = dq_off; 2760a82a623SDan Williams coef[0] = raid6_gfexi[failb-faila]; 2770a82a623SDan Williams coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]; 2780403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); 279*4f86ff55SYufen Yu tx = async_sum_product(dq, dq_off, srcs, src_offs, coef, bytes, submit); 2800a82a623SDan Williams 2810a82a623SDan Williams /* Dy = P+Pxy+Dx */ 2820a82a623SDan Williams srcs[0] = dp; 283*4f86ff55SYufen Yu src_offs[0] = dp_off; 2840a82a623SDan Williams srcs[1] = dq; 285*4f86ff55SYufen Yu src_offs[1] = dq_off; 2860a82a623SDan Williams init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn, 2870a82a623SDan Williams cb_param, scribble); 288*4f86ff55SYufen Yu tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit); 2890a82a623SDan Williams 2900a82a623SDan Williams return tx; 2910a82a623SDan Williams } 2920a82a623SDan Williams 2930a82a623SDan Williams static struct dma_async_tx_descriptor * 2940a82a623SDan Williams __2data_recov_n(int disks, size_t bytes, int faila, int failb, 295*4f86ff55SYufen Yu struct page **blocks, unsigned int *offs, 296*4f86ff55SYufen Yu struct async_submit_ctl *submit) 2970a82a623SDan Williams { 2980a82a623SDan Williams struct dma_async_tx_descriptor *tx = NULL; 2990a82a623SDan Williams struct page *p, *q, *dp, *dq; 300*4f86ff55SYufen Yu unsigned int p_off, q_off, dp_off, dq_off; 3010a82a623SDan Williams struct page *srcs[2]; 302*4f86ff55SYufen Yu unsigned int src_offs[2]; 3030a82a623SDan Williams unsigned char coef[2]; 3040a82a623SDan Williams enum async_tx_flags flags = submit->flags; 3050a82a623SDan Williams dma_async_tx_callback cb_fn = submit->cb_fn; 3060a82a623SDan Williams void *cb_param = submit->cb_param; 3070a82a623SDan Williams void *scribble = submit->scribble; 3080a82a623SDan Williams 3090a82a623SDan Williams p = blocks[disks-2]; 310*4f86ff55SYufen Yu p_off = offs[disks-2]; 3110a82a623SDan Williams q = blocks[disks-1]; 312*4f86ff55SYufen Yu q_off = offs[disks-1]; 3130a82a623SDan Williams 3140a82a623SDan Williams /* Compute syndrome with zero for the missing data pages 3150a82a623SDan Williams * Use the dead data pages as temporary storage for 3160a82a623SDan Williams * delta p and delta q 3170a82a623SDan Williams */ 3180a82a623SDan Williams dp = blocks[faila]; 319*4f86ff55SYufen Yu dp_off = offs[faila]; 3205dd33c9aSNeilBrown blocks[faila] = NULL; 3210a82a623SDan Williams blocks[disks-2] = dp; 322*4f86ff55SYufen Yu offs[disks-2] = dp_off; 3230a82a623SDan Williams dq = blocks[failb]; 324*4f86ff55SYufen Yu dq_off = offs[failb]; 3255dd33c9aSNeilBrown blocks[failb] = NULL; 3260a82a623SDan Williams blocks[disks-1] = dq; 327*4f86ff55SYufen Yu offs[disks-1] = dq_off; 3280a82a623SDan Williams 3290403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); 330*4f86ff55SYufen Yu tx = async_gen_syndrome(blocks, offs, disks, bytes, submit); 3310a82a623SDan Williams 3320a82a623SDan Williams /* Restore pointer table */ 3330a82a623SDan Williams blocks[faila] = dp; 334*4f86ff55SYufen Yu offs[faila] = dp_off; 3350a82a623SDan Williams blocks[failb] = dq; 336*4f86ff55SYufen Yu offs[failb] = dq_off; 3370a82a623SDan Williams blocks[disks-2] = p; 338*4f86ff55SYufen Yu offs[disks-2] = p_off; 3390a82a623SDan Williams blocks[disks-1] = q; 340*4f86ff55SYufen Yu offs[disks-1] = q_off; 3410a82a623SDan Williams 3420a82a623SDan Williams /* compute P + Pxy */ 3430a82a623SDan Williams srcs[0] = dp; 344*4f86ff55SYufen Yu src_offs[0] = dp_off; 3450a82a623SDan Williams srcs[1] = p; 346*4f86ff55SYufen Yu src_offs[1] = p_off; 3470403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, 3480403e382SDan Williams NULL, NULL, scribble); 349*4f86ff55SYufen Yu tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit); 3500a82a623SDan Williams 3510a82a623SDan Williams /* compute Q + Qxy */ 3520a82a623SDan Williams srcs[0] = dq; 353*4f86ff55SYufen Yu src_offs[0] = dq_off; 3540a82a623SDan Williams srcs[1] = q; 355*4f86ff55SYufen Yu src_offs[1] = q_off; 3560403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, 3570403e382SDan Williams NULL, NULL, scribble); 358*4f86ff55SYufen Yu tx = async_xor_offs(dq, dq_off, srcs, src_offs, 2, bytes, submit); 3590a82a623SDan Williams 3600a82a623SDan Williams /* Dx = A*(P+Pxy) + B*(Q+Qxy) */ 3610a82a623SDan Williams srcs[0] = dp; 362*4f86ff55SYufen Yu src_offs[0] = dp_off; 3630a82a623SDan Williams srcs[1] = dq; 364*4f86ff55SYufen Yu src_offs[1] = dq_off; 3650a82a623SDan Williams coef[0] = raid6_gfexi[failb-faila]; 3660a82a623SDan Williams coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]; 3670403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); 368*4f86ff55SYufen Yu tx = async_sum_product(dq, dq_off, srcs, src_offs, coef, bytes, submit); 3690a82a623SDan Williams 3700a82a623SDan Williams /* Dy = P+Pxy+Dx */ 3710a82a623SDan Williams srcs[0] = dp; 372*4f86ff55SYufen Yu src_offs[0] = dp_off; 3730a82a623SDan Williams srcs[1] = dq; 374*4f86ff55SYufen Yu src_offs[1] = dq_off; 3750a82a623SDan Williams init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn, 3760a82a623SDan Williams cb_param, scribble); 377*4f86ff55SYufen Yu tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit); 3780a82a623SDan Williams 3790a82a623SDan Williams return tx; 3800a82a623SDan Williams } 3810a82a623SDan Williams 3820a82a623SDan Williams /** 3830a82a623SDan Williams * async_raid6_2data_recov - asynchronously calculate two missing data blocks 3840a82a623SDan Williams * @disks: number of disks in the RAID-6 array 3850a82a623SDan Williams * @bytes: block size 3860a82a623SDan Williams * @faila: first failed drive index 3870a82a623SDan Williams * @failb: second failed drive index 3880a82a623SDan Williams * @blocks: array of source pointers where the last two entries are p and q 389*4f86ff55SYufen Yu * @offs: array of offset for pages in blocks 3900a82a623SDan Williams * @submit: submission/completion modifiers 3910a82a623SDan Williams */ 3920a82a623SDan Williams struct dma_async_tx_descriptor * 3930a82a623SDan Williams async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb, 394*4f86ff55SYufen Yu struct page **blocks, unsigned int *offs, 395*4f86ff55SYufen Yu struct async_submit_ctl *submit) 3960a82a623SDan Williams { 3975157b4aaSDan Williams void *scribble = submit->scribble; 398da17bf43SDan Williams int non_zero_srcs, i; 399da17bf43SDan Williams 4000a82a623SDan Williams BUG_ON(faila == failb); 4010a82a623SDan Williams if (failb < faila) 4020a82a623SDan Williams swap(faila, failb); 4030a82a623SDan Williams 4040a82a623SDan Williams pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes); 4050a82a623SDan Williams 4065157b4aaSDan Williams /* if a dma resource is not available or a scribble buffer is not 4075157b4aaSDan Williams * available punt to the synchronous path. In the 'dma not 4085157b4aaSDan Williams * available' case be sure to use the scribble buffer to 4095157b4aaSDan Williams * preserve the content of 'blocks' as the caller intended. 4100a82a623SDan Williams */ 4115157b4aaSDan Williams if (!async_dma_find_channel(DMA_PQ) || !scribble) { 4125157b4aaSDan Williams void **ptrs = scribble ? scribble : (void **) blocks; 4130a82a623SDan Williams 4140a82a623SDan Williams async_tx_quiesce(&submit->depend_tx); 4150a82a623SDan Williams for (i = 0; i < disks; i++) 4165dd33c9aSNeilBrown if (blocks[i] == NULL) 4175dd33c9aSNeilBrown ptrs[i] = (void *) raid6_empty_zero_page; 4185dd33c9aSNeilBrown else 419*4f86ff55SYufen Yu ptrs[i] = page_address(blocks[i]) + offs[i]; 4200a82a623SDan Williams 4210a82a623SDan Williams raid6_2data_recov(disks, bytes, faila, failb, ptrs); 4220a82a623SDan Williams 4230a82a623SDan Williams async_tx_sync_epilog(submit); 4240a82a623SDan Williams 4250a82a623SDan Williams return NULL; 4260a82a623SDan Williams } 4270a82a623SDan Williams 428da17bf43SDan Williams non_zero_srcs = 0; 429da17bf43SDan Williams for (i = 0; i < disks-2 && non_zero_srcs < 4; i++) 430da17bf43SDan Williams if (blocks[i]) 431da17bf43SDan Williams non_zero_srcs++; 432da17bf43SDan Williams switch (non_zero_srcs) { 433da17bf43SDan Williams case 0: 434da17bf43SDan Williams case 1: 435da17bf43SDan Williams /* There must be at least 2 sources - the failed devices. */ 436da17bf43SDan Williams BUG(); 437da17bf43SDan Williams 438da17bf43SDan Williams case 2: 4390a82a623SDan Williams /* dma devices do not uniformly understand a zero source pq 4400a82a623SDan Williams * operation (in contrast to the synchronous case), so 441da17bf43SDan Williams * explicitly handle the special case of a 4 disk array with 442da17bf43SDan Williams * both data disks missing. 4430a82a623SDan Williams */ 444*4f86ff55SYufen Yu return __2data_recov_4(disks, bytes, faila, failb, 445*4f86ff55SYufen Yu blocks, offs, submit); 446da17bf43SDan Williams case 3: 4470a82a623SDan Williams /* dma devices do not uniformly understand a single 4480a82a623SDan Williams * source pq operation (in contrast to the synchronous 449da17bf43SDan Williams * case), so explicitly handle the special case of a 5 disk 450da17bf43SDan Williams * array with 2 of 3 data disks missing. 4510a82a623SDan Williams */ 452*4f86ff55SYufen Yu return __2data_recov_5(disks, bytes, faila, failb, 453*4f86ff55SYufen Yu blocks, offs, submit); 4540a82a623SDan Williams default: 455*4f86ff55SYufen Yu return __2data_recov_n(disks, bytes, faila, failb, 456*4f86ff55SYufen Yu blocks, offs, submit); 4570a82a623SDan Williams } 4580a82a623SDan Williams } 4590a82a623SDan Williams EXPORT_SYMBOL_GPL(async_raid6_2data_recov); 4600a82a623SDan Williams 4610a82a623SDan Williams /** 4620a82a623SDan Williams * async_raid6_datap_recov - asynchronously calculate a data and the 'p' block 4630a82a623SDan Williams * @disks: number of disks in the RAID-6 array 4640a82a623SDan Williams * @bytes: block size 4650a82a623SDan Williams * @faila: failed drive index 4660a82a623SDan Williams * @blocks: array of source pointers where the last two entries are p and q 467*4f86ff55SYufen Yu * @offs: array of offset for pages in blocks 4680a82a623SDan Williams * @submit: submission/completion modifiers 4690a82a623SDan Williams */ 4700a82a623SDan Williams struct dma_async_tx_descriptor * 4710a82a623SDan Williams async_raid6_datap_recov(int disks, size_t bytes, int faila, 472*4f86ff55SYufen Yu struct page **blocks, unsigned int *offs, 473*4f86ff55SYufen Yu struct async_submit_ctl *submit) 4740a82a623SDan Williams { 4750a82a623SDan Williams struct dma_async_tx_descriptor *tx = NULL; 4760a82a623SDan Williams struct page *p, *q, *dq; 477*4f86ff55SYufen Yu unsigned int p_off, q_off, dq_off; 4780a82a623SDan Williams u8 coef; 4790a82a623SDan Williams enum async_tx_flags flags = submit->flags; 4800a82a623SDan Williams dma_async_tx_callback cb_fn = submit->cb_fn; 4810a82a623SDan Williams void *cb_param = submit->cb_param; 4820a82a623SDan Williams void *scribble = submit->scribble; 483da17bf43SDan Williams int good_srcs, good, i; 4840a82a623SDan Williams struct page *srcs[2]; 485*4f86ff55SYufen Yu unsigned int src_offs[2]; 4860a82a623SDan Williams 4870a82a623SDan Williams pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes); 4880a82a623SDan Williams 4895157b4aaSDan Williams /* if a dma resource is not available or a scribble buffer is not 4905157b4aaSDan Williams * available punt to the synchronous path. In the 'dma not 4915157b4aaSDan Williams * available' case be sure to use the scribble buffer to 4925157b4aaSDan Williams * preserve the content of 'blocks' as the caller intended. 4930a82a623SDan Williams */ 4945157b4aaSDan Williams if (!async_dma_find_channel(DMA_PQ) || !scribble) { 4955157b4aaSDan Williams void **ptrs = scribble ? scribble : (void **) blocks; 4960a82a623SDan Williams 4970a82a623SDan Williams async_tx_quiesce(&submit->depend_tx); 4980a82a623SDan Williams for (i = 0; i < disks; i++) 4995dd33c9aSNeilBrown if (blocks[i] == NULL) 5005dd33c9aSNeilBrown ptrs[i] = (void*)raid6_empty_zero_page; 5015dd33c9aSNeilBrown else 502*4f86ff55SYufen Yu ptrs[i] = page_address(blocks[i]) + offs[i]; 5030a82a623SDan Williams 5040a82a623SDan Williams raid6_datap_recov(disks, bytes, faila, ptrs); 5050a82a623SDan Williams 5060a82a623SDan Williams async_tx_sync_epilog(submit); 5070a82a623SDan Williams 5080a82a623SDan Williams return NULL; 5090a82a623SDan Williams } 5100a82a623SDan Williams 511da17bf43SDan Williams good_srcs = 0; 512da17bf43SDan Williams good = -1; 513da17bf43SDan Williams for (i = 0; i < disks-2; i++) { 514da17bf43SDan Williams if (i == faila) 515da17bf43SDan Williams continue; 516da17bf43SDan Williams if (blocks[i]) { 517da17bf43SDan Williams good = i; 518da17bf43SDan Williams good_srcs++; 519da17bf43SDan Williams if (good_srcs > 1) 520da17bf43SDan Williams break; 521da17bf43SDan Williams } 522da17bf43SDan Williams } 523da17bf43SDan Williams BUG_ON(good_srcs == 0); 524da17bf43SDan Williams 5250a82a623SDan Williams p = blocks[disks-2]; 526*4f86ff55SYufen Yu p_off = offs[disks-2]; 5270a82a623SDan Williams q = blocks[disks-1]; 528*4f86ff55SYufen Yu q_off = offs[disks-1]; 5290a82a623SDan Williams 5300a82a623SDan Williams /* Compute syndrome with zero for the missing data page 5310a82a623SDan Williams * Use the dead data page as temporary storage for delta q 5320a82a623SDan Williams */ 5330a82a623SDan Williams dq = blocks[faila]; 534*4f86ff55SYufen Yu dq_off = offs[faila]; 5355dd33c9aSNeilBrown blocks[faila] = NULL; 5360a82a623SDan Williams blocks[disks-1] = dq; 537*4f86ff55SYufen Yu offs[disks-1] = dq_off; 5380a82a623SDan Williams 539da17bf43SDan Williams /* in the 4-disk case we only need to perform a single source 540da17bf43SDan Williams * multiplication with the one good data block. 5410a82a623SDan Williams */ 542da17bf43SDan Williams if (good_srcs == 1) { 5430a82a623SDan Williams struct page *g = blocks[good]; 544*4f86ff55SYufen Yu unsigned int g_off = offs[good]; 5450a82a623SDan Williams 5460403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, 5470403e382SDan Williams scribble); 548*4f86ff55SYufen Yu tx = async_memcpy(p, g, p_off, g_off, bytes, submit); 5490a82a623SDan Williams 5500403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, 5510403e382SDan Williams scribble); 552*4f86ff55SYufen Yu tx = async_mult(dq, dq_off, g, g_off, 553*4f86ff55SYufen Yu raid6_gfexp[good], bytes, submit); 5540a82a623SDan Williams } else { 5550403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, 5560403e382SDan Williams scribble); 557*4f86ff55SYufen Yu tx = async_gen_syndrome(blocks, offs, disks, bytes, submit); 5580a82a623SDan Williams } 5590a82a623SDan Williams 5600a82a623SDan Williams /* Restore pointer table */ 5610a82a623SDan Williams blocks[faila] = dq; 562*4f86ff55SYufen Yu offs[faila] = dq_off; 5630a82a623SDan Williams blocks[disks-1] = q; 564*4f86ff55SYufen Yu offs[disks-1] = q_off; 5650a82a623SDan Williams 5660a82a623SDan Williams /* calculate g^{-faila} */ 5670a82a623SDan Williams coef = raid6_gfinv[raid6_gfexp[faila]]; 5680a82a623SDan Williams 5690a82a623SDan Williams srcs[0] = dq; 570*4f86ff55SYufen Yu src_offs[0] = dq_off; 5710a82a623SDan Williams srcs[1] = q; 572*4f86ff55SYufen Yu src_offs[1] = q_off; 5730403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx, 5740403e382SDan Williams NULL, NULL, scribble); 575*4f86ff55SYufen Yu tx = async_xor_offs(dq, dq_off, srcs, src_offs, 2, bytes, submit); 5760a82a623SDan Williams 5770403e382SDan Williams init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble); 578*4f86ff55SYufen Yu tx = async_mult(dq, dq_off, dq, dq_off, coef, bytes, submit); 5790a82a623SDan Williams 5800a82a623SDan Williams srcs[0] = p; 581*4f86ff55SYufen Yu src_offs[0] = p_off; 5820a82a623SDan Williams srcs[1] = dq; 583*4f86ff55SYufen Yu src_offs[1] = dq_off; 5840a82a623SDan Williams init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn, 5850a82a623SDan Williams cb_param, scribble); 586*4f86ff55SYufen Yu tx = async_xor_offs(p, p_off, srcs, src_offs, 2, bytes, submit); 5870a82a623SDan Williams 5880a82a623SDan Williams return tx; 5890a82a623SDan Williams } 5900a82a623SDan Williams EXPORT_SYMBOL_GPL(async_raid6_datap_recov); 5910a82a623SDan Williams 5920a82a623SDan Williams MODULE_AUTHOR("Dan Williams <dan.j.williams@intel.com>"); 5930a82a623SDan Williams MODULE_DESCRIPTION("asynchronous RAID-6 recovery api"); 5940a82a623SDan Williams MODULE_LICENSE("GPL"); 595