18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Asynchronous RAID-6 recovery calculations ASYNC_TX API.
48c2ecf20Sopenharmony_ci * Copyright(c) 2009 Intel Corporation
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * based on raid6recov.c:
78c2ecf20Sopenharmony_ci *   Copyright 2002 H. Peter Anvin
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
138c2ecf20Sopenharmony_ci#include <linux/raid/pq.h>
148c2ecf20Sopenharmony_ci#include <linux/async_tx.h>
158c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
188c2ecf20Sopenharmony_ciasync_sum_product(struct page *dest, unsigned int d_off,
198c2ecf20Sopenharmony_ci		struct page **srcs, unsigned int *src_offs, unsigned char *coef,
208c2ecf20Sopenharmony_ci		size_t len, struct async_submit_ctl *submit)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ,
238c2ecf20Sopenharmony_ci						      &dest, 1, srcs, 2, len);
248c2ecf20Sopenharmony_ci	struct dma_device *dma = chan ? chan->device : NULL;
258c2ecf20Sopenharmony_ci	struct dmaengine_unmap_data *unmap = NULL;
268c2ecf20Sopenharmony_ci	const u8 *amul, *bmul;
278c2ecf20Sopenharmony_ci	u8 ax, bx;
288c2ecf20Sopenharmony_ci	u8 *a, *b, *c;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	if (dma)
318c2ecf20Sopenharmony_ci		unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOWAIT);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (unmap) {
348c2ecf20Sopenharmony_ci		struct device *dev = dma->dev;
358c2ecf20Sopenharmony_ci		dma_addr_t pq[2];
368c2ecf20Sopenharmony_ci		struct dma_async_tx_descriptor *tx;
378c2ecf20Sopenharmony_ci		enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci		if (submit->flags & ASYNC_TX_FENCE)
408c2ecf20Sopenharmony_ci			dma_flags |= DMA_PREP_FENCE;
418c2ecf20Sopenharmony_ci		unmap->addr[0] = dma_map_page(dev, srcs[0], src_offs[0],
428c2ecf20Sopenharmony_ci						len, DMA_TO_DEVICE);
438c2ecf20Sopenharmony_ci		unmap->addr[1] = dma_map_page(dev, srcs[1], src_offs[1],
448c2ecf20Sopenharmony_ci						len, DMA_TO_DEVICE);
458c2ecf20Sopenharmony_ci		unmap->to_cnt = 2;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci		unmap->addr[2] = dma_map_page(dev, dest, d_off,
488c2ecf20Sopenharmony_ci						len, DMA_BIDIRECTIONAL);
498c2ecf20Sopenharmony_ci		unmap->bidi_cnt = 1;
508c2ecf20Sopenharmony_ci		/* engine only looks at Q, but expects it to follow P */
518c2ecf20Sopenharmony_ci		pq[1] = unmap->addr[2];
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci		unmap->len = len;
548c2ecf20Sopenharmony_ci		tx = dma->device_prep_dma_pq(chan, pq, unmap->addr, 2, coef,
558c2ecf20Sopenharmony_ci					     len, dma_flags);
568c2ecf20Sopenharmony_ci		if (tx) {
578c2ecf20Sopenharmony_ci			dma_set_unmap(tx, unmap);
588c2ecf20Sopenharmony_ci			async_tx_submit(chan, tx, submit);
598c2ecf20Sopenharmony_ci			dmaengine_unmap_put(unmap);
608c2ecf20Sopenharmony_ci			return tx;
618c2ecf20Sopenharmony_ci		}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		/* could not get a descriptor, unmap and fall through to
648c2ecf20Sopenharmony_ci		 * the synchronous path
658c2ecf20Sopenharmony_ci		 */
668c2ecf20Sopenharmony_ci		dmaengine_unmap_put(unmap);
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* run the operation synchronously */
708c2ecf20Sopenharmony_ci	async_tx_quiesce(&submit->depend_tx);
718c2ecf20Sopenharmony_ci	amul = raid6_gfmul[coef[0]];
728c2ecf20Sopenharmony_ci	bmul = raid6_gfmul[coef[1]];
738c2ecf20Sopenharmony_ci	a = page_address(srcs[0]) + src_offs[0];
748c2ecf20Sopenharmony_ci	b = page_address(srcs[1]) + src_offs[1];
758c2ecf20Sopenharmony_ci	c = page_address(dest) + d_off;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	while (len--) {
788c2ecf20Sopenharmony_ci		ax    = amul[*a++];
798c2ecf20Sopenharmony_ci		bx    = bmul[*b++];
808c2ecf20Sopenharmony_ci		*c++ = ax ^ bx;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return NULL;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
878c2ecf20Sopenharmony_ciasync_mult(struct page *dest, unsigned int d_off, struct page *src,
888c2ecf20Sopenharmony_ci		unsigned int s_off, u8 coef, size_t len,
898c2ecf20Sopenharmony_ci		struct async_submit_ctl *submit)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct dma_chan *chan = async_tx_find_channel(submit, DMA_PQ,
928c2ecf20Sopenharmony_ci						      &dest, 1, &src, 1, len);
938c2ecf20Sopenharmony_ci	struct dma_device *dma = chan ? chan->device : NULL;
948c2ecf20Sopenharmony_ci	struct dmaengine_unmap_data *unmap = NULL;
958c2ecf20Sopenharmony_ci	const u8 *qmul; /* Q multiplier table */
968c2ecf20Sopenharmony_ci	u8 *d, *s;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (dma)
998c2ecf20Sopenharmony_ci		unmap = dmaengine_get_unmap_data(dma->dev, 3, GFP_NOWAIT);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (unmap) {
1028c2ecf20Sopenharmony_ci		dma_addr_t dma_dest[2];
1038c2ecf20Sopenharmony_ci		struct device *dev = dma->dev;
1048c2ecf20Sopenharmony_ci		struct dma_async_tx_descriptor *tx;
1058c2ecf20Sopenharmony_ci		enum dma_ctrl_flags dma_flags = DMA_PREP_PQ_DISABLE_P;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		if (submit->flags & ASYNC_TX_FENCE)
1088c2ecf20Sopenharmony_ci			dma_flags |= DMA_PREP_FENCE;
1098c2ecf20Sopenharmony_ci		unmap->addr[0] = dma_map_page(dev, src, s_off,
1108c2ecf20Sopenharmony_ci						len, DMA_TO_DEVICE);
1118c2ecf20Sopenharmony_ci		unmap->to_cnt++;
1128c2ecf20Sopenharmony_ci		unmap->addr[1] = dma_map_page(dev, dest, d_off,
1138c2ecf20Sopenharmony_ci						len, DMA_BIDIRECTIONAL);
1148c2ecf20Sopenharmony_ci		dma_dest[1] = unmap->addr[1];
1158c2ecf20Sopenharmony_ci		unmap->bidi_cnt++;
1168c2ecf20Sopenharmony_ci		unmap->len = len;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		/* this looks funny, but the engine looks for Q at
1198c2ecf20Sopenharmony_ci		 * dma_dest[1] and ignores dma_dest[0] as a dest
1208c2ecf20Sopenharmony_ci		 * due to DMA_PREP_PQ_DISABLE_P
1218c2ecf20Sopenharmony_ci		 */
1228c2ecf20Sopenharmony_ci		tx = dma->device_prep_dma_pq(chan, dma_dest, unmap->addr,
1238c2ecf20Sopenharmony_ci					     1, &coef, len, dma_flags);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci		if (tx) {
1268c2ecf20Sopenharmony_ci			dma_set_unmap(tx, unmap);
1278c2ecf20Sopenharmony_ci			dmaengine_unmap_put(unmap);
1288c2ecf20Sopenharmony_ci			async_tx_submit(chan, tx, submit);
1298c2ecf20Sopenharmony_ci			return tx;
1308c2ecf20Sopenharmony_ci		}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		/* could not get a descriptor, unmap and fall through to
1338c2ecf20Sopenharmony_ci		 * the synchronous path
1348c2ecf20Sopenharmony_ci		 */
1358c2ecf20Sopenharmony_ci		dmaengine_unmap_put(unmap);
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* no channel available, or failed to allocate a descriptor, so
1398c2ecf20Sopenharmony_ci	 * perform the operation synchronously
1408c2ecf20Sopenharmony_ci	 */
1418c2ecf20Sopenharmony_ci	async_tx_quiesce(&submit->depend_tx);
1428c2ecf20Sopenharmony_ci	qmul  = raid6_gfmul[coef];
1438c2ecf20Sopenharmony_ci	d = page_address(dest) + d_off;
1448c2ecf20Sopenharmony_ci	s = page_address(src) + s_off;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	while (len--)
1478c2ecf20Sopenharmony_ci		*d++ = qmul[*s++];
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return NULL;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
1538c2ecf20Sopenharmony_ci__2data_recov_4(int disks, size_t bytes, int faila, int failb,
1548c2ecf20Sopenharmony_ci		struct page **blocks, unsigned int *offs,
1558c2ecf20Sopenharmony_ci		struct async_submit_ctl *submit)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
1588c2ecf20Sopenharmony_ci	struct page *p, *q, *a, *b;
1598c2ecf20Sopenharmony_ci	unsigned int p_off, q_off, a_off, b_off;
1608c2ecf20Sopenharmony_ci	struct page *srcs[2];
1618c2ecf20Sopenharmony_ci	unsigned int src_offs[2];
1628c2ecf20Sopenharmony_ci	unsigned char coef[2];
1638c2ecf20Sopenharmony_ci	enum async_tx_flags flags = submit->flags;
1648c2ecf20Sopenharmony_ci	dma_async_tx_callback cb_fn = submit->cb_fn;
1658c2ecf20Sopenharmony_ci	void *cb_param = submit->cb_param;
1668c2ecf20Sopenharmony_ci	void *scribble = submit->scribble;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	p = blocks[disks-2];
1698c2ecf20Sopenharmony_ci	p_off = offs[disks-2];
1708c2ecf20Sopenharmony_ci	q = blocks[disks-1];
1718c2ecf20Sopenharmony_ci	q_off = offs[disks-1];
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	a = blocks[faila];
1748c2ecf20Sopenharmony_ci	a_off = offs[faila];
1758c2ecf20Sopenharmony_ci	b = blocks[failb];
1768c2ecf20Sopenharmony_ci	b_off = offs[failb];
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* in the 4 disk case P + Pxy == P and Q + Qxy == Q */
1798c2ecf20Sopenharmony_ci	/* Dx = A*(P+Pxy) + B*(Q+Qxy) */
1808c2ecf20Sopenharmony_ci	srcs[0] = p;
1818c2ecf20Sopenharmony_ci	src_offs[0] = p_off;
1828c2ecf20Sopenharmony_ci	srcs[1] = q;
1838c2ecf20Sopenharmony_ci	src_offs[1] = q_off;
1848c2ecf20Sopenharmony_ci	coef[0] = raid6_gfexi[failb-faila];
1858c2ecf20Sopenharmony_ci	coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]];
1868c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
1878c2ecf20Sopenharmony_ci	tx = async_sum_product(b, b_off, srcs, src_offs, coef, bytes, submit);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* Dy = P+Pxy+Dx */
1908c2ecf20Sopenharmony_ci	srcs[0] = p;
1918c2ecf20Sopenharmony_ci	src_offs[0] = p_off;
1928c2ecf20Sopenharmony_ci	srcs[1] = b;
1938c2ecf20Sopenharmony_ci	src_offs[1] = b_off;
1948c2ecf20Sopenharmony_ci	init_async_submit(submit, flags | ASYNC_TX_XOR_ZERO_DST, tx, cb_fn,
1958c2ecf20Sopenharmony_ci			  cb_param, scribble);
1968c2ecf20Sopenharmony_ci	tx = async_xor_offs(a, a_off, srcs, src_offs, 2, bytes, submit);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return tx;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
2038c2ecf20Sopenharmony_ci__2data_recov_5(int disks, size_t bytes, int faila, int failb,
2048c2ecf20Sopenharmony_ci		struct page **blocks, unsigned int *offs,
2058c2ecf20Sopenharmony_ci		struct async_submit_ctl *submit)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
2088c2ecf20Sopenharmony_ci	struct page *p, *q, *g, *dp, *dq;
2098c2ecf20Sopenharmony_ci	unsigned int p_off, q_off, g_off, dp_off, dq_off;
2108c2ecf20Sopenharmony_ci	struct page *srcs[2];
2118c2ecf20Sopenharmony_ci	unsigned int src_offs[2];
2128c2ecf20Sopenharmony_ci	unsigned char coef[2];
2138c2ecf20Sopenharmony_ci	enum async_tx_flags flags = submit->flags;
2148c2ecf20Sopenharmony_ci	dma_async_tx_callback cb_fn = submit->cb_fn;
2158c2ecf20Sopenharmony_ci	void *cb_param = submit->cb_param;
2168c2ecf20Sopenharmony_ci	void *scribble = submit->scribble;
2178c2ecf20Sopenharmony_ci	int good_srcs, good, i;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	good_srcs = 0;
2208c2ecf20Sopenharmony_ci	good = -1;
2218c2ecf20Sopenharmony_ci	for (i = 0; i < disks-2; i++) {
2228c2ecf20Sopenharmony_ci		if (blocks[i] == NULL)
2238c2ecf20Sopenharmony_ci			continue;
2248c2ecf20Sopenharmony_ci		if (i == faila || i == failb)
2258c2ecf20Sopenharmony_ci			continue;
2268c2ecf20Sopenharmony_ci		good = i;
2278c2ecf20Sopenharmony_ci		good_srcs++;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci	BUG_ON(good_srcs > 1);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	p = blocks[disks-2];
2328c2ecf20Sopenharmony_ci	p_off = offs[disks-2];
2338c2ecf20Sopenharmony_ci	q = blocks[disks-1];
2348c2ecf20Sopenharmony_ci	q_off = offs[disks-1];
2358c2ecf20Sopenharmony_ci	g = blocks[good];
2368c2ecf20Sopenharmony_ci	g_off = offs[good];
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* Compute syndrome with zero for the missing data pages
2398c2ecf20Sopenharmony_ci	 * Use the dead data pages as temporary storage for delta p and
2408c2ecf20Sopenharmony_ci	 * delta q
2418c2ecf20Sopenharmony_ci	 */
2428c2ecf20Sopenharmony_ci	dp = blocks[faila];
2438c2ecf20Sopenharmony_ci	dp_off = offs[faila];
2448c2ecf20Sopenharmony_ci	dq = blocks[failb];
2458c2ecf20Sopenharmony_ci	dq_off = offs[failb];
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
2488c2ecf20Sopenharmony_ci	tx = async_memcpy(dp, g, dp_off, g_off, bytes, submit);
2498c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
2508c2ecf20Sopenharmony_ci	tx = async_mult(dq, dq_off, g, g_off,
2518c2ecf20Sopenharmony_ci			raid6_gfexp[good], bytes, submit);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* compute P + Pxy */
2548c2ecf20Sopenharmony_ci	srcs[0] = dp;
2558c2ecf20Sopenharmony_ci	src_offs[0] = dp_off;
2568c2ecf20Sopenharmony_ci	srcs[1] = p;
2578c2ecf20Sopenharmony_ci	src_offs[1] = p_off;
2588c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
2598c2ecf20Sopenharmony_ci			  NULL, NULL, scribble);
2608c2ecf20Sopenharmony_ci	tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* compute Q + Qxy */
2638c2ecf20Sopenharmony_ci	srcs[0] = dq;
2648c2ecf20Sopenharmony_ci	src_offs[0] = dq_off;
2658c2ecf20Sopenharmony_ci	srcs[1] = q;
2668c2ecf20Sopenharmony_ci	src_offs[1] = q_off;
2678c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
2688c2ecf20Sopenharmony_ci			  NULL, NULL, scribble);
2698c2ecf20Sopenharmony_ci	tx = async_xor_offs(dq, dq_off, srcs, src_offs, 2, bytes, submit);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* Dx = A*(P+Pxy) + B*(Q+Qxy) */
2728c2ecf20Sopenharmony_ci	srcs[0] = dp;
2738c2ecf20Sopenharmony_ci	src_offs[0] = dp_off;
2748c2ecf20Sopenharmony_ci	srcs[1] = dq;
2758c2ecf20Sopenharmony_ci	src_offs[1] = dq_off;
2768c2ecf20Sopenharmony_ci	coef[0] = raid6_gfexi[failb-faila];
2778c2ecf20Sopenharmony_ci	coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]];
2788c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
2798c2ecf20Sopenharmony_ci	tx = async_sum_product(dq, dq_off, srcs, src_offs, coef, bytes, submit);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* Dy = P+Pxy+Dx */
2828c2ecf20Sopenharmony_ci	srcs[0] = dp;
2838c2ecf20Sopenharmony_ci	src_offs[0] = dp_off;
2848c2ecf20Sopenharmony_ci	srcs[1] = dq;
2858c2ecf20Sopenharmony_ci	src_offs[1] = dq_off;
2868c2ecf20Sopenharmony_ci	init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn,
2878c2ecf20Sopenharmony_ci			  cb_param, scribble);
2888c2ecf20Sopenharmony_ci	tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return tx;
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *
2948c2ecf20Sopenharmony_ci__2data_recov_n(int disks, size_t bytes, int faila, int failb,
2958c2ecf20Sopenharmony_ci	      struct page **blocks, unsigned int *offs,
2968c2ecf20Sopenharmony_ci		  struct async_submit_ctl *submit)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
2998c2ecf20Sopenharmony_ci	struct page *p, *q, *dp, *dq;
3008c2ecf20Sopenharmony_ci	unsigned int p_off, q_off, dp_off, dq_off;
3018c2ecf20Sopenharmony_ci	struct page *srcs[2];
3028c2ecf20Sopenharmony_ci	unsigned int src_offs[2];
3038c2ecf20Sopenharmony_ci	unsigned char coef[2];
3048c2ecf20Sopenharmony_ci	enum async_tx_flags flags = submit->flags;
3058c2ecf20Sopenharmony_ci	dma_async_tx_callback cb_fn = submit->cb_fn;
3068c2ecf20Sopenharmony_ci	void *cb_param = submit->cb_param;
3078c2ecf20Sopenharmony_ci	void *scribble = submit->scribble;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	p = blocks[disks-2];
3108c2ecf20Sopenharmony_ci	p_off = offs[disks-2];
3118c2ecf20Sopenharmony_ci	q = blocks[disks-1];
3128c2ecf20Sopenharmony_ci	q_off = offs[disks-1];
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* Compute syndrome with zero for the missing data pages
3158c2ecf20Sopenharmony_ci	 * Use the dead data pages as temporary storage for
3168c2ecf20Sopenharmony_ci	 * delta p and delta q
3178c2ecf20Sopenharmony_ci	 */
3188c2ecf20Sopenharmony_ci	dp = blocks[faila];
3198c2ecf20Sopenharmony_ci	dp_off = offs[faila];
3208c2ecf20Sopenharmony_ci	blocks[faila] = NULL;
3218c2ecf20Sopenharmony_ci	blocks[disks-2] = dp;
3228c2ecf20Sopenharmony_ci	offs[disks-2] = dp_off;
3238c2ecf20Sopenharmony_ci	dq = blocks[failb];
3248c2ecf20Sopenharmony_ci	dq_off = offs[failb];
3258c2ecf20Sopenharmony_ci	blocks[failb] = NULL;
3268c2ecf20Sopenharmony_ci	blocks[disks-1] = dq;
3278c2ecf20Sopenharmony_ci	offs[disks-1] = dq_off;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
3308c2ecf20Sopenharmony_ci	tx = async_gen_syndrome(blocks, offs, disks, bytes, submit);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	/* Restore pointer table */
3338c2ecf20Sopenharmony_ci	blocks[faila]   = dp;
3348c2ecf20Sopenharmony_ci	offs[faila] = dp_off;
3358c2ecf20Sopenharmony_ci	blocks[failb]   = dq;
3368c2ecf20Sopenharmony_ci	offs[failb] = dq_off;
3378c2ecf20Sopenharmony_ci	blocks[disks-2] = p;
3388c2ecf20Sopenharmony_ci	offs[disks-2] = p_off;
3398c2ecf20Sopenharmony_ci	blocks[disks-1] = q;
3408c2ecf20Sopenharmony_ci	offs[disks-1] = q_off;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* compute P + Pxy */
3438c2ecf20Sopenharmony_ci	srcs[0] = dp;
3448c2ecf20Sopenharmony_ci	src_offs[0] = dp_off;
3458c2ecf20Sopenharmony_ci	srcs[1] = p;
3468c2ecf20Sopenharmony_ci	src_offs[1] = p_off;
3478c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
3488c2ecf20Sopenharmony_ci			  NULL, NULL, scribble);
3498c2ecf20Sopenharmony_ci	tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* compute Q + Qxy */
3528c2ecf20Sopenharmony_ci	srcs[0] = dq;
3538c2ecf20Sopenharmony_ci	src_offs[0] = dq_off;
3548c2ecf20Sopenharmony_ci	srcs[1] = q;
3558c2ecf20Sopenharmony_ci	src_offs[1] = q_off;
3568c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
3578c2ecf20Sopenharmony_ci			  NULL, NULL, scribble);
3588c2ecf20Sopenharmony_ci	tx = async_xor_offs(dq, dq_off, srcs, src_offs, 2, bytes, submit);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	/* Dx = A*(P+Pxy) + B*(Q+Qxy) */
3618c2ecf20Sopenharmony_ci	srcs[0] = dp;
3628c2ecf20Sopenharmony_ci	src_offs[0] = dp_off;
3638c2ecf20Sopenharmony_ci	srcs[1] = dq;
3648c2ecf20Sopenharmony_ci	src_offs[1] = dq_off;
3658c2ecf20Sopenharmony_ci	coef[0] = raid6_gfexi[failb-faila];
3668c2ecf20Sopenharmony_ci	coef[1] = raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]];
3678c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
3688c2ecf20Sopenharmony_ci	tx = async_sum_product(dq, dq_off, srcs, src_offs, coef, bytes, submit);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	/* Dy = P+Pxy+Dx */
3718c2ecf20Sopenharmony_ci	srcs[0] = dp;
3728c2ecf20Sopenharmony_ci	src_offs[0] = dp_off;
3738c2ecf20Sopenharmony_ci	srcs[1] = dq;
3748c2ecf20Sopenharmony_ci	src_offs[1] = dq_off;
3758c2ecf20Sopenharmony_ci	init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn,
3768c2ecf20Sopenharmony_ci			  cb_param, scribble);
3778c2ecf20Sopenharmony_ci	tx = async_xor_offs(dp, dp_off, srcs, src_offs, 2, bytes, submit);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return tx;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci/**
3838c2ecf20Sopenharmony_ci * async_raid6_2data_recov - asynchronously calculate two missing data blocks
3848c2ecf20Sopenharmony_ci * @disks: number of disks in the RAID-6 array
3858c2ecf20Sopenharmony_ci * @bytes: block size
3868c2ecf20Sopenharmony_ci * @faila: first failed drive index
3878c2ecf20Sopenharmony_ci * @failb: second failed drive index
3888c2ecf20Sopenharmony_ci * @blocks: array of source pointers where the last two entries are p and q
3898c2ecf20Sopenharmony_ci * @offs: array of offset for pages in blocks
3908c2ecf20Sopenharmony_ci * @submit: submission/completion modifiers
3918c2ecf20Sopenharmony_ci */
3928c2ecf20Sopenharmony_cistruct dma_async_tx_descriptor *
3938c2ecf20Sopenharmony_ciasync_raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
3948c2ecf20Sopenharmony_ci			struct page **blocks, unsigned int *offs,
3958c2ecf20Sopenharmony_ci			struct async_submit_ctl *submit)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	void *scribble = submit->scribble;
3988c2ecf20Sopenharmony_ci	int non_zero_srcs, i;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	BUG_ON(faila == failb);
4018c2ecf20Sopenharmony_ci	if (failb < faila)
4028c2ecf20Sopenharmony_ci		swap(faila, failb);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/* if a dma resource is not available or a scribble buffer is not
4078c2ecf20Sopenharmony_ci	 * available punt to the synchronous path.  In the 'dma not
4088c2ecf20Sopenharmony_ci	 * available' case be sure to use the scribble buffer to
4098c2ecf20Sopenharmony_ci	 * preserve the content of 'blocks' as the caller intended.
4108c2ecf20Sopenharmony_ci	 */
4118c2ecf20Sopenharmony_ci	if (!async_dma_find_channel(DMA_PQ) || !scribble) {
4128c2ecf20Sopenharmony_ci		void **ptrs = scribble ? scribble : (void **) blocks;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci		async_tx_quiesce(&submit->depend_tx);
4158c2ecf20Sopenharmony_ci		for (i = 0; i < disks; i++)
4168c2ecf20Sopenharmony_ci			if (blocks[i] == NULL)
4178c2ecf20Sopenharmony_ci				ptrs[i] = (void *) raid6_empty_zero_page;
4188c2ecf20Sopenharmony_ci			else
4198c2ecf20Sopenharmony_ci				ptrs[i] = page_address(blocks[i]) + offs[i];
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		raid6_2data_recov(disks, bytes, faila, failb, ptrs);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci		async_tx_sync_epilog(submit);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		return NULL;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	non_zero_srcs = 0;
4298c2ecf20Sopenharmony_ci	for (i = 0; i < disks-2 && non_zero_srcs < 4; i++)
4308c2ecf20Sopenharmony_ci		if (blocks[i])
4318c2ecf20Sopenharmony_ci			non_zero_srcs++;
4328c2ecf20Sopenharmony_ci	switch (non_zero_srcs) {
4338c2ecf20Sopenharmony_ci	case 0:
4348c2ecf20Sopenharmony_ci	case 1:
4358c2ecf20Sopenharmony_ci		/* There must be at least 2 sources - the failed devices. */
4368c2ecf20Sopenharmony_ci		BUG();
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	case 2:
4398c2ecf20Sopenharmony_ci		/* dma devices do not uniformly understand a zero source pq
4408c2ecf20Sopenharmony_ci		 * operation (in contrast to the synchronous case), so
4418c2ecf20Sopenharmony_ci		 * explicitly handle the special case of a 4 disk array with
4428c2ecf20Sopenharmony_ci		 * both data disks missing.
4438c2ecf20Sopenharmony_ci		 */
4448c2ecf20Sopenharmony_ci		return __2data_recov_4(disks, bytes, faila, failb,
4458c2ecf20Sopenharmony_ci				blocks, offs, submit);
4468c2ecf20Sopenharmony_ci	case 3:
4478c2ecf20Sopenharmony_ci		/* dma devices do not uniformly understand a single
4488c2ecf20Sopenharmony_ci		 * source pq operation (in contrast to the synchronous
4498c2ecf20Sopenharmony_ci		 * case), so explicitly handle the special case of a 5 disk
4508c2ecf20Sopenharmony_ci		 * array with 2 of 3 data disks missing.
4518c2ecf20Sopenharmony_ci		 */
4528c2ecf20Sopenharmony_ci		return __2data_recov_5(disks, bytes, faila, failb,
4538c2ecf20Sopenharmony_ci				blocks, offs, submit);
4548c2ecf20Sopenharmony_ci	default:
4558c2ecf20Sopenharmony_ci		return __2data_recov_n(disks, bytes, faila, failb,
4568c2ecf20Sopenharmony_ci				blocks, offs, submit);
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(async_raid6_2data_recov);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci/**
4628c2ecf20Sopenharmony_ci * async_raid6_datap_recov - asynchronously calculate a data and the 'p' block
4638c2ecf20Sopenharmony_ci * @disks: number of disks in the RAID-6 array
4648c2ecf20Sopenharmony_ci * @bytes: block size
4658c2ecf20Sopenharmony_ci * @faila: failed drive index
4668c2ecf20Sopenharmony_ci * @blocks: array of source pointers where the last two entries are p and q
4678c2ecf20Sopenharmony_ci * @offs: array of offset for pages in blocks
4688c2ecf20Sopenharmony_ci * @submit: submission/completion modifiers
4698c2ecf20Sopenharmony_ci */
4708c2ecf20Sopenharmony_cistruct dma_async_tx_descriptor *
4718c2ecf20Sopenharmony_ciasync_raid6_datap_recov(int disks, size_t bytes, int faila,
4728c2ecf20Sopenharmony_ci			struct page **blocks, unsigned int *offs,
4738c2ecf20Sopenharmony_ci			struct async_submit_ctl *submit)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *tx = NULL;
4768c2ecf20Sopenharmony_ci	struct page *p, *q, *dq;
4778c2ecf20Sopenharmony_ci	unsigned int p_off, q_off, dq_off;
4788c2ecf20Sopenharmony_ci	u8 coef;
4798c2ecf20Sopenharmony_ci	enum async_tx_flags flags = submit->flags;
4808c2ecf20Sopenharmony_ci	dma_async_tx_callback cb_fn = submit->cb_fn;
4818c2ecf20Sopenharmony_ci	void *cb_param = submit->cb_param;
4828c2ecf20Sopenharmony_ci	void *scribble = submit->scribble;
4838c2ecf20Sopenharmony_ci	int good_srcs, good, i;
4848c2ecf20Sopenharmony_ci	struct page *srcs[2];
4858c2ecf20Sopenharmony_ci	unsigned int src_offs[2];
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	pr_debug("%s: disks: %d len: %zu\n", __func__, disks, bytes);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* if a dma resource is not available or a scribble buffer is not
4908c2ecf20Sopenharmony_ci	 * available punt to the synchronous path.  In the 'dma not
4918c2ecf20Sopenharmony_ci	 * available' case be sure to use the scribble buffer to
4928c2ecf20Sopenharmony_ci	 * preserve the content of 'blocks' as the caller intended.
4938c2ecf20Sopenharmony_ci	 */
4948c2ecf20Sopenharmony_ci	if (!async_dma_find_channel(DMA_PQ) || !scribble) {
4958c2ecf20Sopenharmony_ci		void **ptrs = scribble ? scribble : (void **) blocks;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		async_tx_quiesce(&submit->depend_tx);
4988c2ecf20Sopenharmony_ci		for (i = 0; i < disks; i++)
4998c2ecf20Sopenharmony_ci			if (blocks[i] == NULL)
5008c2ecf20Sopenharmony_ci				ptrs[i] = (void*)raid6_empty_zero_page;
5018c2ecf20Sopenharmony_ci			else
5028c2ecf20Sopenharmony_ci				ptrs[i] = page_address(blocks[i]) + offs[i];
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci		raid6_datap_recov(disks, bytes, faila, ptrs);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		async_tx_sync_epilog(submit);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci		return NULL;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	good_srcs = 0;
5128c2ecf20Sopenharmony_ci	good = -1;
5138c2ecf20Sopenharmony_ci	for (i = 0; i < disks-2; i++) {
5148c2ecf20Sopenharmony_ci		if (i == faila)
5158c2ecf20Sopenharmony_ci			continue;
5168c2ecf20Sopenharmony_ci		if (blocks[i]) {
5178c2ecf20Sopenharmony_ci			good = i;
5188c2ecf20Sopenharmony_ci			good_srcs++;
5198c2ecf20Sopenharmony_ci			if (good_srcs > 1)
5208c2ecf20Sopenharmony_ci				break;
5218c2ecf20Sopenharmony_ci		}
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci	BUG_ON(good_srcs == 0);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	p = blocks[disks-2];
5268c2ecf20Sopenharmony_ci	p_off = offs[disks-2];
5278c2ecf20Sopenharmony_ci	q = blocks[disks-1];
5288c2ecf20Sopenharmony_ci	q_off = offs[disks-1];
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	/* Compute syndrome with zero for the missing data page
5318c2ecf20Sopenharmony_ci	 * Use the dead data page as temporary storage for delta q
5328c2ecf20Sopenharmony_ci	 */
5338c2ecf20Sopenharmony_ci	dq = blocks[faila];
5348c2ecf20Sopenharmony_ci	dq_off = offs[faila];
5358c2ecf20Sopenharmony_ci	blocks[faila] = NULL;
5368c2ecf20Sopenharmony_ci	blocks[disks-1] = dq;
5378c2ecf20Sopenharmony_ci	offs[disks-1] = dq_off;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	/* in the 4-disk case we only need to perform a single source
5408c2ecf20Sopenharmony_ci	 * multiplication with the one good data block.
5418c2ecf20Sopenharmony_ci	 */
5428c2ecf20Sopenharmony_ci	if (good_srcs == 1) {
5438c2ecf20Sopenharmony_ci		struct page *g = blocks[good];
5448c2ecf20Sopenharmony_ci		unsigned int g_off = offs[good];
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci		init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL,
5478c2ecf20Sopenharmony_ci				  scribble);
5488c2ecf20Sopenharmony_ci		tx = async_memcpy(p, g, p_off, g_off, bytes, submit);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL,
5518c2ecf20Sopenharmony_ci				  scribble);
5528c2ecf20Sopenharmony_ci		tx = async_mult(dq, dq_off, g, g_off,
5538c2ecf20Sopenharmony_ci				raid6_gfexp[good], bytes, submit);
5548c2ecf20Sopenharmony_ci	} else {
5558c2ecf20Sopenharmony_ci		init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL,
5568c2ecf20Sopenharmony_ci				  scribble);
5578c2ecf20Sopenharmony_ci		tx = async_gen_syndrome(blocks, offs, disks, bytes, submit);
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	/* Restore pointer table */
5618c2ecf20Sopenharmony_ci	blocks[faila]   = dq;
5628c2ecf20Sopenharmony_ci	offs[faila] = dq_off;
5638c2ecf20Sopenharmony_ci	blocks[disks-1] = q;
5648c2ecf20Sopenharmony_ci	offs[disks-1] = q_off;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* calculate g^{-faila} */
5678c2ecf20Sopenharmony_ci	coef = raid6_gfinv[raid6_gfexp[faila]];
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	srcs[0] = dq;
5708c2ecf20Sopenharmony_ci	src_offs[0] = dq_off;
5718c2ecf20Sopenharmony_ci	srcs[1] = q;
5728c2ecf20Sopenharmony_ci	src_offs[1] = q_off;
5738c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE|ASYNC_TX_XOR_DROP_DST, tx,
5748c2ecf20Sopenharmony_ci			  NULL, NULL, scribble);
5758c2ecf20Sopenharmony_ci	tx = async_xor_offs(dq, dq_off, srcs, src_offs, 2, bytes, submit);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	init_async_submit(submit, ASYNC_TX_FENCE, tx, NULL, NULL, scribble);
5788c2ecf20Sopenharmony_ci	tx = async_mult(dq, dq_off, dq, dq_off, coef, bytes, submit);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	srcs[0] = p;
5818c2ecf20Sopenharmony_ci	src_offs[0] = p_off;
5828c2ecf20Sopenharmony_ci	srcs[1] = dq;
5838c2ecf20Sopenharmony_ci	src_offs[1] = dq_off;
5848c2ecf20Sopenharmony_ci	init_async_submit(submit, flags | ASYNC_TX_XOR_DROP_DST, tx, cb_fn,
5858c2ecf20Sopenharmony_ci			  cb_param, scribble);
5868c2ecf20Sopenharmony_ci	tx = async_xor_offs(p, p_off, srcs, src_offs, 2, bytes, submit);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	return tx;
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(async_raid6_datap_recov);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dan Williams <dan.j.williams@intel.com>");
5938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("asynchronous RAID-6 recovery api");
5948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
595