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 "../algos.h" 11 12 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); 13 14 #define RAID6_KUNIT_SEED 42 15 16 #define NDISKS 16 /* Including P and Q */ 17 18 static struct rnd_state rng; 19 static void *dataptrs[NDISKS]; 20 static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 21 static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 22 static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 23 24 static void makedata(int start, int stop) 25 { 26 int i; 27 28 for (i = start; i <= stop; i++) { 29 prandom_bytes_state(&rng, data[i], PAGE_SIZE); 30 dataptrs[i] = data[i]; 31 } 32 } 33 34 static char member_type(int d) 35 { 36 switch (d) { 37 case NDISKS-2: 38 return 'P'; 39 case NDISKS-1: 40 return 'Q'; 41 default: 42 return 'D'; 43 } 44 } 45 46 static void test_disks(struct kunit *test, const struct raid6_calls *calls, 47 const struct raid6_recov_calls *ra, int faila, int failb) 48 { 49 memset(recovi, 0xf0, PAGE_SIZE); 50 memset(recovj, 0xba, PAGE_SIZE); 51 52 dataptrs[faila] = recovi; 53 dataptrs[failb] = recovj; 54 55 if (failb == NDISKS - 1) { 56 /* 57 * We don't implement the data+Q failure scenario, since it 58 * is equivalent to a RAID-5 failure (XOR, then recompute Q). 59 */ 60 if (faila != NDISKS - 2) 61 goto skip; 62 63 /* P+Q failure. Just rebuild the syndrome. */ 64 calls->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); 65 } else if (failb == NDISKS - 2) { 66 /* data+P failure. */ 67 ra->datap(NDISKS, PAGE_SIZE, faila, dataptrs); 68 } else { 69 /* data+data failure. */ 70 ra->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); 71 } 72 73 KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE, 74 "algo=%-8s/%-8s faila miscompared: %3d[%c] (failb=%3d[%c])\n", 75 calls->name, ra->name, 76 faila, member_type(faila), 77 failb, member_type(failb)); 78 KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE, 79 "algo=%-8s/%-8s failb miscompared: %3d[%c] (faila=%3d[%c])\n", 80 calls->name, ra->name, 81 failb, member_type(failb), 82 faila, member_type(faila)); 83 84 skip: 85 dataptrs[faila] = data[faila]; 86 dataptrs[failb] = data[failb]; 87 } 88 89 static void raid6_test(struct kunit *test) 90 { 91 int i, j, p1, p2; 92 unsigned int r, g; 93 94 for (r = 0; ; r++) { 95 const struct raid6_recov_calls *ra = raid6_recov_algo_find(r); 96 97 if (!ra) 98 break; 99 100 for (g = 0; ; g++) { 101 const struct raid6_calls *calls = raid6_algo_find(g); 102 103 if (!calls) 104 break; 105 106 /* Nuke syndromes */ 107 memset(data[NDISKS - 2], 0xee, PAGE_SIZE); 108 memset(data[NDISKS - 1], 0xee, PAGE_SIZE); 109 110 /* Generate assumed good syndrome */ 111 calls->gen_syndrome(NDISKS, PAGE_SIZE, 112 (void **)&dataptrs); 113 114 for (i = 0; i < NDISKS-1; i++) 115 for (j = i+1; j < NDISKS; j++) 116 test_disks(test, calls, ra, i, j); 117 118 if (!calls->xor_syndrome) 119 continue; 120 121 for (p1 = 0; p1 < NDISKS-2; p1++) 122 for (p2 = p1; p2 < NDISKS-2; p2++) { 123 124 /* Simulate rmw run */ 125 calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, 126 (void **)&dataptrs); 127 makedata(p1, p2); 128 calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, 129 (void **)&dataptrs); 130 131 for (i = 0; i < NDISKS-1; i++) 132 for (j = i+1; j < NDISKS; j++) 133 test_disks(test, calls, 134 ra, i, j); 135 } 136 137 } 138 } 139 } 140 141 static struct kunit_case raid6_test_cases[] = { 142 KUNIT_CASE(raid6_test), 143 {}, 144 }; 145 146 static int raid6_suite_init(struct kunit_suite *suite) 147 { 148 prandom_seed_state(&rng, RAID6_KUNIT_SEED); 149 makedata(0, NDISKS - 1); 150 return 0; 151 } 152 153 static struct kunit_suite raid6_test_suite = { 154 .name = "raid6", 155 .test_cases = raid6_test_cases, 156 .suite_init = raid6_suite_init, 157 }; 158 kunit_test_suite(raid6_test_suite); 159 160 MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions"); 161 MODULE_LICENSE("GPL"); 162