1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved 4 * 5 * Test RAID-6 recovery algorithms. 6 */ 7 8 #include <kunit/test.h> 9 #include <linux/prandom.h> 10 #include <linux/vmalloc.h> 11 #include "../algos.h" 12 13 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); 14 15 #define RAID6_KUNIT_SEED 42 16 #define RAID6_KUNIT_MAX_FAILURES 2 17 18 #define NDISKS 16 /* Including P and Q */ 19 20 static struct rnd_state rng; 21 static void *dataptrs[NDISKS]; 22 static void *test_buffers[NDISKS]; 23 static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; 24 25 struct test_args { 26 unsigned int recov_idx; 27 const struct raid6_recov_calls *recov; 28 unsigned int gen_idx; 29 const struct raid6_calls *gen; 30 }; 31 32 static struct test_args args; 33 34 static void makedata(int start, int stop) 35 { 36 int i; 37 38 for (i = start; i <= stop; i++) { 39 prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE); 40 dataptrs[i] = test_buffers[i]; 41 } 42 } 43 44 static char member_type(int d) 45 { 46 switch (d) { 47 case NDISKS-2: 48 return 'P'; 49 case NDISKS-1: 50 return 'Q'; 51 default: 52 return 'D'; 53 } 54 } 55 56 static void test_recover(struct kunit *test, int faila, int failb) 57 { 58 const struct test_args *ta = test->param_value; 59 int i; 60 61 for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 62 memset(test_recov_buffers[i], 0xf0, PAGE_SIZE); 63 64 dataptrs[faila] = test_recov_buffers[0]; 65 dataptrs[failb] = test_recov_buffers[1]; 66 67 if (failb == NDISKS - 1) { 68 /* 69 * We don't implement the data+Q failure scenario, since it 70 * is equivalent to a RAID-5 failure (XOR, then recompute Q). 71 */ 72 if (faila != NDISKS - 2) 73 goto skip; 74 75 /* P+Q failure. Just rebuild the syndrome. */ 76 ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); 77 } else if (failb == NDISKS - 2) { 78 /* data+P failure. */ 79 ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs); 80 } else { 81 /* data+data failure. */ 82 ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); 83 } 84 85 KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0], 86 PAGE_SIZE, 87 "faila miscompared: %3d[%c] (failb=%3d[%c])\n", 88 faila, member_type(faila), 89 failb, member_type(failb)); 90 KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1], 91 PAGE_SIZE, 92 "failb miscompared: %3d[%c] (faila=%3d[%c])\n", 93 failb, member_type(failb), 94 faila, member_type(faila)); 95 96 skip: 97 dataptrs[faila] = test_buffers[faila]; 98 dataptrs[failb] = test_buffers[failb]; 99 } 100 101 static void raid6_test(struct kunit *test) 102 { 103 const struct test_args *ta = test->param_value; 104 int i, j, p1, p2; 105 106 /* Nuke syndromes */ 107 memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE); 108 memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE); 109 110 /* Generate assumed good syndrome */ 111 ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs); 112 113 for (i = 0; i < NDISKS - 1; i++) 114 for (j = i + 1; j < NDISKS; j++) 115 test_recover(test, i, j); 116 117 if (!ta->gen->xor_syndrome) 118 return; 119 120 for (p1 = 0; p1 < NDISKS - 2; p1++) { 121 for (p2 = p1; p2 < NDISKS - 2; p2++) { 122 /* Simulate rmw run */ 123 ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, 124 (void **)&dataptrs); 125 makedata(p1, p2); 126 ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, 127 (void **)&dataptrs); 128 129 for (i = 0; i < NDISKS - 1; i++) 130 for (j = i + 1; j < NDISKS; j++) 131 test_recover(test, i, j); 132 } 133 } 134 } 135 136 static const void *raid6_gen_params(struct kunit *test, const void *prev, 137 char *desc) 138 { 139 if (!prev) { 140 memset(&args, 0, sizeof(args)); 141 next_algo: 142 args.recov_idx = 0; 143 args.gen = raid6_algo_find(args.gen_idx); 144 if (!args.gen) 145 return NULL; 146 } 147 148 if (args.recov) 149 args.recov_idx++; 150 args.recov = raid6_recov_algo_find(args.recov_idx); 151 if (!args.recov) { 152 args.gen_idx++; 153 goto next_algo; 154 } 155 156 snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s", 157 args.gen->name, args.recov->name); 158 return &args; 159 } 160 161 static struct kunit_case raid6_test_cases[] = { 162 KUNIT_CASE_PARAM(raid6_test, raid6_gen_params), 163 {}, 164 }; 165 166 static int raid6_suite_init(struct kunit_suite *suite) 167 { 168 int i; 169 170 prandom_seed_state(&rng, RAID6_KUNIT_SEED); 171 172 /* 173 * Allocate the test buffer using vmalloc() with a page-aligned length 174 * so that it is immediately followed by a guard page. This allows 175 * buffer overreads to be detected, even in assembly code. 176 */ 177 for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) { 178 test_recov_buffers[i] = vmalloc(PAGE_SIZE); 179 if (!test_recov_buffers[i]) 180 goto out_free_recov_buffers; 181 } 182 for (i = 0; i < NDISKS; i++) { 183 test_buffers[i] = vmalloc(PAGE_SIZE); 184 if (!test_buffers[i]) 185 goto out_free_buffers; 186 } 187 188 makedata(0, NDISKS - 1); 189 190 return 0; 191 192 out_free_buffers: 193 for (i = 0; i < NDISKS; i++) 194 vfree(test_buffers[i]); 195 memset(test_buffers, 0, sizeof(test_buffers)); 196 out_free_recov_buffers: 197 for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 198 vfree(test_recov_buffers[i]); 199 memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); 200 return -ENOMEM; 201 } 202 203 static void raid6_suite_exit(struct kunit_suite *suite) 204 { 205 int i; 206 207 for (i = 0; i < NDISKS; i++) 208 vfree(test_buffers[i]); 209 memset(test_buffers, 0, sizeof(test_buffers)); 210 for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 211 vfree(test_recov_buffers[i]); 212 memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); 213 } 214 215 static struct kunit_suite raid6_test_suite = { 216 .name = "raid6", 217 .test_cases = raid6_test_cases, 218 .suite_init = raid6_suite_init, 219 .suite_exit = raid6_suite_exit, 220 }; 221 kunit_test_suite(raid6_test_suite); 222 223 MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions"); 224 MODULE_LICENSE("GPL"); 225