xref: /openbmc/linux/crypto/async_tx/raid6test.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cb3c8299SDan Williams /*
3cb3c8299SDan Williams  * asynchronous raid6 recovery self test
4cb3c8299SDan Williams  * Copyright (c) 2009, Intel Corporation.
5cb3c8299SDan Williams  *
6cb3c8299SDan Williams  * based on drivers/md/raid6test/test.c:
7cb3c8299SDan Williams  * 	Copyright 2002-2007 H. Peter Anvin
8cb3c8299SDan Williams  */
9cb3c8299SDan Williams #include <linux/async_tx.h>
105a0e3ad6STejun Heo #include <linux/gfp.h>
11b7f080cfSAlexey Dobriyan #include <linux/mm.h>
12cb3c8299SDan Williams #include <linux/random.h>
134bb33cc8SPaul Gortmaker #include <linux/module.h>
14cb3c8299SDan Williams 
15cb3c8299SDan Williams #undef pr
16cb3c8299SDan Williams #define pr(fmt, args...) pr_info("raid6test: " fmt, ##args)
17cb3c8299SDan Williams 
1809ec0f58SDan Williams #define NDISKS 64 /* Including P and Q */
19cb3c8299SDan Williams 
20cb3c8299SDan Williams static struct page *dataptrs[NDISKS];
21d69454bcSYufen Yu unsigned int dataoffs[NDISKS];
221b6df693SDan Williams static addr_conv_t addr_conv[NDISKS];
23cb3c8299SDan Williams static struct page *data[NDISKS+3];
24cb3c8299SDan Williams static struct page *spare;
25cb3c8299SDan Williams static struct page *recovi;
26cb3c8299SDan Williams static struct page *recovj;
27cb3c8299SDan Williams 
callback(void * param)28cb3c8299SDan Williams static void callback(void *param)
29cb3c8299SDan Williams {
30cb3c8299SDan Williams 	struct completion *cmp = param;
31cb3c8299SDan Williams 
32cb3c8299SDan Williams 	complete(cmp);
33cb3c8299SDan Williams }
34cb3c8299SDan Williams 
makedata(int disks)35cb3c8299SDan Williams static void makedata(int disks)
36cb3c8299SDan Williams {
373ec39abdSAkinobu Mita 	int i;
38cb3c8299SDan Williams 
39cb3c8299SDan Williams 	for (i = 0; i < disks; i++) {
40*197173dbSJason A. Donenfeld 		get_random_bytes(page_address(data[i]), PAGE_SIZE);
41cb3c8299SDan Williams 		dataptrs[i] = data[i];
42d69454bcSYufen Yu 		dataoffs[i] = 0;
43cb3c8299SDan Williams 	}
44cb3c8299SDan Williams }
45cb3c8299SDan Williams 
disk_type(int d,int disks)46cb3c8299SDan Williams static char disk_type(int d, int disks)
47cb3c8299SDan Williams {
48cb3c8299SDan Williams 	if (d == disks - 2)
49cb3c8299SDan Williams 		return 'P';
50cb3c8299SDan Williams 	else if (d == disks - 1)
51cb3c8299SDan Williams 		return 'Q';
52cb3c8299SDan Williams 	else
53cb3c8299SDan Williams 		return 'D';
54cb3c8299SDan Williams }
55cb3c8299SDan Williams 
56cb3c8299SDan Williams /* Recover two failed blocks. */
raid6_dual_recov(int disks,size_t bytes,int faila,int failb,struct page ** ptrs,unsigned int * offs)57d69454bcSYufen Yu static void raid6_dual_recov(int disks, size_t bytes, int faila, int failb,
58d69454bcSYufen Yu 		struct page **ptrs, unsigned int *offs)
59cb3c8299SDan Williams {
60cb3c8299SDan Williams 	struct async_submit_ctl submit;
61cb3c8299SDan Williams 	struct completion cmp;
62cb3c8299SDan Williams 	struct dma_async_tx_descriptor *tx = NULL;
63cb3c8299SDan Williams 	enum sum_check_flags result = ~0;
64cb3c8299SDan Williams 
65cb3c8299SDan Williams 	if (faila > failb)
66cb3c8299SDan Williams 		swap(faila, failb);
67cb3c8299SDan Williams 
68cb3c8299SDan Williams 	if (failb == disks-1) {
69cb3c8299SDan Williams 		if (faila == disks-2) {
70cb3c8299SDan Williams 			/* P+Q failure.  Just rebuild the syndrome. */
71cb3c8299SDan Williams 			init_async_submit(&submit, 0, NULL, NULL, NULL, addr_conv);
72d69454bcSYufen Yu 			tx = async_gen_syndrome(ptrs, offs,
73d69454bcSYufen Yu 					disks, bytes, &submit);
74cb3c8299SDan Williams 		} else {
7589a7e2f7SKyle Spiers 			struct page *blocks[NDISKS];
76cb3c8299SDan Williams 			struct page *dest;
77cb3c8299SDan Williams 			int count = 0;
78cb3c8299SDan Williams 			int i;
79cb3c8299SDan Williams 
8089a7e2f7SKyle Spiers 			BUG_ON(disks > NDISKS);
8189a7e2f7SKyle Spiers 
82cb3c8299SDan Williams 			/* data+Q failure.  Reconstruct data from P,
83cb3c8299SDan Williams 			 * then rebuild syndrome
84cb3c8299SDan Williams 			 */
85cb3c8299SDan Williams 			for (i = disks; i-- ; ) {
86cb3c8299SDan Williams 				if (i == faila || i == failb)
87cb3c8299SDan Williams 					continue;
88cb3c8299SDan Williams 				blocks[count++] = ptrs[i];
89cb3c8299SDan Williams 			}
90cb3c8299SDan Williams 			dest = ptrs[faila];
91cb3c8299SDan Williams 			init_async_submit(&submit, ASYNC_TX_XOR_ZERO_DST, NULL,
92cb3c8299SDan Williams 					  NULL, NULL, addr_conv);
93cb3c8299SDan Williams 			tx = async_xor(dest, blocks, 0, count, bytes, &submit);
94cb3c8299SDan Williams 
95cb3c8299SDan Williams 			init_async_submit(&submit, 0, tx, NULL, NULL, addr_conv);
96d69454bcSYufen Yu 			tx = async_gen_syndrome(ptrs, offs,
97d69454bcSYufen Yu 					disks, bytes, &submit);
98cb3c8299SDan Williams 		}
99cb3c8299SDan Williams 	} else {
100cb3c8299SDan Williams 		if (failb == disks-2) {
101cb3c8299SDan Williams 			/* data+P failure. */
102cb3c8299SDan Williams 			init_async_submit(&submit, 0, NULL, NULL, NULL, addr_conv);
103d69454bcSYufen Yu 			tx = async_raid6_datap_recov(disks, bytes,
1044f86ff55SYufen Yu 					faila, ptrs, offs, &submit);
105cb3c8299SDan Williams 		} else {
106cb3c8299SDan Williams 			/* data+data failure. */
107cb3c8299SDan Williams 			init_async_submit(&submit, 0, NULL, NULL, NULL, addr_conv);
108d69454bcSYufen Yu 			tx = async_raid6_2data_recov(disks, bytes,
1094f86ff55SYufen Yu 					faila, failb, ptrs, offs, &submit);
110cb3c8299SDan Williams 		}
111cb3c8299SDan Williams 	}
112cb3c8299SDan Williams 	init_completion(&cmp);
113cb3c8299SDan Williams 	init_async_submit(&submit, ASYNC_TX_ACK, tx, callback, &cmp, addr_conv);
114d69454bcSYufen Yu 	tx = async_syndrome_val(ptrs, offs,
115d69454bcSYufen Yu 			disks, bytes, &result, spare, 0, &submit);
116cb3c8299SDan Williams 	async_tx_issue_pending(tx);
117cb3c8299SDan Williams 
118cb3c8299SDan Williams 	if (wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)) == 0)
119cb3c8299SDan Williams 		pr("%s: timeout! (faila: %d failb: %d disks: %d)\n",
120cb3c8299SDan Williams 		   __func__, faila, failb, disks);
121cb3c8299SDan Williams 
122cb3c8299SDan Williams 	if (result != 0)
123cb3c8299SDan Williams 		pr("%s: validation failure! faila: %d failb: %d sum_check_flags: %x\n",
124cb3c8299SDan Williams 		   __func__, faila, failb, result);
125cb3c8299SDan Williams }
126cb3c8299SDan Williams 
test_disks(int i,int j,int disks)127cb3c8299SDan Williams static int test_disks(int i, int j, int disks)
128cb3c8299SDan Williams {
129cb3c8299SDan Williams 	int erra, errb;
130cb3c8299SDan Williams 
131cb3c8299SDan Williams 	memset(page_address(recovi), 0xf0, PAGE_SIZE);
132cb3c8299SDan Williams 	memset(page_address(recovj), 0xba, PAGE_SIZE);
133cb3c8299SDan Williams 
134cb3c8299SDan Williams 	dataptrs[i] = recovi;
135cb3c8299SDan Williams 	dataptrs[j] = recovj;
136cb3c8299SDan Williams 
137d69454bcSYufen Yu 	raid6_dual_recov(disks, PAGE_SIZE, i, j, dataptrs, dataoffs);
138cb3c8299SDan Williams 
139cb3c8299SDan Williams 	erra = memcmp(page_address(data[i]), page_address(recovi), PAGE_SIZE);
140cb3c8299SDan Williams 	errb = memcmp(page_address(data[j]), page_address(recovj), PAGE_SIZE);
141cb3c8299SDan Williams 
142cb3c8299SDan Williams 	pr("%s(%d, %d): faila=%3d(%c)  failb=%3d(%c)  %s\n",
143cb3c8299SDan Williams 	   __func__, i, j, i, disk_type(i, disks), j, disk_type(j, disks),
144cb3c8299SDan Williams 	   (!erra && !errb) ? "OK" : !erra ? "ERRB" : !errb ? "ERRA" : "ERRAB");
145cb3c8299SDan Williams 
146cb3c8299SDan Williams 	dataptrs[i] = data[i];
147cb3c8299SDan Williams 	dataptrs[j] = data[j];
148cb3c8299SDan Williams 
149cb3c8299SDan Williams 	return erra || errb;
150cb3c8299SDan Williams }
151cb3c8299SDan Williams 
test(int disks,int * tests)152cb3c8299SDan Williams static int test(int disks, int *tests)
153cb3c8299SDan Williams {
154cb3c8299SDan Williams 	struct dma_async_tx_descriptor *tx;
155cb3c8299SDan Williams 	struct async_submit_ctl submit;
156cb3c8299SDan Williams 	struct completion cmp;
157cb3c8299SDan Williams 	int err = 0;
158cb3c8299SDan Williams 	int i, j;
159cb3c8299SDan Williams 
160cb3c8299SDan Williams 	recovi = data[disks];
161cb3c8299SDan Williams 	recovj = data[disks+1];
162cb3c8299SDan Williams 	spare  = data[disks+2];
163cb3c8299SDan Williams 
164cb3c8299SDan Williams 	makedata(disks);
165cb3c8299SDan Williams 
166cb3c8299SDan Williams 	/* Nuke syndromes */
167cb3c8299SDan Williams 	memset(page_address(data[disks-2]), 0xee, PAGE_SIZE);
168cb3c8299SDan Williams 	memset(page_address(data[disks-1]), 0xee, PAGE_SIZE);
169cb3c8299SDan Williams 
170cb3c8299SDan Williams 	/* Generate assumed good syndrome */
171cb3c8299SDan Williams 	init_completion(&cmp);
172cb3c8299SDan Williams 	init_async_submit(&submit, ASYNC_TX_ACK, NULL, callback, &cmp, addr_conv);
173d69454bcSYufen Yu 	tx = async_gen_syndrome(dataptrs, dataoffs, disks, PAGE_SIZE, &submit);
174cb3c8299SDan Williams 	async_tx_issue_pending(tx);
175cb3c8299SDan Williams 
176cb3c8299SDan Williams 	if (wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)) == 0) {
177cb3c8299SDan Williams 		pr("error: initial gen_syndrome(%d) timed out\n", disks);
178cb3c8299SDan Williams 		return 1;
179cb3c8299SDan Williams 	}
180cb3c8299SDan Williams 
181cb3c8299SDan Williams 	pr("testing the %d-disk case...\n", disks);
182cb3c8299SDan Williams 	for (i = 0; i < disks-1; i++)
183cb3c8299SDan Williams 		for (j = i+1; j < disks; j++) {
184cb3c8299SDan Williams 			(*tests)++;
185cb3c8299SDan Williams 			err += test_disks(i, j, disks);
186cb3c8299SDan Williams 		}
187cb3c8299SDan Williams 
188cb3c8299SDan Williams 	return err;
189cb3c8299SDan Williams }
190cb3c8299SDan Williams 
191cb3c8299SDan Williams 
raid6_test(void)19233837be3SXiu Jianfeng static int __init raid6_test(void)
193cb3c8299SDan Williams {
194cb3c8299SDan Williams 	int err = 0;
195cb3c8299SDan Williams 	int tests = 0;
196cb3c8299SDan Williams 	int i;
197cb3c8299SDan Williams 
198cb3c8299SDan Williams 	for (i = 0; i < NDISKS+3; i++) {
199cb3c8299SDan Williams 		data[i] = alloc_page(GFP_KERNEL);
200cb3c8299SDan Williams 		if (!data[i]) {
201cb3c8299SDan Williams 			while (i--)
202cb3c8299SDan Williams 				put_page(data[i]);
203cb3c8299SDan Williams 			return -ENOMEM;
204cb3c8299SDan Williams 		}
205cb3c8299SDan Williams 	}
206cb3c8299SDan Williams 
207cb3c8299SDan Williams 	/* the 4-disk and 5-disk cases are special for the recovery code */
208cb3c8299SDan Williams 	if (NDISKS > 4)
209cb3c8299SDan Williams 		err += test(4, &tests);
210cb3c8299SDan Williams 	if (NDISKS > 5)
211cb3c8299SDan Williams 		err += test(5, &tests);
212e02a0e47SDan Williams 	/* the 11 and 12 disk cases are special for ioatdma (p-disabled
213e02a0e47SDan Williams 	 * q-continuation without extended descriptor)
214e02a0e47SDan Williams 	 */
215e02a0e47SDan Williams 	if (NDISKS > 12) {
216e02a0e47SDan Williams 		err += test(11, &tests);
217e02a0e47SDan Williams 		err += test(12, &tests);
218e02a0e47SDan Williams 	}
21909ec0f58SDan Williams 
2204920a4a7STom Rix 	/* the 24 disk case is special for ioatdma as it is the boundary point
22109ec0f58SDan Williams 	 * at which it needs to switch from 8-source ops to 16-source
22209ec0f58SDan Williams 	 * ops for continuation (assumes DMA_HAS_PQ_CONTINUE is not set)
22309ec0f58SDan Williams 	 */
22409ec0f58SDan Williams 	if (NDISKS > 24)
22509ec0f58SDan Williams 		err += test(24, &tests);
22609ec0f58SDan Williams 
227cb3c8299SDan Williams 	err += test(NDISKS, &tests);
228cb3c8299SDan Williams 
229cb3c8299SDan Williams 	pr("\n");
230cb3c8299SDan Williams 	pr("complete (%d tests, %d failure%s)\n",
231cb3c8299SDan Williams 	   tests, err, err == 1 ? "" : "s");
232cb3c8299SDan Williams 
233cb3c8299SDan Williams 	for (i = 0; i < NDISKS+3; i++)
234cb3c8299SDan Williams 		put_page(data[i]);
235cb3c8299SDan Williams 
236cb3c8299SDan Williams 	return 0;
237cb3c8299SDan Williams }
238cb3c8299SDan Williams 
raid6_test_exit(void)23933837be3SXiu Jianfeng static void __exit raid6_test_exit(void)
240cb3c8299SDan Williams {
241cb3c8299SDan Williams }
242cb3c8299SDan Williams 
243cb3c8299SDan Williams /* when compiled-in wait for drivers to load first (assumes dma drivers
2444920a4a7STom Rix  * are also compiled-in)
245cb3c8299SDan Williams  */
246cb3c8299SDan Williams late_initcall(raid6_test);
247cb3c8299SDan Williams module_exit(raid6_test_exit);
248cb3c8299SDan Williams MODULE_AUTHOR("Dan Williams <dan.j.williams@intel.com>");
249cb3c8299SDan Williams MODULE_DESCRIPTION("asynchronous RAID-6 recovery self tests");
250cb3c8299SDan Williams MODULE_LICENSE("GPL");
251