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 const struct raid6_calls *const *algo; 92 const struct raid6_recov_calls *const *ra; 93 int i, j, p1, p2; 94 95 for (ra = raid6_recov_algos; *ra; ra++) { 96 if ((*ra)->valid && !(*ra)->valid()) 97 continue; 98 99 for (algo = raid6_algos; *algo; algo++) { 100 const struct raid6_calls *calls = *algo; 101 102 if (calls->valid && !calls->valid()) 103 continue; 104 105 /* Nuke syndromes */ 106 memset(data[NDISKS - 2], 0xee, PAGE_SIZE); 107 memset(data[NDISKS - 1], 0xee, PAGE_SIZE); 108 109 /* Generate assumed good syndrome */ 110 calls->gen_syndrome(NDISKS, PAGE_SIZE, 111 (void **)&dataptrs); 112 113 for (i = 0; i < NDISKS-1; i++) 114 for (j = i+1; j < NDISKS; j++) 115 test_disks(test, calls, *ra, i, j); 116 117 if (!calls->xor_syndrome) 118 continue; 119 120 for (p1 = 0; p1 < NDISKS-2; p1++) 121 for (p2 = p1; p2 < NDISKS-2; p2++) { 122 123 /* Simulate rmw run */ 124 calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, 125 (void **)&dataptrs); 126 makedata(p1, p2); 127 calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, 128 (void **)&dataptrs); 129 130 for (i = 0; i < NDISKS-1; i++) 131 for (j = i+1; j < NDISKS; j++) 132 test_disks(test, calls, 133 *ra, i, j); 134 } 135 136 } 137 } 138 } 139 140 static struct kunit_case raid6_test_cases[] = { 141 KUNIT_CASE(raid6_test), 142 {}, 143 }; 144 145 static int raid6_suite_init(struct kunit_suite *suite) 146 { 147 prandom_seed_state(&rng, RAID6_KUNIT_SEED); 148 makedata(0, NDISKS - 1); 149 return 0; 150 } 151 152 static struct kunit_suite raid6_test_suite = { 153 .name = "raid6", 154 .test_cases = raid6_test_cases, 155 .suite_init = raid6_suite_init, 156 }; 157 kunit_test_suite(raid6_test_suite); 158 159 MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions"); 160 MODULE_LICENSE("GPL"); 161