162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * xor offload engine api 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright © 2006, Intel Corporation. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Dan Williams <dan.j.williams@intel.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * with architecture considerations by: 1062306a36Sopenharmony_ci * Neil Brown <neilb@suse.de> 1162306a36Sopenharmony_ci * Jeff Garzik <jeff@garzik.org> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1862306a36Sopenharmony_ci#include <linux/raid/xor.h> 1962306a36Sopenharmony_ci#include <linux/async_tx.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* do_async_xor - dma map the pages and perform the xor with an engine */ 2262306a36Sopenharmony_cistatic __async_inline struct dma_async_tx_descriptor * 2362306a36Sopenharmony_cido_async_xor(struct dma_chan *chan, struct dmaengine_unmap_data *unmap, 2462306a36Sopenharmony_ci struct async_submit_ctl *submit) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct dma_device *dma = chan->device; 2762306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx = NULL; 2862306a36Sopenharmony_ci dma_async_tx_callback cb_fn_orig = submit->cb_fn; 2962306a36Sopenharmony_ci void *cb_param_orig = submit->cb_param; 3062306a36Sopenharmony_ci enum async_tx_flags flags_orig = submit->flags; 3162306a36Sopenharmony_ci enum dma_ctrl_flags dma_flags = 0; 3262306a36Sopenharmony_ci int src_cnt = unmap->to_cnt; 3362306a36Sopenharmony_ci int xor_src_cnt; 3462306a36Sopenharmony_ci dma_addr_t dma_dest = unmap->addr[unmap->to_cnt]; 3562306a36Sopenharmony_ci dma_addr_t *src_list = unmap->addr; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci while (src_cnt) { 3862306a36Sopenharmony_ci dma_addr_t tmp; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci submit->flags = flags_orig; 4162306a36Sopenharmony_ci xor_src_cnt = min(src_cnt, (int)dma->max_xor); 4262306a36Sopenharmony_ci /* if we are submitting additional xors, leave the chain open 4362306a36Sopenharmony_ci * and clear the callback parameters 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci if (src_cnt > xor_src_cnt) { 4662306a36Sopenharmony_ci submit->flags &= ~ASYNC_TX_ACK; 4762306a36Sopenharmony_ci submit->flags |= ASYNC_TX_FENCE; 4862306a36Sopenharmony_ci submit->cb_fn = NULL; 4962306a36Sopenharmony_ci submit->cb_param = NULL; 5062306a36Sopenharmony_ci } else { 5162306a36Sopenharmony_ci submit->cb_fn = cb_fn_orig; 5262306a36Sopenharmony_ci submit->cb_param = cb_param_orig; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci if (submit->cb_fn) 5562306a36Sopenharmony_ci dma_flags |= DMA_PREP_INTERRUPT; 5662306a36Sopenharmony_ci if (submit->flags & ASYNC_TX_FENCE) 5762306a36Sopenharmony_ci dma_flags |= DMA_PREP_FENCE; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* Drivers force forward progress in case they can not provide a 6062306a36Sopenharmony_ci * descriptor 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci tmp = src_list[0]; 6362306a36Sopenharmony_ci if (src_list > unmap->addr) 6462306a36Sopenharmony_ci src_list[0] = dma_dest; 6562306a36Sopenharmony_ci tx = dma->device_prep_dma_xor(chan, dma_dest, src_list, 6662306a36Sopenharmony_ci xor_src_cnt, unmap->len, 6762306a36Sopenharmony_ci dma_flags); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (unlikely(!tx)) 7062306a36Sopenharmony_ci async_tx_quiesce(&submit->depend_tx); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* spin wait for the preceding transactions to complete */ 7362306a36Sopenharmony_ci while (unlikely(!tx)) { 7462306a36Sopenharmony_ci dma_async_issue_pending(chan); 7562306a36Sopenharmony_ci tx = dma->device_prep_dma_xor(chan, dma_dest, 7662306a36Sopenharmony_ci src_list, 7762306a36Sopenharmony_ci xor_src_cnt, unmap->len, 7862306a36Sopenharmony_ci dma_flags); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci src_list[0] = tmp; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci dma_set_unmap(tx, unmap); 8362306a36Sopenharmony_ci async_tx_submit(chan, tx, submit); 8462306a36Sopenharmony_ci submit->depend_tx = tx; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (src_cnt > xor_src_cnt) { 8762306a36Sopenharmony_ci /* drop completed sources */ 8862306a36Sopenharmony_ci src_cnt -= xor_src_cnt; 8962306a36Sopenharmony_ci /* use the intermediate result a source */ 9062306a36Sopenharmony_ci src_cnt++; 9162306a36Sopenharmony_ci src_list += xor_src_cnt - 1; 9262306a36Sopenharmony_ci } else 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return tx; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void 10062306a36Sopenharmony_cido_sync_xor_offs(struct page *dest, unsigned int offset, 10162306a36Sopenharmony_ci struct page **src_list, unsigned int *src_offs, 10262306a36Sopenharmony_ci int src_cnt, size_t len, struct async_submit_ctl *submit) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci int i; 10562306a36Sopenharmony_ci int xor_src_cnt = 0; 10662306a36Sopenharmony_ci int src_off = 0; 10762306a36Sopenharmony_ci void *dest_buf; 10862306a36Sopenharmony_ci void **srcs; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (submit->scribble) 11162306a36Sopenharmony_ci srcs = submit->scribble; 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci srcs = (void **) src_list; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* convert to buffer pointers */ 11662306a36Sopenharmony_ci for (i = 0; i < src_cnt; i++) 11762306a36Sopenharmony_ci if (src_list[i]) 11862306a36Sopenharmony_ci srcs[xor_src_cnt++] = page_address(src_list[i]) + 11962306a36Sopenharmony_ci (src_offs ? src_offs[i] : offset); 12062306a36Sopenharmony_ci src_cnt = xor_src_cnt; 12162306a36Sopenharmony_ci /* set destination address */ 12262306a36Sopenharmony_ci dest_buf = page_address(dest) + offset; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (submit->flags & ASYNC_TX_XOR_ZERO_DST) 12562306a36Sopenharmony_ci memset(dest_buf, 0, len); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci while (src_cnt > 0) { 12862306a36Sopenharmony_ci /* process up to 'MAX_XOR_BLOCKS' sources */ 12962306a36Sopenharmony_ci xor_src_cnt = min(src_cnt, MAX_XOR_BLOCKS); 13062306a36Sopenharmony_ci xor_blocks(xor_src_cnt, len, dest_buf, &srcs[src_off]); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* drop completed sources */ 13362306a36Sopenharmony_ci src_cnt -= xor_src_cnt; 13462306a36Sopenharmony_ci src_off += xor_src_cnt; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci async_tx_sync_epilog(submit); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic inline bool 14162306a36Sopenharmony_cidma_xor_aligned_offsets(struct dma_device *device, unsigned int offset, 14262306a36Sopenharmony_ci unsigned int *src_offs, int src_cnt, int len) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int i; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!is_dma_xor_aligned(device, offset, 0, len)) 14762306a36Sopenharmony_ci return false; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!src_offs) 15062306a36Sopenharmony_ci return true; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci for (i = 0; i < src_cnt; i++) { 15362306a36Sopenharmony_ci if (!is_dma_xor_aligned(device, src_offs[i], 0, len)) 15462306a36Sopenharmony_ci return false; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci return true; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/** 16062306a36Sopenharmony_ci * async_xor_offs - attempt to xor a set of blocks with a dma engine. 16162306a36Sopenharmony_ci * @dest: destination page 16262306a36Sopenharmony_ci * @offset: dst offset to start transaction 16362306a36Sopenharmony_ci * @src_list: array of source pages 16462306a36Sopenharmony_ci * @src_offs: array of source pages offset, NULL means common src/dst offset 16562306a36Sopenharmony_ci * @src_cnt: number of source pages 16662306a36Sopenharmony_ci * @len: length in bytes 16762306a36Sopenharmony_ci * @submit: submission / completion modifiers 16862306a36Sopenharmony_ci * 16962306a36Sopenharmony_ci * honored flags: ASYNC_TX_ACK, ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DST 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * xor_blocks always uses the dest as a source so the 17262306a36Sopenharmony_ci * ASYNC_TX_XOR_ZERO_DST flag must be set to not include dest data in 17362306a36Sopenharmony_ci * the calculation. The assumption with dma engines is that they only 17462306a36Sopenharmony_ci * use the destination buffer as a source when it is explicitly specified 17562306a36Sopenharmony_ci * in the source list. 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * src_list note: if the dest is also a source it must be at index zero. 17862306a36Sopenharmony_ci * The contents of this array will be overwritten if a scribble region 17962306a36Sopenharmony_ci * is not specified. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_cistruct dma_async_tx_descriptor * 18262306a36Sopenharmony_ciasync_xor_offs(struct page *dest, unsigned int offset, 18362306a36Sopenharmony_ci struct page **src_list, unsigned int *src_offs, 18462306a36Sopenharmony_ci int src_cnt, size_t len, struct async_submit_ctl *submit) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct dma_chan *chan = async_tx_find_channel(submit, DMA_XOR, 18762306a36Sopenharmony_ci &dest, 1, src_list, 18862306a36Sopenharmony_ci src_cnt, len); 18962306a36Sopenharmony_ci struct dma_device *device = chan ? chan->device : NULL; 19062306a36Sopenharmony_ci struct dmaengine_unmap_data *unmap = NULL; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci BUG_ON(src_cnt <= 1); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (device) 19562306a36Sopenharmony_ci unmap = dmaengine_get_unmap_data(device->dev, src_cnt+1, GFP_NOWAIT); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (unmap && dma_xor_aligned_offsets(device, offset, 19862306a36Sopenharmony_ci src_offs, src_cnt, len)) { 19962306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx; 20062306a36Sopenharmony_ci int i, j; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* run the xor asynchronously */ 20362306a36Sopenharmony_ci pr_debug("%s (async): len: %zu\n", __func__, len); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci unmap->len = len; 20662306a36Sopenharmony_ci for (i = 0, j = 0; i < src_cnt; i++) { 20762306a36Sopenharmony_ci if (!src_list[i]) 20862306a36Sopenharmony_ci continue; 20962306a36Sopenharmony_ci unmap->to_cnt++; 21062306a36Sopenharmony_ci unmap->addr[j++] = dma_map_page(device->dev, src_list[i], 21162306a36Sopenharmony_ci src_offs ? src_offs[i] : offset, 21262306a36Sopenharmony_ci len, DMA_TO_DEVICE); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* map it bidirectional as it may be re-used as a source */ 21662306a36Sopenharmony_ci unmap->addr[j] = dma_map_page(device->dev, dest, offset, len, 21762306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 21862306a36Sopenharmony_ci unmap->bidi_cnt = 1; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci tx = do_async_xor(chan, unmap, submit); 22162306a36Sopenharmony_ci dmaengine_unmap_put(unmap); 22262306a36Sopenharmony_ci return tx; 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci dmaengine_unmap_put(unmap); 22562306a36Sopenharmony_ci /* run the xor synchronously */ 22662306a36Sopenharmony_ci pr_debug("%s (sync): len: %zu\n", __func__, len); 22762306a36Sopenharmony_ci WARN_ONCE(chan, "%s: no space for dma address conversion\n", 22862306a36Sopenharmony_ci __func__); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* in the sync case the dest is an implied source 23162306a36Sopenharmony_ci * (assumes the dest is the first source) 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci if (submit->flags & ASYNC_TX_XOR_DROP_DST) { 23462306a36Sopenharmony_ci src_cnt--; 23562306a36Sopenharmony_ci src_list++; 23662306a36Sopenharmony_ci if (src_offs) 23762306a36Sopenharmony_ci src_offs++; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* wait for any prerequisite operations */ 24162306a36Sopenharmony_ci async_tx_quiesce(&submit->depend_tx); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci do_sync_xor_offs(dest, offset, src_list, src_offs, 24462306a36Sopenharmony_ci src_cnt, len, submit); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return NULL; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(async_xor_offs); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/** 25262306a36Sopenharmony_ci * async_xor - attempt to xor a set of blocks with a dma engine. 25362306a36Sopenharmony_ci * @dest: destination page 25462306a36Sopenharmony_ci * @src_list: array of source pages 25562306a36Sopenharmony_ci * @offset: common src/dst offset to start transaction 25662306a36Sopenharmony_ci * @src_cnt: number of source pages 25762306a36Sopenharmony_ci * @len: length in bytes 25862306a36Sopenharmony_ci * @submit: submission / completion modifiers 25962306a36Sopenharmony_ci * 26062306a36Sopenharmony_ci * honored flags: ASYNC_TX_ACK, ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DST 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * xor_blocks always uses the dest as a source so the 26362306a36Sopenharmony_ci * ASYNC_TX_XOR_ZERO_DST flag must be set to not include dest data in 26462306a36Sopenharmony_ci * the calculation. The assumption with dma engines is that they only 26562306a36Sopenharmony_ci * use the destination buffer as a source when it is explicitly specified 26662306a36Sopenharmony_ci * in the source list. 26762306a36Sopenharmony_ci * 26862306a36Sopenharmony_ci * src_list note: if the dest is also a source it must be at index zero. 26962306a36Sopenharmony_ci * The contents of this array will be overwritten if a scribble region 27062306a36Sopenharmony_ci * is not specified. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_cistruct dma_async_tx_descriptor * 27362306a36Sopenharmony_ciasync_xor(struct page *dest, struct page **src_list, unsigned int offset, 27462306a36Sopenharmony_ci int src_cnt, size_t len, struct async_submit_ctl *submit) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci return async_xor_offs(dest, offset, src_list, NULL, 27762306a36Sopenharmony_ci src_cnt, len, submit); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(async_xor); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int page_is_zero(struct page *p, unsigned int offset, size_t len) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci return !memchr_inv(page_address(p) + offset, 0, len); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic inline struct dma_chan * 28762306a36Sopenharmony_cixor_val_chan(struct async_submit_ctl *submit, struct page *dest, 28862306a36Sopenharmony_ci struct page **src_list, int src_cnt, size_t len) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci #ifdef CONFIG_ASYNC_TX_DISABLE_XOR_VAL_DMA 29162306a36Sopenharmony_ci return NULL; 29262306a36Sopenharmony_ci #endif 29362306a36Sopenharmony_ci return async_tx_find_channel(submit, DMA_XOR_VAL, &dest, 1, src_list, 29462306a36Sopenharmony_ci src_cnt, len); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/** 29862306a36Sopenharmony_ci * async_xor_val_offs - attempt a xor parity check with a dma engine. 29962306a36Sopenharmony_ci * @dest: destination page used if the xor is performed synchronously 30062306a36Sopenharmony_ci * @offset: des offset in pages to start transaction 30162306a36Sopenharmony_ci * @src_list: array of source pages 30262306a36Sopenharmony_ci * @src_offs: array of source pages offset, NULL means common src/det offset 30362306a36Sopenharmony_ci * @src_cnt: number of source pages 30462306a36Sopenharmony_ci * @len: length in bytes 30562306a36Sopenharmony_ci * @result: 0 if sum == 0 else non-zero 30662306a36Sopenharmony_ci * @submit: submission / completion modifiers 30762306a36Sopenharmony_ci * 30862306a36Sopenharmony_ci * honored flags: ASYNC_TX_ACK 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * src_list note: if the dest is also a source it must be at index zero. 31162306a36Sopenharmony_ci * The contents of this array will be overwritten if a scribble region 31262306a36Sopenharmony_ci * is not specified. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_cistruct dma_async_tx_descriptor * 31562306a36Sopenharmony_ciasync_xor_val_offs(struct page *dest, unsigned int offset, 31662306a36Sopenharmony_ci struct page **src_list, unsigned int *src_offs, 31762306a36Sopenharmony_ci int src_cnt, size_t len, enum sum_check_flags *result, 31862306a36Sopenharmony_ci struct async_submit_ctl *submit) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct dma_chan *chan = xor_val_chan(submit, dest, src_list, src_cnt, len); 32162306a36Sopenharmony_ci struct dma_device *device = chan ? chan->device : NULL; 32262306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx = NULL; 32362306a36Sopenharmony_ci struct dmaengine_unmap_data *unmap = NULL; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci BUG_ON(src_cnt <= 1); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (device) 32862306a36Sopenharmony_ci unmap = dmaengine_get_unmap_data(device->dev, src_cnt, GFP_NOWAIT); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (unmap && src_cnt <= device->max_xor && 33162306a36Sopenharmony_ci dma_xor_aligned_offsets(device, offset, src_offs, src_cnt, len)) { 33262306a36Sopenharmony_ci unsigned long dma_prep_flags = 0; 33362306a36Sopenharmony_ci int i; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci pr_debug("%s: (async) len: %zu\n", __func__, len); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (submit->cb_fn) 33862306a36Sopenharmony_ci dma_prep_flags |= DMA_PREP_INTERRUPT; 33962306a36Sopenharmony_ci if (submit->flags & ASYNC_TX_FENCE) 34062306a36Sopenharmony_ci dma_prep_flags |= DMA_PREP_FENCE; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (i = 0; i < src_cnt; i++) { 34362306a36Sopenharmony_ci unmap->addr[i] = dma_map_page(device->dev, src_list[i], 34462306a36Sopenharmony_ci src_offs ? src_offs[i] : offset, 34562306a36Sopenharmony_ci len, DMA_TO_DEVICE); 34662306a36Sopenharmony_ci unmap->to_cnt++; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci unmap->len = len; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci tx = device->device_prep_dma_xor_val(chan, unmap->addr, src_cnt, 35162306a36Sopenharmony_ci len, result, 35262306a36Sopenharmony_ci dma_prep_flags); 35362306a36Sopenharmony_ci if (unlikely(!tx)) { 35462306a36Sopenharmony_ci async_tx_quiesce(&submit->depend_tx); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci while (!tx) { 35762306a36Sopenharmony_ci dma_async_issue_pending(chan); 35862306a36Sopenharmony_ci tx = device->device_prep_dma_xor_val(chan, 35962306a36Sopenharmony_ci unmap->addr, src_cnt, len, result, 36062306a36Sopenharmony_ci dma_prep_flags); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci dma_set_unmap(tx, unmap); 36462306a36Sopenharmony_ci async_tx_submit(chan, tx, submit); 36562306a36Sopenharmony_ci } else { 36662306a36Sopenharmony_ci enum async_tx_flags flags_orig = submit->flags; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci pr_debug("%s: (sync) len: %zu\n", __func__, len); 36962306a36Sopenharmony_ci WARN_ONCE(device && src_cnt <= device->max_xor, 37062306a36Sopenharmony_ci "%s: no space for dma address conversion\n", 37162306a36Sopenharmony_ci __func__); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci submit->flags |= ASYNC_TX_XOR_DROP_DST; 37462306a36Sopenharmony_ci submit->flags &= ~ASYNC_TX_ACK; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci tx = async_xor_offs(dest, offset, src_list, src_offs, 37762306a36Sopenharmony_ci src_cnt, len, submit); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci async_tx_quiesce(&tx); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci *result = !page_is_zero(dest, offset, len) << SUM_CHECK_P; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci async_tx_sync_epilog(submit); 38462306a36Sopenharmony_ci submit->flags = flags_orig; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci dmaengine_unmap_put(unmap); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return tx; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(async_xor_val_offs); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/** 39362306a36Sopenharmony_ci * async_xor_val - attempt a xor parity check with a dma engine. 39462306a36Sopenharmony_ci * @dest: destination page used if the xor is performed synchronously 39562306a36Sopenharmony_ci * @src_list: array of source pages 39662306a36Sopenharmony_ci * @offset: offset in pages to start transaction 39762306a36Sopenharmony_ci * @src_cnt: number of source pages 39862306a36Sopenharmony_ci * @len: length in bytes 39962306a36Sopenharmony_ci * @result: 0 if sum == 0 else non-zero 40062306a36Sopenharmony_ci * @submit: submission / completion modifiers 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * honored flags: ASYNC_TX_ACK 40362306a36Sopenharmony_ci * 40462306a36Sopenharmony_ci * src_list note: if the dest is also a source it must be at index zero. 40562306a36Sopenharmony_ci * The contents of this array will be overwritten if a scribble region 40662306a36Sopenharmony_ci * is not specified. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_cistruct dma_async_tx_descriptor * 40962306a36Sopenharmony_ciasync_xor_val(struct page *dest, struct page **src_list, unsigned int offset, 41062306a36Sopenharmony_ci int src_cnt, size_t len, enum sum_check_flags *result, 41162306a36Sopenharmony_ci struct async_submit_ctl *submit) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci return async_xor_val_offs(dest, offset, src_list, NULL, src_cnt, 41462306a36Sopenharmony_ci len, result, submit); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(async_xor_val); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 41962306a36Sopenharmony_ciMODULE_DESCRIPTION("asynchronous xor/xor-zero-sum api"); 42062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 421