linux-raid.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andre Noll <maan@systemlinux.org>
To: Dan Williams <dan.j.williams@intel.com>
Cc: linux-raid@vger.kernel.org, linux-kernel@vger.kernel.org,
	neilb@suse.de, maciej.sosnowski@intel.com,
	Ilya Yanok <yanok@emcraft.com>, Yuri Tikhonov <yur@emcraft.com>
Subject: Re: [PATCH 06/13] async_tx: add support for asynchronous GF multiplication
Date: Thu, 19 Mar 2009 21:09:59 +0100	[thread overview]
Message-ID: <20090319200959.GB10491@skl-net.de> (raw)
In-Reply-To: <20090318192046.20375.89854.stgit@dwillia2-linux.ch.intel.com>

[-- Attachment #1: Type: text/plain, Size: 10978 bytes --]

On 12:20, Dan Williams wrote:
> +static __async_inline struct dma_async_tx_descriptor *
> +do_async_pq(struct dma_chan *chan, struct page **blocks, unsigned char *scfs,
> +	    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)
> +{

Wow, this function takes 10 arguments..

> +	struct dma_device *dma = chan->device;
> +	dma_addr_t dma_dest[2], dma_src[src_cnt];
> +	struct dma_async_tx_descriptor *tx = NULL;
> +	dma_async_tx_callback _cb_fn;
> +	void *_cb_param;
> +	unsigned char *scf = NULL;
> +	int i, src_off = 0;
> +	unsigned short pq_src_cnt;
> +	enum async_tx_flags async_flags;
> +	enum dma_ctrl_flags dma_flags = 0;
> +	int idx;
> +	u8 coefs[src_cnt];
> +
> +	/* DMAs use destinations as sources, so use BIDIRECTIONAL mapping */
> +	if (blocks[src_cnt])
> +		dma_dest[0] = dma_map_page(dma->dev, blocks[src_cnt],
> +					   offset, len, DMA_BIDIRECTIONAL);
> +	else
> +		dma_flags |= DMA_PREP_PQ_DISABLE_P;
> +	if (blocks[src_cnt+1])
> +		dma_dest[1] = dma_map_page(dma->dev, blocks[src_cnt+1],
> +					   offset, len, DMA_BIDIRECTIONAL);
> +	else
> +		dma_flags |= DMA_PREP_PQ_DISABLE_Q;
> +
> +	/* convert source addresses being careful to collapse 'zero'
> +	 * sources and update the coefficients accordingly
> +	 */
> +	for (i = 0, idx = 0; i < src_cnt; i++) {
> +		if (is_raid6_zero_block(blocks[i]))
> +			continue;
> +		dma_src[idx] = dma_map_page(dma->dev, blocks[i],
> +					    offset, len, DMA_TO_DEVICE);
> +		coefs[idx] = scfs[i];

coefs[] seems not to be used anywhere. BTW: coefs is an array of u8
while unsigned char is used elswhere for the coefficients troughout
the file.  Any chance to unify this?

> +	while (src_cnt > 0) {
> +		async_flags = flags;
> +		pq_src_cnt = min(src_cnt, dma_maxpq(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) {
> +			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;
> +		if (scfs)
> +			scf = &scfs[src_off];

If scfs is NULL, we have a NULL pointer dereference earlier.

> +
> +		/* 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_pq(chan, dma_dest,
> +					     &dma_src[src_off], pq_src_cnt,
> +					     scf, 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_pq(chan, dma_dest,
> +					&dma_src[src_off], pq_src_cnt,
> +					scf, len, dma_flags);
> +		}

This can be simplified to

	for (;;) {
		tx = dma->device_prep_dma_pq(...);
		if (likely(tx))
			break;
		async_tx_quiesce(&depend_tx);
		dma_async_issue_pending(chan);
	}

which avoids duplicating the device_prep_dma_pq() call. The additional
calls to async_tx_quiesce() will be a no-op and we're spinning anyway.

> +/**
> + * do_sync_pq - synchronously calculate P and Q
> + */
> +static void
> +do_sync_pq(struct page **blocks, unsigned char *scfs, 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)
> +{
> +	u8 *p = NULL;
> +	u8 *q = NULL;
> +	u8 *ptrs[src_cnt];
> +	int d, z;
> +	u8 wd, wq, wp;

The scope of wd, wq and wp can be reduced to the for() loop in which they
are used.

> +
> +	/* address convert inputs */
> +	if (blocks[src_cnt])
> +		p = (u8 *)(page_address(blocks[src_cnt]) + offset);
> +	if (blocks[src_cnt+1])
> +		q = (u8 *)(page_address(blocks[src_cnt+1]) + offset);

Unnecessary casts.

> +			ptrs[z] = (u8 *)(page_address(blocks[z]) + offset);

again

> +/**
> + * async_pq - attempt to do XOR and Galois calculations in parallel using
> + *	a dma engine.
> + * @blocks: source block array from 0 to (src_cnt-1) with the p destination
> + *	at blocks[src_cnt] and q at blocks[src_cnt + 1]. Only one of two
> + *	destinations may be present (another then has to be set to NULL).
> + *	NOTE: client code must assume the contents of this array are destroyed
> + * @offset: offset in pages to start transaction
> + * @src_cnt: number of source pages
> + * @scfs: array of source coefficients used in GF-multiplication
> + * @len: length in bytes
> + * @flags: ASYNC_TX_ACK, ASYNC_TX_DEP_ACK, ASYNC_TX_ASYNC_ONLY
> + * @depend_tx: depends on the result of this transaction.
> + * @cb_fn: function to call when the operation completes
> + * @cb_param: parameter to pass to the callback routine
> + */

General remark: Many of the public functions in this file use a
similar set of parameters. As these functions my grow more users
in the future, it might ease maintainability in the long run if the
common parameter set would be combined to a structure and each of the
public functions would take a pointer to such a structure. Changing
the interface would then be a matter of altering only that structure
at one place while all function declarations could be left unchanged.

> +struct dma_async_tx_descriptor *
> +async_pq(struct page **blocks, unsigned int offset, int src_cnt,
> +	 unsigned char *scfs, 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_PQ,
> +					&blocks[src_cnt], 2,
> +					blocks, src_cnt, len);
> +	struct dma_device *device = chan ? chan->device : NULL;
> +	struct dma_async_tx_descriptor *tx = NULL;
> +	bool do_async = false;
> +
> +	if (device && (src_cnt <= dma_maxpq(device, 0) ||
> +		       dma_maxpq(device, DMA_PREP_CONTINUE) > 0))
> +		do_async = true;
> +
> +	if (!device && (flags & ASYNC_TX_ASYNC_ONLY))
> +		return NULL;
> +
> +	if (do_async) {
> +		/* run pq asynchronously */
> +		tx = do_async_pq(chan, blocks, scfs, offset, src_cnt, len,
> +				 flags, depend_tx, cb_fn, cb_param);
> +	} else {
> +		/* run pq synchronously */
> +		if (!blocks[src_cnt+1]) { /* only p requested, just xor */
> +			flags |= ASYNC_TX_XOR_ZERO_DST;
> +			return async_xor(blocks[src_cnt], blocks, offset,
> +					 src_cnt, len, flags, depend_tx,
> +					 cb_fn, cb_param);
> +		}
> +
> +		/* wait for any prerequisite operations */
> +		async_tx_quiesce(&depend_tx);
> +
> +		do_sync_pq(blocks, scfs, offset, src_cnt, len, flags,
> +			depend_tx, cb_fn, cb_param);

Is it really OK to return NULL here?

> +	}
> +
> +	return tx;
> +}
> +EXPORT_SYMBOL_GPL(async_pq);

This function could be simplified a bit as follows:

	if (!device && (flags & ASYNC_TX_ASYNC_ONLY))
		return NULL;
	if (device && (src_cnt <= dma_maxpq(device, 0) ||
			dma_maxpq(device, DMA_PREP_CONTINUE) > 0))
		/* run pq asynchronously */
		return do_async_pq(chan, blocks, scfs, offset, src_cnt, len,
			flags, depend_tx, cb_fn, cb_param);

	/* run pq synchronously */
	if (!blocks[src_cnt+1]) {
		flags |= ASYNC_TX_XOR_ZERO_DST;
		return async_xor(blocks[src_cnt], blocks, offset,
			src_cnt, len, flags, depend_tx,cb_fn, cb_param);
	}
	/* wait for any prerequisite operations */
	async_tx_quiesce(&depend_tx);
	do_sync_pq(blocks, scfs, offset, src_cnt, len, flags,
		depend_tx, cb_fn, cb_param);
	return NULL;

This decreases the indent level and gets rid of the local variables
tx and do_async.

> +/**
> + * async_gen_syndrome - attempt to generate P (xor) and Q (Reed-Solomon code)
> + *	with a dma engine for a given set of blocks.  This routine assumes a
> + *	field of GF(2^8) with a primitive polynomial of 0x11d and a generator
> + *	of {02}.
> + * @blocks: source block array ordered from 0..src_cnt-1 with the P destination
> + *	at blocks[src_cnt] and Q at blocks[src_cnt + 1]. Only one of two
> + *	destinations may be present (another then has to be set to NULL).  Some

s/another/the other

> + * @src_cnt: number of source pages: 2 < src_cnt <= 255

This should be 1 < src_cnt, no? Tangent: Can this restriction be
relaxed to 0 < src_cnt (see current discussion on linux-raid)?

> +struct dma_async_tx_descriptor *
> +async_gen_syndrome(struct page **blocks, 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_chan *chan = async_tx_find_channel(depend_tx, DMA_PQ,
> +						     &blocks[src_cnt], 2,
> +						     blocks, src_cnt, len);
> +	struct dma_device *device = chan ? chan->device : NULL;
> +	struct dma_async_tx_descriptor *tx = NULL;
> +	bool do_async = false;
> +
> +	BUG_ON(src_cnt > 255 || (!blocks[src_cnt] && !blocks[src_cnt+1]));

src_cnt < 2 is also a bug.

> +
> +	if (device && (src_cnt <= dma_maxpq(device, 0) ||
> +		       dma_maxpq(device, DMA_PREP_CONTINUE) > 0))
> +		do_async = true;
> +
> +	if (!do_async && (flags & ASYNC_TX_ASYNC_ONLY))
> +		return NULL;
> +
> +	if (do_async) {
> +		/* run the p+q asynchronously */
> +		tx = do_async_pq(chan, blocks, (uint8_t *)raid6_gfexp,
> +				 offset, src_cnt, len, flags, depend_tx,
> +				 cb_fn, cb_param);
> +	} else {
> +		/* run the pq synchronously */
> +		/* wait for any prerequisite operations */
> +		async_tx_quiesce(&depend_tx);
> +
> +		if (!blocks[src_cnt])
> +			blocks[src_cnt] = scribble;
> +		if (!blocks[src_cnt+1])
> +			blocks[src_cnt+1] = scribble;
> +		do_sync_gen_syndrome(blocks, offset, src_cnt, len, flags,
> +				     depend_tx, cb_fn, cb_param);
> +	}
> +
> +	return tx;
> +}
> +EXPORT_SYMBOL_GPL(async_gen_syndrome);

Roughly the same comments as to async_pq() apply.

> +/**
> + * async_syndrome_zero_sum - attempt a P (xor) and Q (Reed-Solomon code)
> + *	parities check with a dma engine. This routine assumes a field of
> + *	GF(2^8) with a primitive polynomial of 0x11d and a generator of {02}.

DRY. Honestly, that function is amost identical to async_pq_zero_sum(),
the only difference being that async_pq_zero_sum() is more general
as it works for arbitrary coefficient vectors due to its additional
scfs parameter. So async_syndrome_zero_sum() should really call
async_pq_zero_sum() rather than duplicating its logic.

Regards
Andre
-- 
The only person who always got his work done by Friday was Robinson Crusoe

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

  parent reply	other threads:[~2009-03-19 20:09 UTC|newest]

Thread overview: 48+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-03-18 19:20 [PATCH 00/13] Asynchronous raid6 acceleration (part 1 of 2) Dan Williams
2009-03-18 19:20 ` [PATCH 01/13] md/raid6: move raid6 data processing to raid6_pq.ko Dan Williams
2009-03-19 20:09   ` Andre Noll
2009-03-22 17:22     ` Dan Williams
2009-03-18 19:20 ` [PATCH 02/13] async_tx: don't use src_list argument of async_xor() for dma addresses Dan Williams
2009-03-19 20:10   ` Andre Noll
2009-03-25 17:11     ` Dan Williams
2009-03-26 10:39       ` Andre Noll
2009-03-18 19:20 ` [PATCH 03/13] async_tx: provide __async_inline for HAS_DMA=n archs Dan Williams
2009-03-18 19:20 ` [PATCH 04/13] async_tx: kill needless module_{init|exit} Dan Williams
2009-03-18 19:20 ` [PATCH 05/13] async_tx: add sum check flags Dan Williams
2009-03-18 19:20 ` [PATCH 06/13] async_tx: add support for asynchronous GF multiplication Dan Williams
2009-03-19 16:06   ` H. Peter Anvin
2009-03-19 17:20     ` Dan Williams
2009-03-20 22:43       ` H. Peter Anvin
2009-03-20 23:00         ` Ilya Yanok
2009-03-20 23:25           ` H. Peter Anvin
2009-03-21  0:06             ` Ilya Yanok
2009-03-21  2:30               ` H. Peter Anvin
2009-03-21 10:19                 ` Ilya Yanok
2009-03-21 19:16                   ` H. Peter Anvin
2009-03-21 15:19                 ` Dan Williams
2009-03-21 19:15                   ` H. Peter Anvin
2009-03-21 22:14                     ` Dan Williams
2009-03-21 22:26                       ` Ilya Yanok
2009-03-21 22:46                         ` Dan Williams
2009-03-21 20:05     ` Ilya Yanok
2009-03-21 22:00       ` Dan Williams
2009-03-21 22:43         ` Ilya Yanok
2009-03-21 22:53           ` Dan Williams
2009-03-22 21:37             ` Ilya Yanok
2009-03-19 20:09   ` Andre Noll [this message]
2009-03-30 14:30   ` Sosnowski, Maciej
2009-03-18 19:20 ` [PATCH 07/13] async_tx: add support for asynchronous RAID6 recovery operations Dan Williams
2009-03-23 10:11   ` Andre Noll
2009-03-30 14:30   ` Sosnowski, Maciej
2009-03-18 19:20 ` [PATCH 08/13] iop-adma: P+Q support for iop13xx adma engines Dan Williams
2009-03-18 19:21 ` [PATCH 09/13] iop-adma: P+Q self test Dan Williams
2009-03-20  0:14   ` Neil Brown
2009-03-20  0:19     ` Dan Williams
2009-03-30 14:30   ` Sosnowski, Maciej
2009-03-18 19:21 ` [PATCH 10/13] dmaengine: allow dma support for async_tx to be toggled Dan Williams
2009-03-18 19:21 ` [PATCH 11/13] dmatest: add xor test Dan Williams
2009-03-18 19:21 ` [PATCH 12/13] dmatest: add dma interrupts and callbacks Dan Williams
2009-03-18 19:21 ` [PATCH 13/13] dmatest: add pq support Dan Williams
2009-03-18 23:43 ` [PATCH 00/13] Asynchronous raid6 acceleration (part 1 of 2) Andi Kleen
2009-03-19 17:08   ` Dan Williams
2009-03-20  0:30 ` Neil Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090319200959.GB10491@skl-net.de \
    --to=maan@systemlinux.org \
    --cc=dan.j.williams@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-raid@vger.kernel.org \
    --cc=maciej.sosnowski@intel.com \
    --cc=neilb@suse.de \
    --cc=yanok@emcraft.com \
    --cc=yur@emcraft.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).