162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Asynchronous RAID-6 recovery calculations ASYNC_TX API.
462306a36Sopenharmony_ci * Copyright(c) 2009 Intel Corporation
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * based on raid6recov.c:
762306a36Sopenharmony_ci *   Copyright 2002 H. Peter Anvin
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1362306a36Sopenharmony_ci#include <linux/raid/pq.h>
1462306a36Sopenharmony_ci#include <linux/async_tx.h>
1562306a36Sopenharmony_ci#include <linux/dmaengine.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
1862306a36Sopenharmony_ciasync_sum_product(struct page *dest, unsigned int d_off,
1962306a36Sopenharmony_ci		struct page **srcs, unsigned int *src_offs, unsigned char *coef,
2062306a36Sopenharmony_ci		size_t len, struct async_submit_ctl *submit)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ,
2362306a36Sopenharmony_ci						      &dest, 1, srcs, 2, len);
2462306a36Sopenharmony_ci	struct dma_device *dma = chan ? chan->device : NULL;
2562306a36Sopenharmony_ci	struct dmaengine_unmap_data *unmap = NULL;
2662306a36Sopenharmony_ci	const u8 *amul, *bmul;
2762306a36Sopenharmony_ci	u8 ax, bx;
2862306a36Sopenharmony_ci	u8 *a, *b, *c;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (dma)
3162306a36Sopenharmony_ci		unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOWAIT);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (unmap) {
3462306a36Sopenharmony_ci		struct device *dev = dma->dev;
3562306a36Sopenharmony_ci		dma_addr_t pq[2];
3662306a36Sopenharmony_ci		struct dma_async_tx_descriptor *tx;
3762306a36Sopenharmony_ci		enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci		if (submit->flags & ASYNC_TX_FENCE)
4062306a36Sopenharmony_ci			dma_flags |= DMA_PREP_FENCE;
4162306a36Sopenharmony_ci		unmap->addr[0] = dma_map_page(dev, srcs[0], src_offs[0],
4262306a36Sopenharmony_ci						len, DMA_TO_DEVICE);
4362306a36Sopenharmony_ci		unmap->addr[1] = dma_map_page(dev, srcs[1], src_offs[1],
4462306a36Sopenharmony_ci						len, DMA_TO_DEVICE);
4562306a36Sopenharmony_ci		unmap->to_cnt = 2;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		unmap->addr[2] = dma_map_page(dev, dest, d_off,
4862306a36Sopenharmony_ci						len, DMA_BIDIRECTIONAL);
4962306a36Sopenharmony_ci		unmap->bidi_cnt = 1;
5062306a36Sopenharmony_ci		/* engine only looks at Q, but expects it to follow P */
5162306a36Sopenharmony_ci		pq[1] = unmap->addr[2];
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		unmap->len = len;
5462306a36Sopenharmony_ci		tx = dma->device_prep_dma_pq(chan, pq, unmap->addr, 2, coef,
5562306a36Sopenharmony_ci					     len, dma_flags);
5662306a36Sopenharmony_ci		if (tx) {
5762306a36Sopenharmony_ci			dma_set_unmap(tx, unmap);
5862306a36Sopenharmony_ci			async_tx_submit(chan, tx, submit);
5962306a36Sopenharmony_ci			dmaengine_unmap_put(unmap);
6062306a36Sopenharmony_ci			return tx;
6162306a36Sopenharmony_ci		}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		/* could not get a descriptor, unmap and fall through to
6462306a36Sopenharmony_ci		 * the synchronous path
6562306a36Sopenharmony_ci		 */
6662306a36Sopenharmony_ci		dmaengine_unmap_put(unmap);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* run the operation synchronously */
7062306a36Sopenharmony_ci	async_tx_quiesce(&submit->depend_tx);
7162306a36Sopenharmony_ci	amul = raid6_gfmul[coef[0]];
7262306a36Sopenharmony_ci	bmul = raid6_gfmul[coef[1]];
7362306a36Sopenharmony_ci	a = page_address(srcs[0]) + src_offs[0];
7462306a36Sopenharmony_ci	b = page_address(srcs[1]) + src_offs[1];
7562306a36Sopenharmony_ci	c = page_address(dest) + d_off;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	while (len--) {
7862306a36Sopenharmony_ci		ax    = amul[*a++];
7962306a36Sopenharmony_ci		bx    = bmul[*b++];
8062306a36Sopenharmony_ci		*c++ = ax ^ bx;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return NULL;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
8762306a36Sopenharmony_ciasync_mult(struct page *dest, unsigned int d_off, struct page *src,
8862306a36Sopenharmony_ci		unsigned int s_off, u8 coef, size_t len,
8962306a36Sopenharmony_ci		struct async_submit_ctl *submit)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ,
9262306a36Sopenharmony_ci						      &dest, 1, &src, 1, len);
9362306a36Sopenharmony_ci	struct dma_device *dma = chan ? chan->device : NULL;
9462306a36Sopenharmony_ci	struct dmaengine_unmap_data *unmap = NULL;
9562306a36Sopenharmony_ci	const u8 *qmul; /* Q multiplier table */
9662306a36Sopenharmony_ci	u8 *d, *s;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (dma)
9962306a36Sopenharmony_ci		unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOWAIT);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (unmap) {
10262306a36Sopenharmony_ci		dma_addr_t dma_dest[2];
10362306a36Sopenharmony_ci		struct device *dev = dma->dev;
10462306a36Sopenharmony_ci		struct dma_async_tx_descriptor *tx;
10562306a36Sopenharmony_ci		enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		if (submit->flags & ASYNC_TX_FENCE)
10862306a36Sopenharmony_ci			dma_flags |= DMA_PREP_FENCE;
10962306a36Sopenharmony_ci		unmap->addr[0] = dma_map_page(dev, src, s_off,
11062306a36Sopenharmony_ci						len, DMA_TO_DEVICE);
11162306a36Sopenharmony_ci		unmap->to_cnt++;
11262306a36Sopenharmony_ci		unmap->addr[1] = dma_map_page(dev, dest, d_off,
11362306a36Sopenharmony_ci						len, DMA_BIDIRECTIONAL);
11462306a36Sopenharmony_ci		dma_dest[1] = unmap->addr[1];
11562306a36Sopenharmony_ci		unmap->bidi_cnt++;
11662306a36Sopenharmony_ci		unmap->len = len;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		/* this looks funny, but the engine looks for Q at
11962306a36Sopenharmony_ci		 * dma_dest[1] and ignores dma_dest[0] as a dest
12062306a36Sopenharmony_ci		 * due to DMA_PREP_PQ_DISABLE_P
12162306a36Sopenharmony_ci		 */
12262306a36Sopenharmony_ci		tx = dma->device_prep_dma_pq(chan, dma_dest, unmap->addr,
12362306a36Sopenharmony_ci					     1, &coef, len, dma_flags);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		if (tx) {
12662306a36Sopenharmony_ci			dma_set_unmap(tx, unmap);
12762306a36Sopenharmony_ci			dmaengine_unmap_put(unmap);
12862306a36Sopenharmony_ci			async_tx_submit(chan, tx, submit);
12962306a36Sopenharmony_ci			return tx;
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		/* could not get a descriptor, unmap and fall through to
13362306a36Sopenharmony_ci		 * the synchronous path
13462306a36Sopenharmony_ci		 */
13562306a36Sopenharmony_ci		dmaengine_unmap_put(unmap);
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* no channel available, or failed to allocate a descriptor, so
13962306a36Sopenharmony_ci	 * perform the operation synchronously
14062306a36Sopenharmony_ci	 */
14162306a36Sopenharmony_ci	async_tx_quiesce(&submit->depend_tx);
14262306a36Sopenharmony_ci	qmul  = raid6_gfmul[coef];
14362306a36Sopenharmony_ci	d = page_address(dest) + d_off;
14462306a36Sopenharmony_ci	s = page_address(src) + s_off;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	while (len--)
14762306a36Sopenharmony_ci		*d++ = qmul[*s++];
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return NULL;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
15362306a36Sopenharmony_ci__2data_recov_4(int disks, size_t bytes, int faila, int failb,
15462306a36Sopenharmony_ci		struct page **blocks, unsigned int *offs,
15562306a36Sopenharmony_ci		struct async_submit_ctl *submit)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
15862306a36Sopenharmony_ci	struct page *p, *q, *a, *b;
15962306a36Sopenharmony_ci	unsigned int p_off, q_off, a_off, b_off;
16062306a36Sopenharmony_ci	struct page *srcs[2];
16162306a36Sopenharmony_ci	unsigned int src_offs[2];
16262306a36Sopenharmony_ci	unsigned char coef[2];
16362306a36Sopenharmony_ci	enum async_tx_flags flags = submit->flags;
16462306a36Sopenharmony_ci	dma_async_tx_callback cb_fn = submit->cb_fn;
16562306a36Sopenharmony_ci	void *cb_param = submit->cb_param;
16662306a36Sopenharmony_ci	void *scribble = submit->scribble;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	p = blocks[disks-2];
16962306a36Sopenharmony_ci	p_off = offs[disks-2];
17062306a36Sopenharmony_ci	q = blocks[disks-1];
17162306a36Sopenharmony_ci	q_off = offs[disks-1];
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	a = blocks[faila];
17462306a36Sopenharmony_ci	a_off = offs[faila];
17562306a36Sopenharmony_ci	b = blocks[failb];
17662306a36Sopenharmony_ci	b_off = offs[failb];
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* in the 4 disk case P + Pxy == P and Q + Qxy == Q */
17962306a36Sopenharmony_ci	/* Dx = A*(P+Pxy) + B*(Q+Qxy) */
18062306a36Sopenharmony_ci	srcs[0] = p;
18162306a36Sopenharmony_ci	src_offs[0] = p_off;
18262306a36Sopenharmony_ci	srcs[1] = q;
18362306a36Sopenharmony_ci	src_offs[1] = q_off;
18462306a36Sopenharmony_ci	coef[0] = raid6_gfexi[failb-faila];
18562306a36Sopenharmony_ci	coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]];
18662306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
18762306a36Sopenharmony_ci	tx = async_sum_product(b, b_off, srcs, src_offs, coef, bytes, submit);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* Dy = P+Pxy+Dx */
19062306a36Sopenharmony_ci	srcs[0] = p;
19162306a36Sopenharmony_ci	src_offs[0] = p_off;
19262306a36Sopenharmony_ci	srcs[1] = b;
19362306a36Sopenharmony_ci	src_offs[1] = b_off;
19462306a36Sopenharmony_ci	init_async_submit(submit, flags | ASYNC_TX_XOR_ZERO_DST, tx, cb_fn,
19562306a36Sopenharmony_ci			  cb_param, scribble);
19662306a36Sopenharmony_ci	tx = async_xor_offs(a, a_off, srcs, src_offs, 2, bytes, submit);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return tx;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
20362306a36Sopenharmony_ci__2data_recov_5(int disks, size_t bytes, int faila, int failb,
20462306a36Sopenharmony_ci		struct page **blocks, unsigned int *offs,
20562306a36Sopenharmony_ci		struct async_submit_ctl *submit)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
20862306a36Sopenharmony_ci	struct page *p, *q, *g, *dp, *dq;
20962306a36Sopenharmony_ci	unsigned int p_off, q_off, g_off, dp_off, dq_off;
21062306a36Sopenharmony_ci	struct page *srcs[2];
21162306a36Sopenharmony_ci	unsigned int src_offs[2];
21262306a36Sopenharmony_ci	unsigned char coef[2];
21362306a36Sopenharmony_ci	enum async_tx_flags flags = submit->flags;
21462306a36Sopenharmony_ci	dma_async_tx_callback cb_fn = submit->cb_fn;
21562306a36Sopenharmony_ci	void *cb_param = submit->cb_param;
21662306a36Sopenharmony_ci	void *scribble = submit->scribble;
21762306a36Sopenharmony_ci	int good_srcs, good, i;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	good_srcs = 0;
22062306a36Sopenharmony_ci	good = -1;
22162306a36Sopenharmony_ci	for (i = 0; i < disks-2; i++) {
22262306a36Sopenharmony_ci		if (blocks[i] == NULL)
22362306a36Sopenharmony_ci			continue;
22462306a36Sopenharmony_ci		if (i == faila || i == failb)
22562306a36Sopenharmony_ci			continue;
22662306a36Sopenharmony_ci		good = i;
22762306a36Sopenharmony_ci		good_srcs++;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci	BUG_ON(good_srcs > 1);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	p = blocks[disks-2];
23262306a36Sopenharmony_ci	p_off = offs[disks-2];
23362306a36Sopenharmony_ci	q = blocks[disks-1];
23462306a36Sopenharmony_ci	q_off = offs[disks-1];
23562306a36Sopenharmony_ci	g = blocks[good];
23662306a36Sopenharmony_ci	g_off = offs[good];
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Compute syndrome with zero for the missing data pages
23962306a36Sopenharmony_ci	 * Use the dead data pages as temporary storage for delta p and
24062306a36Sopenharmony_ci	 * delta q
24162306a36Sopenharmony_ci	 */
24262306a36Sopenharmony_ci	dp = blocks[faila];
24362306a36Sopenharmony_ci	dp_off = offs[faila];
24462306a36Sopenharmony_ci	dq = blocks[failb];
24562306a36Sopenharmony_ci	dq_off = offs[failb];
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
24862306a36Sopenharmony_ci	tx = async_memcpy(dp, g, dp_off, g_off, bytes, submit);
24962306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
25062306a36Sopenharmony_ci	tx = async_mult(dq, dq_off, g, g_off,
25162306a36Sopenharmony_ci			raid6_gfexp[good], bytes, submit);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* compute P + Pxy */
25462306a36Sopenharmony_ci	srcs[0] = dp;
25562306a36Sopenharmony_ci	src_offs[0] = dp_off;
25662306a36Sopenharmony_ci	srcs[1] = p;
25762306a36Sopenharmony_ci	src_offs[1] = p_off;
25862306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
25962306a36Sopenharmony_ci			  NULL, NULL, scribble);
26062306a36Sopenharmony_ci	tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/* compute Q + Qxy */
26362306a36Sopenharmony_ci	srcs[0] = dq;
26462306a36Sopenharmony_ci	src_offs[0] = dq_off;
26562306a36Sopenharmony_ci	srcs[1] = q;
26662306a36Sopenharmony_ci	src_offs[1] = q_off;
26762306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
26862306a36Sopenharmony_ci			  NULL, NULL, scribble);
26962306a36Sopenharmony_ci	tx = async_xor_offs(dq, dq_off, srcs, src_offs, 2, bytes, submit);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* Dx = A*(P+Pxy) + B*(Q+Qxy) */
27262306a36Sopenharmony_ci	srcs[0] = dp;
27362306a36Sopenharmony_ci	src_offs[0] = dp_off;
27462306a36Sopenharmony_ci	srcs[1] = dq;
27562306a36Sopenharmony_ci	src_offs[1] = dq_off;
27662306a36Sopenharmony_ci	coef[0] = raid6_gfexi[failb-faila];
27762306a36Sopenharmony_ci	coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]];
27862306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
27962306a36Sopenharmony_ci	tx = async_sum_product(dq, dq_off, srcs, src_offs, coef, bytes, submit);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Dy = P+Pxy+Dx */
28262306a36Sopenharmony_ci	srcs[0] = dp;
28362306a36Sopenharmony_ci	src_offs[0] = dp_off;
28462306a36Sopenharmony_ci	srcs[1] = dq;
28562306a36Sopenharmony_ci	src_offs[1] = dq_off;
28662306a36Sopenharmony_ci	init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn,
28762306a36Sopenharmony_ci			  cb_param, scribble);
28862306a36Sopenharmony_ci	tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return tx;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
29462306a36Sopenharmony_ci__2data_recov_n(int disks, size_t bytes, int faila, int failb,
29562306a36Sopenharmony_ci	      struct page **blocks, unsigned int *offs,
29662306a36Sopenharmony_ci		  struct async_submit_ctl *submit)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
29962306a36Sopenharmony_ci	struct page *p, *q, *dp, *dq;
30062306a36Sopenharmony_ci	unsigned int p_off, q_off, dp_off, dq_off;
30162306a36Sopenharmony_ci	struct page *srcs[2];
30262306a36Sopenharmony_ci	unsigned int src_offs[2];
30362306a36Sopenharmony_ci	unsigned char coef[2];
30462306a36Sopenharmony_ci	enum async_tx_flags flags = submit->flags;
30562306a36Sopenharmony_ci	dma_async_tx_callback cb_fn = submit->cb_fn;
30662306a36Sopenharmony_ci	void *cb_param = submit->cb_param;
30762306a36Sopenharmony_ci	void *scribble = submit->scribble;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	p = blocks[disks-2];
31062306a36Sopenharmony_ci	p_off = offs[disks-2];
31162306a36Sopenharmony_ci	q = blocks[disks-1];
31262306a36Sopenharmony_ci	q_off = offs[disks-1];
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* Compute syndrome with zero for the missing data pages
31562306a36Sopenharmony_ci	 * Use the dead data pages as temporary storage for
31662306a36Sopenharmony_ci	 * delta p and delta q
31762306a36Sopenharmony_ci	 */
31862306a36Sopenharmony_ci	dp = blocks[faila];
31962306a36Sopenharmony_ci	dp_off = offs[faila];
32062306a36Sopenharmony_ci	blocks[faila] = NULL;
32162306a36Sopenharmony_ci	blocks[disks-2] = dp;
32262306a36Sopenharmony_ci	offs[disks-2] = dp_off;
32362306a36Sopenharmony_ci	dq = blocks[failb];
32462306a36Sopenharmony_ci	dq_off = offs[failb];
32562306a36Sopenharmony_ci	blocks[failb] = NULL;
32662306a36Sopenharmony_ci	blocks[disks-1] = dq;
32762306a36Sopenharmony_ci	offs[disks-1] = dq_off;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
33062306a36Sopenharmony_ci	tx = async_gen_syndrome(blocks, offs, disks, bytes, submit);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* Restore pointer table */
33362306a36Sopenharmony_ci	blocks[faila]   = dp;
33462306a36Sopenharmony_ci	offs[faila] = dp_off;
33562306a36Sopenharmony_ci	blocks[failb]   = dq;
33662306a36Sopenharmony_ci	offs[failb] = dq_off;
33762306a36Sopenharmony_ci	blocks[disks-2] = p;
33862306a36Sopenharmony_ci	offs[disks-2] = p_off;
33962306a36Sopenharmony_ci	blocks[disks-1] = q;
34062306a36Sopenharmony_ci	offs[disks-1] = q_off;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* compute P + Pxy */
34362306a36Sopenharmony_ci	srcs[0] = dp;
34462306a36Sopenharmony_ci	src_offs[0] = dp_off;
34562306a36Sopenharmony_ci	srcs[1] = p;
34662306a36Sopenharmony_ci	src_offs[1] = p_off;
34762306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
34862306a36Sopenharmony_ci			  NULL, NULL, scribble);
34962306a36Sopenharmony_ci	tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* compute Q + Qxy */
35262306a36Sopenharmony_ci	srcs[0] = dq;
35362306a36Sopenharmony_ci	src_offs[0] = dq_off;
35462306a36Sopenharmony_ci	srcs[1] = q;
35562306a36Sopenharmony_ci	src_offs[1] = q_off;
35662306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
35762306a36Sopenharmony_ci			  NULL, NULL, scribble);
35862306a36Sopenharmony_ci	tx = async_xor_offs(dq, dq_off, srcs, src_offs, 2, bytes, submit);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* Dx = A*(P+Pxy) + B*(Q+Qxy) */
36162306a36Sopenharmony_ci	srcs[0] = dp;
36262306a36Sopenharmony_ci	src_offs[0] = dp_off;
36362306a36Sopenharmony_ci	srcs[1] = dq;
36462306a36Sopenharmony_ci	src_offs[1] = dq_off;
36562306a36Sopenharmony_ci	coef[0] = raid6_gfexi[failb-faila];
36662306a36Sopenharmony_ci	coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]];
36762306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
36862306a36Sopenharmony_ci	tx = async_sum_product(dq, dq_off, srcs, src_offs, coef, bytes, submit);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* Dy = P+Pxy+Dx */
37162306a36Sopenharmony_ci	srcs[0] = dp;
37262306a36Sopenharmony_ci	src_offs[0] = dp_off;
37362306a36Sopenharmony_ci	srcs[1] = dq;
37462306a36Sopenharmony_ci	src_offs[1] = dq_off;
37562306a36Sopenharmony_ci	init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn,
37662306a36Sopenharmony_ci			  cb_param, scribble);
37762306a36Sopenharmony_ci	tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return tx;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/**
38362306a36Sopenharmony_ci * async_raid6_2data_recov - asynchronously calculate two missing data blocks
38462306a36Sopenharmony_ci * @disks: number of disks in the RAID-6 array
38562306a36Sopenharmony_ci * @bytes: block size
38662306a36Sopenharmony_ci * @faila: first failed drive index
38762306a36Sopenharmony_ci * @failb: second failed drive index
38862306a36Sopenharmony_ci * @blocks: array of source pointers where the last two entries are p and q
38962306a36Sopenharmony_ci * @offs: array of offset for pages in blocks
39062306a36Sopenharmony_ci * @submit: submission/completion modifiers
39162306a36Sopenharmony_ci */
39262306a36Sopenharmony_cistruct dma_async_tx_descriptor *
39362306a36Sopenharmony_ciasync_raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
39462306a36Sopenharmony_ci			struct page **blocks, unsigned int *offs,
39562306a36Sopenharmony_ci			struct async_submit_ctl *submit)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	void *scribble = submit->scribble;
39862306a36Sopenharmony_ci	int non_zero_srcs, i;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	BUG_ON(faila == failb);
40162306a36Sopenharmony_ci	if (failb < faila)
40262306a36Sopenharmony_ci		swap(faila, failb);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* if a dma resource is not available or a scribble buffer is not
40762306a36Sopenharmony_ci	 * available punt to the synchronous path.  In the 'dma not
40862306a36Sopenharmony_ci	 * available' case be sure to use the scribble buffer to
40962306a36Sopenharmony_ci	 * preserve the content of 'blocks' as the caller intended.
41062306a36Sopenharmony_ci	 */
41162306a36Sopenharmony_ci	if (!async_dma_find_channel(DMA_PQ) || !scribble) {
41262306a36Sopenharmony_ci		void **ptrs = scribble ? scribble : (void **) blocks;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		async_tx_quiesce(&submit->depend_tx);
41562306a36Sopenharmony_ci		for (i = 0; i < disks; i++)
41662306a36Sopenharmony_ci			if (blocks[i] == NULL)
41762306a36Sopenharmony_ci				ptrs[i] = (void *) raid6_empty_zero_page;
41862306a36Sopenharmony_ci			else
41962306a36Sopenharmony_ci				ptrs[i] = page_address(blocks[i]) + offs[i];
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		raid6_2data_recov(disks, bytes, faila, failb, ptrs);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		async_tx_sync_epilog(submit);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		return NULL;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	non_zero_srcs = 0;
42962306a36Sopenharmony_ci	for (i = 0; i < disks-2 && non_zero_srcs < 4; i++)
43062306a36Sopenharmony_ci		if (blocks[i])
43162306a36Sopenharmony_ci			non_zero_srcs++;
43262306a36Sopenharmony_ci	switch (non_zero_srcs) {
43362306a36Sopenharmony_ci	case 0:
43462306a36Sopenharmony_ci	case 1:
43562306a36Sopenharmony_ci		/* There must be at least 2 sources - the failed devices. */
43662306a36Sopenharmony_ci		BUG();
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	case 2:
43962306a36Sopenharmony_ci		/* dma devices do not uniformly understand a zero source pq
44062306a36Sopenharmony_ci		 * operation (in contrast to the synchronous case), so
44162306a36Sopenharmony_ci		 * explicitly handle the special case of a 4 disk array with
44262306a36Sopenharmony_ci		 * both data disks missing.
44362306a36Sopenharmony_ci		 */
44462306a36Sopenharmony_ci		return __2data_recov_4(disks, bytes, faila, failb,
44562306a36Sopenharmony_ci				blocks, offs, submit);
44662306a36Sopenharmony_ci	case 3:
44762306a36Sopenharmony_ci		/* dma devices do not uniformly understand a single
44862306a36Sopenharmony_ci		 * source pq operation (in contrast to the synchronous
44962306a36Sopenharmony_ci		 * case), so explicitly handle the special case of a 5 disk
45062306a36Sopenharmony_ci		 * array with 2 of 3 data disks missing.
45162306a36Sopenharmony_ci		 */
45262306a36Sopenharmony_ci		return __2data_recov_5(disks, bytes, faila, failb,
45362306a36Sopenharmony_ci				blocks, offs, submit);
45462306a36Sopenharmony_ci	default:
45562306a36Sopenharmony_ci		return __2data_recov_n(disks, bytes, faila, failb,
45662306a36Sopenharmony_ci				blocks, offs, submit);
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(async_raid6_2data_recov);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/**
46262306a36Sopenharmony_ci * async_raid6_datap_recov - asynchronously calculate a data and the 'p' block
46362306a36Sopenharmony_ci * @disks: number of disks in the RAID-6 array
46462306a36Sopenharmony_ci * @bytes: block size
46562306a36Sopenharmony_ci * @faila: failed drive index
46662306a36Sopenharmony_ci * @blocks: array of source pointers where the last two entries are p and q
46762306a36Sopenharmony_ci * @offs: array of offset for pages in blocks
46862306a36Sopenharmony_ci * @submit: submission/completion modifiers
46962306a36Sopenharmony_ci */
47062306a36Sopenharmony_cistruct dma_async_tx_descriptor *
47162306a36Sopenharmony_ciasync_raid6_datap_recov(int disks, size_t bytes, int faila,
47262306a36Sopenharmony_ci			struct page **blocks, unsigned int *offs,
47362306a36Sopenharmony_ci			struct async_submit_ctl *submit)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
47662306a36Sopenharmony_ci	struct page *p, *q, *dq;
47762306a36Sopenharmony_ci	unsigned int p_off, q_off, dq_off;
47862306a36Sopenharmony_ci	u8 coef;
47962306a36Sopenharmony_ci	enum async_tx_flags flags = submit->flags;
48062306a36Sopenharmony_ci	dma_async_tx_callback cb_fn = submit->cb_fn;
48162306a36Sopenharmony_ci	void *cb_param = submit->cb_param;
48262306a36Sopenharmony_ci	void *scribble = submit->scribble;
48362306a36Sopenharmony_ci	int good_srcs, good, i;
48462306a36Sopenharmony_ci	struct page *srcs[2];
48562306a36Sopenharmony_ci	unsigned int src_offs[2];
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* if a dma resource is not available or a scribble buffer is not
49062306a36Sopenharmony_ci	 * available punt to the synchronous path.  In the 'dma not
49162306a36Sopenharmony_ci	 * available' case be sure to use the scribble buffer to
49262306a36Sopenharmony_ci	 * preserve the content of 'blocks' as the caller intended.
49362306a36Sopenharmony_ci	 */
49462306a36Sopenharmony_ci	if (!async_dma_find_channel(DMA_PQ) || !scribble) {
49562306a36Sopenharmony_ci		void **ptrs = scribble ? scribble : (void **) blocks;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		async_tx_quiesce(&submit->depend_tx);
49862306a36Sopenharmony_ci		for (i = 0; i < disks; i++)
49962306a36Sopenharmony_ci			if (blocks[i] == NULL)
50062306a36Sopenharmony_ci				ptrs[i] = (void*)raid6_empty_zero_page;
50162306a36Sopenharmony_ci			else
50262306a36Sopenharmony_ci				ptrs[i] = page_address(blocks[i]) + offs[i];
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		raid6_datap_recov(disks, bytes, faila, ptrs);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		async_tx_sync_epilog(submit);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		return NULL;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	good_srcs = 0;
51262306a36Sopenharmony_ci	good = -1;
51362306a36Sopenharmony_ci	for (i = 0; i < disks-2; i++) {
51462306a36Sopenharmony_ci		if (i == faila)
51562306a36Sopenharmony_ci			continue;
51662306a36Sopenharmony_ci		if (blocks[i]) {
51762306a36Sopenharmony_ci			good = i;
51862306a36Sopenharmony_ci			good_srcs++;
51962306a36Sopenharmony_ci			if (good_srcs > 1)
52062306a36Sopenharmony_ci				break;
52162306a36Sopenharmony_ci		}
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	BUG_ON(good_srcs == 0);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	p = blocks[disks-2];
52662306a36Sopenharmony_ci	p_off = offs[disks-2];
52762306a36Sopenharmony_ci	q = blocks[disks-1];
52862306a36Sopenharmony_ci	q_off = offs[disks-1];
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* Compute syndrome with zero for the missing data page
53162306a36Sopenharmony_ci	 * Use the dead data page as temporary storage for delta q
53262306a36Sopenharmony_ci	 */
53362306a36Sopenharmony_ci	dq = blocks[faila];
53462306a36Sopenharmony_ci	dq_off = offs[faila];
53562306a36Sopenharmony_ci	blocks[faila] = NULL;
53662306a36Sopenharmony_ci	blocks[disks-1] = dq;
53762306a36Sopenharmony_ci	offs[disks-1] = dq_off;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	/* in the 4-disk case we only need to perform a single source
54062306a36Sopenharmony_ci	 * multiplication with the one good data block.
54162306a36Sopenharmony_ci	 */
54262306a36Sopenharmony_ci	if (good_srcs == 1) {
54362306a36Sopenharmony_ci		struct page *g = blocks[good];
54462306a36Sopenharmony_ci		unsigned int g_off = offs[good];
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL,
54762306a36Sopenharmony_ci				  scribble);
54862306a36Sopenharmony_ci		tx = async_memcpy(p, g, p_off, g_off, bytes, submit);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL,
55162306a36Sopenharmony_ci				  scribble);
55262306a36Sopenharmony_ci		tx = async_mult(dq, dq_off, g, g_off,
55362306a36Sopenharmony_ci				raid6_gfexp[good], bytes, submit);
55462306a36Sopenharmony_ci	} else {
55562306a36Sopenharmony_ci		init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL,
55662306a36Sopenharmony_ci				  scribble);
55762306a36Sopenharmony_ci		tx = async_gen_syndrome(blocks, offs, disks, bytes, submit);
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/* Restore pointer table */
56162306a36Sopenharmony_ci	blocks[faila]   = dq;
56262306a36Sopenharmony_ci	offs[faila] = dq_off;
56362306a36Sopenharmony_ci	blocks[disks-1] = q;
56462306a36Sopenharmony_ci	offs[disks-1] = q_off;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/* calculate g^{-faila} */
56762306a36Sopenharmony_ci	coef = raid6_gfinv[raid6_gfexp[faila]];
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	srcs[0] = dq;
57062306a36Sopenharmony_ci	src_offs[0] = dq_off;
57162306a36Sopenharmony_ci	srcs[1] = q;
57262306a36Sopenharmony_ci	src_offs[1] = q_off;
57362306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
57462306a36Sopenharmony_ci			  NULL, NULL, scribble);
57562306a36Sopenharmony_ci	tx = async_xor_offs(dq, dq_off, srcs, src_offs, 2, bytes, submit);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
57862306a36Sopenharmony_ci	tx = async_mult(dq, dq_off, dq, dq_off, coef, bytes, submit);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	srcs[0] = p;
58162306a36Sopenharmony_ci	src_offs[0] = p_off;
58262306a36Sopenharmony_ci	srcs[1] = dq;
58362306a36Sopenharmony_ci	src_offs[1] = dq_off;
58462306a36Sopenharmony_ci	init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn,
58562306a36Sopenharmony_ci			  cb_param, scribble);
58662306a36Sopenharmony_ci	tx = async_xor_offs(p, p_off, srcs, src_offs, 2, bytes, submit);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	return tx;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(async_raid6_datap_recov);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ciMODULE_AUTHOR("Dan Williams <dan.j.williams@intel.com>");
59362306a36Sopenharmony_ciMODULE_DESCRIPTION("asynchronous RAID-6 recovery api");
59462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
595