Asynchronous raid6 acceleration (part 1 of 3)
Subsystems (8)
v3 cover Asynchronous raid6 acceleration (part 1 of 3)
Extend the async_tx api to include routines for offloading raid6 operations to hardware. This is part1, part2 contains raid6 extensions to the iop-adma driver, and part3 contains the changes to make the md/raid6 driver asynchronous.
The combined series is available at:
git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx.git raid6
The changes since v2 [1] are:
1/ Moved various stripe handling resources to a percpu allocation.
Specifically the 'scribble' buffer (for passing and converting address lists) and the raid6 spare page have been moved.
2/ Added 4-disk and 5-disk special cases to the raid6 recovery code as
they are special with respect to offload engines. The recovery algorithms use a zero page in the place of the dead data pages. When the async_tx api sees the raid6_empty_zero_page as a source it simply drops it in the asynchronous path. This can result, for example, in a PQ operation with a source count of zero in the case of recovering 2 data disks in a 4-disk array. This does not map to hardware so the recovery code refactors the operation in these special cases. The raid6test module has been updated to hit these cases.
I can carry this in async_tx.git/next, or let me know if you would rather pull this into md.git/for-next.
Thanks,
Dan
[1]: http://marc.info/?l=linux-raid&m=124269482227272&w=2
Note, I know that "md/raid6: release spare page at ->stop()" is already upstream, but I have not rebased the tree to preserve the testing that has gone into this series to date.
---
Dan Williams (14):
async_tx: rename zero_sum to val
async_tx: kill ASYNC_TX_DEP_ACK flag
async_tx: structify submission arguments, add scribble
async_xor: permit callers to pass in a 'dma/page scribble' region md/raid6: release spare page at ->stop()
md/raid6: move the spare page to a percpu allocation md/raid5,6: add percpu scribble region for buffer lists
async_tx: add sum check flags
async_tx: kill needless module_{init|exit}
async_tx: remove walk of tx->parent chain in dma_wait_for_async_tx
async_tx: add support for asynchronous GF multiplication
async_tx: add support for asynchronous RAID6 recovery operations
dmatest: add pq support
async_tx: raid6 recovery self test
Documentation/crypto/async-tx-api.txt | 75 +++- arch/arm/include/asm/hardware/iop3xx-adma.h | 5 arch/arm/mach-iop13xx/include/mach/adma.h | 12 - arch/arm/mach-iop13xx/setup.c | 10 - arch/arm/plat-iop/adma.c | 2 crypto/async_tx/Kconfig | 9 + crypto/async_tx/Makefile | 3 crypto/async_tx/async_memcpy.c | 39 +- crypto/async_tx/async_memset.c | 38 +- crypto/async_tx/async_pq.c | 388 +++++++++++++++++++++++ crypto/async_tx/async_raid6_recov.c | 448 +++++++++++++++++++++++++++ crypto/async_tx/async_tx.c | 83 ++--- crypto/async_tx/async_xor.c | 199 ++++++------ crypto/async_tx/raid6test.c | 241 ++++++++++++++ drivers/dma/Kconfig | 2 drivers/dma/dmaengine.c | 53 +-- drivers/dma/dmatest.c | 26 ++ drivers/dma/iop-adma.c | 40 +- drivers/md/Kconfig | 13 + drivers/md/raid5.c | 452 ++++++++++++++++++--------- drivers/md/raid5.h | 22 + include/linux/async_tx.h | 126 ++++++-- include/linux/dmaengine.h | 116 ++++++- 23 files changed, 1907 insertions(+), 495 deletions(-) create mode 100644 crypto/async_tx/async_pq.c create mode 100644 crypto/async_tx/async_raid6_recov.c create mode 100644 crypto/async_tx/raid6test.c
PATCH v3 1/14 async_tx: rename zero_sum to val
'zero_sum' does not properly describe the operation of generating parity and checking that it validates against an existing buffer. Change the name of the operation to 'val' (for 'validate'). This is in anticipation of the p+q case where it is a requirement to identify the target parity buffers separately from the source buffers, because the target parity buffers will not have corresponding pq coefficients.
--- Documentation/crypto/async-tx-api.txt | 14 ++++++------ arch/arm/mach-iop13xx/setup.c | 8 +++---- arch/arm/plat-iop/adma.c | 2 +- crypto/async_tx/async_xor.c | 16 +++++++------- drivers/dma/dmaengine.c | 4 ++- drivers/dma/iop-adma.c | 38 +++++++++++++++++---------------- drivers/md/raid5.c | 2 +- include/linux/async_tx.h | 2 +- include/linux/dmaengine.h | 8 +++---- 9 files changed, 47 insertions(+), 47 deletions(-)
Documentation/crypto/async-tx-api.txt +7 -7
@@ -61,13 +61,13 @@ async_<operation>(<op specific parameters>, void *callback_parameter); 3.2 Supported operations: -memcpy - memory copy between a source and a destination buffer -memset - fill a destination buffer with a byte value -xor - xor a series of source buffers and write the result to a - destination buffer -xor_zero_sum - xor a series of source buffers and set a flag if the - result is zero. The implementation attempts to prevent - writes to memory +memcpy - memory copy between a source and a destination buffer +memset - fill a destination buffer with a byte value +xor - xor a series of source buffers and write the result to a + destination buffer +xor_val - xor a series of source buffers and set a flag if the + result is zero. The implementation attempts to prevent + writes to memory 3.3 Descriptor management: The return value is non-NULL and points to a 'descriptor' when the operation
arch/arm/mach-iop13xx/setup.c +4 -4
@@ -478,7 +478,7 @@ void __init iop13xx_platform_init(void)dma_cap_set (DMA_MEMCPY , plat_data->cap_mask);dma_cap_set (DMA_XOR , plat_data->cap_mask);dma_cap_set (DMA_DUAL_XOR , plat_data->cap_mask); -dma_cap_set (DMA_ZERO_SUM , plat_data->cap_mask); +dma_cap_set (DMA_XOR_VAL , plat_data->cap_mask);dma_cap_set (DMA_MEMSET , plat_data->cap_mask);dma_cap_set (DMA_MEMCPY_CRC32C , plat_data->cap_mask);dma_cap_set (DMA_INTERRUPT , plat_data->cap_mask);
@@ -490,7 +490,7 @@ void __init iop13xx_platform_init(void)dma_cap_set (DMA_MEMCPY , plat_data->cap_mask);dma_cap_set (DMA_XOR , plat_data->cap_mask);dma_cap_set (DMA_DUAL_XOR , plat_data->cap_mask); -dma_cap_set (DMA_ZERO_SUM , plat_data->cap_mask); +dma_cap_set (DMA_XOR_VAL , plat_data->cap_mask);dma_cap_set (DMA_MEMSET , plat_data->cap_mask);dma_cap_set (DMA_MEMCPY_CRC32C , plat_data->cap_mask);dma_cap_set (DMA_INTERRUPT , plat_data->cap_mask);
@@ -502,13 +502,13 @@ void __init iop13xx_platform_init(void)dma_cap_set (DMA_MEMCPY , plat_data->cap_mask);dma_cap_set (DMA_XOR , plat_data->cap_mask);dma_cap_set (DMA_DUAL_XOR , plat_data->cap_mask); -dma_cap_set (DMA_ZERO_SUM , plat_data->cap_mask); +dma_cap_set (DMA_XOR_VAL , plat_data->cap_mask);dma_cap_set (DMA_MEMSET , plat_data->cap_mask);dma_cap_set (DMA_MEMCPY_CRC32C , plat_data->cap_mask);dma_cap_set (DMA_INTERRUPT , plat_data->cap_mask);dma_cap_set (DMA_PQ_XOR , plat_data->cap_mask);dma_cap_set (DMA_PQ_UPDATE , plat_data->cap_mask); -dma_cap_set (DMA_PQ_ZERO_SUM , plat_data->cap_mask); +dma_cap_set (DMA_PQ_VAL , plat_data->cap_mask);break ; } }
arch/arm/plat-iop/adma.c +1 -1
@@ -198,7 +198,7 @@ static int __init iop3xx_adma_cap_init(void)dma_cap_set (DMA_INTERRUPT , iop3xx_aau_data.cap_mask);#else dma_cap_set (DMA_XOR , iop3xx_aau_data.cap_mask); -dma_cap_set (DMA_ZERO_SUM , iop3xx_aau_data.cap_mask); +dma_cap_set (DMA_XOR_VAL , iop3xx_aau_data.cap_mask);dma_cap_set (DMA_MEMSET , iop3xx_aau_data.cap_mask);dma_cap_set (DMA_INTERRUPT , iop3xx_aau_data.cap_mask);#endif
crypto/async_tx/async_xor.c +8 -8
@@ -222,7 +222,7 @@ static int page_is_zero(struct page *p, unsigned int offset, size_t len) }/** -* async_xor_zero_sum - attempt a xor parity check with a dma engine. +* 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. The dest page must be listed as a source * at index zero. The contents of this array may be overwritten.
@@ -236,13 +236,13 @@ static int page_is_zero(struct page *p, unsigned int offset, size_t len)* @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, +async_xor_val (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 , +struct dma_chan *chan =async_tx_find_channel (depend_tx,DMA_XOR_VAL , &dest,1 , src_list, src_cnt, len);struct dma_device *device = chan ? chan->device :NULL ;
@@ -261,15 +261,15 @@ async_xor_zero_sum(struct page *dest, struct page **src_list, 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); + tx = device->device_prep_dma_xor_val (chan, dma_src, src_cnt, + len, result, + dma_prep_flags);if (unlikely (!tx)) {async_tx_quiesce (&depend_tx);while (!tx) {dma_async_issue_pending (chan); - tx = device->device_prep_dma_zero_sum (chan, + tx = device->device_prep_dma_xor_val (chan, dma_src, src_cnt, len, result, dma_prep_flags); }
@@ -296,7 +296,7 @@ async_xor_zero_sum(struct page *dest, struct page **src_list,return tx; } -EXPORT_SYMBOL_GPL (async_xor_zero_sum); +EXPORT_SYMBOL_GPL (async_xor_val);static int __init async_xor_init (void ) {
drivers/dma/dmaengine.c +2 -2
@@ -644,8 +644,8 @@ int dma_async_device_register(struct dma_device *device) !device->device_prep_dma_memcpy);BUG_ON (dma_has_cap (DMA_XOR , device->cap_mask) && !device->device_prep_dma_xor); -BUG_ON (dma_has_cap (DMA_ZERO_SUM , device->cap_mask) && - !device->device_prep_dma_zero_sum); +BUG_ON (dma_has_cap (DMA_XOR_VAL , device->cap_mask) && + !device->device_prep_dma_xor_val);BUG_ON (dma_has_cap (DMA_MEMSET , device->cap_mask) && !device->device_prep_dma_memset); BUG_ON(dma_has_cap(DMA_INTERRUPT, device->cap_mask) &&
drivers/dma/iop-adma.c +19 -19
@@ -660,9 +660,9 @@ iop_adma_prep_dma_xor(struct dma_chan *chan, dma_addr_t dma_dest, }static struct dma_async_tx_descriptor * -iop_adma_prep_dma_zero_sum (struct dma_chan *chan,dma_addr_t *dma_src, -unsigned int src_cnt,size_t len,u32 *result, -unsigned long flags) +iop_adma_prep_dma_xor_val (struct dma_chan *chan,dma_addr_t *dma_src, +unsigned int src_cnt,size_t len,u32 *result, +unsigned long flags) {struct iop_adma_chan *iop_chan =to_iop_adma_chan (chan);struct iop_adma_desc_slot *sw_desc, *grp_start;
@@ -906,7 +906,7 @@ out:#define IOP_ADMA_NUM_SRC_TEST 4/* must be <= 15 */ static int __devinit -iop_adma_xor_zero_sum_self_test (struct iop_adma_device *device) +iop_adma_xor_val_self_test (struct iop_adma_device *device) {int i, src_idx;struct page *dest;
@@ -1002,7 +1002,7 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) PAGE_SIZE, DMA_TO_DEVICE);/* skip zero sum if the capability is not present */ -if (!dma_has_cap (DMA_ZERO_SUM , dma_chan->device->cap_mask)) +if (!dma_has_cap (DMA_XOR_VAL , dma_chan->device->cap_mask)) goto free_resources;/* zero sum the sources with the destintation page */
@@ -1016,10 +1016,10 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) dma_srcs[i] =dma_map_page (dma_chan->device->dev, zero_sum_srcs[i],0 ,PAGE_SIZE ,DMA_TO_DEVICE ); - tx =iop_adma_prep_dma_zero_sum (dma_chan, dma_srcs, -IOP_ADMA_NUM_SRC_TEST +1 ,PAGE_SIZE , - &zero_sum_result, -DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); + tx =iop_adma_prep_dma_xor_val (dma_chan, dma_srcs, +IOP_ADMA_NUM_SRC_TEST +1 ,PAGE_SIZE , + &zero_sum_result, +DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); cookie =iop_adma_tx_submit (tx);iop_adma_issue_pending (dma_chan);
@@ -1072,10 +1072,10 @@ iop_adma_xor_zero_sum_self_test(struct iop_adma_device *device) dma_srcs[i] =dma_map_page (dma_chan->device->dev, zero_sum_srcs[i],0 ,PAGE_SIZE ,DMA_TO_DEVICE ); - tx =iop_adma_prep_dma_zero_sum (dma_chan, dma_srcs, -IOP_ADMA_NUM_SRC_TEST +1 ,PAGE_SIZE , - &zero_sum_result, -DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); + tx =iop_adma_prep_dma_xor_val (dma_chan, dma_srcs, +IOP_ADMA_NUM_SRC_TEST +1 ,PAGE_SIZE , + &zero_sum_result, +DMA_PREP_INTERRUPT |DMA_CTRL_ACK ); cookie =iop_adma_tx_submit (tx);iop_adma_issue_pending (dma_chan);
@@ -1192,9 +1192,9 @@ static int __devinit iop_adma_probe(struct platform_device *pdev) dma_dev->max_xor =iop_adma_get_max_xor (); dma_dev->device_prep_dma_xor = iop_adma_prep_dma_xor; } -if (dma_has_cap (DMA_ZERO_SUM , dma_dev->cap_mask)) - dma_dev->device_prep_dma_zero_sum = - iop_adma_prep_dma_zero_sum; +if (dma_has_cap (DMA_XOR_VAL , dma_dev->cap_mask)) + dma_dev->device_prep_dma_xor_val = + iop_adma_prep_dma_xor_val;if (dma_has_cap (DMA_INTERRUPT , dma_dev->cap_mask)) dma_dev->device_prep_dma_interrupt = iop_adma_prep_dma_interrupt;
@@ -1249,7 +1249,7 @@ static int __devinit iop_adma_probe(struct platform_device *pdev)if (dma_has_cap (DMA_XOR , dma_dev->cap_mask) ||dma_has_cap (DMA_MEMSET , dma_dev->cap_mask)) { - ret =iop_adma_xor_zero_sum_self_test (adev); + ret =iop_adma_xor_val_self_test (adev);dev_dbg (&pdev->dev,"xor self test returned %d\n" , ret);if (ret) goto err_free_iop_chan;
@@ -1259,10 +1259,10 @@ static int __devinit iop_adma_probe(struct platform_device *pdev)"( %s%s%s%s%s%s%s%s%s%s)\n" ,dma_has_cap (DMA_PQ_XOR , dma_dev->cap_mask) ?"pq_xor " :"" ,dma_has_cap (DMA_PQ_UPDATE , dma_dev->cap_mask) ?"pq_update " :"" , -dma_has_cap (DMA_PQ_ZERO_SUM , dma_dev->cap_mask) ?"pq_zero_sum " :"" , +dma_has_cap (DMA_PQ_VAL , dma_dev->cap_mask) ?"pq_val " :"" ,dma_has_cap (DMA_XOR , dma_dev->cap_mask) ?"xor " :"" ,dma_has_cap (DMA_DUAL_XOR , dma_dev->cap_mask) ?"dual_xor " :"" , -dma_has_cap (DMA_ZERO_SUM , dma_dev->cap_mask) ?"xor_zero_sum " :"" , +dma_has_cap (DMA_XOR_VAL , dma_dev->cap_mask) ?"xor_val " :"" ,dma_has_cap (DMA_MEMSET , dma_dev->cap_mask) ?"fill " :"" ,dma_has_cap (DMA_MEMCPY_CRC32C , dma_dev->cap_mask) ?"cpy+crc " :"" , dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "",
drivers/md/raid5.c +1 -1
@@ -854,7 +854,7 @@ static void ops_run_check(struct stripe_head *sh) xor_srcs[count++] = dev->page; } - tx =async_xor_zero_sum (xor_dest, xor_srcs,0 , count,STRIPE_SIZE , + tx =async_xor_val (xor_dest, xor_srcs,0 , count,STRIPE_SIZE , &sh->ops.zero_sum_result,0 ,NULL ,NULL ,NULL );atomic_inc (&sh->count);
include/linux/async_tx.h +1 -1
@@ -117,7 +117,7 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset, dma_async_tx_callback cb_fn, void *cb_fn_param);struct dma_async_tx_descriptor * -async_xor_zero_sum(struct page *dest, struct page **src_list, +async_xor_val(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,
include/linux/dmaengine.h +4 -4
@@ -55,8 +55,8 @@ enum dma_transaction_type {DMA_PQ_XOR ,DMA_DUAL_XOR ,DMA_PQ_UPDATE , -DMA_ZERO_SUM , -DMA_PQ_ZERO_SUM , +DMA_XOR_VAL , +DMA_PQ_VAL ,DMA_MEMSET ,DMA_MEMCPY_CRC32C , DMA_INTERRUPT,
@@ -214,7 +214,7 @@ struct dma_async_tx_descriptor {* @device_free_chan_resources: release DMA channel's resources * @device_prep_dma_memcpy: prepares a memcpy operation * @device_prep_dma_xor: prepares a xor operation -* @device_prep_dma_zero_sum: prepares a zero_sum operation +* @device_prep_dma_xor_val: prepares a xor validation operation * @device_prep_dma_memset: prepares a memset operation * @device_prep_dma_interrupt: prepares an end of chain interrupt operation * @device_prep_slave_sg: prepares a slave dma operation
@@ -243,7 +243,7 @@ struct dma_device {struct dma_async_tx_descriptor *(*device_prep_dma_xor)(struct dma_chan *chan,dma_addr_t dest,dma_addr_t *src,unsigned int src_cnt,size_t len,unsigned long flags); -struct dma_async_tx_descriptor *(*device_prep_dma_zero_sum)( +struct dma_async_tx_descriptor *(*device_prep_dma_xor_val)(struct dma_chan *chan,dma_addr_t *src,unsigned int src_cnt,size_t len,u32 *result,unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_memset)(
PATCH v3 2/14 async_tx: kill ASYNC_TX_DEP_ACK flag
In reply to Dan WilliamsIn support of inter-channel chaining async_tx utilizes an ack flag to gate whether a dependent operation can be chained to another. While the flag is not set the chain can be considered open for appending. Setting the ack flag closes the chain and flags the descriptor for garbage collection. The ASYNC_TX_DEP_ACK flag essentially means "close the chain after adding this dependency". Since each operation can only have one child the api now implicitly sets the ack flag at dependency submission time. This removes an unnecessary management burden from clients of the api.
[ Impact: clean up and enforce one dependency per operation ]
--- Documentation/crypto/async-tx-api.txt | 9 ++++----- crypto/async_tx/async_memcpy.c | 2 +- crypto/async_tx/async_memset.c | 2 +- crypto/async_tx/async_tx.c | 4 ++-- crypto/async_tx/async_xor.c | 6 ++---- drivers/md/raid5.c | 25 +++++++++++-------------- include/linux/async_tx.h | 4 +--- 7 files changed, 22 insertions(+), 30 deletions(-)
Documentation/crypto/async-tx-api.txt +4 -5
@@ -80,8 +80,8 @@ acknowledged by the application before the offload engine driver is allowed to recycle (or free) the descriptor. A descriptor can be acked by one of the following methods: 1/ setting the ASYNC_TX_ACK flag if no child operations are to be submitted -2/ setting the ASYNC_TX_DEP_ACK flag to acknowledge the parent - descriptor of a new operation. +2/ submitting an unacknowledged descriptor as a dependency to another + async_tx call will implicitly set the acknowledged state. 3/ calling async_tx_ack() on the descriptor. 3.4 When does the operation execute?
@@ -136,10 +136,9 @@ int run_xor_copy_xor(struct page **xor_srcs, tx = async_xor(xor_dest, xor_srcs, 0, xor_src_cnt, xor_len, ASYNC_TX_XOR_DROP_DST, NULL, NULL, NULL); - tx = async_memcpy(copy_dest, copy_src, 0, 0, copy_len, - ASYNC_TX_DEP_ACK, tx, NULL, NULL); + tx = async_memcpy(copy_dest, copy_src, 0, 0, copy_len, tx, NULL, NULL); tx = async_xor(xor_dest, xor_srcs, 0, xor_src_cnt, xor_len, - ASYNC_TX_XOR_DROP_DST | ASYNC_TX_DEP_ACK | ASYNC_TX_ACK, + ASYNC_TX_XOR_DROP_DST | ASYNC_TX_ACK, tx, complete_xor_copy_xor, NULL); async_tx_issue_pending_all();
crypto/async_tx/async_memcpy.c +1 -1
@@ -35,7 +35,7 @@* @src: src page * @offset: offset in pages to start transaction * @len: length in bytes -* @flags: ASYNC_TX_ACK, ASYNC_TX_DEP_ACK, +* @flags: ASYNC_TX_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
crypto/async_tx/async_memset.c +1 -1
@@ -35,7 +35,7 @@* @val: fill value * @offset: offset in pages to start transaction * @len: length in bytes -* @flags: ASYNC_TX_ACK, ASYNC_TX_DEP_ACK +* @flags: ASYNC_TX_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
crypto/async_tx/async_tx.c +2 -2
@@ -223,7 +223,7 @@ async_tx_submit(struct dma_chan *chan, struct dma_async_tx_descriptor *tx,if (flags &ASYNC_TX_ACK )async_tx_ack (tx); -if (depend_tx && (flags &ASYNC_TX_DEP_ACK )) +if (depend_tx)async_tx_ack (depend_tx); }EXPORT_SYMBOL_GPL (async_tx_submit);
@@ -231,7 +231,7 @@ EXPORT_SYMBOL_GPL(async_tx_submit);/** * 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 +* @flags: ASYNC_TX_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
crypto/async_tx/async_xor.c +2 -4
@@ -105,7 +105,6 @@ do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list, _cb_param); depend_tx = tx; - flags |=ASYNC_TX_DEP_ACK ;if (src_cnt > xor_src_cnt) {/* drop completed sources */
@@ -168,8 +167,7 @@ do_sync_xor(struct page *dest, struct page **src_list, unsigned int offset,* @offset: offset in pages to start transaction * @src_cnt: number of source pages * @len: length in bytes -* @flags: ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DEST, -* ASYNC_TX_ACK, ASYNC_TX_DEP_ACK +* @flags: ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DEST, ASYNC_TX_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
@@ -230,7 +228,7 @@ static int page_is_zero(struct page *p, unsigned int offset, size_t len)* @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 +* @flags: ASYNC_TX_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
drivers/md/raid5.c +11 -14
@@ -525,14 +525,12 @@ async_copy_data(int frombio, struct bio *bio, struct page *page, bio_page =bio_iovec_idx (bio, i)->bv_page;if (frombio) tx =async_memcpy (page, bio_page, page_offset, - b_offset, clen, -ASYNC_TX_DEP_ACK , - tx,NULL ,NULL ); + b_offset, clen,0 , + tx,NULL ,NULL );else tx =async_memcpy (bio_page, page, b_offset, - page_offset, clen, -ASYNC_TX_DEP_ACK , - tx,NULL ,NULL ); + page_offset, clen,0 , + tx,NULL ,NULL ); }if (clen < len)/* hit end of page */ break ;
@@ -615,8 +613,7 @@ static void ops_run_biofill(struct stripe_head *sh) }atomic_inc (&sh->count); -async_trigger_callback (ASYNC_TX_DEP_ACK |ASYNC_TX_ACK , tx, - ops_complete_biofill, sh); +async_trigger_callback (ASYNC_TX_ACK , tx, ops_complete_biofill, sh); }static void ops_complete_compute5 (void *stripe_head_ref)
@@ -701,8 +698,8 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) } tx =async_xor (xor_dest, xor_srcs,0 , count,STRIPE_SIZE , -ASYNC_TX_DEP_ACK |ASYNC_TX_XOR_DROP_DST , tx, - ops_complete_prexor, sh); +ASYNC_TX_XOR_DROP_DST , tx, + ops_complete_prexor, sh);return tx; }
@@ -809,7 +806,7 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx)* set ASYNC_TX_XOR_DROP_DST and ASYNC_TX_XOR_ZERO_DST * for the synchronous xor case */ - flags =ASYNC_TX_DEP_ACK |ASYNC_TX_ACK | + flags =ASYNC_TX_ACK | (prexor ?ASYNC_TX_XOR_DROP_DST :ASYNC_TX_XOR_ZERO_DST );atomic_inc (&sh->count);
@@ -858,7 +855,7 @@ static void ops_run_check(struct stripe_head *sh) &sh->ops.zero_sum_result, 0, NULL, NULL, NULL);atomic_inc (&sh->count); - tx =async_trigger_callback (ASYNC_TX_DEP_ACK |ASYNC_TX_ACK , tx, + tx =async_trigger_callback (ASYNC_TX_ACK , tx, ops_complete_check, sh); }
@@ -2687,8 +2684,8 @@ static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh,/* place all the copies on one channel */ tx =async_memcpy (sh2->dev[dd_idx].page, - sh->dev[i].page,0 ,0 ,STRIPE_SIZE , -ASYNC_TX_DEP_ACK , tx,NULL ,NULL ); + sh->dev[i].page,0 ,0 ,STRIPE_SIZE , +0 , tx,NULL ,NULL );set_bit (R5_Expanded, &sh2->dev[dd_idx].flags);set_bit (R5_UPTODATE , &sh2->dev[dd_idx].flags);
include/linux/async_tx.h +1 -3
@@ -58,13 +58,11 @@ struct dma_chan_ref {* array. * @ASYNC_TX_ACK: immediately ack the descriptor, precludes setting up a * dependency chain -* @ASYNC_TX_DEP_ACK: ack the dependency descriptor. Useful for chaining. */ enum async_tx_flags {ASYNC_TX_XOR_ZERO_DST = (1 <<0 ),ASYNC_TX_XOR_DROP_DST = (1 <<1 ), -ASYNC_TX_ACK = (1 <<3 ), -ASYNC_TX_DEP_ACK = (1 <<4 ), +ASYNC_TX_ACK = (1 <<2 ), };#ifdef CONFIG_DMA_ENGINE
PATCH v3 3/14 async_tx: structify submission arguments, add scribble
In reply to Dan WilliamsPrepare the api for the arrival of a new parameter, 'scribble'. This will allow callers to identify scratchpad memory for dma address or page address conversions. As this adds yet another parameter, take this opportunity to convert the common submission parameters (flags, dependency, callback, and callback argument) into an object that is passed by reference.
Also, take this opportunity to fix up the kerneldoc and add notes about the relevant ASYNC_TX_* flags for each routine.
[ Impact: moves api pass-by-value parameters to a pass-by-reference struct ]
--- Documentation/crypto/async-tx-api.txt | 6 -- crypto/async_tx/async_memcpy.c | 26 +++---- crypto/async_tx/async_memset.c | 25 +++---- crypto/async_tx/async_tx.c | 51 +++++++------- crypto/async_tx/async_xor.c | 123 ++++++++++++++++----------------- drivers/md/raid5.c | 59 +++++++++------- include/linux/async_tx.h | 84 ++++++++++++++--------- 7 files changed, 200 insertions(+), 174 deletions(-)
Documentation/crypto/async-tx-api.txt +1 -5
@@ -54,11 +54,7 @@ features surfaced as a result: 3.1 General format of the API: struct dma_async_tx_descriptor * -async_<operation>(<op specific parameters>, - enum async_tx_flags flags, - struct dma_async_tx_descriptor *dependency, - dma_async_tx_callback callback_routine, - void *callback_parameter); +async_<operation>(<op specific parameters>, struct async_submit ctl *submit) 3.2 Supported operations: memcpy - memory copy between a source and a destination buffer
crypto/async_tx/async_memcpy.c +13 -13
@@ -33,28 +33,28 @@* async_memcpy - attempt to copy memory with a dma engine. * @dest: destination page * @src: src page -* @offset: offset in pages to start transaction +* @dest_offset: offset into 'dest' to start transaction +* @src_offset: offset into 'src' to start transaction * @len: length in bytes -* @flags: ASYNC_TX_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 +* @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,enum async_tx_flags flags, -struct dma_async_tx_descriptor *depend_tx, -dma_async_tx_callback cb_fn,void *cb_param) +unsigned int src_offset,size_t len, +struct async_submit_ctl *submit) { -struct dma_chan *chan =async_tx_find_channel (depend_tx,DMA_MEMCPY , +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) {dma_addr_t dma_dest, dma_src; -unsigned long dma_prep_flags = cb_fn ?DMA_PREP_INTERRUPT :0 ; +unsigned long dma_prep_flags; + dma_prep_flags = submit->cb_fn ?DMA_PREP_INTERRUPT :0 ; dma_dest =dma_map_page (device->dev, dest, dest_offset, len,DMA_FROM_DEVICE );
@@ -67,13 +67,13 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,if (tx) {pr_debug ("%s: (async) len: %zu\n" , __func__, len); -async_tx_submit (chan, tx, flags, depend_tx, cb_fn, cb_param); +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 (&depend_tx); +async_tx_quiesce (&submit->depend_tx); dest_buf =kmap_atomic (dest,KM_USER0 ) + dest_offset; src_buf =kmap_atomic (src,KM_USER1 ) + src_offset;
@@ -83,7 +83,7 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset,kunmap_atomic (dest_buf,KM_USER0 );kunmap_atomic (src_buf,KM_USER1 ); -async_tx_sync_epilog (cb_fn, cb_param); +async_tx_sync_epilog (submit); }return tx;
crypto/async_tx/async_memset.c +11 -14
@@ -35,26 +35,23 @@* @val: fill value * @offset: offset in pages to start transaction * @len: length in bytes -* @flags: ASYNC_TX_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 +* +* honored flags: ASYNC_TX_ACK */ 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) +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 (depend_tx,DMA_MEMSET , +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) {dma_addr_t dma_dest; -unsigned long dma_prep_flags = cb_fn ?DMA_PREP_INTERRUPT :0 ; +unsigned long dma_prep_flags; + dma_prep_flags = submit->cb_fn ?DMA_PREP_INTERRUPT :0 ; dma_dest =dma_map_page (device->dev, dest, offset, len,DMA_FROM_DEVICE );
@@ -64,19 +61,19 @@ async_memset(struct page *dest, int val, unsigned int offset,if (tx) {pr_debug ("%s: (async) len: %zu\n" , __func__, len); -async_tx_submit (chan, tx, flags, depend_tx, cb_fn, cb_param); +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 = (void *) (((char *)page_address (dest)) + offset); + dest_buf =page_address (dest) + offset;/* wait for any prerequisite operations */ -async_tx_quiesce (&depend_tx); +async_tx_quiesce (&submit->depend_tx);memset (dest_buf, val, len); -async_tx_sync_epilog (cb_fn, cb_param); +async_tx_sync_epilog (submit); }return tx;
crypto/async_tx/async_tx.c +27 -24
106 lines not on this page · raw message
crypto/async_tx/async_xor.c +60 -63
254 lines not on this page · raw message
drivers/md/raid5.c +34 -25
158 lines not on this page · raw message
include/linux/async_tx.h +54 -30
123 lines not on this page · raw message
PATCH v3 4/14 async_xor: permit callers to pass in a 'dma/page scribble' region
In reply to Dan Williamsasync_xor() needs space to perform dma and page address conversions. In most cases the code can simply reuse the struct page * array because the size of the native pointer matches the size of a dma/page address. In order to support archs where sizeof(dma_addr_t) is larger than sizeof(struct page *), or to preserve the input parameters, we utilize a memory region passed in by the caller.
Since the code is now prepared to handle the case where it cannot perform address conversions on the stack, we no longer need the !HIGHMEM64G dependency in drivers/dma/Kconfig.
[ Impact: don't clobber input buffers for address conversions ]
--- Documentation/crypto/async-tx-api.txt | 43 +++++++++++++++--------- crypto/async_tx/async_xor.c | 60 ++++++++++++++++----------------- drivers/dma/Kconfig | 2 + 3 files changed, 58 insertions(+), 47 deletions(-)
Documentation/crypto/async-tx-api.txt +28 -15
@@ -115,29 +115,42 @@ of an operation. Perform a xor->copy->xor operation where each operation depends on the result from the previous operation: -void complete_xor_copy_xor(void *param) +void callback(void *param) { - printk("complete\n"); + struct completion *cmp = param; + + complete(cmp); } -int run_xor_copy_xor(struct page **xor_srcs, - int xor_src_cnt, - struct page *xor_dest, - size_t xor_len, - struct page *copy_src, - struct page *copy_dest, - size_t copy_len) +void run_xor_copy_xor(struct page **xor_srcs, + int xor_src_cnt, + struct page *xor_dest, + size_t xor_len, + struct page *copy_src, + struct page *copy_dest, + size_t copy_len) { struct dma_async_tx_descriptor *tx; + addr_conv_t addr_conv[xor_src_cnt]; + struct async_submit_ctl submit; + addr_conv_t addr_conv[NDISKS]; + struct completion cmp; + + init_async_submit(&submit, ASYNC_TX_XOR_DROP_DST, NULL, NULL, NULL, + addr_conv); + tx = async_xor(xor_dest, xor_srcs, 0, xor_src_cnt, xor_len, &submit) - tx = async_xor(xor_dest, xor_srcs, 0, xor_src_cnt, xor_len, - ASYNC_TX_XOR_DROP_DST, NULL, NULL, NULL); - tx = async_memcpy(copy_dest, copy_src, 0, 0, copy_len, tx, NULL, NULL); - tx = async_xor(xor_dest, xor_srcs, 0, xor_src_cnt, xor_len, - ASYNC_TX_XOR_DROP_DST | ASYNC_TX_ACK, - tx, complete_xor_copy_xor, NULL); + submit->depend_tx = tx; + tx = async_memcpy(copy_dest, copy_src, 0, 0, copy_len, &submit); + + init_completion(&cmp); + init_async_submit(&submit, ASYNC_TX_XOR_DROP_DST | ASYNC_TX_ACK, tx, + callback, &cmp, addr_conv); + tx = async_xor(xor_dest, xor_srcs, 0, xor_src_cnt, xor_len, &submit); async_tx_issue_pending_all(); + + wait_for_completion(&cmp); } See include/linux/async_tx.h for more information on the flags. See the
crypto/async_tx/async_xor.c +29 -31
112 lines not on this page · raw message
drivers/dma/Kconfig +1 -1
@@ -4,7 +4,7 @@ menuconfig DMADEVICES bool "DMA Engine support" - depends on !HIGHMEM64G && HAS_DMA + depends on HAS_DMA help DMA engines can do asynchronous data transfers without involving the host CPU. Currently, this framework can be
PATCH v3 5/14 md/raid6: release spare page at ->stop()
In reply to Dan WilliamsAdd missing call to safe_put_page from stop() by unifying open coded raid5_conf_t de-allocation under free_conf().
--- drivers/md/raid5.c | 26 ++++++++++++-------------- 1 files changed, 12 insertions(+), 14 deletions(-)
drivers/md/raid5.c +12 -14
@@ -4309,6 +4309,15 @@ raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks)return sectors * (raid_disks - conf->max_degraded); } +static void free_conf (raid5_conf_t *conf) +{ +shrink_stripes (conf); +safe_put_page (conf->spare_page); +kfree (conf->disks); +kfree (conf->stripe_hashtbl); +kfree (conf); +} +static raid5_conf_t *setup_conf (mddev_t *mddev) {raid5_conf_t *conf;
@@ -4438,11 +4447,7 @@ static raid5_conf_t *setup_conf(mddev_t *mddev) abort:if (conf) { -shrink_stripes (conf); -safe_put_page (conf->spare_page); -kfree (conf->disks); -kfree (conf->stripe_hashtbl); -kfree (conf); +free_conf (conf);return ERR_PTR (-EIO ); }else return ERR_PTR (-ENOMEM );
@@ -4608,12 +4613,8 @@ abort:md_unregister_thread (mddev->thread); mddev->thread =NULL ;if (conf) { -shrink_stripes (conf);print_raid5_conf (conf); -safe_put_page (conf->spare_page); -kfree (conf->disks); -kfree (conf->stripe_hashtbl); -kfree (conf); +free_conf (conf); } mddev->private =NULL ;printk (KERN_ALERT "raid5: failed to run raid set %s\n" ,mdname (mddev));
@@ -4628,13 +4629,10 @@ static int stop(mddev_t *mddev)md_unregister_thread (mddev->thread); mddev->thread =NULL ; -shrink_stripes (conf); -kfree (conf->stripe_hashtbl); mddev->queue->backing_dev_info.congested_fn =NULL ;blk_sync_queue (mddev->queue);/* the unplug fn references 'conf'*/ sysfs_remove_group (&mddev->kobj, &raid5_attrs_group); -kfree (conf->disks); -kfree (conf); +free_conf (conf); mddev->private =NULL ;return 0 ; }
PATCH v3 6/14 md/raid6: move the spare page to a percpu allocation
In reply to Dan WilliamsIn preparation for asynchronous handling of raid6 operations move the spare page to a percpu allocation to allow multiple simultaneous synchronous raid6 recovery operations.
Make this allocation cpu hotplug aware to maximize allocation efficiency.
--- drivers/md/raid5.c | 252 +++++++++++++++++++++++++++++++++++----------------- drivers/md/raid5.h | 9 +- 2 files changed, 175 insertions(+), 86 deletions(-)
drivers/md/raid5.c +168 -84
@@ -48,6 +48,7 @@#include <linux/raid/pq.h> #include <linux/async_tx.h> #include <linux/seq_file.h> +#include <linux/cpu.h> #include "md.h" #include "raid5.h" #include "bitmap.h"
@@ -2565,14 +2566,15 @@ static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh,static void handle_parity_checks6 (raid5_conf_t *conf,struct stripe_head *sh, -struct stripe_head_state *s, -struct r6_state *r6s,struct page *tmp_page, -int disks) +struct stripe_head_state *s, +struct r6_state *r6s,int disks) {int update_p =0 , update_q =0 ;struct r5dev *dev;int pd_idx = sh->pd_idx;int qd_idx = sh->qd_idx; +unsigned long cpu; +struct page *tmp_page;set_bit (STRIPE_HANDLE , &sh->state);
@@ -2583,78 +2585,75 @@ static void handle_parity_checks6(raid5_conf_t *conf, struct stripe_head *sh,* case we can only check one of them, possibly using the * other to generate missing data */ - -/* If !tmp_page, we cannot do the calculations, -* but as we have set STRIPE_HANDLE, we will soon be called -* by stripe_handle with a tmp_page - just wait until then. -*/ -if (tmp_page) { -if (s->failed == r6s->q_failed) { -/* The only possible failed device holds 'Q', so it -* makes sense to check P (If anything else were failed, -* we would have used P to recreate it). -*/ -compute_block_1 (sh, pd_idx,1 ); -if (!page_is_zero (sh->dev[pd_idx].page)) { -compute_block_1 (sh, pd_idx,0 ); - update_p =1 ; - } - } -if (!r6s->q_failed && s->failed <2 ) { -/* q is not failed, and we didn't use it to generate -* anything, so it makes sense to check it -*/ -memcpy (page_address (tmp_page), -page_address (sh->dev[qd_idx].page), -STRIPE_SIZE ); -compute_parity6 (sh,UPDATE_PARITY ); -if (memcmp (page_address (tmp_page), -page_address (sh->dev[qd_idx].page), -STRIPE_SIZE ) !=0 ) { -clear_bit (STRIPE_INSYNC , &sh->state); - update_q =1 ; - } + cpu =get_cpu (); + tmp_page =per_cpu_ptr (conf->percpu, cpu)->spare_page; +if (s->failed == r6s->q_failed) { +/* The only possible failed device holds 'Q', so it +* makes sense to check P (If anything else were failed, +* we would have used P to recreate it). +*/ +compute_block_1 (sh, pd_idx,1 ); +if (!page_is_zero (sh->dev[pd_idx].page)) { +compute_block_1 (sh, pd_idx,0 ); + update_p =1 ; } -if (update_p || update_q) { - conf->mddev->resync_mismatches +=STRIPE_SECTORS ; -if (test_bit (MD_RECOVERY_CHECK , &conf->mddev->recovery)) -/* don't try to repair!! */ - update_p = update_q =0 ; + } +if (!r6s->q_failed && s->failed <2 ) { +/* q is not failed, and we didn't use it to generate +* anything, so it makes sense to check it +*/ +memcpy (page_address (tmp_page), +page_address (sh->dev[qd_idx].page), +STRIPE_SIZE ); +compute_parity6 (sh,UPDATE_PARITY ); +if (memcmp (page_address (tmp_page), +page_address (sh->dev[qd_idx].page), +STRIPE_SIZE ) !=0 ) { +clear_bit (STRIPE_INSYNC , &sh->state); + update_q =1 ; } + } +put_cpu (); -/* now write out any block on a failed drive, -* or P or Q if they need it -*/ +if (update_p || update_q) { + conf->mddev->resync_mismatches +=STRIPE_SECTORS ; +if (test_bit (MD_RECOVERY_CHECK , &conf->mddev->recovery)) +/* don't try to repair!! */ + update_p = update_q =0 ; + } -if (s->failed ==2 ) { - dev = &sh->dev[r6s->failed_num[1 ]]; - s->locked++; -set_bit (R5_LOCKED , &dev->flags); -set_bit (R5_Wantwrite, &dev->flags); - } -if (s->failed >=1 ) { - dev = &sh->dev[r6s->failed_num[0 ]]; - s->locked++; -set_bit (R5_LOCKED , &dev->flags); -set_bit (R5_Wantwrite, &dev->flags); - } +/* now write out any block on a failed drive, +* or P or Q if they need it +*/ -if (update_p) { - dev = &sh->dev[pd_idx]; - s->locked++; -set_bit (R5_LOCKED , &dev->flags); -set_bit (R5_Wantwrite, &dev->flags); - } -if (update_q) { - dev = &sh->dev[qd_idx]; - s->locked++; -set_bit (R5_LOCKED , &dev->flags); -set_bit (R5_Wantwrite, &dev->flags); - } -clear_bit (STRIPE_DEGRADED , &sh->state); +if (s->failed ==2 ) { + dev = &sh->dev[r6s->failed_num[1 ]]; + s->locked++; +set_bit (R5_LOCKED , &dev->flags); +set_bit (R5_Wantwrite, &dev->flags); + } +if (s->failed >=1 ) { + dev = &sh->dev[r6s->failed_num[0 ]]; + s->locked++; +set_bit (R5_LOCKED , &dev->flags); +set_bit (R5_Wantwrite, &dev->flags); + } -set_bit (STRIPE_INSYNC , &sh->state); +if (update_p) { + dev = &sh->dev[pd_idx]; + s->locked++; +set_bit (R5_LOCKED , &dev->flags); +set_bit (R5_Wantwrite, &dev->flags); + } +if (update_q) { + dev = &sh->dev[qd_idx]; + s->locked++; +set_bit (R5_LOCKED , &dev->flags); +set_bit (R5_Wantwrite, &dev->flags); } +clear_bit (STRIPE_DEGRADED , &sh->state); + +set_bit (STRIPE_INSYNC , &sh->state); } static void handle_stripe_expansion(raid5_conf_t *conf, struct stripe_head *sh,
@@ -3009,7 +3008,7 @@ static bool handle_stripe5(struct stripe_head *sh)return blocked_rdev ==NULL ; } -static bool handle_stripe6 (struct stripe_head *sh,struct page *tmp_page) +static bool handle_stripe6 (struct stripe_head *sh) {raid5_conf_t *conf = sh->raid_conf;int disks = sh->disks;
@@ -3164,7 +3163,7 @@ static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page)* data is available */ if (s.syncing && s.locked ==0 && !test_bit (STRIPE_INSYNC , &sh->state)) -handle_parity_checks6 (conf, sh, &s, &r6s, tmp_page, disks); +handle_parity_checks6 (conf, sh, &s, &r6s, disks);if (s.syncing && s.locked ==0 &&test_bit (STRIPE_INSYNC , &sh->state)) {md_done_sync (conf->mddev,STRIPE_SECTORS ,1 );
@@ -3247,16 +3246,14 @@ static bool handle_stripe6(struct stripe_head *sh, struct page *tmp_page) }/* returns true if the stripe was handled */ -static bool handle_stripe (struct stripe_head *sh,struct page *tmp_page) +static bool handle_stripe (struct stripe_head *sh) {if (sh->raid_conf->level ==6 ) -return handle_stripe6 (sh, tmp_page); +return handle_stripe6 (sh);else return handle_stripe5 (sh); } - -static void raid5_activate_delayed (raid5_conf_t *conf) {if (atomic_read (&conf->preread_active_stripes) <IO_THRESHOLD ) {
@@ -4047,7 +4044,7 @@ static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skispin_unlock (&sh->lock);/* wait for any blocked device to be handled */ -while (unlikely (!handle_stripe (sh,NULL ))) +while (unlikely (!handle_stripe (sh))) ;release_stripe (sh);
@@ -4104,7 +4101,7 @@ static int retry_aligned_read(raid5_conf_t *conf, struct bio *raid_bio)return handled; } -handle_stripe (sh,NULL ); +handle_stripe (sh);release_stripe (sh); handled++; }
@@ -4168,7 +4165,7 @@ static void raid5d(mddev_t *mddev)spin_unlock_irq (&conf->device_lock); handled++; -handle_stripe (sh, conf->spare_page); +handle_stripe (sh);release_stripe (sh);spin_lock_irq (&conf->device_lock);
@@ -4309,15 +4306,104 @@ raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks)return sectors * (raid_disks - conf->max_degraded); } +static void raid5_free_percpu (raid5_conf_t *conf) +{ +struct raid5_percpu *percpu; +unsigned long cpu; + +if (!conf->percpu) +return ; + +get_online_cpus (); +for_each_possible_cpu (cpu) { + percpu =per_cpu_ptr (conf->percpu, cpu); +safe_put_page (percpu->spare_page); + } +#ifdef CONFIG_HOTPLUG_CPU +unregister_cpu_notifier (&conf->cpu_notify); +#endif +put_online_cpus (); + +free_percpu (conf->percpu); +} +static void free_conf (raid5_conf_t *conf) {shrink_stripes (conf); -safe_put_page (conf->spare_page); +raid5_free_percpu (conf);kfree (conf->disks);kfree (conf->stripe_hashtbl);kfree (conf); } +#ifdef CONFIG_HOTPLUG_CPU +static int raid456_cpu_notify (struct notifier_block *nfb,unsigned long action, +void *hcpu) +{ +raid5_conf_t *conf =container_of (nfb, raid5_conf_t, cpu_notify); +long cpu = (long )hcpu; +struct raid5_percpu *percpu =per_cpu_ptr (conf->percpu, cpu); + +switch (action) { +case CPU_UP_PREPARE : +case CPU_UP_PREPARE_FROZEN : +if (!percpu->spare_page) + percpu->spare_page =alloc_page (GFP_KERNEL ); +if (!percpu->spare_page) { +pr_err ("%s: failed memory allocation for cpu%ld\n" , + __func__, cpu); +return NOTIFY_BAD ; + } +break ; +case CPU_DEAD : +case CPU_DEAD_FROZEN : +safe_put_page (percpu->spare_page); + percpu->spare_page =NULL ; +break ; +default : +break ; + } +return NOTIFY_OK ; +} +#endif + +static int raid5_alloc_percpu (raid5_conf_t *conf) +{ +unsigned long cpu; +struct page *spare_page; +struct raid5_percpu *allcpus; +int err; + +/* the only percpu data is the raid6 spare page */ +if (conf->level !=6 ) +return 0 ; + + allcpus = alloc_percpu(struct raid5_percpu); +if (!allcpus) +return -ENOMEM ; + conf->percpu = allcpus; + +get_online_cpus (); + err =0 ; +for_each_present_cpu (cpu) { + spare_page =alloc_page (GFP_KERNEL ); +if (!spare_page) { + err = -ENOMEM ; +break ; + } +per_cpu_ptr (conf->percpu, cpu)->spare_page = spare_page; + } +#ifdef CONFIG_HOTPLUG_CPU + conf->cpu_notify.notifier_call = raid456_cpu_notify; + conf->cpu_notify.priority =0 ; +if (err ==0 ) + err =register_cpu_notifier (&conf->cpu_notify); +#endif +put_online_cpus (); + +return err; +} +static raid5_conf_t *setup_conf (mddev_t *mddev) {raid5_conf_t *conf;
@@ -4372,11 +4458,10 @@ static raid5_conf_t *setup_conf(mddev_t *mddev)if ((conf->stripe_hashtbl =kzalloc (PAGE_SIZE ,GFP_KERNEL )) ==NULL ) goto abort; -if (mddev->new_level ==6 ) { - conf->spare_page =alloc_page (GFP_KERNEL ); -if (!conf->spare_page) - goto abort; - } + conf->level = mddev->new_level; +if (raid5_alloc_percpu (conf) !=0 ) + goto abort; +spin_lock_init (&conf->device_lock);init_waitqueue_head (&conf->wait_for_stripe);init_waitqueue_head (&conf->wait_for_overlap);
@@ -4412,7 +4497,6 @@ static raid5_conf_t *setup_conf(mddev_t *mddev) } conf->chunk_size = mddev->new_chunk; - conf->level = mddev->new_level;if (conf->level ==6 ) conf->max_degraded =2 ;else
drivers/md/raid5.h +7 -2
@@ -383,8 +383,13 @@ struct raid5_private_data {* (fresh device added). * Cleared when a sync completes. */ - -struct page *spare_page;/* Used when checking P/Q in raid6 */ +/* per cpu variables */ +struct raid5_percpu { +struct page *spare_page;/* Used when checking P/Q in raid6 */ + } *percpu; +#ifdef CONFIG_HOTPLUG_CPU +struct notifier_block cpu_notify; +#endif /* * Free stripes pool
PATCH v3 7/14 md/raid5, 6: add percpu scribble region for buffer lists
In reply to Dan WilliamsUse percpu memory rather than stack for storing the buffer lists used in parity calculations. Include space for dma address conversions and pass that to async_tx via the async_submit_ctl.scribble pointer.
[ Impact: move memory pressure from stack to heap ]
--- drivers/md/raid5.c | 132 ++++++++++++++++++++++++++++++++++++++++------------ drivers/md/raid5.h | 8 +++ 2 files changed, 110 insertions(+), 30 deletions(-)
drivers/md/raid5.c +102 -30
@@ -642,11 +642,18 @@ static void ops_complete_compute5(void *stripe_head_ref)release_stripe (sh); } -static struct dma_async_tx_descriptor *ops_run_compute5 (struct stripe_head *sh) +/* return a pointer to the address conversion region of the scribble buffer */ +static addr_conv_t *to_addr_conv (struct stripe_head *sh, +struct raid5_percpu *percpu) +{ +return percpu->scribble +sizeof (struct page *) * (sh->disks +2 ); +} + +static struct dma_async_tx_descriptor * +ops_run_compute5 (struct stripe_head *sh,struct raid5_percpu *percpu) { -/* kernel stack size limits the total number of disks */ int disks = sh->disks; -struct page *xor_srcs[disks]; +struct page **xor_srcs = percpu->scribble;int target = sh->ops.target;struct r5dev *tgt = &sh->dev[target];struct page *xor_dest = tgt->page;
@@ -666,7 +673,7 @@ static struct dma_async_tx_descriptor *ops_run_compute5(struct stripe_head *sh)atomic_inc (&sh->count);init_async_submit (&submit,ASYNC_TX_XOR_ZERO_DST ,NULL , - ops_complete_compute5, sh,NULL ); + ops_complete_compute5, sh,to_addr_conv (sh, percpu));if (unlikely (count ==1 )) tx =async_memcpy (xor_dest, xor_srcs[0 ],0 ,0 ,STRIPE_SIZE , &submit);else
@@ -684,11 +691,11 @@ static void ops_complete_prexor(void *stripe_head_ref) }static struct dma_async_tx_descriptor * -ops_run_prexor (struct stripe_head *sh,struct dma_async_tx_descriptor *tx) +ops_run_prexor (struct stripe_head *sh,struct raid5_percpu *percpu, +struct dma_async_tx_descriptor *tx) { -/* kernel stack size limits the total number of disks */ int disks = sh->disks; -struct page *xor_srcs[disks]; +struct page **xor_srcs = percpu->scribble;int count =0 , pd_idx = sh->pd_idx, i;struct async_submit_ctl submit;
@@ -706,7 +713,7 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) }init_async_submit (&submit,ASYNC_TX_XOR_DROP_DST , tx, - ops_complete_prexor, sh,NULL ); + ops_complete_prexor, sh,to_addr_conv (sh, percpu)); tx =async_xor (xor_dest, xor_srcs,0 , count,STRIPE_SIZE , &submit);return tx;
@@ -775,11 +782,11 @@ static void ops_complete_postxor(void *stripe_head_ref) }static void -ops_run_postxor (struct stripe_head *sh,struct dma_async_tx_descriptor *tx) +ops_run_postxor (struct stripe_head *sh,struct raid5_percpu *percpu, +struct dma_async_tx_descriptor *tx) { -/* kernel stack size limits the total number of disks */ int disks = sh->disks; -struct page *xor_srcs[disks]; +struct page **xor_srcs = percpu->scribble;struct async_submit_ctl submit;int count =0 , pd_idx = sh->pd_idx, i;struct page *xor_dest;
@@ -819,7 +826,8 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx)atomic_inc (&sh->count); -init_async_submit (&submit, flags, tx, ops_complete_postxor, sh,NULL ); +init_async_submit (&submit, flags, tx, ops_complete_postxor, sh, +to_addr_conv (sh, percpu));if (unlikely (count ==1 )) tx =async_memcpy (xor_dest, xor_srcs[0 ],0 ,0 ,STRIPE_SIZE , &submit);else
@@ -838,11 +846,10 @@ static void ops_complete_check(void *stripe_head_ref)release_stripe (sh); } -static void ops_run_check (struct stripe_head *sh) +static void ops_run_check (struct stripe_head *sh,struct raid5_percpu *percpu) { -/* kernel stack size limits the total number of disks */ int disks = sh->disks; -struct page *xor_srcs[disks]; +struct page **xor_srcs = percpu->scribble;struct dma_async_tx_descriptor *tx;struct async_submit_ctl submit;
@@ -858,7 +865,8 @@ static void ops_run_check(struct stripe_head *sh) xor_srcs[count++] = dev->page; } -init_async_submit (&submit,0 ,NULL ,NULL ,NULL ,NULL ); +init_async_submit (&submit,0 ,NULL ,NULL ,NULL , +to_addr_conv (sh, percpu)); tx =async_xor_val (xor_dest, xor_srcs,0 , count,STRIPE_SIZE , &sh->ops.zero_sum_result, &submit);
@@ -871,21 +879,26 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) {int overlap_clear =0 , i, disks = sh->disks;struct dma_async_tx_descriptor *tx =NULL ; +raid5_conf_t *conf = sh->raid_conf; +struct raid5_percpu *percpu; +unsigned long cpu; + cpu =get_cpu (); + percpu =per_cpu_ptr (conf->percpu, cpu);if (test_bit (STRIPE_OP_BIOFILL , &ops_request)) {ops_run_biofill (sh); overlap_clear++; }if (test_bit (STRIPE_OP_COMPUTE_BLK , &ops_request)) { - tx =ops_run_compute5 (sh); + tx =ops_run_compute5 (sh, percpu);/* terminate the chain if postxor is not set to be run */ if (tx && !test_bit (STRIPE_OP_POSTXOR , &ops_request))async_tx_ack (tx); }if (test_bit (STRIPE_OP_PREXOR , &ops_request)) - tx =ops_run_prexor (sh, tx); + tx =ops_run_prexor (sh, percpu, tx);if (test_bit (STRIPE_OP_BIODRAIN , &ops_request)) { tx =ops_run_biodrain (sh, tx);
@@ -893,10 +906,10 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) }if (test_bit (STRIPE_OP_POSTXOR , &ops_request)) -ops_run_postxor (sh, tx); +ops_run_postxor (sh, percpu, tx);if (test_bit (STRIPE_OP_CHECK , &ops_request)) -ops_run_check (sh); +ops_run_check (sh, percpu);if (overlap_clear)for (i = disks; i--; ) {
@@ -904,6 +917,7 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request)if (test_and_clear_bit (R5_Overlap, &dev->flags))wake_up (&sh->raid_conf->wait_for_overlap); } +put_cpu (); }static int grow_one_stripe (raid5_conf_t *conf)
@@ -953,6 +967,28 @@ static int grow_stripes(raid5_conf_t *conf, int num)return 0 ; } +/** +* scribble_len - return the required size of the scribble region +* @num - total number of disks in the array +* +* The size must be enough to contain: +* 1/ a struct page pointer for each device in the array +2 +* 2/ room to convert each entry in (1) to its corresponding dma +* (dma_map_page()) or page (page_address()) address. +* +* Note: the +2 is for the destination buffers of the ddf/raid6 case where we +* calculate over all devices (not just the data blocks), using zeros in place +* of the P and Q blocks. +*/ +static size_t scribble_len (int num) +{ +size_t len; + + len =sizeof (struct page *) * (num+2 ) +sizeof (addr_conv_t) * (num+2 ); + +return len; +} +static int resize_stripes (raid5_conf_t *conf,int newsize) {/* Make all the stripes able to hold 'newsize' devices.
@@ -981,6 +1017,7 @@ static int resize_stripes(raid5_conf_t *conf, int newsize)struct stripe_head *osh, *nsh;LIST_HEAD (newstripes);struct disk_info *ndisks; +unsigned long cpu;int err;struct kmem_cache *sc;int i;
@@ -1046,7 +1083,7 @@ static int resize_stripes(raid5_conf_t *conf, int newsize)/* Step 3. * At this point, we are holding all the stripes so the array * is completely stalled, so now is a good time to resize -* conf->disks. +* conf->disks and the scribble region */ ndisks =kzalloc (newsize *sizeof (struct disk_info ),GFP_NOIO );if (ndisks) {
@@ -1057,10 +1094,30 @@ static int resize_stripes(raid5_conf_t *conf, int newsize) }else err = -ENOMEM ; +get_online_cpus (); + conf->scribble_len =scribble_len (newsize); +for_each_present_cpu (cpu) { +struct raid5_percpu *percpu; +void *scribble; + + percpu =per_cpu_ptr (conf->percpu, cpu); + scribble =kmalloc (conf->scribble_len,GFP_NOIO ); + +if (scribble) { +kfree (percpu->scribble); + percpu->scribble = scribble; + }else { + err = -ENOMEM ; +break ; + } + } +put_online_cpus (); +/* Step 4, return new stripes to service */ while (!list_empty (&newstripes)) { nsh = list_entry(newstripes.next, struct stripe_head, lru);list_del_init (&nsh->lru); +for (i=conf->raid_disks; i < newsize; i++)if (nsh->dev[i].page ==NULL ) {struct page *p =alloc_page (GFP_NOIO );
@@ -4318,6 +4375,7 @@ static void raid5_free_percpu(raid5_conf_t *conf)for_each_possible_cpu (cpu) { percpu =per_cpu_ptr (conf->percpu, cpu);safe_put_page (percpu->spare_page); +kfree (percpu->scribble); }#ifdef CONFIG_HOTPLUG_CPU unregister_cpu_notifier (&conf->cpu_notify);
@@ -4347,9 +4405,15 @@ static int raid456_cpu_notify(struct notifier_block *nfb, unsigned long action,switch (action) {case CPU_UP_PREPARE :case CPU_UP_PREPARE_FROZEN : -if (!percpu->spare_page) +if (conf->level ==6 && !percpu->spare_page) percpu->spare_page =alloc_page (GFP_KERNEL ); -if (!percpu->spare_page) { +if (!percpu->scribble) + percpu->scribble =kmalloc (conf->scribble_len,GFP_KERNEL ); + +if (!percpu->scribble || + (conf->level ==6 && !percpu->spare_page)) { +safe_put_page (percpu->spare_page); +kfree (percpu->scribble);pr_err ("%s: failed memory allocation for cpu%ld\n" , __func__, cpu);return NOTIFY_BAD ;
@@ -4358,7 +4422,9 @@ static int raid456_cpu_notify(struct notifier_block *nfb, unsigned long action,case CPU_DEAD :case CPU_DEAD_FROZEN :safe_put_page (percpu->spare_page); +kfree (percpu->scribble); percpu->spare_page =NULL ; + percpu->scribble =NULL ;break ;default :break ;
@@ -4372,12 +4438,9 @@ static int raid5_alloc_percpu(raid5_conf_t *conf)unsigned long cpu;struct page *spare_page;struct raid5_percpu *allcpus; +void *scribble;int err; -/* the only percpu data is the raid6 spare page */ -if (conf->level !=6 ) -return 0 ; - allcpus = alloc_percpu(struct raid5_percpu);if (!allcpus)return -ENOMEM ;
@@ -4386,12 +4449,20 @@ static int raid5_alloc_percpu(raid5_conf_t *conf)get_online_cpus (); err =0 ;for_each_present_cpu (cpu) { - spare_page =alloc_page (GFP_KERNEL ); -if (!spare_page) { +if (conf->level ==6 ) { + spare_page =alloc_page (GFP_KERNEL ); +if (!spare_page) { + err = -ENOMEM ; +break ; + } +per_cpu_ptr (conf->percpu, cpu)->spare_page = spare_page; + } + scribble =kmalloc (scribble_len (conf->raid_disks),GFP_KERNEL ); +if (!scribble) { err = -ENOMEM ;break ; } -per_cpu_ptr (conf->percpu, cpu)->spare_page = spare_page; +per_cpu_ptr (conf->percpu, cpu)->scribble = scribble; }#ifdef CONFIG_HOTPLUG_CPU conf->cpu_notify.notifier_call = raid456_cpu_notify;
@@ -4443,6 +4514,7 @@ static raid5_conf_t *setup_conf(mddev_t *mddev) goto abort; conf->raid_disks = mddev->raid_disks; + conf->scribble_len =scribble_len (conf->raid_disks);if (mddev->reshape_position == MaxSector) conf->previous_raid_disks = mddev->raid_disks;else
drivers/md/raid5.h +8 -0
@@ -386,7 +386,15 @@ struct raid5_private_data {/* per cpu variables */ struct raid5_percpu {struct page *spare_page;/* Used when checking P/Q in raid6 */ +void *scribble;/* space for constructing buffer +* lists and performing address +* conversions +*/ } *percpu; +size_t scribble_len;/* size of scribble region must be +* associated with conf to handle +* cpu hotplug while reshaping +*/ #ifdef CONFIG_HOTPLUG_CPU struct notifier_block cpu_notify;#endif
PATCH v3 8/14 async_tx: add sum check flags
In reply to Dan WilliamsReplace the flat zero_sum_result with a collection of flags to contain the P (xor) zero-sum result, and the soon to be utilized Q (raid6 reed solomon syndrome) zero-sum result. Use the SUM_CHECK_ namespace instead of DMA_ since these flags will be used on non-dma-zero-sum enabled platforms.
--- arch/arm/include/asm/hardware/iop3xx-adma.h | 5 +++-- arch/arm/mach-iop13xx/include/mach/adma.h | 12 +++++++----- crypto/async_tx/async_xor.c | 4 ++-- drivers/md/raid5.c | 2 +- drivers/md/raid5.h | 5 +++-- include/linux/async_tx.h | 2 +- include/linux/dmaengine.h | 21 ++++++++++++++++++++- 7 files changed, 37 insertions(+), 14 deletions(-)
arch/arm/include/asm/hardware/iop3xx-adma.h +3 -2
@@ -756,13 +756,14 @@ static inline void iop_desc_set_block_fill_val(struct iop_adma_desc_slot *desc, hw_desc->src[0 ] = val; } -static inline int iop_desc_get_zero_result (struct iop_adma_desc_slot *desc) +static inline enum sum_check_flags +iop_desc_get_zero_result (struct iop_adma_desc_slot *desc) {struct iop3xx_desc_aau *hw_desc = desc->hw_desc;struct iop3xx_aau_desc_ctrl desc_ctrl = hw_desc->desc_ctrl_field;iop_paranoia (!(desc_ctrl.tx_complete && desc_ctrl.zero_result_en)); -return desc_ctrl.zero_result_err; +return desc_ctrl.zero_result_err <<SUM_CHECK_P ; }static inline void iop_chan_append (struct iop_adma_chan *chan)
arch/arm/mach-iop13xx/include/mach/adma.h +7 -5
@@ -428,18 +428,20 @@ static inline void iop_desc_set_block_fill_val(struct iop_adma_desc_slot *desc, hw_desc->block_fill_data = val; } -static inline int iop_desc_get_zero_result (struct iop_adma_desc_slot *desc) +static inline enum sum_check_flags +iop_desc_get_zero_result (struct iop_adma_desc_slot *desc) {struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc;struct iop13xx_adma_desc_ctrl desc_ctrl = hw_desc->desc_ctrl_field;struct iop13xx_adma_byte_count byte_count = hw_desc->byte_count_field; +enum sum_check_flags flags;BUG_ON (!(byte_count.tx_complete && desc_ctrl.zero_result)); -if (desc_ctrl.pq_xfer_en) -return byte_count.zero_result_err_q; -else -return byte_count.zero_result_err; + flags = byte_count.zero_result_err_q <<SUM_CHECK_Q ; + flags |= byte_count.zero_result_err <<SUM_CHECK_P ; + +return flags; }static inline void iop_chan_append (struct iop_adma_chan *chan)
crypto/async_tx/async_xor.c +2 -2
@@ -246,7 +246,7 @@ static int page_is_zero(struct page *p, unsigned int offset, size_t len)*/ struct dma_async_tx_descriptor *async_xor_val (struct page *dest,struct page **src_list,unsigned int offset, -int src_cnt,size_t len,u32 *result, +int src_cnt,size_t len,enum sum_check_flags *result,struct async_submit_ctl *submit) { struct dma_chan *chan = async_tx_find_channel(submit, DMA_XOR_VAL,
@@ -304,7 +304,7 @@ async_xor_val(struct page *dest, struct page **src_list, unsigned int offset,async_tx_quiesce (&tx); - *result =page_is_zero (dest, offset, len) ?0 :1 ; + *result = !page_is_zero (dest, offset, len) <<SUM_CHECK_P ;async_tx_sync_epilog (submit); submit->flags = flags_orig;
drivers/md/raid5.c +1 -1
@@ -2590,7 +2590,7 @@ static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh,* we are done. Otherwise update the mismatch count and repair * parity if !MD_RECOVERY_CHECK */ -if (sh->ops.zero_sum_result ==0 ) +if ((sh->ops.zero_sum_result &SUM_CHECK_P_RESULT ) ==0 )/* parity is correct (on disc, * not in buffer any more) */
drivers/md/raid5.h +3 -2
@@ -2,6 +2,7 @@#define _RAID5_H#include <linux/raid/xor.h> +#include <linux/dmaengine.h> /* *
@@ -215,8 +216,8 @@ struct stripe_head {* @target - STRIPE_OP_COMPUTE_BLK target */ struct stripe_operations { -int target; -u32 zero_sum_result; +int target; +enum sum_check_flags zero_sum_result; } ops;struct r5dev {struct bio req;
include/linux/async_tx.h +1 -1
@@ -148,7 +148,7 @@ async_xor(struct page *dest, struct page **src_list, unsigned int offset,struct dma_async_tx_descriptor *async_xor_val (struct page *dest,struct page **src_list,unsigned int offset, -int src_cnt,size_t len,u32 *result, +int src_cnt,size_t len,enum sum_check_flags *result,struct async_submit_ctl *submit); struct dma_async_tx_descriptor *
include/linux/dmaengine.h +20 -1
@@ -87,6 +87,25 @@ enum dma_ctrl_flags { };/** +* enum sum_check_bits - bit position of pq_check_flags +*/ +enum sum_check_bits { +SUM_CHECK_P =0 , +SUM_CHECK_Q =1 , +}; + +/** +* enum pq_check_flags - result of async_{xor,pq}_zero_sum operations +* @SUM_CHECK_P_RESULT - 1 if xor zero sum error, 0 otherwise +* @SUM_CHECK_Q_RESULT - 1 if reed-solomon zero sum error, 0 otherwise +*/ +enum sum_check_flags { +SUM_CHECK_P_RESULT = (1 <<SUM_CHECK_P ), +SUM_CHECK_Q_RESULT = (1 <<SUM_CHECK_Q ), +}; + + +/** * dma_cap_mask_t - capabilities bitmap modeled after cpumask_t. * See linux/cpumask.h */
@@ -245,7 +264,7 @@ struct dma_device { unsigned int src_cnt, size_t len, unsigned long flags);struct dma_async_tx_descriptor *(*device_prep_dma_xor_val)(struct dma_chan *chan,dma_addr_t *src,unsigned int src_cnt, -size_t len,u32 *result,unsigned long flags); +size_t len,enum sum_check_flags *result,unsigned long flags);struct dma_async_tx_descriptor *(*device_prep_dma_memset)(struct dma_chan *chan,dma_addr_t dest,int value,size_t len,unsigned long flags);
PATCH v3 9/14 async_tx: kill needless module_{init|exit}
In reply to Dan WilliamsIf module_init and module_exit are nops then neither need to be defined.
[ Impact: pure cleanup ]
--- crypto/async_tx/async_memcpy.c | 13 ------------- crypto/async_tx/async_memset.c | 13 ------------- crypto/async_tx/async_tx.c | 17 +++-------------- 3 files changed, 3 insertions(+), 40 deletions(-)
crypto/async_tx/async_memcpy.c +0 -13
@@ -90,19 +90,6 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, }EXPORT_SYMBOL_GPL (async_memcpy); -static int __init async_memcpy_init (void ) -{ -return 0 ; -} - -static void __exit async_memcpy_exit(void) -{ -do { }while (0 ); -} - -module_init (async_memcpy_init); -module_exit (async_memcpy_exit); -MODULE_AUTHOR ("Intel Corporation" );MODULE_DESCRIPTION ("asynchronous memcpy api" );MODULE_LICENSE ("GPL" );
crypto/async_tx/async_memset.c +0 -13
@@ -80,19 +80,6 @@ async_memset(struct page *dest, int val, unsigned int offset, size_t len, }EXPORT_SYMBOL_GPL (async_memset); -static int __init async_memset_init (void ) -{ -return 0 ; -} - -static void __exit async_memset_exit(void) -{ -do { }while (0 ); -} - -module_init (async_memset_init); -module_exit (async_memset_exit); -MODULE_AUTHOR ("Intel Corporation" );MODULE_DESCRIPTION ("asynchronous memset api" );MODULE_LICENSE ("GPL" );
crypto/async_tx/async_tx.c +3 -14
@@ -42,6 +42,9 @@ static void __exit async_tx_exit(void)async_dmaengine_put (); } +module_init (async_tx_init); +module_exit (async_tx_exit); +/** * __async_tx_find_channel - find a channel to carry out the operation or let * the transaction execute synchronously
@@ -61,17 +64,6 @@ __async_tx_find_channel(struct async_submit_ctl *submit, return async_dma_find_channel(tx_type); }EXPORT_SYMBOL_GPL (__async_tx_find_channel); -#else -static int __init async_tx_init (void ) -{ -printk (KERN_INFO "async_tx: api initialized (sync-only)\n" ); -return 0 ; -} - -static void __exit async_tx_exit(void) -{ -do { }while (0 ); -}#endif
@@ -298,9 +290,6 @@ void async_tx_quiesce(struct dma_async_tx_descriptor **tx) }EXPORT_SYMBOL_GPL (async_tx_quiesce); -module_init (async_tx_init); -module_exit (async_tx_exit); -MODULE_AUTHOR ("Intel Corporation" );MODULE_DESCRIPTION ("Asynchronous Bulk Memory Transactions API" );MODULE_LICENSE ("GPL" );
PATCH v3 10/14 async_tx: remove walk of tx->parent chain in dma_wait_for_async_tx
In reply to Dan WilliamsWe currently walk the parent chain when waiting for a given tx to complete however this walk may race with the driver cleanup routine. The routines in async_raid6_recov.c may fall back to the synchronous path at any point so we need to be prepared to call async_tx_quiesce() (which calls dma_wait_for_async_tx). To remove the ->parent walk we guarantee that every time a dependency is attached ->issue_pending() is invoked, then we can simply poll the initial descriptor until completion.
This also allows for a lighter weight 'issue pending' implementation as there is no longer a requirement to iterate through all the channels' ->issue_pending() routines as long as operations have been submitted in an ordered chain. async_tx_issue_pending() is added for this case.
--- crypto/async_tx/async_tx.c | 13 +++++++------ drivers/dma/dmaengine.c | 45 ++++++++++---------------------------------- include/linux/async_tx.h | 23 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 41 deletions(-)
crypto/async_tx/async_tx.c +7 -6
@@ -77,8 +77,8 @@ static void async_tx_channel_switch(struct dma_async_tx_descriptor *depend_tx, struct dma_async_tx_descriptor *tx) { - struct dma_chan *chan; -struct dma_device *device; + struct dma_chan *chan = depend_tx->chan; +struct dma_device *device = chan->device;struct dma_async_tx_descriptor *intr_tx = (void *) ~0 ;/* first check to see if we can still append to depend_tx */
@@ -90,11 +90,11 @@ async_tx_channel_switch(struct dma_async_tx_descriptor *depend_tx, }spin_unlock_bh (&depend_tx->lock); -if (!intr_tx) +/* attached dependency, flush the parent channel */ +if (!intr_tx) { + device->device_issue_pending (chan);return ; - - chan = depend_tx->chan; - device = chan->device; + }/* see if we can schedule an interrupt * otherwise poll for completion
@@ -128,6 +128,7 @@ async_tx_channel_switch(struct dma_async_tx_descriptor *depend_tx, intr_tx->tx_submit (intr_tx);async_tx_ack (intr_tx); } + device->device_issue_pending (chan); }else {if (dma_wait_for_async_tx (depend_tx) ==DMA_ERROR ) panic("%s: DMA_ERROR waiting for depend_tx\n",
drivers/dma/dmaengine.c +10 -35
59 lines not on this page · raw message
include/linux/async_tx.h +23 -0
@@ -83,6 +83,24 @@ struct async_submit_ctl {#ifdef CONFIG_DMA_ENGINE #define async_tx_issue_pending_all dma_issue_pending_all + +/** +* async_tx_issue_pending - send pending descriptor to the hardware channel +* @tx: descriptor handle to retrieve hardware context +* +* Note: any dependent operations will have already been issued by +* async_tx_channel_switch, or (in the case of no channel switch) will +* be already pending on this channel. +*/ +static inline void async_tx_issue_pending (struct dma_async_tx_descriptor *tx) +{ +if (likely (tx)) { +struct dma_chan *chan = tx->chan; +struct dma_device *dma = chan->device; + + dma->device_issue_pending (chan); + } +}#ifdef CONFIG_ARCH_HAS_ASYNC_TX_FIND_CHANNEL #include <asm/async_tx.h> #else
@@ -98,6 +116,11 @@ static inline void async_tx_issue_pending_all(void)do { }while (0 ); } +static inline void async_tx_issue_pending (struct dma_async_tx_descriptor *tx) +{ +do { }while (0 ); +} +static inline struct dma_chan * async_tx_find_channel(struct async_submit_ctl *submit, enum dma_transaction_type tx_type, struct page **dst,
PATCH v3 11/14 async_tx: add support for asynchronous GF multiplication
In reply to Dan Williams[ Based on an original patch by Yuri Tikhonov ]
This adds support for doing asynchronous GF multiplication by adding two additional functions to the async_tx API:
async_gen_syndrome() does simultaneous XOR and Galois field
multiplication of sources.
async_syndrome_val() validates the given source buffers against known P
and Q values.
When a request is made to run async_pq against more than the hardware maximum number of supported sources we need to reuse the previous generated P and Q values as sources into the next operation. Care must be taken to remove Q from P' and P from Q'. For example to perform a 5 source pq op with hardware that only supports 4 sources at a time the following approach is taken:
p, q = PQ(src0, src1, src2, src3, COEF({01}, {02}, {04}, {08})) p', q' = PQ(p, q, q, src4, COEF({00}, {01}, {00}, {10}))
p' = p + q + q + src4 = p + src4
q' = {00}*p + {01}*q + {00}*q + {10}*src4 = q + {10}*src4
Note: 4 is the minimum acceptable maxpq otherwise we punt to synchronous-software path.
The DMA_PREP_CONTINUE flag indicates to the driver to reuse p and q as sources (in the above manner) and fill the remaining slots up to maxpq with the new sources/coefficients.
Note1: Some devices have native support for P+Q continuation and can skip this extra work. Devices with this capability can advertise it with dma_set_maxpq. It is up to each driver how to handle the DMA_PREP_CONTINUE flag.
Note2: The api supports disabling the generation of P when generating Q, this is ignored by the synchronous path but is implemented by some dma devices to save unnecessary writes. In this case the continuation algorithm is simplified to only reuse Q as a source.
--- Documentation/crypto/async-tx-api.txt | 3 arch/arm/mach-iop13xx/setup.c | 2 crypto/async_tx/Kconfig | 4 crypto/async_tx/Makefile | 1 crypto/async_tx/async_pq.c | 388 +++++++++++++++++++++++++++++++++ crypto/async_tx/async_xor.c | 2 drivers/dma/dmaengine.c | 4 drivers/dma/iop-adma.c | 2 include/linux/async_tx.h | 9 + include/linux/dmaengine.h | 87 +++++++ 10 files changed, 493 insertions(+), 9 deletions(-) create mode 100644 crypto/async_tx/async_pq.c
Documentation/crypto/async-tx-api.txt +3 -0
@@ -64,6 +64,9 @@ xor - xor a series of source buffers and write the result to a xor_val - xor a series of source buffers and set a flag if the result is zero. The implementation attempts to prevent writes to memory +pq - generate the p+q (raid6 syndrome) from a series of source buffers +pq_val - validate that a p and or q buffer are in sync with a given series of + sources 3.3 Descriptor management: The return value is non-NULL and points to a 'descriptor' when the operation
arch/arm/mach-iop13xx/setup.c +1 -1
@@ -506,7 +506,7 @@ void __init iop13xx_platform_init(void)dma_cap_set (DMA_MEMSET , plat_data->cap_mask);dma_cap_set (DMA_MEMCPY_CRC32C , plat_data->cap_mask);dma_cap_set (DMA_INTERRUPT , plat_data->cap_mask); -dma_cap_set (DMA_PQ_XOR , plat_data->cap_mask); +dma_cap_set (DMA_PQ , plat_data->cap_mask);dma_cap_set (DMA_PQ_UPDATE , plat_data->cap_mask);dma_cap_set (DMA_PQ_VAL , plat_data->cap_mask);break ;
crypto/async_tx/Kconfig +4 -0
@@ -14,3 +14,7 @@ config ASYNC_MEMSET tristate select ASYNC_CORE +config ASYNC_PQ + tristate + select ASYNC_CORE +
crypto/async_tx/Makefile +1 -0
@@ -2,3 +2,4 @@ obj-$(CONFIG_ASYNC_CORE) += async_tx.o obj-$(CONFIG_ASYNC_MEMCPY) += async_memcpy.o obj-$(CONFIG_ASYNC_MEMSET) += async_memset.o obj-$(CONFIG_ASYNC_XOR) += async_xor.o +obj-$(CONFIG_ASYNC_PQ) += async_pq.o
crypto/async_tx/async_pq.c new file +388 -0
388 lines not on this page · raw message
crypto/async_tx/async_xor.c +1 -1
@@ -62,7 +62,7 @@ do_async_xor(struct dma_chan *chan, struct page *dest, struct page **src_list,while (src_cnt) { submit->flags = flags_orig; dma_flags =0 ; - xor_src_cnt =min (src_cnt, dma->max_xor); + 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
drivers/dma/dmaengine.c +4 -0
@@ -646,6 +646,10 @@ int dma_async_device_register(struct dma_device *device) !device->device_prep_dma_xor);BUG_ON (dma_has_cap (DMA_XOR_VAL , device->cap_mask) && !device->device_prep_dma_xor_val); +BUG_ON (dma_has_cap (DMA_PQ , device->cap_mask) && + !device->device_prep_dma_pq); +BUG_ON (dma_has_cap (DMA_PQ_VAL , device->cap_mask) && + !device->device_prep_dma_pq_val);BUG_ON (dma_has_cap (DMA_MEMSET , device->cap_mask) && !device->device_prep_dma_memset); BUG_ON(dma_has_cap(DMA_INTERRUPT, device->cap_mask) &&
drivers/dma/iop-adma.c +1 -1
@@ -1257,7 +1257,7 @@ static int __devinit iop_adma_probe(struct platform_device *pdev) dev_printk(KERN_INFO, &pdev->dev, "Intel(R) IOP: " "( %s%s%s%s%s%s%s%s%s%s)\n", - dma_has_cap(DMA_PQ_XOR, dma_dev->cap_mask) ? "pq_xor " : "", + dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "", dma_has_cap(DMA_PQ_UPDATE, dma_dev->cap_mask) ? "pq_update " : "", dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "", dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "",
include/linux/async_tx.h +9 -0
@@ -185,5 +185,14 @@ async_memset(struct page *dest, int val, unsigned int offset,struct dma_async_tx_descriptor *async_trigger_callback (struct async_submit_ctl *submit); +struct dma_async_tx_descriptor * +async_gen_syndrome (struct page **blocks,unsigned int offset,int src_cnt, +size_t len,struct async_submit_ctl *submit); + +struct dma_async_tx_descriptor * +async_syndrome_val (struct page **blocks,unsigned int offset,int src_cnt, +size_t len,enum sum_check_flags *pqres,struct page *spare, +struct async_submit_ctl *submit); +void async_tx_quiesce (struct dma_async_tx_descriptor **tx);#endif /* _ASYNC_TX_H_ */
include/linux/dmaengine.h +81 -6
139 lines not on this page · raw message
PATCH v3 12/14 async_tx: add support for asynchronous RAID6 recovery operations
In reply to Dan Williams async_raid6_2data_recov() recovers two data disk failures
async_raid6_datap_recov() recovers a data disk and the P disk
These routines are a port of the synchronous versions found in drivers/md/raid6recov.c. The primary difference is breaking out the xor operations into separate calls to async_xor. Two helper routines are introduced to perform scalar multiplication where needed. async_sum_product() multiplies two sources by scalar coefficients and then sums (xor) the result. async_mult() simply multiplies a single source by a scalar.
This implemention also includes, in contrast to the original synchronous-only code, special case handling for the 4-disk and 5-disk array cases. In these situations the default N-disk algorithm will present 0-source or 1-source operations to dma devices. To cover for dma devices where the minimum source count is 2 we implement 4-disk and 5-disk handling in the recovery code.
[ Impact: asynchronous raid6 recovery routines for 2data and datap cases ]
--- Documentation/crypto/async-tx-api.txt | 4 crypto/async_tx/Kconfig | 5 crypto/async_tx/Makefile | 1 crypto/async_tx/async_raid6_recov.c | 448 +++++++++++++++++++++++++++++++++ include/linux/async_tx.h | 8 + 5 files changed, 466 insertions(+), 0 deletions(-) create mode 100644 crypto/async_tx/async_raid6_recov.c
Documentation/crypto/async-tx-api.txt +4 -0
@@ -67,6 +67,10 @@ xor_val - xor a series of source buffers and set a flag if the pq - generate the p+q (raid6 syndrome) from a series of source buffers pq_val - validate that a p and or q buffer are in sync with a given series of sources +datap - (raid6_datap_recov) recover a raid6 data block and the p block + from the given sources +2data - (raid6_2data_recov) recover 2 raid6 data blocks from the given + sources 3.3 Descriptor management: The return value is non-NULL and points to a 'descriptor' when the operation
crypto/async_tx/Kconfig +5 -0
@@ -18,3 +18,8 @@ config ASYNC_PQ tristate select ASYNC_CORE +config ASYNC_RAID6_RECOV + tristate + select ASYNC_CORE + select ASYNC_PQ +
crypto/async_tx/Makefile +1 -0
@@ -3,3 +3,4 @@ obj-$(CONFIG_ASYNC_MEMCPY) += async_memcpy.o obj-$(CONFIG_ASYNC_MEMSET) += async_memset.o obj-$(CONFIG_ASYNC_XOR) += async_xor.o obj-$(CONFIG_ASYNC_PQ) += async_pq.o +obj-$(CONFIG_ASYNC_RAID6_RECOV) += async_raid6_recov.o
crypto/async_tx/async_raid6_recov.c new file +448 -0
448 lines not on this page · raw message
include/linux/async_tx.h +8 -0
@@ -194,5 +194,13 @@ async_syndrome_val(struct page **blocks, unsigned int offset, int src_cnt, size_t len, enum sum_check_flags *pqres, struct page *spare, struct async_submit_ctl *submit); +struct dma_async_tx_descriptor * +async_raid6_2data_recov (int src_num,size_t bytes,int faila,int failb, +struct page **ptrs,struct async_submit_ctl *submit); + +struct dma_async_tx_descriptor * +async_raid6_datap_recov (int src_num,size_t bytes,int faila, +struct page **ptrs,struct async_submit_ctl *submit); +void async_tx_quiesce (struct dma_async_tx_descriptor **tx);#endif /* _ASYNC_TX_H_ */
PATCH v3 13/14 dmatest: add pq support
In reply to Dan WilliamsTest raid6 p+q operations with a simple "always multiply by 1" q calculation to fit into dmatest's current destination verification scheme.
--- drivers/dma/dmatest.c | 26 ++++++++++++++++++++++++++ 1 files changed, 26 insertions(+), 0 deletions(-)
drivers/dma/dmatest.c +26 -0
@@ -43,6 +43,11 @@ module_param(xor_sources, uint, S_IRUGO);MODULE_PARM_DESC (xor_sources,"Number of xor source buffers (default: 3)" ); +static unsigned int pq_sources =3 ; +module_param (pq_sources, uint,S_IRUGO ); +MODULE_PARM_DESC (pq_sources, +"Number of p+q source buffers (default: 3)" ); +/* * Initialization patterns. All bytes in the source buffer has bit 7 * set, all bytes in the destination buffer has bit 7 cleared.
@@ -227,6 +232,7 @@ static int dmatest_func(void *data)dma_cookie_t cookie;enum dma_status status;enum dma_ctrl_flags flags; +u8 pq_coefs[pq_sources];int ret;int src_cnt;int dst_cnt;
@@ -243,6 +249,11 @@ static int dmatest_func(void *data)else if (thread->type ==DMA_XOR ) { src_cnt = xor_sources |1 ;/* force odd to ensure dst = src */ dst_cnt =1 ; + }else if (thread->type ==DMA_PQ ) { + src_cnt = pq_sources |1 ;/* force odd to ensure dst = src */ + dst_cnt =2 ; +for (i =0 ; i < pq_sources; i++) + pq_coefs[i] =1 ; }else goto err_srcs;
@@ -310,6 +321,15 @@ static int dmatest_func(void *data) dma_dsts[0 ] + dst_off, dma_srcs, xor_sources, len, flags); + else if (thread->type == DMA_PQ) { +dma_addr_t dma_pq[dst_cnt]; + +for (i =0 ; i < dst_cnt; i++) + dma_pq[i] = dma_dsts[i] + dst_off; + tx = dev->device_prep_dma_pq (chan, dma_pq, dma_srcs, + pq_sources, pq_coefs, + len, flags); + }if (!tx) {for (i =0 ; i < src_cnt; i++)
@@ -446,6 +466,8 @@ static int dmatest_add_threads(struct dmatest_chan *dtc, enum dma_transaction_ty op ="copy" ; else if (type == DMA_XOR) op ="xor" ; + else if (type == DMA_PQ) + op ="pq" ; else return -EINVAL;
@@ -501,6 +523,10 @@ static int dmatest_add_channel(struct dma_chan *chan) cnt =dmatest_add_threads (dtc,DMA_XOR ); thread_count += cnt >0 ?:0 ; } +if (dma_has_cap (DMA_PQ , dma_dev->cap_mask)) { + cnt =dmatest_add_threads (dtc,DMA_PQ ); + thread_count += cnt >0 ?:0 ; + }pr_info ("dmatest: Started %u threads using %s\n" , thread_count,dma_chan_name (chan));
PATCH v3 14/14 async_tx: raid6 recovery self test
In reply to Dan WilliamsPort drivers/md/raid6test/test.c to use the async raid6 recovery routines. This is meant as a unit test for raid6 acceleration drivers. In addition to the 16-drive test case this implements tests for the 4-disk and 5-disk special cases (dma devices can not generically handle less than 2 sources), and adds a test for the D+Q case.
--- crypto/async_tx/Makefile | 1 crypto/async_tx/raid6test.c | 241 +++++++++++++++++++++++++++++++++++++++++++ drivers/md/Kconfig | 13 ++ 3 files changed, 255 insertions(+), 0 deletions(-) create mode 100644 crypto/async_tx/raid6test.c
crypto/async_tx/Makefile +1 -0
@@ -4,3 +4,4 @@ obj-$(CONFIG_ASYNC_MEMSET) += async_memset.o obj-$(CONFIG_ASYNC_XOR) += async_xor.o obj-$(CONFIG_ASYNC_PQ) += async_pq.o obj-$(CONFIG_ASYNC_RAID6_RECOV) += async_raid6_recov.o +obj-$(CONFIG_ASYNC_RAID6_TEST) += raid6test.o
crypto/async_tx/raid6test.c new file +241 -0
241 lines not on this page · raw message
drivers/md/Kconfig +13 -0
@@ -155,6 +155,19 @@ config MD_RAID456 config MD_RAID6_PQ tristate +config ASYNC_RAID6_TEST + tristate "Self test for hardware accelerated raid6 recovery" + depends on MD_RAID6_PQ + select ASYNC_RAID6_RECOV + ---help--- + This is a one-shot self test that permutes through the + recovery of all the possible two disk failure scenarios for a + N-disk array. Recovery is performed with the asynchronous + raid6 recovery routines, and will optionally use an offload + engine if one is available. + + If unsure, say N. + config MD_MULTIPATH tristate "Multipath I/O support" depends on BLK_DEV_MD