/** * do_async_pqxor - asynchronously calculate P and/or Q */ static struct dma_async_tx_descriptor * do_async_pqxor(struct dma_device *device, struct dma_chan *chan, struct page *pdest, struct page *qdest, struct page **src_list, unsigned char *scoef_list, unsigned int offset, unsigned int src_cnt, size_t len, enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_param) { struct page *dest; dma_addr_t dma_dest[2]; dma_addr_t *dma_src = (dma_addr_t *) src_list; unsigned char *scf = qdest ? scoef_list : NULL; struct dma_async_tx_descriptor *tx; int i, dst_cnt = 0, zdst = flags & ASYNC_TX_XOR_ZERO_DST ? 1 : 0; unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0; if (flags & ASYNC_TX_XOR_ZERO_DST) dma_prep_flags |= DMA_PREP_ZERO_DST; /* One parity (P or Q) calculation is initiated always; * first always try Q */ dest = qdest ? qdest : pdest; dma_dest[dst_cnt++] = dma_map_page(device->dev, dest, offset, len, DMA_FROM_DEVICE); /* Switch to the next destination */ if (qdest && pdest) { /* Both destinations are set, thus here we deal with P */ dma_dest[dst_cnt++] = dma_map_page(device->dev, pdest, offset, len, DMA_FROM_DEVICE); } for (i = 0; i < src_cnt; i++) dma_src[i] = dma_map_page(device->dev, src_list[i], offset, len, DMA_TO_DEVICE); /* Since we have clobbered the src_list we are committed * to doing this asynchronously. Drivers force forward progress * in case they can not provide a descriptor */ tx = device->device_prep_dma_pqxor(chan, dma_dest, dst_cnt, dma_src, src_cnt, scf, len, dma_prep_flags); if (!tx) { if (depend_tx) dma_wait_for_async_tx(depend_tx); while (!tx) tx = device->device_prep_dma_pqxor(chan, dma_dest, dst_cnt, dma_src, src_cnt, scf, len, dma_prep_flags); } async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); return tx; }
struct dma_async_tx_descriptor * async_trigger_callback(struct async_submit_ctl *submit) { struct dma_chan *chan; struct dma_device *device; struct dma_async_tx_descriptor *tx; struct dma_async_tx_descriptor *depend_tx = submit->depend_tx; if (depend_tx) { chan = depend_tx->chan; device = chan->device; if (device && !dma_has_cap(DMA_INTERRUPT, device->cap_mask)) device = NULL; tx = device ? device->device_prep_dma_interrupt(chan, 0) : NULL; } else tx = NULL; if (tx) { pr_debug("%s: (async)\n", __func__); async_tx_submit(chan, tx, submit); } else { pr_debug("%s: (sync)\n", __func__); async_tx_quiesce(&submit->depend_tx); async_tx_sync_epilog(submit); } return tx; }
/** * async_memcpy - attempt to copy memory with a dma engine. * @dest: destination page * @src: src page * @dest_offset: offset into 'dest' to start transaction * @src_offset: offset into 'src' to start transaction * @len: length in bytes * @submit: submission / completion modifiers * * honored flags: ASYNC_TX_ACK */ struct dma_async_tx_descriptor * async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, unsigned int src_offset, size_t len, struct async_submit_ctl *submit) { struct dma_chan *chan = async_tx_find_channel(submit, DMA_MEMCPY, &dest, 1, &src, 1, len); struct dma_device *device = chan ? chan->device : NULL; struct dma_async_tx_descriptor *tx = NULL; if (device && is_dma_copy_aligned(device, src_offset, dest_offset, len)) { dma_addr_t dma_dest, dma_src; unsigned long dma_prep_flags = 0; if (submit->cb_fn) dma_prep_flags |= DMA_PREP_INTERRUPT; if (submit->flags & ASYNC_TX_FENCE) dma_prep_flags |= DMA_PREP_FENCE; dma_dest = dma_map_page(device->dev, dest, dest_offset, len, DMA_FROM_DEVICE); dma_src = dma_map_page(device->dev, src, src_offset, len, DMA_TO_DEVICE); tx = device->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, dma_prep_flags); if (!tx) { dma_unmap_page(device->dev, dma_dest, len, DMA_FROM_DEVICE); dma_unmap_page(device->dev, dma_src, len, DMA_TO_DEVICE); } } if (tx) { pr_debug("%s: (async) len: %zu\n", __func__, len); async_tx_submit(chan, tx, submit); } else { void *dest_buf, *src_buf; pr_debug("%s: (sync) len: %zu\n", __func__, len); /* wait for any prerequisite operations */ async_tx_quiesce(&submit->depend_tx); dest_buf = kmap_atomic(dest, KM_USER0) + dest_offset; src_buf = kmap_atomic(src, KM_USER1) + src_offset; memcpy(dest_buf, src_buf, len); kunmap_atomic(src_buf, KM_USER1); kunmap_atomic(dest_buf, KM_USER0); async_tx_sync_epilog(submit); } return tx; }
static struct dma_async_tx_descriptor * async_sum_product(struct page *dest, struct page **srcs, unsigned char *coef, size_t len, struct async_submit_ctl *submit) { struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ, &dest, 1, srcs, 2, len); struct dma_device *dma = chan ? chan->device : NULL; const u8 *amul, *bmul; u8 ax, bx; u8 *a, *b, *c; if (dma) { dma_addr_t dma_dest[2]; dma_addr_t dma_src[2]; struct device *dev = dma->dev; struct dma_async_tx_descriptor *tx; enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P; if (submit->flags & ASYNC_TX_FENCE) dma_flags |= DMA_PREP_FENCE; dma_dest[1] = dma_map_page(dev, dest, 0, len, DMA_BIDIRECTIONAL); dma_src[0] = dma_map_page(dev, srcs[0], 0, len, DMA_TO_DEVICE); dma_src[1] = dma_map_page(dev, srcs[1], 0, len, DMA_TO_DEVICE); tx = dma->device_prep_dma_pq(chan, dma_dest, dma_src, 2, coef, len, dma_flags); if (tx) { async_tx_submit(chan, tx, submit); return tx; } /* could not get a descriptor, unmap and fall through to * the synchronous path */ dma_unmap_page(dev, dma_dest[1], len, DMA_BIDIRECTIONAL); dma_unmap_page(dev, dma_src[0], len, DMA_TO_DEVICE); dma_unmap_page(dev, dma_src[1], len, DMA_TO_DEVICE); } /* run the operation synchronously */ async_tx_quiesce(&submit->depend_tx); amul = raid6_gfmul[coef[0]]; bmul = raid6_gfmul[coef[1]]; a = page_address(srcs[0]); b = page_address(srcs[1]); c = page_address(dest); while (len--) { ax = amul[*a++]; bx = bmul[*b++]; *c++ = ax ^ bx; } return NULL; }
/** * async_memset - attempt to fill memory with a dma engine. * @dest: destination page * @val: fill value * @offset: offset in pages to start transaction * @len: length in bytes * @flags: ASYNC_TX_ACK, ASYNC_TX_DEP_ACK * @depend_tx: memset depends on the result of this transaction * @cb_fn: function to call when the memcpy completes * @cb_param: parameter to pass to the callback routine */ struct dma_async_tx_descriptor * async_memset(struct page *dest, int val, unsigned int offset, size_t len, enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_param) { struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_MEMSET, &dest, 1, NULL, 0, len); struct dma_device *device = chan ? chan->device : NULL; struct dma_async_tx_descriptor *tx = NULL; if (device) { dma_addr_t dma_dest; unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0; dma_dest = dma_map_page(device->dev, dest, offset, len, DMA_FROM_DEVICE); tx = device->device_prep_dma_memset(chan, dma_dest, val, len, dma_prep_flags); } if (tx) { pr_debug("%s: (async) len: %zu\n", __func__, len); async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); } else { /* run the memset synchronously */ void *dest_buf; pr_debug("%s: (sync) len: %zu\n", __func__, len); dest_buf = (void *) (((char *) page_address(dest)) + offset); /* wait for any prerequisite operations */ if (depend_tx) { /* if ack is already set then we cannot be sure * we are referring to the correct operation */ BUG_ON(depend_tx->ack); if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) panic("%s: DMA_ERROR waiting for depend_tx\n", __func__); } memset(dest_buf, val, len); async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); } return tx; }
static struct dma_async_tx_descriptor * async_mult(struct page *dest, struct page *src, u8 coef, size_t len, struct async_submit_ctl *submit) { struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ, &dest, 1, &src, 1, len); struct dma_device *dma = chan ? chan->device : NULL; const u8 *qmul; /* Q multiplier table */ u8 *d, *s; if (dma) { dma_addr_t dma_dest[2]; dma_addr_t dma_src[1]; struct device *dev = dma->dev; struct dma_async_tx_descriptor *tx; enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P; if (submit->flags & ASYNC_TX_FENCE) dma_flags |= DMA_PREP_FENCE; dma_dest[1] = dma_map_page(dev, dest, 0, len, DMA_BIDIRECTIONAL); dma_src[0] = dma_map_page(dev, src, 0, len, DMA_TO_DEVICE); tx = dma->device_prep_dma_pq(chan, dma_dest, dma_src, 1, &coef, len, dma_flags); if (tx) { async_tx_submit(chan, tx, submit); return tx; } /* could not get a descriptor, unmap and fall through to * the synchronous path */ dma_unmap_page(dev, dma_dest[1], len, DMA_BIDIRECTIONAL); dma_unmap_page(dev, dma_src[0], len, DMA_TO_DEVICE); } /* no channel available, or failed to allocate a descriptor, so * perform the operation synchronously */ async_tx_quiesce(&submit->depend_tx); qmul = raid6_gfmul[coef]; d = page_address(dest); s = page_address(src); while (len--) *d++ = qmul[*s++]; return NULL; }
struct dma_async_tx_descriptor * async_memset(struct page *dest, int val, unsigned int offset, size_t len, struct async_submit_ctl *submit) { struct dma_chan *chan = async_tx_find_channel(submit, DMA_MEMSET, &dest, 1, NULL, 0, len); struct dma_device *device = chan ? chan->device : NULL; struct dma_async_tx_descriptor *tx = NULL; if (device && is_dma_fill_aligned(device, offset, 0, len)) { dma_addr_t dma_dest; unsigned long dma_prep_flags = 0; if (submit->cb_fn) dma_prep_flags |= DMA_PREP_INTERRUPT; if (submit->flags & ASYNC_TX_FENCE) dma_prep_flags |= DMA_PREP_FENCE; dma_dest = dma_map_page(device->dev, dest, offset, len, DMA_FROM_DEVICE); tx = device->device_prep_dma_memset(chan, dma_dest, val, len, dma_prep_flags); } if (tx) { pr_debug("%s: (async) len: %zu\n", __func__, len); async_tx_submit(chan, tx, submit); } else { /* run the memset synchronously */ void *dest_buf; pr_debug("%s: (sync) len: %zu\n", __func__, len); dest_buf = page_address(dest) + offset; /* wait for any prerequisite operations */ async_tx_quiesce(&submit->depend_tx); memset(dest_buf, val, len); async_tx_sync_epilog(submit); } return tx; }
/* do_async_xor - dma map the pages and perform the xor with an engine. * This routine is marked __always_inline so it can be compiled away * when CONFIG_DMA_ENGINE=n */ static __always_inline struct dma_async_tx_descriptor * do_async_xor(struct dma_device *device, struct dma_chan *chan, struct page *dest, struct page **src_list, unsigned int offset, unsigned int src_cnt, size_t len, enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_param) { dma_addr_t dma_dest; dma_addr_t *dma_src = (dma_addr_t *) src_list; struct dma_async_tx_descriptor *tx; int i; unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0; pr_debug("%s: len: %zu\n", __func__, len); dma_dest = dma_map_page(device->dev, dest, offset, len, DMA_FROM_DEVICE); for (i = 0; i < src_cnt; i++) dma_src[i] = dma_map_page(device->dev, src_list[i], offset, len, DMA_TO_DEVICE); /* Since we have clobbered the src_list we are committed * to doing this asynchronously. Drivers force forward progress * in case they can not provide a descriptor */ tx = device->device_prep_dma_xor(chan, dma_dest, dma_src, src_cnt, len, dma_prep_flags); if (!tx) { if (depend_tx) dma_wait_for_async_tx(depend_tx); while (!tx) tx = device->device_prep_dma_xor(chan, dma_dest, dma_src, src_cnt, len, dma_prep_flags); } async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); return tx; }
/** * async_trigger_callback - schedules the callback function to be run after * any dependent operations have been completed. * @flags: ASYNC_TX_ACK, ASYNC_TX_DEP_ACK * @depend_tx: 'callback' requires the completion of this transaction * @cb_fn: function to call after depend_tx completes * @cb_param: parameter to pass to the callback routine */ struct dma_async_tx_descriptor * async_trigger_callback(enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_param) { struct dma_chan *chan; struct dma_device *device; struct dma_async_tx_descriptor *tx; if (depend_tx) { chan = depend_tx->chan; device = chan->device; /* see if we can schedule an interrupt * otherwise poll for completion */ if (device && !dma_has_cap(DMA_INTERRUPT, device->cap_mask)) device = NULL; tx = device ? device->device_prep_dma_interrupt(chan, 0) : NULL; } else tx = NULL; if (tx) { pr_debug("%s: (async)\n", __func__); async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); } else { pr_debug("%s: (sync)\n", __func__); /* wait for any prerequisite operations */ async_tx_quiesce(&depend_tx); async_tx_sync_epilog(cb_fn, cb_param); } return tx; }
/** * async_memcpy - attempt to copy memory with a dma engine. * @dest: destination page * @src: src page * @offset: offset in pages to start transaction * @len: length in bytes * @flags: ASYNC_TX_ACK, ASYNC_TX_DEP_ACK, * @depend_tx: memcpy depends on the result of this transaction * @cb_fn: function to call when the memcpy completes * @cb_param: parameter to pass to the callback routine */ struct dma_async_tx_descriptor * async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, unsigned int src_offset, size_t len, enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_param) { struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_MEMCPY, &dest, 1, &src, 1, len); struct dma_device *device = chan ? chan->device : NULL; struct dma_async_tx_descriptor *tx = NULL; if (device) { dma_addr_t dma_dest, dma_src; unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0; dma_dest = dma_map_page(device->dev, dest, dest_offset, len, DMA_FROM_DEVICE); dma_src = dma_map_page(device->dev, src, src_offset, len, DMA_TO_DEVICE); tx = device->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, dma_prep_flags); } if (tx) { pr_debug("%s: (async) len: %zu\n", __func__, len); async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); } else { void *dest_buf, *src_buf; pr_debug("%s: (sync) len: %zu\n", __func__, len); /* wait for any prerequisite operations */ if (depend_tx) { /* if ack is already set then we cannot be sure * we are referring to the correct operation */ BUG_ON(depend_tx->ack); if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) panic("%s: DMA_ERROR waiting for depend_tx\n", __func__); } if (flags & ASYNC_TX_KMAP_DST) dest_buf = kmap_atomic(dest, KM_USER0) + dest_offset; else dest_buf = page_address(dest) + dest_offset; if (flags & ASYNC_TX_KMAP_SRC) src_buf = kmap_atomic(src, KM_USER0) + src_offset; else src_buf = page_address(src) + src_offset; memcpy(dest_buf, src_buf, len); if (flags & ASYNC_TX_KMAP_DST) kunmap_atomic(dest_buf, KM_USER0); if (flags & ASYNC_TX_KMAP_SRC) kunmap_atomic(src_buf, KM_USER0); async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); } return tx; }
/** * async_memcpy - attempt to copy memory with a dma engine. * @dest: destination page * @src: src page * @dest_offset: offset into 'dest' to start transaction * @src_offset: offset into 'src' to start transaction * @len: length in bytes * @submit: submission / completion modifiers * * honored flags: ASYNC_TX_ACK */ struct dma_async_tx_descriptor * async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, unsigned int src_offset, size_t len, struct async_submit_ctl *submit) { struct dma_chan *chan = async_tx_find_channel(submit, DMA_MEMCPY, &dest, 1, &src, 1, len); struct dma_device *device = chan ? chan->device : NULL; struct dma_async_tx_descriptor *tx = NULL; struct dmaengine_unmap_data *unmap = NULL; if (device) unmap = dmaengine_get_unmap_data(device->dev, 2, GFP_NOWAIT); if (unmap && is_dma_copy_aligned(device, src_offset, dest_offset, len)) { unsigned long dma_prep_flags = 0; if (submit->cb_fn) dma_prep_flags |= DMA_PREP_INTERRUPT; if (submit->flags & ASYNC_TX_FENCE) dma_prep_flags |= DMA_PREP_FENCE; unmap->to_cnt = 1; unmap->addr[0] = dma_map_page(device->dev, src, src_offset, len, DMA_TO_DEVICE); unmap->from_cnt = 1; unmap->addr[1] = dma_map_page(device->dev, dest, dest_offset, len, DMA_FROM_DEVICE); unmap->len = len; tx = device->device_prep_dma_memcpy(chan, unmap->addr[1], unmap->addr[0], len, dma_prep_flags); } if (tx) { pr_debug("%s: (async) len: %zu\n", __func__, len); dma_set_unmap(tx, unmap); async_tx_submit(chan, tx, submit); } else { void *dest_buf, *src_buf; pr_debug("%s: (sync) len: %zu\n", __func__, len); /* wait for any prerequisite operations */ async_tx_quiesce(&submit->depend_tx); dest_buf = kmap_atomic(dest) + dest_offset; src_buf = kmap_atomic(src) + src_offset; memcpy(dest_buf, src_buf, len); kunmap_atomic(src_buf); kunmap_atomic(dest_buf); async_tx_sync_epilog(submit); } dmaengine_unmap_put(unmap); return tx; }
/* do_async_xor - dma map the pages and perform the xor with an engine */ static __async_inline struct dma_async_tx_descriptor * do_async_xor(struct dma_chan *chan, struct dmaengine_unmap_data *unmap, struct async_submit_ctl *submit) { struct dma_device *dma = chan->device; struct dma_async_tx_descriptor *tx = NULL; dma_async_tx_callback cb_fn_orig = submit->cb_fn; void *cb_param_orig = submit->cb_param; enum async_tx_flags flags_orig = submit->flags; enum dma_ctrl_flags dma_flags = 0; int src_cnt = unmap->to_cnt; int xor_src_cnt; dma_addr_t dma_dest = unmap->addr[unmap->to_cnt]; dma_addr_t *src_list = unmap->addr; while (src_cnt) { dma_addr_t tmp; submit->flags = flags_orig; xor_src_cnt = min(src_cnt, (int)dma->max_xor); /* if we are submitting additional xors, leave the chain open * and clear the callback parameters */ if (src_cnt > xor_src_cnt) { submit->flags &= ~ASYNC_TX_ACK; submit->flags |= ASYNC_TX_FENCE; submit->cb_fn = NULL; submit->cb_param = NULL; } else { submit->cb_fn = cb_fn_orig; submit->cb_param = cb_param_orig; } if (submit->cb_fn) dma_flags |= DMA_PREP_INTERRUPT; if (submit->flags & ASYNC_TX_FENCE) dma_flags |= DMA_PREP_FENCE; /* Drivers force forward progress in case they can not provide a * descriptor */ tmp = src_list[0]; if (src_list > unmap->addr) src_list[0] = dma_dest; tx = dma->device_prep_dma_xor(chan, dma_dest, src_list, xor_src_cnt, unmap->len, dma_flags); if (unlikely(!tx)) async_tx_quiesce(&submit->depend_tx); /* spin wait for the preceding transactions to complete */ while (unlikely(!tx)) { dma_async_issue_pending(chan); tx = dma->device_prep_dma_xor(chan, dma_dest, src_list, xor_src_cnt, unmap->len, dma_flags); } src_list[0] = tmp; dma_set_unmap(tx, unmap); async_tx_submit(chan, tx, submit); submit->depend_tx = tx; if (src_cnt > xor_src_cnt) { /* drop completed sources */ src_cnt -= xor_src_cnt; /* use the intermediate result a source */ src_cnt++; src_list += xor_src_cnt - 1; } else break; } return tx; }
/* do_async_xor - dma map the pages and perform the xor with an engine */ static __async_inline struct dma_async_tx_descriptor * do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list, unsigned int offset, int src_cnt, size_t len, dma_addr_t *dma_src, struct async_submit_ctl *submit) { struct dma_device *dma = chan->device; struct dma_async_tx_descriptor *tx = NULL; int src_off = 0; int i; dma_async_tx_callback cb_fn_orig = submit->cb_fn; void *cb_param_orig = submit->cb_param; enum async_tx_flags flags_orig = submit->flags; enum dma_ctrl_flags dma_flags; int xor_src_cnt = 0; dma_addr_t dma_dest; /* map the dest bidrectional in case it is re-used as a source */ dma_dest = dma_map_page(dma->dev, dest, offset, len, DMA_BIDIRECTIONAL); for (i = 0; i < src_cnt; i++) { /* only map the dest once */ if (!src_list[i]) continue; if (unlikely(src_list[i] == dest)) { dma_src[xor_src_cnt++] = dma_dest; continue; } dma_src[xor_src_cnt++] = dma_map_page(dma->dev, src_list[i], offset, len, DMA_TO_DEVICE); } src_cnt = xor_src_cnt; while (src_cnt) { submit->flags = flags_orig; dma_flags = 0; xor_src_cnt = min(src_cnt, (int)dma->max_xor); /* if we are submitting additional xors, leave the chain open, * clear the callback parameters, and leave the destination * buffer mapped */ if (src_cnt > xor_src_cnt) { submit->flags &= ~ASYNC_TX_ACK; submit->flags |= ASYNC_TX_FENCE; dma_flags = DMA_COMPL_SKIP_DEST_UNMAP; submit->cb_fn = NULL; submit->cb_param = NULL; } else { submit->cb_fn = cb_fn_orig; submit->cb_param = cb_param_orig; } if (submit->cb_fn) dma_flags |= DMA_PREP_INTERRUPT; if (submit->flags & ASYNC_TX_FENCE) dma_flags |= DMA_PREP_FENCE; /* Since we have clobbered the src_list we are committed * to doing this asynchronously. Drivers force forward progress * in case they can not provide a descriptor */ tx = dma->device_prep_dma_xor(chan, dma_dest, &dma_src[src_off], xor_src_cnt, len, dma_flags); if (unlikely(!tx)) async_tx_quiesce(&submit->depend_tx); /* spin wait for the preceding transactions to complete */ while (unlikely(!tx)) { dma_async_issue_pending(chan); tx = dma->device_prep_dma_xor(chan, dma_dest, &dma_src[src_off], xor_src_cnt, len, dma_flags); } async_tx_submit(chan, tx, submit); submit->depend_tx = tx; if (src_cnt > xor_src_cnt) { /* drop completed sources */ src_cnt -= xor_src_cnt; src_off += xor_src_cnt; /* use the intermediate result a source */ dma_src[--src_off] = dma_dest; src_cnt++; } else break; } return tx; }
/** * async_xor_val - attempt a xor parity check with a dma engine. * @dest: destination page used if the xor is performed synchronously * @src_list: array of source pages * @offset: offset in pages to start transaction * @src_cnt: number of source pages * @len: length in bytes * @result: 0 if sum == 0 else non-zero * @submit: submission / completion modifiers * * honored flags: ASYNC_TX_ACK * * src_list note: if the dest is also a source it must be at index zero. * The contents of this array will be overwritten if a scribble region * is not specified. */ struct dma_async_tx_descriptor * async_xor_val(struct page *dest, struct page **src_list, unsigned int offset, int src_cnt, size_t len, enum sum_check_flags *result, struct async_submit_ctl *submit) { struct dma_chan *chan = xor_val_chan(submit, dest, src_list, src_cnt, len); struct dma_device *device = chan ? chan->device : NULL; struct dma_async_tx_descriptor *tx = NULL; dma_addr_t *dma_src = NULL; BUG_ON(src_cnt <= 1); if (submit->scribble) dma_src = submit->scribble; else if (sizeof(dma_addr_t) <= sizeof(struct page *)) dma_src = (dma_addr_t *) src_list; if (dma_src && device && src_cnt <= device->max_xor && is_dma_xor_aligned(device, offset, 0, len)) { unsigned long dma_prep_flags = 0; int i; pr_debug("%s: (async) len: %zu\n", __func__, len); if (submit->cb_fn) dma_prep_flags |= DMA_PREP_INTERRUPT; if (submit->flags & ASYNC_TX_FENCE) dma_prep_flags |= DMA_PREP_FENCE; for (i = 0; i < src_cnt; i++) dma_src[i] = dma_map_page(device->dev, src_list[i], offset, len, DMA_TO_DEVICE); tx = device->device_prep_dma_xor_val(chan, dma_src, src_cnt, len, result, dma_prep_flags); if (unlikely(!tx)) { async_tx_quiesce(&submit->depend_tx); while (!tx) { dma_async_issue_pending(chan); tx = device->device_prep_dma_xor_val(chan, dma_src, src_cnt, len, result, dma_prep_flags); } } async_tx_submit(chan, tx, submit); } else { enum async_tx_flags flags_orig = submit->flags; pr_debug("%s: (sync) len: %zu\n", __func__, len); WARN_ONCE(device && src_cnt <= device->max_xor, "%s: no space for dma address conversion\n", __func__); submit->flags |= ASYNC_TX_XOR_DROP_DST; submit->flags &= ~ASYNC_TX_ACK; tx = async_xor(dest, src_list, offset, src_cnt, len, submit); async_tx_quiesce(&tx); *result = !page_is_zero(dest, offset, len) << SUM_CHECK_P; async_tx_sync_epilog(submit); submit->flags = flags_orig; } return tx; }
/** * async_xor_zero_sum - attempt a xor parity check with a dma engine. * @dest: destination page used if the xor is performed synchronously * @src_list: array of source pages. The dest page must be listed as a source * at index zero. The contents of this array may be overwritten. * @offset: offset in pages to start transaction * @src_cnt: number of source pages * @len: length in bytes * @result: 0 if sum == 0 else non-zero * @flags: ASYNC_TX_ACK, ASYNC_TX_DEP_ACK * @depend_tx: xor depends on the result of this transaction. * @cb_fn: function to call when the xor completes * @cb_param: parameter to pass to the callback routine */ struct dma_async_tx_descriptor * async_xor_zero_sum(struct page *dest, struct page **src_list, unsigned int offset, int src_cnt, size_t len, u32 *result, enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_param) { struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_ZERO_SUM, &dest, 1, src_list, src_cnt, len); struct dma_device *device = chan ? chan->device : NULL; struct dma_async_tx_descriptor *tx = NULL; BUG_ON(src_cnt <= 1); if (device && src_cnt <= device->max_xor) { dma_addr_t *dma_src = (dma_addr_t *) src_list; unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0; int i; pr_debug("%s: (async) len: %zu\n", __func__, len); for (i = 0; i < src_cnt; i++) dma_src[i] = dma_map_page(device->dev, src_list[i], offset, len, DMA_TO_DEVICE); tx = device->device_prep_dma_zero_sum(chan, dma_src, src_cnt, len, result, dma_prep_flags); if (!tx) { if (depend_tx) dma_wait_for_async_tx(depend_tx); while (!tx) tx = device->device_prep_dma_zero_sum(chan, dma_src, src_cnt, len, result, dma_prep_flags); } async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); } else { unsigned long xor_flags = flags; pr_debug("%s: (sync) len: %zu\n", __func__, len); xor_flags |= ASYNC_TX_XOR_DROP_DST; xor_flags &= ~ASYNC_TX_ACK; tx = async_xor(dest, src_list, offset, src_cnt, len, xor_flags, depend_tx, NULL, NULL); if (tx) { if (dma_wait_for_async_tx(tx) == DMA_ERROR) panic("%s: DMA_ERROR waiting for tx\n", __func__); async_tx_ack(tx); } *result = page_is_zero(dest, offset, len) ? 0 : 1; tx = NULL; async_tx_sync_epilog(flags, depend_tx, cb_fn, cb_param); } return tx; }
/** * async_syndrome_val - asynchronously validate a raid6 syndrome * @blocks: source blocks from idx 0..disks-3, P @ disks-2 and Q @ disks-1 * @offset: common offset into each block (src and dest) to start transaction * @disks: number of blocks (including missing P or Q, see below) * @len: length of operation in bytes * @pqres: on val failure SUM_CHECK_P_RESULT and/or SUM_CHECK_Q_RESULT are set * @spare: temporary result buffer for the synchronous case * @submit: submission / completion modifiers * * The same notes from async_gen_syndrome apply to the 'blocks', * and 'disks' parameters of this routine. The synchronous path * requires a temporary result buffer and submit->scribble to be * specified. */ struct dma_async_tx_descriptor * async_syndrome_val(struct page **blocks, unsigned int offset, int disks, size_t len, enum sum_check_flags *pqres, struct page *spare, struct async_submit_ctl *submit) { struct dma_chan *chan = pq_val_chan(submit, blocks, disks, len); struct dma_device *device = chan ? chan->device : NULL; struct dma_async_tx_descriptor *tx; unsigned char coefs[disks-2]; enum dma_ctrl_flags dma_flags = submit->cb_fn ? DMA_PREP_INTERRUPT : 0; struct dmaengine_unmap_data *unmap = NULL; BUG_ON(disks < 4); if (device) unmap = dmaengine_get_unmap_data(device->dev, disks, GFP_NOWAIT); if (unmap && disks <= dma_maxpq(device, 0) && is_dma_pq_aligned(device, offset, 0, len)) { struct device *dev = device->dev; dma_addr_t pq[2]; int i, j = 0, src_cnt = 0; pr_debug("%s: (async) disks: %d len: %zu\n", __func__, disks, len); unmap->len = len; for (i = 0; i < disks-2; i++) if (likely(blocks[i])) { unmap->addr[j] = dma_map_page(dev, blocks[i], offset, len, DMA_TO_DEVICE); coefs[j] = raid6_gfexp[i]; unmap->to_cnt++; src_cnt++; j++; } if (!P(blocks, disks)) { pq[0] = 0; dma_flags |= DMA_PREP_PQ_DISABLE_P; } else { pq[0] = dma_map_page(dev, P(blocks, disks), offset, len, DMA_TO_DEVICE); unmap->addr[j++] = pq[0]; unmap->to_cnt++; } if (!Q(blocks, disks)) { pq[1] = 0; dma_flags |= DMA_PREP_PQ_DISABLE_Q; } else { pq[1] = dma_map_page(dev, Q(blocks, disks), offset, len, DMA_TO_DEVICE); unmap->addr[j++] = pq[1]; unmap->to_cnt++; } if (submit->flags & ASYNC_TX_FENCE) dma_flags |= DMA_PREP_FENCE; for (;;) { tx = device->device_prep_dma_pq_val(chan, pq, unmap->addr, src_cnt, coefs, len, pqres, dma_flags); if (likely(tx)) break; async_tx_quiesce(&submit->depend_tx); dma_async_issue_pending(chan); } dma_set_unmap(tx, unmap); async_tx_submit(chan, tx, submit); return tx; } else { struct page *p_src = P(blocks, disks); struct page *q_src = Q(blocks, disks); enum async_tx_flags flags_orig = submit->flags; dma_async_tx_callback cb_fn_orig = submit->cb_fn; void *scribble = submit->scribble; void *cb_param_orig = submit->cb_param; void *p, *q, *s; pr_debug("%s: (sync) disks: %d len: %zu\n", __func__, disks, len); /* caller must provide a temporary result buffer and * allow the input parameters to be preserved */ BUG_ON(!spare || !scribble); /* wait for any prerequisite operations */ async_tx_quiesce(&submit->depend_tx); /* recompute p and/or q into the temporary buffer and then * check to see the result matches the current value */ tx = NULL; *pqres = 0; if (p_src) { init_async_submit(submit, ASYNC_TX_XOR_ZERO_DST, NULL, NULL, NULL, scribble); tx = async_xor(spare, blocks, offset, disks-2, len, submit); async_tx_quiesce(&tx); p = page_address(p_src) + offset; s = page_address(spare) + offset; *pqres |= !!memcmp(p, s, len) << SUM_CHECK_P; } if (q_src) { P(blocks, disks) = NULL; Q(blocks, disks) = spare; init_async_submit(submit, 0, NULL, NULL, NULL, scribble); tx = async_gen_syndrome(blocks, offset, disks, len, submit); async_tx_quiesce(&tx); q = page_address(q_src) + offset; s = page_address(spare) + offset; *pqres |= !!memcmp(q, s, len) << SUM_CHECK_Q; } /* restore P, Q and submit */ P(blocks, disks) = p_src; Q(blocks, disks) = q_src; submit->cb_fn = cb_fn_orig; submit->cb_param = cb_param_orig; submit->flags = flags_orig; async_tx_sync_epilog(submit); return NULL; } }
/** * do_async_gen_syndrome - asynchronously calculate P and/or Q */ static __async_inline struct dma_async_tx_descriptor * do_async_gen_syndrome(struct dma_chan *chan, const unsigned char *scfs, int disks, struct dmaengine_unmap_data *unmap, enum dma_ctrl_flags dma_flags, struct async_submit_ctl *submit) { struct dma_async_tx_descriptor *tx = NULL; struct dma_device *dma = chan->device; enum async_tx_flags flags_orig = submit->flags; dma_async_tx_callback cb_fn_orig = submit->cb_fn; dma_async_tx_callback cb_param_orig = submit->cb_param; int src_cnt = disks - 2; unsigned short pq_src_cnt; dma_addr_t dma_dest[2]; int src_off = 0; if (submit->flags & ASYNC_TX_FENCE) dma_flags |= DMA_PREP_FENCE; while (src_cnt > 0) { submit->flags = flags_orig; pq_src_cnt = min(src_cnt, dma_maxpq(dma, dma_flags)); /* if we are submitting additional pqs, leave the chain open, * clear the callback parameters, and leave the destination * buffers mapped */ if (src_cnt > pq_src_cnt) { submit->flags &= ~ASYNC_TX_ACK; submit->flags |= ASYNC_TX_FENCE; submit->cb_fn = NULL; submit->cb_param = NULL; } else { submit->cb_fn = cb_fn_orig; submit->cb_param = cb_param_orig; if (cb_fn_orig) dma_flags |= DMA_PREP_INTERRUPT; } /* Drivers force forward progress in case they can not provide * a descriptor */ for (;;) { dma_dest[0] = unmap->addr[disks - 2]; dma_dest[1] = unmap->addr[disks - 1]; tx = dma->device_prep_dma_pq(chan, dma_dest, &unmap->addr[src_off], pq_src_cnt, &scfs[src_off], unmap->len, dma_flags); if (likely(tx)) break; async_tx_quiesce(&submit->depend_tx); dma_async_issue_pending(chan); } dma_set_unmap(tx, unmap); async_tx_submit(chan, tx, submit); submit->depend_tx = tx; /* drop completed sources */ src_cnt -= pq_src_cnt; src_off += pq_src_cnt; dma_flags |= DMA_PREP_CONTINUE; } return tx; }
/** * async_xor_zero_sum - attempt a PQ parities check with a dma engine. * @pdest: P-parity destination to check * @qdest: Q-parity destination to check * @src_list: array of source pages; the 1st pointer is qdest, the 2nd - pdest. * @scoef_list: coefficients to use in GF-multiplications * @offset: offset in pages to start transaction * @src_cnt: number of source pages * @len: length in bytes * @presult: 0 if P parity is OK else non-zero * @qresult: 0 if Q parity is OK else non-zero * @flags: ASYNC_TX_ASSUME_COHERENT, ASYNC_TX_ACK, ASYNC_TX_DEP_ACK * @depend_tx: depends on the result of this transaction. * @callback: function to call when the xor completes * @callback_param: parameter to pass to the callback routine */ struct dma_async_tx_descriptor * async_pqxor_zero_sum(struct page *pdest, struct page *qdest, struct page **src_list, unsigned char *scf, unsigned int offset, int src_cnt, size_t len, u32 *presult, u32 *qresult, enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_param) { struct dma_chan *chan = async_tx_find_channel(depend_tx, DMA_PQ_ZERO_SUM, src_list, 2, &src_list[2], src_cnt, len); struct dma_device *device = chan ? chan->device : NULL; struct dma_async_tx_descriptor *tx = NULL; BUG_ON(src_cnt <= 1); BUG_ON(!qdest || qdest != src_list[0] || pdest != src_list[1]); if (device) { dma_addr_t *dma_src = (dma_addr_t *)src_list; unsigned long dma_prep_flags = cb_fn ? DMA_PREP_INTERRUPT : 0; int i; for (i = 0; i < src_cnt; i++) dma_src[i] = dma_map_page(device->dev, src_list[i], offset, len, DMA_TO_DEVICE); tx = device->device_prep_dma_pqzero_sum(chan, dma_src, src_cnt, scf, len, presult, qresult, dma_prep_flags); if (!tx) { if (depend_tx) dma_wait_for_async_tx(depend_tx); while (!tx) tx = device->device_prep_dma_pqzero_sum(chan, dma_src, src_cnt, scf, len, presult, qresult, dma_prep_flags); } async_tx_submit(chan, tx, flags, depend_tx, cb_fn, cb_param); } else { unsigned long lflags = flags; /* TBD: support for lengths size of more than PAGE_SIZE */ lflags &= ~ASYNC_TX_ACK; spin_lock(&spare_lock); do_sync_pqxor(spare_pages[0], spare_pages[1], &src_list[2], offset, src_cnt - 2, len, lflags, depend_tx, NULL, NULL); if (presult && pdest) *presult = memcmp(page_address(pdest), page_address(spare_pages[0]), len) == 0 ? 0 : 1; if (qresult && qdest) *qresult = memcmp(page_address(qdest), page_address(spare_pages[1]), len) == 0 ? 0 : 1; spin_unlock(&spare_lock); } return tx; }
static __async_inline struct dma_async_tx_descriptor * do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list, unsigned int offset, int src_cnt, size_t len, dma_addr_t *dma_src, struct async_submit_ctl *submit) { struct dma_device *dma = chan->device; struct dma_async_tx_descriptor *tx = NULL; int src_off = 0; int i; dma_async_tx_callback cb_fn_orig = submit->cb_fn; void *cb_param_orig = submit->cb_param; enum async_tx_flags flags_orig = submit->flags; enum dma_ctrl_flags dma_flags; int xor_src_cnt = 0; dma_addr_t dma_dest; dma_dest = dma_map_page(dma->dev, dest, offset, len, DMA_BIDIRECTIONAL); for (i = 0; i < src_cnt; i++) { if (!src_list[i]) continue; if (unlikely(src_list[i] == dest)) { dma_src[xor_src_cnt++] = dma_dest; continue; } dma_src[xor_src_cnt++] = dma_map_page(dma->dev, src_list[i], offset, len, DMA_TO_DEVICE); } src_cnt = xor_src_cnt; while (src_cnt) { submit->flags = flags_orig; dma_flags = 0; xor_src_cnt = min(src_cnt, (int)dma->max_xor); if (src_cnt > xor_src_cnt) { submit->flags &= ~ASYNC_TX_ACK; submit->flags |= ASYNC_TX_FENCE; dma_flags = DMA_COMPL_SKIP_DEST_UNMAP; submit->cb_fn = NULL; submit->cb_param = NULL; } else { submit->cb_fn = cb_fn_orig; submit->cb_param = cb_param_orig; } if (submit->cb_fn) dma_flags |= DMA_PREP_INTERRUPT; if (submit->flags & ASYNC_TX_FENCE) dma_flags |= DMA_PREP_FENCE; tx = dma->device_prep_dma_xor(chan, dma_dest, &dma_src[src_off], xor_src_cnt, len, dma_flags); if (unlikely(!tx)) async_tx_quiesce(&submit->depend_tx); while (unlikely(!tx)) { dma_async_issue_pending(chan); tx = dma->device_prep_dma_xor(chan, dma_dest, &dma_src[src_off], xor_src_cnt, len, dma_flags); } async_tx_submit(chan, tx, submit); submit->depend_tx = tx; if (src_cnt > xor_src_cnt) { src_cnt -= xor_src_cnt; src_off += xor_src_cnt; dma_src[--src_off] = dma_dest; src_cnt++; } else break; } return tx; }
/* do_async_xor - dma map the pages and perform the xor with an engine. * This routine is marked __always_inline so it can be compiled away * when CONFIG_DMA_ENGINE=n */ static __always_inline struct dma_async_tx_descriptor * do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list, unsigned int offset, int src_cnt, size_t len, enum async_tx_flags flags, struct dma_async_tx_descriptor *depend_tx, dma_async_tx_callback cb_fn, void *cb_param) { struct dma_device *dma = chan->device; dma_addr_t *dma_src = (dma_addr_t *) src_list; struct dma_async_tx_descriptor *tx = NULL; int src_off = 0; int i; dma_async_tx_callback _cb_fn; void *_cb_param; enum async_tx_flags async_flags; enum dma_ctrl_flags dma_flags; int xor_src_cnt; dma_addr_t dma_dest; dma_dest = dma_map_page(dma->dev, dest, offset, len, DMA_FROM_DEVICE); for (i = 0; i < src_cnt; i++) dma_src[i] = dma_map_page(dma->dev, src_list[i], offset, len, DMA_TO_DEVICE); while (src_cnt) { async_flags = flags; dma_flags = 0; xor_src_cnt = min(src_cnt, dma->max_xor); /* if we are submitting additional xors, leave the chain open, * clear the callback parameters, and leave the destination * buffer mapped */ if (src_cnt > xor_src_cnt) { async_flags &= ~ASYNC_TX_ACK; dma_flags = DMA_COMPL_SKIP_DEST_UNMAP; _cb_fn = NULL; _cb_param = NULL; } else { _cb_fn = cb_fn; _cb_param = cb_param; } if (_cb_fn) dma_flags |= DMA_PREP_INTERRUPT; /* Since we have clobbered the src_list we are committed * to doing this asynchronously. Drivers force forward progress * in case they can not provide a descriptor */ tx = dma->device_prep_dma_xor(chan, dma_dest, &dma_src[src_off], xor_src_cnt, len, dma_flags); if (unlikely(!tx)) async_tx_quiesce(&depend_tx); /* spin wait for the preceeding transactions to complete */ while (unlikely(!tx)) { dma_async_issue_pending(chan); tx = dma->device_prep_dma_xor(chan, dma_dest, &dma_src[src_off], xor_src_cnt, len, dma_flags); } async_tx_submit(chan, tx, async_flags, depend_tx, _cb_fn, _cb_param); depend_tx = tx; flags |= ASYNC_TX_DEP_ACK; if (src_cnt > xor_src_cnt) { /* drop completed sources */ src_cnt -= xor_src_cnt; src_off += xor_src_cnt; /* use the intermediate result a source */ dma_src[--src_off] = dma_dest; src_cnt++; } else break; } return tx; }
/** * do_async_gen_syndrome - asynchronously calculate P and/or Q */ static __async_inline struct dma_async_tx_descriptor * do_async_gen_syndrome(struct dma_chan *chan, struct page **blocks, const unsigned char *scfs, unsigned int offset, int disks, size_t len, dma_addr_t *dma_src, struct async_submit_ctl *submit) { struct dma_async_tx_descriptor *tx = NULL; struct dma_device *dma = chan->device; enum dma_ctrl_flags dma_flags = 0; enum async_tx_flags flags_orig = submit->flags; dma_async_tx_callback cb_fn_orig = submit->cb_fn; dma_async_tx_callback cb_param_orig = submit->cb_param; int src_cnt = disks - 2; unsigned char coefs[src_cnt]; unsigned short pq_src_cnt; dma_addr_t dma_dest[2]; int src_off = 0; int idx; int i; /* DMAs use destinations as sources, so use BIDIRECTIONAL mapping */ if (P(blocks, disks)) dma_dest[0] = dma_map_page(dma->dev, P(blocks, disks), offset, len, DMA_BIDIRECTIONAL); else dma_flags |= DMA_PREP_PQ_DISABLE_P; if (Q(blocks, disks)) dma_dest[1] = dma_map_page(dma->dev, Q(blocks, disks), offset, len, DMA_BIDIRECTIONAL); else dma_flags |= DMA_PREP_PQ_DISABLE_Q; /* convert source addresses being careful to collapse 'empty' * sources and update the coefficients accordingly */ for (i = 0, idx = 0; i < src_cnt; i++) { if (blocks[i] == NULL) continue; dma_src[idx] = dma_map_page(dma->dev, blocks[i], offset, len, DMA_TO_DEVICE); coefs[idx] = scfs[i]; idx++; } src_cnt = idx; while (src_cnt > 0) { submit->flags = flags_orig; pq_src_cnt = min(src_cnt, dma_maxpq(dma, dma_flags)); /* if we are submitting additional pqs, leave the chain open, * clear the callback parameters, and leave the destination * buffers mapped */ if (src_cnt > pq_src_cnt) { submit->flags &= ~ASYNC_TX_ACK; submit->flags |= ASYNC_TX_FENCE; dma_flags |= DMA_COMPL_SKIP_DEST_UNMAP; submit->cb_fn = NULL; submit->cb_param = NULL; } else { dma_flags &= ~DMA_COMPL_SKIP_DEST_UNMAP; submit->cb_fn = cb_fn_orig; submit->cb_param = cb_param_orig; if (cb_fn_orig) dma_flags |= DMA_PREP_INTERRUPT; } if (submit->flags & ASYNC_TX_FENCE) dma_flags |= DMA_PREP_FENCE; /* Since we have clobbered the src_list we are committed * to doing this asynchronously. Drivers force forward * progress in case they can not provide a descriptor */ for (;;) { tx = dma->device_prep_dma_pq(chan, dma_dest, &dma_src[src_off], pq_src_cnt, &coefs[src_off], len, dma_flags); if (likely(tx)) break; async_tx_quiesce(&submit->depend_tx); dma_async_issue_pending(chan); } async_tx_submit(chan, tx, submit); submit->depend_tx = tx; /* drop completed sources */ src_cnt -= pq_src_cnt; src_off += pq_src_cnt; dma_flags |= DMA_PREP_CONTINUE; } return tx; }
/** * async_syndrome_val - asynchronously validate a raid6 syndrome * @blocks: source blocks from idx 0..disks-3, P @ disks-2 and Q @ disks-1 * @offset: common offset into each block (src and dest) to start transaction * @disks: number of blocks (including missing P or Q, see below) * @len: length of operation in bytes * @pqres: on val failure SUM_CHECK_P_RESULT and/or SUM_CHECK_Q_RESULT are set * @spare: temporary result buffer for the synchronous case * @submit: submission / completion modifiers * * The same notes from async_gen_syndrome apply to the 'blocks', * and 'disks' parameters of this routine. The synchronous path * requires a temporary result buffer and submit->scribble to be * specified. */ struct dma_async_tx_descriptor * async_syndrome_val(struct page **blocks, unsigned int offset, int disks, size_t len, enum sum_check_flags *pqres, struct page *spare, struct async_submit_ctl *submit) { struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ_VAL, NULL, 0, blocks, disks, len); struct dma_device *device = chan ? chan->device : NULL; struct dma_async_tx_descriptor *tx; enum dma_ctrl_flags dma_flags = submit->cb_fn ? DMA_PREP_INTERRUPT : 0; dma_addr_t *dma_src = NULL; BUG_ON(disks < 4); if (submit->scribble) dma_src = submit->scribble; else if (sizeof(dma_addr_t) <= sizeof(struct page *)) dma_src = (dma_addr_t *) blocks; if (dma_src && device && disks <= dma_maxpq(device, 0) && is_dma_pq_aligned(device, offset, 0, len)) { struct device *dev = device->dev; dma_addr_t *pq = &dma_src[disks-2]; int i; pr_debug("%s: (async) disks: %d len: %zu\n", __func__, disks, len); if (!P(blocks, disks)) dma_flags |= DMA_PREP_PQ_DISABLE_P; if (!Q(blocks, disks)) dma_flags |= DMA_PREP_PQ_DISABLE_Q; if (submit->flags & ASYNC_TX_FENCE) dma_flags |= DMA_PREP_FENCE; for (i = 0; i < disks; i++) if (likely(blocks[i])) { BUG_ON(is_raid6_zero_block(blocks[i])); dma_src[i] = dma_map_page(dev, blocks[i], offset, len, DMA_TO_DEVICE); } for (;;) { tx = device->device_prep_dma_pq_val(chan, pq, dma_src, disks - 2, raid6_gfexp, len, pqres, dma_flags); if (likely(tx)) break; async_tx_quiesce(&submit->depend_tx); dma_async_issue_pending(chan); } async_tx_submit(chan, tx, submit); return tx; } else { struct page *p_src = P(blocks, disks); struct page *q_src = Q(blocks, disks); enum async_tx_flags flags_orig = submit->flags; dma_async_tx_callback cb_fn_orig = submit->cb_fn; void *scribble = submit->scribble; void *cb_param_orig = submit->cb_param; void *p, *q, *s; pr_debug("%s: (sync) disks: %d len: %zu\n", __func__, disks, len); /* caller must provide a temporary result buffer and * allow the input parameters to be preserved */ BUG_ON(!spare || !scribble); /* wait for any prerequisite operations */ async_tx_quiesce(&submit->depend_tx); /* recompute p and/or q into the temporary buffer and then * check to see the result matches the current value */ tx = NULL; *pqres = 0; if (p_src) { init_async_submit(submit, ASYNC_TX_XOR_ZERO_DST, NULL, NULL, NULL, scribble); tx = async_xor(spare, blocks, offset, disks-2, len, submit); async_tx_quiesce(&tx); p = page_address(p_src) + offset; s = page_address(spare) + offset; *pqres |= !!memcmp(p, s, len) << SUM_CHECK_P; } if (q_src) { P(blocks, disks) = NULL; Q(blocks, disks) = spare; init_async_submit(submit, 0, NULL, NULL, NULL, scribble); tx = async_gen_syndrome(blocks, offset, disks, len, submit); async_tx_quiesce(&tx); q = page_address(q_src) + offset; s = page_address(spare) + offset; *pqres |= !!memcmp(q, s, len) << SUM_CHECK_Q; } /* restore P, Q and submit */ P(blocks, disks) = p_src; Q(blocks, disks) = q_src; submit->cb_fn = cb_fn_orig; submit->cb_param = cb_param_orig; submit->flags = flags_orig; async_tx_sync_epilog(submit); return NULL; } }