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