18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright(c) 2007 Yuri Tikhonov <yur@emcraft.com> 48c2ecf20Sopenharmony_ci * Copyright(c) 2009 Intel Corporation 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 108c2ecf20Sopenharmony_ci#include <linux/raid/pq.h> 118c2ecf20Sopenharmony_ci#include <linux/async_tx.h> 128c2ecf20Sopenharmony_ci#include <linux/gfp.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/** 158c2ecf20Sopenharmony_ci * pq_scribble_page - space to hold throwaway P or Q buffer for 168c2ecf20Sopenharmony_ci * synchronous gen_syndrome 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_cistatic struct page *pq_scribble_page; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* the struct page *blocks[] parameter passed to async_gen_syndrome() 218c2ecf20Sopenharmony_ci * and async_syndrome_val() contains the 'P' destination address at 228c2ecf20Sopenharmony_ci * blocks[disks-2] and the 'Q' destination address at blocks[disks-1] 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * note: these are macros as they are used as lvalues 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci#define P(b, d) (b[d-2]) 278c2ecf20Sopenharmony_ci#define Q(b, d) (b[d-1]) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define MAX_DISKS 255 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/** 328c2ecf20Sopenharmony_ci * do_async_gen_syndrome - asynchronously calculate P and/or Q 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cistatic __async_inline struct dma_async_tx_descriptor * 358c2ecf20Sopenharmony_cido_async_gen_syndrome(struct dma_chan *chan, 368c2ecf20Sopenharmony_ci const unsigned char *scfs, int disks, 378c2ecf20Sopenharmony_ci struct dmaengine_unmap_data *unmap, 388c2ecf20Sopenharmony_ci enum dma_ctrl_flags dma_flags, 398c2ecf20Sopenharmony_ci struct async_submit_ctl *submit) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx = NULL; 428c2ecf20Sopenharmony_ci struct dma_device *dma = chan->device; 438c2ecf20Sopenharmony_ci enum async_tx_flags flags_orig = submit->flags; 448c2ecf20Sopenharmony_ci dma_async_tx_callback cb_fn_orig = submit->cb_fn; 458c2ecf20Sopenharmony_ci dma_async_tx_callback cb_param_orig = submit->cb_param; 468c2ecf20Sopenharmony_ci int src_cnt = disks - 2; 478c2ecf20Sopenharmony_ci unsigned short pq_src_cnt; 488c2ecf20Sopenharmony_ci dma_addr_t dma_dest[2]; 498c2ecf20Sopenharmony_ci int src_off = 0; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci while (src_cnt > 0) { 528c2ecf20Sopenharmony_ci submit->flags = flags_orig; 538c2ecf20Sopenharmony_ci pq_src_cnt = min(src_cnt, dma_maxpq(dma, dma_flags)); 548c2ecf20Sopenharmony_ci /* if we are submitting additional pqs, leave the chain open, 558c2ecf20Sopenharmony_ci * clear the callback parameters, and leave the destination 568c2ecf20Sopenharmony_ci * buffers mapped 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci if (src_cnt > pq_src_cnt) { 598c2ecf20Sopenharmony_ci submit->flags &= ~ASYNC_TX_ACK; 608c2ecf20Sopenharmony_ci submit->flags |= ASYNC_TX_FENCE; 618c2ecf20Sopenharmony_ci submit->cb_fn = NULL; 628c2ecf20Sopenharmony_ci submit->cb_param = NULL; 638c2ecf20Sopenharmony_ci } else { 648c2ecf20Sopenharmony_ci submit->cb_fn = cb_fn_orig; 658c2ecf20Sopenharmony_ci submit->cb_param = cb_param_orig; 668c2ecf20Sopenharmony_ci if (cb_fn_orig) 678c2ecf20Sopenharmony_ci dma_flags |= DMA_PREP_INTERRUPT; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci if (submit->flags & ASYNC_TX_FENCE) 708c2ecf20Sopenharmony_ci dma_flags |= DMA_PREP_FENCE; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Drivers force forward progress in case they can not provide 738c2ecf20Sopenharmony_ci * a descriptor 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci for (;;) { 768c2ecf20Sopenharmony_ci dma_dest[0] = unmap->addr[disks - 2]; 778c2ecf20Sopenharmony_ci dma_dest[1] = unmap->addr[disks - 1]; 788c2ecf20Sopenharmony_ci tx = dma->device_prep_dma_pq(chan, dma_dest, 798c2ecf20Sopenharmony_ci &unmap->addr[src_off], 808c2ecf20Sopenharmony_ci pq_src_cnt, 818c2ecf20Sopenharmony_ci &scfs[src_off], unmap->len, 828c2ecf20Sopenharmony_ci dma_flags); 838c2ecf20Sopenharmony_ci if (likely(tx)) 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci async_tx_quiesce(&submit->depend_tx); 868c2ecf20Sopenharmony_ci dma_async_issue_pending(chan); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci dma_set_unmap(tx, unmap); 908c2ecf20Sopenharmony_ci async_tx_submit(chan, tx, submit); 918c2ecf20Sopenharmony_ci submit->depend_tx = tx; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* drop completed sources */ 948c2ecf20Sopenharmony_ci src_cnt -= pq_src_cnt; 958c2ecf20Sopenharmony_ci src_off += pq_src_cnt; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci dma_flags |= DMA_PREP_CONTINUE; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return tx; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/** 1048c2ecf20Sopenharmony_ci * do_sync_gen_syndrome - synchronously calculate a raid6 syndrome 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_cistatic void 1078c2ecf20Sopenharmony_cido_sync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks, 1088c2ecf20Sopenharmony_ci size_t len, struct async_submit_ctl *submit) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci void **srcs; 1118c2ecf20Sopenharmony_ci int i; 1128c2ecf20Sopenharmony_ci int start = -1, stop = disks - 3; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (submit->scribble) 1158c2ecf20Sopenharmony_ci srcs = submit->scribble; 1168c2ecf20Sopenharmony_ci else 1178c2ecf20Sopenharmony_ci srcs = (void **) blocks; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci for (i = 0; i < disks; i++) { 1208c2ecf20Sopenharmony_ci if (blocks[i] == NULL) { 1218c2ecf20Sopenharmony_ci BUG_ON(i > disks - 3); /* P or Q can't be zero */ 1228c2ecf20Sopenharmony_ci srcs[i] = (void*)raid6_empty_zero_page; 1238c2ecf20Sopenharmony_ci } else { 1248c2ecf20Sopenharmony_ci srcs[i] = page_address(blocks[i]) + offsets[i]; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (i < disks - 2) { 1278c2ecf20Sopenharmony_ci stop = i; 1288c2ecf20Sopenharmony_ci if (start == -1) 1298c2ecf20Sopenharmony_ci start = i; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci if (submit->flags & ASYNC_TX_PQ_XOR_DST) { 1348c2ecf20Sopenharmony_ci BUG_ON(!raid6_call.xor_syndrome); 1358c2ecf20Sopenharmony_ci if (start >= 0) 1368c2ecf20Sopenharmony_ci raid6_call.xor_syndrome(disks, start, stop, len, srcs); 1378c2ecf20Sopenharmony_ci } else 1388c2ecf20Sopenharmony_ci raid6_call.gen_syndrome(disks, len, srcs); 1398c2ecf20Sopenharmony_ci async_tx_sync_epilog(submit); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic inline bool 1438c2ecf20Sopenharmony_ciis_dma_pq_aligned_offs(struct dma_device *dev, unsigned int *offs, 1448c2ecf20Sopenharmony_ci int src_cnt, size_t len) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci int i; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < src_cnt; i++) { 1498c2ecf20Sopenharmony_ci if (!is_dma_pq_aligned(dev, offs[i], 0, len)) 1508c2ecf20Sopenharmony_ci return false; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci return true; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/** 1568c2ecf20Sopenharmony_ci * async_gen_syndrome - asynchronously calculate a raid6 syndrome 1578c2ecf20Sopenharmony_ci * @blocks: source blocks from idx 0..disks-3, P @ disks-2 and Q @ disks-1 1588c2ecf20Sopenharmony_ci * @offsets: offset array into each block (src and dest) to start transaction 1598c2ecf20Sopenharmony_ci * @disks: number of blocks (including missing P or Q, see below) 1608c2ecf20Sopenharmony_ci * @len: length of operation in bytes 1618c2ecf20Sopenharmony_ci * @submit: submission/completion modifiers 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * General note: This routine assumes a field of GF(2^8) with a 1648c2ecf20Sopenharmony_ci * primitive polynomial of 0x11d and a generator of {02}. 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * 'disks' note: callers can optionally omit either P or Q (but not 1678c2ecf20Sopenharmony_ci * both) from the calculation by setting blocks[disks-2] or 1688c2ecf20Sopenharmony_ci * blocks[disks-1] to NULL. When P or Q is omitted 'len' must be <= 1698c2ecf20Sopenharmony_ci * PAGE_SIZE as a temporary buffer of this size is used in the 1708c2ecf20Sopenharmony_ci * synchronous path. 'disks' always accounts for both destination 1718c2ecf20Sopenharmony_ci * buffers. If any source buffers (blocks[i] where i < disks - 2) are 1728c2ecf20Sopenharmony_ci * set to NULL those buffers will be replaced with the raid6_zero_page 1738c2ecf20Sopenharmony_ci * in the synchronous path and omitted in the hardware-asynchronous 1748c2ecf20Sopenharmony_ci * path. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistruct dma_async_tx_descriptor * 1778c2ecf20Sopenharmony_ciasync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks, 1788c2ecf20Sopenharmony_ci size_t len, struct async_submit_ctl *submit) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci int src_cnt = disks - 2; 1818c2ecf20Sopenharmony_ci struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ, 1828c2ecf20Sopenharmony_ci &P(blocks, disks), 2, 1838c2ecf20Sopenharmony_ci blocks, src_cnt, len); 1848c2ecf20Sopenharmony_ci struct dma_device *device = chan ? chan->device : NULL; 1858c2ecf20Sopenharmony_ci struct dmaengine_unmap_data *unmap = NULL; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci BUG_ON(disks > MAX_DISKS || !(P(blocks, disks) || Q(blocks, disks))); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (device) 1908c2ecf20Sopenharmony_ci unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOWAIT); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* XORing P/Q is only implemented in software */ 1938c2ecf20Sopenharmony_ci if (unmap && !(submit->flags & ASYNC_TX_PQ_XOR_DST) && 1948c2ecf20Sopenharmony_ci (src_cnt <= dma_maxpq(device, 0) || 1958c2ecf20Sopenharmony_ci dma_maxpq(device, DMA_PREP_CONTINUE) > 0) && 1968c2ecf20Sopenharmony_ci is_dma_pq_aligned_offs(device, offsets, disks, len)) { 1978c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 1988c2ecf20Sopenharmony_ci enum dma_ctrl_flags dma_flags = 0; 1998c2ecf20Sopenharmony_ci unsigned char coefs[MAX_DISKS]; 2008c2ecf20Sopenharmony_ci int i, j; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* run the p+q asynchronously */ 2038c2ecf20Sopenharmony_ci pr_debug("%s: (async) disks: %d len: %zu\n", 2048c2ecf20Sopenharmony_ci __func__, disks, len); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* convert source addresses being careful to collapse 'empty' 2078c2ecf20Sopenharmony_ci * sources and update the coefficients accordingly 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_ci unmap->len = len; 2108c2ecf20Sopenharmony_ci for (i = 0, j = 0; i < src_cnt; i++) { 2118c2ecf20Sopenharmony_ci if (blocks[i] == NULL) 2128c2ecf20Sopenharmony_ci continue; 2138c2ecf20Sopenharmony_ci unmap->addr[j] = dma_map_page(device->dev, blocks[i], 2148c2ecf20Sopenharmony_ci offsets[i], len, DMA_TO_DEVICE); 2158c2ecf20Sopenharmony_ci coefs[j] = raid6_gfexp[i]; 2168c2ecf20Sopenharmony_ci unmap->to_cnt++; 2178c2ecf20Sopenharmony_ci j++; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * DMAs use destinations as sources, 2228c2ecf20Sopenharmony_ci * so use BIDIRECTIONAL mapping 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci unmap->bidi_cnt++; 2258c2ecf20Sopenharmony_ci if (P(blocks, disks)) 2268c2ecf20Sopenharmony_ci unmap->addr[j++] = dma_map_page(device->dev, P(blocks, disks), 2278c2ecf20Sopenharmony_ci P(offsets, disks), 2288c2ecf20Sopenharmony_ci len, DMA_BIDIRECTIONAL); 2298c2ecf20Sopenharmony_ci else { 2308c2ecf20Sopenharmony_ci unmap->addr[j++] = 0; 2318c2ecf20Sopenharmony_ci dma_flags |= DMA_PREP_PQ_DISABLE_P; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci unmap->bidi_cnt++; 2358c2ecf20Sopenharmony_ci if (Q(blocks, disks)) 2368c2ecf20Sopenharmony_ci unmap->addr[j++] = dma_map_page(device->dev, Q(blocks, disks), 2378c2ecf20Sopenharmony_ci Q(offsets, disks), 2388c2ecf20Sopenharmony_ci len, DMA_BIDIRECTIONAL); 2398c2ecf20Sopenharmony_ci else { 2408c2ecf20Sopenharmony_ci unmap->addr[j++] = 0; 2418c2ecf20Sopenharmony_ci dma_flags |= DMA_PREP_PQ_DISABLE_Q; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci tx = do_async_gen_syndrome(chan, coefs, j, unmap, dma_flags, submit); 2458c2ecf20Sopenharmony_ci dmaengine_unmap_put(unmap); 2468c2ecf20Sopenharmony_ci return tx; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci dmaengine_unmap_put(unmap); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* run the pq synchronously */ 2528c2ecf20Sopenharmony_ci pr_debug("%s: (sync) disks: %d len: %zu\n", __func__, disks, len); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* wait for any prerequisite operations */ 2558c2ecf20Sopenharmony_ci async_tx_quiesce(&submit->depend_tx); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!P(blocks, disks)) { 2588c2ecf20Sopenharmony_ci P(blocks, disks) = pq_scribble_page; 2598c2ecf20Sopenharmony_ci P(offsets, disks) = 0; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci if (!Q(blocks, disks)) { 2628c2ecf20Sopenharmony_ci Q(blocks, disks) = pq_scribble_page; 2638c2ecf20Sopenharmony_ci Q(offsets, disks) = 0; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci do_sync_gen_syndrome(blocks, offsets, disks, len, submit); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return NULL; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(async_gen_syndrome); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic inline struct dma_chan * 2728c2ecf20Sopenharmony_cipq_val_chan(struct async_submit_ctl *submit, struct page **blocks, int disks, size_t len) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci #ifdef CONFIG_ASYNC_TX_DISABLE_PQ_VAL_DMA 2758c2ecf20Sopenharmony_ci return NULL; 2768c2ecf20Sopenharmony_ci #endif 2778c2ecf20Sopenharmony_ci return async_tx_find_channel(submit, DMA_PQ_VAL, NULL, 0, blocks, 2788c2ecf20Sopenharmony_ci disks, len); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/** 2828c2ecf20Sopenharmony_ci * async_syndrome_val - asynchronously validate a raid6 syndrome 2838c2ecf20Sopenharmony_ci * @blocks: source blocks from idx 0..disks-3, P @ disks-2 and Q @ disks-1 2848c2ecf20Sopenharmony_ci * @offset: common offset into each block (src and dest) to start transaction 2858c2ecf20Sopenharmony_ci * @disks: number of blocks (including missing P or Q, see below) 2868c2ecf20Sopenharmony_ci * @len: length of operation in bytes 2878c2ecf20Sopenharmony_ci * @pqres: on val failure SUM_CHECK_P_RESULT and/or SUM_CHECK_Q_RESULT are set 2888c2ecf20Sopenharmony_ci * @spare: temporary result buffer for the synchronous case 2898c2ecf20Sopenharmony_ci * @s_off: spare buffer page offset 2908c2ecf20Sopenharmony_ci * @submit: submission / completion modifiers 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * The same notes from async_gen_syndrome apply to the 'blocks', 2938c2ecf20Sopenharmony_ci * and 'disks' parameters of this routine. The synchronous path 2948c2ecf20Sopenharmony_ci * requires a temporary result buffer and submit->scribble to be 2958c2ecf20Sopenharmony_ci * specified. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistruct dma_async_tx_descriptor * 2988c2ecf20Sopenharmony_ciasync_syndrome_val(struct page **blocks, unsigned int *offsets, int disks, 2998c2ecf20Sopenharmony_ci size_t len, enum sum_check_flags *pqres, struct page *spare, 3008c2ecf20Sopenharmony_ci unsigned int s_off, struct async_submit_ctl *submit) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct dma_chan *chan = pq_val_chan(submit, blocks, disks, len); 3038c2ecf20Sopenharmony_ci struct dma_device *device = chan ? chan->device : NULL; 3048c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 3058c2ecf20Sopenharmony_ci unsigned char coefs[MAX_DISKS]; 3068c2ecf20Sopenharmony_ci enum dma_ctrl_flags dma_flags = submit->cb_fn ? DMA_PREP_INTERRUPT : 0; 3078c2ecf20Sopenharmony_ci struct dmaengine_unmap_data *unmap = NULL; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci BUG_ON(disks < 4 || disks > MAX_DISKS); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (device) 3128c2ecf20Sopenharmony_ci unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOWAIT); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (unmap && disks <= dma_maxpq(device, 0) && 3158c2ecf20Sopenharmony_ci is_dma_pq_aligned_offs(device, offsets, disks, len)) { 3168c2ecf20Sopenharmony_ci struct device *dev = device->dev; 3178c2ecf20Sopenharmony_ci dma_addr_t pq[2]; 3188c2ecf20Sopenharmony_ci int i, j = 0, src_cnt = 0; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci pr_debug("%s: (async) disks: %d len: %zu\n", 3218c2ecf20Sopenharmony_ci __func__, disks, len); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci unmap->len = len; 3248c2ecf20Sopenharmony_ci for (i = 0; i < disks-2; i++) 3258c2ecf20Sopenharmony_ci if (likely(blocks[i])) { 3268c2ecf20Sopenharmony_ci unmap->addr[j] = dma_map_page(dev, blocks[i], 3278c2ecf20Sopenharmony_ci offsets[i], len, 3288c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 3298c2ecf20Sopenharmony_ci coefs[j] = raid6_gfexp[i]; 3308c2ecf20Sopenharmony_ci unmap->to_cnt++; 3318c2ecf20Sopenharmony_ci src_cnt++; 3328c2ecf20Sopenharmony_ci j++; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (!P(blocks, disks)) { 3368c2ecf20Sopenharmony_ci pq[0] = 0; 3378c2ecf20Sopenharmony_ci dma_flags |= DMA_PREP_PQ_DISABLE_P; 3388c2ecf20Sopenharmony_ci } else { 3398c2ecf20Sopenharmony_ci pq[0] = dma_map_page(dev, P(blocks, disks), 3408c2ecf20Sopenharmony_ci P(offsets, disks), len, 3418c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 3428c2ecf20Sopenharmony_ci unmap->addr[j++] = pq[0]; 3438c2ecf20Sopenharmony_ci unmap->to_cnt++; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci if (!Q(blocks, disks)) { 3468c2ecf20Sopenharmony_ci pq[1] = 0; 3478c2ecf20Sopenharmony_ci dma_flags |= DMA_PREP_PQ_DISABLE_Q; 3488c2ecf20Sopenharmony_ci } else { 3498c2ecf20Sopenharmony_ci pq[1] = dma_map_page(dev, Q(blocks, disks), 3508c2ecf20Sopenharmony_ci Q(offsets, disks), len, 3518c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 3528c2ecf20Sopenharmony_ci unmap->addr[j++] = pq[1]; 3538c2ecf20Sopenharmony_ci unmap->to_cnt++; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (submit->flags & ASYNC_TX_FENCE) 3578c2ecf20Sopenharmony_ci dma_flags |= DMA_PREP_FENCE; 3588c2ecf20Sopenharmony_ci for (;;) { 3598c2ecf20Sopenharmony_ci tx = device->device_prep_dma_pq_val(chan, pq, 3608c2ecf20Sopenharmony_ci unmap->addr, 3618c2ecf20Sopenharmony_ci src_cnt, 3628c2ecf20Sopenharmony_ci coefs, 3638c2ecf20Sopenharmony_ci len, pqres, 3648c2ecf20Sopenharmony_ci dma_flags); 3658c2ecf20Sopenharmony_ci if (likely(tx)) 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci async_tx_quiesce(&submit->depend_tx); 3688c2ecf20Sopenharmony_ci dma_async_issue_pending(chan); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci dma_set_unmap(tx, unmap); 3728c2ecf20Sopenharmony_ci async_tx_submit(chan, tx, submit); 3738c2ecf20Sopenharmony_ci } else { 3748c2ecf20Sopenharmony_ci struct page *p_src = P(blocks, disks); 3758c2ecf20Sopenharmony_ci unsigned int p_off = P(offsets, disks); 3768c2ecf20Sopenharmony_ci struct page *q_src = Q(blocks, disks); 3778c2ecf20Sopenharmony_ci unsigned int q_off = Q(offsets, disks); 3788c2ecf20Sopenharmony_ci enum async_tx_flags flags_orig = submit->flags; 3798c2ecf20Sopenharmony_ci dma_async_tx_callback cb_fn_orig = submit->cb_fn; 3808c2ecf20Sopenharmony_ci void *scribble = submit->scribble; 3818c2ecf20Sopenharmony_ci void *cb_param_orig = submit->cb_param; 3828c2ecf20Sopenharmony_ci void *p, *q, *s; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci pr_debug("%s: (sync) disks: %d len: %zu\n", 3858c2ecf20Sopenharmony_ci __func__, disks, len); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* caller must provide a temporary result buffer and 3888c2ecf20Sopenharmony_ci * allow the input parameters to be preserved 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_ci BUG_ON(!spare || !scribble); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* wait for any prerequisite operations */ 3938c2ecf20Sopenharmony_ci async_tx_quiesce(&submit->depend_tx); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* recompute p and/or q into the temporary buffer and then 3968c2ecf20Sopenharmony_ci * check to see the result matches the current value 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci tx = NULL; 3998c2ecf20Sopenharmony_ci *pqres = 0; 4008c2ecf20Sopenharmony_ci if (p_src) { 4018c2ecf20Sopenharmony_ci init_async_submit(submit, ASYNC_TX_XOR_ZERO_DST, NULL, 4028c2ecf20Sopenharmony_ci NULL, NULL, scribble); 4038c2ecf20Sopenharmony_ci tx = async_xor_offs(spare, s_off, 4048c2ecf20Sopenharmony_ci blocks, offsets, disks-2, len, submit); 4058c2ecf20Sopenharmony_ci async_tx_quiesce(&tx); 4068c2ecf20Sopenharmony_ci p = page_address(p_src) + p_off; 4078c2ecf20Sopenharmony_ci s = page_address(spare) + s_off; 4088c2ecf20Sopenharmony_ci *pqres |= !!memcmp(p, s, len) << SUM_CHECK_P; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (q_src) { 4128c2ecf20Sopenharmony_ci P(blocks, disks) = NULL; 4138c2ecf20Sopenharmony_ci Q(blocks, disks) = spare; 4148c2ecf20Sopenharmony_ci Q(offsets, disks) = s_off; 4158c2ecf20Sopenharmony_ci init_async_submit(submit, 0, NULL, NULL, NULL, scribble); 4168c2ecf20Sopenharmony_ci tx = async_gen_syndrome(blocks, offsets, disks, 4178c2ecf20Sopenharmony_ci len, submit); 4188c2ecf20Sopenharmony_ci async_tx_quiesce(&tx); 4198c2ecf20Sopenharmony_ci q = page_address(q_src) + q_off; 4208c2ecf20Sopenharmony_ci s = page_address(spare) + s_off; 4218c2ecf20Sopenharmony_ci *pqres |= !!memcmp(q, s, len) << SUM_CHECK_Q; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* restore P, Q and submit */ 4258c2ecf20Sopenharmony_ci P(blocks, disks) = p_src; 4268c2ecf20Sopenharmony_ci P(offsets, disks) = p_off; 4278c2ecf20Sopenharmony_ci Q(blocks, disks) = q_src; 4288c2ecf20Sopenharmony_ci Q(offsets, disks) = q_off; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci submit->cb_fn = cb_fn_orig; 4318c2ecf20Sopenharmony_ci submit->cb_param = cb_param_orig; 4328c2ecf20Sopenharmony_ci submit->flags = flags_orig; 4338c2ecf20Sopenharmony_ci async_tx_sync_epilog(submit); 4348c2ecf20Sopenharmony_ci tx = NULL; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci dmaengine_unmap_put(unmap); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return tx; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(async_syndrome_val); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int __init async_pq_init(void) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci pq_scribble_page = alloc_page(GFP_KERNEL); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (pq_scribble_page) 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci pr_err("%s: failed to allocate required spare page\n", __func__); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return -ENOMEM; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic void __exit async_pq_exit(void) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci __free_page(pq_scribble_page); 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cimodule_init(async_pq_init); 4608c2ecf20Sopenharmony_cimodule_exit(async_pq_exit); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("asynchronous raid6 syndrome generation/validation"); 4638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 464