1 /* 2 * asynchronous raid6 recovery self test 3 * Copyright (c) 2009, Intel Corporation. 4 * 5 * based on drivers/md/raid6test/test.c: 6 * Copyright 2002-2007 H. Peter Anvin 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms and conditions of the GNU General Public License, 10 * version 2, as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program; if not, write to the Free Software Foundation, Inc., 19 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 */ 22 #include <linux/async_tx.h> 23 #include <linux/gfp.h> 24 #include <linux/mm.h> 25 #include <linux/random.h> 26 #include <linux/module.h> 27 28 #undef pr 29 #define pr(fmt, args...) pr_info("raid6test: " fmt, ##args) 30 31 #define NDISKS 16 /* Including P and Q */ 32 33 static struct page *dataptrs[NDISKS]; 34 static addr_conv_t addr_conv[NDISKS]; 35 static struct page *data[NDISKS+3]; 36 static struct page *spare; 37 static struct page *recovi; 38 static struct page *recovj; 39 40 static void callback(void *param) 41 { 42 struct completion *cmp = param; 43 44 complete(cmp); 45 } 46 47 static void makedata(int disks) 48 { 49 int i, j; 50 51 for (i = 0; i < disks; i++) { 52 for (j = 0; j < PAGE_SIZE/sizeof(u32); j += sizeof(u32)) { 53 u32 *p = page_address(data[i]) + j; 54 55 *p = random32(); 56 } 57 58 dataptrs[i] = data[i]; 59 } 60 } 61 62 static char disk_type(int d, int disks) 63 { 64 if (d == disks - 2) 65 return 'P'; 66 else if (d == disks - 1) 67 return 'Q'; 68 else 69 return 'D'; 70 } 71 72 /* Recover two failed blocks. */ 73 static void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, struct page **ptrs) 74 { 75 struct async_submit_ctl submit; 76 struct completion cmp; 77 struct dma_async_tx_descriptor *tx = NULL; 78 enum sum_check_flags result = ~0; 79 80 if (faila > failb) 81 swap(faila, failb); 82 83 if (failb == disks-1) { 84 if (faila == disks-2) { 85 /* P+Q failure. Just rebuild the syndrome. */ 86 init_async_submit(&submit, 0, NULL, NULL, NULL, addr_conv); 87 tx = async_gen_syndrome(ptrs, 0, disks, bytes, &submit); 88 } else { 89 struct page *blocks[disks]; 90 struct page *dest; 91 int count = 0; 92 int i; 93 94 /* data+Q failure. Reconstruct data from P, 95 * then rebuild syndrome 96 */ 97 for (i = disks; i-- ; ) { 98 if (i == faila || i == failb) 99 continue; 100 blocks[count++] = ptrs[i]; 101 } 102 dest = ptrs[faila]; 103 init_async_submit(&submit, ASYNC_TX_XOR_ZERO_DST, NULL, 104 NULL, NULL, addr_conv); 105 tx = async_xor(dest, blocks, 0, count, bytes, &submit); 106 107 init_async_submit(&submit, 0, tx, NULL, NULL, addr_conv); 108 tx = async_gen_syndrome(ptrs, 0, disks, bytes, &submit); 109 } 110 } else { 111 if (failb == disks-2) { 112 /* data+P failure. */ 113 init_async_submit(&submit, 0, NULL, NULL, NULL, addr_conv); 114 tx = async_raid6_datap_recov(disks, bytes, faila, ptrs, &submit); 115 } else { 116 /* data+data failure. */ 117 init_async_submit(&submit, 0, NULL, NULL, NULL, addr_conv); 118 tx = async_raid6_2data_recov(disks, bytes, faila, failb, ptrs, &submit); 119 } 120 } 121 init_completion(&cmp); 122 init_async_submit(&submit, ASYNC_TX_ACK, tx, callback, &cmp, addr_conv); 123 tx = async_syndrome_val(ptrs, 0, disks, bytes, &result, spare, &submit); 124 async_tx_issue_pending(tx); 125 126 if (wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)) == 0) 127 pr("%s: timeout! (faila: %d failb: %d disks: %d)\n", 128 __func__, faila, failb, disks); 129 130 if (result != 0) 131 pr("%s: validation failure! faila: %d failb: %d sum_check_flags: %x\n", 132 __func__, faila, failb, result); 133 } 134 135 static int test_disks(int i, int j, int disks) 136 { 137 int erra, errb; 138 139 memset(page_address(recovi), 0xf0, PAGE_SIZE); 140 memset(page_address(recovj), 0xba, PAGE_SIZE); 141 142 dataptrs[i] = recovi; 143 dataptrs[j] = recovj; 144 145 raid6_dual_recov(disks, PAGE_SIZE, i, j, dataptrs); 146 147 erra = memcmp(page_address(data[i]), page_address(recovi), PAGE_SIZE); 148 errb = memcmp(page_address(data[j]), page_address(recovj), PAGE_SIZE); 149 150 pr("%s(%d, %d): faila=%3d(%c) failb=%3d(%c) %s\n", 151 __func__, i, j, i, disk_type(i, disks), j, disk_type(j, disks), 152 (!erra && !errb) ? "OK" : !erra ? "ERRB" : !errb ? "ERRA" : "ERRAB"); 153 154 dataptrs[i] = data[i]; 155 dataptrs[j] = data[j]; 156 157 return erra || errb; 158 } 159 160 static int test(int disks, int *tests) 161 { 162 struct dma_async_tx_descriptor *tx; 163 struct async_submit_ctl submit; 164 struct completion cmp; 165 int err = 0; 166 int i, j; 167 168 recovi = data[disks]; 169 recovj = data[disks+1]; 170 spare = data[disks+2]; 171 172 makedata(disks); 173 174 /* Nuke syndromes */ 175 memset(page_address(data[disks-2]), 0xee, PAGE_SIZE); 176 memset(page_address(data[disks-1]), 0xee, PAGE_SIZE); 177 178 /* Generate assumed good syndrome */ 179 init_completion(&cmp); 180 init_async_submit(&submit, ASYNC_TX_ACK, NULL, callback, &cmp, addr_conv); 181 tx = async_gen_syndrome(dataptrs, 0, disks, PAGE_SIZE, &submit); 182 async_tx_issue_pending(tx); 183 184 if (wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)) == 0) { 185 pr("error: initial gen_syndrome(%d) timed out\n", disks); 186 return 1; 187 } 188 189 pr("testing the %d-disk case...\n", disks); 190 for (i = 0; i < disks-1; i++) 191 for (j = i+1; j < disks; j++) { 192 (*tests)++; 193 err += test_disks(i, j, disks); 194 } 195 196 return err; 197 } 198 199 200 static int raid6_test(void) 201 { 202 int err = 0; 203 int tests = 0; 204 int i; 205 206 for (i = 0; i < NDISKS+3; i++) { 207 data[i] = alloc_page(GFP_KERNEL); 208 if (!data[i]) { 209 while (i--) 210 put_page(data[i]); 211 return -ENOMEM; 212 } 213 } 214 215 /* the 4-disk and 5-disk cases are special for the recovery code */ 216 if (NDISKS > 4) 217 err += test(4, &tests); 218 if (NDISKS > 5) 219 err += test(5, &tests); 220 /* the 11 and 12 disk cases are special for ioatdma (p-disabled 221 * q-continuation without extended descriptor) 222 */ 223 if (NDISKS > 12) { 224 err += test(11, &tests); 225 err += test(12, &tests); 226 } 227 err += test(NDISKS, &tests); 228 229 pr("\n"); 230 pr("complete (%d tests, %d failure%s)\n", 231 tests, err, err == 1 ? "" : "s"); 232 233 for (i = 0; i < NDISKS+3; i++) 234 put_page(data[i]); 235 236 return 0; 237 } 238 239 static void raid6_test_exit(void) 240 { 241 } 242 243 /* when compiled-in wait for drivers to load first (assumes dma drivers 244 * are also compliled-in) 245 */ 246 late_initcall(raid6_test); 247 module_exit(raid6_test_exit); 248 MODULE_AUTHOR("Dan Williams <dan.j.williams@intel.com>"); 249 MODULE_DESCRIPTION("asynchronous RAID-6 recovery self tests"); 250 MODULE_LICENSE("GPL"); 251