13626738bSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0-or-later 23626738bSChristoph Hellwig /* 33626738bSChristoph Hellwig * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved 43626738bSChristoph Hellwig * 53626738bSChristoph Hellwig * Test RAID-6 recovery algorithms. 63626738bSChristoph Hellwig */ 73626738bSChristoph Hellwig 83626738bSChristoph Hellwig #include <kunit/test.h> 93626738bSChristoph Hellwig #include <linux/prandom.h> 10d67c2571SChristoph Hellwig #include <linux/vmalloc.h> 11fa0c812cSChristoph Hellwig #include <linux/raid/pq.h> 12769d603fSChristoph Hellwig #include "../algos.h" 133626738bSChristoph Hellwig 143626738bSChristoph Hellwig MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); 153626738bSChristoph Hellwig 163626738bSChristoph Hellwig #define RAID6_KUNIT_SEED 42 17fa0c812cSChristoph Hellwig #define RAID6_KUNIT_NUM_TEST_ITERS 10 18fa0c812cSChristoph Hellwig #define RAID6_KUNIT_MAX_BUFFERS 64 /* Including P and Q */ 19d67c2571SChristoph Hellwig #define RAID6_KUNIT_MAX_FAILURES 2 20fa0c812cSChristoph Hellwig #define RAID6_KUNIT_MAX_BYTES PAGE_SIZE 213626738bSChristoph Hellwig 223626738bSChristoph Hellwig static struct rnd_state rng; 23fa0c812cSChristoph Hellwig static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS]; 24*8cf0a6c4SChristoph Hellwig static void *aligned_buffers[RAID6_KUNIT_MAX_BUFFERS]; 25d67c2571SChristoph Hellwig static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; 26fa0c812cSChristoph Hellwig static size_t test_buflen; 273626738bSChristoph Hellwig 282175395fSChristoph Hellwig struct test_args { 292175395fSChristoph Hellwig unsigned int recov_idx; 302175395fSChristoph Hellwig const struct raid6_recov_calls *recov; 312175395fSChristoph Hellwig unsigned int gen_idx; 322175395fSChristoph Hellwig const struct raid6_calls *gen; 332175395fSChristoph Hellwig }; 342175395fSChristoph Hellwig 352175395fSChristoph Hellwig static struct test_args args; 362175395fSChristoph Hellwig 37fa0c812cSChristoph Hellwig static u32 rand32(void) 38fa0c812cSChristoph Hellwig { 39fa0c812cSChristoph Hellwig return prandom_u32_state(&rng); 40fa0c812cSChristoph Hellwig } 41fa0c812cSChristoph Hellwig 42fa0c812cSChristoph Hellwig /* Generate a random length that is a multiple of 512. */ 43fa0c812cSChristoph Hellwig static unsigned int random_length(unsigned int max_length) 44fa0c812cSChristoph Hellwig { 45fa0c812cSChristoph Hellwig return round_up((rand32() % max_length) + 1, 512); 46fa0c812cSChristoph Hellwig } 47fa0c812cSChristoph Hellwig 48fa0c812cSChristoph Hellwig static unsigned int random_nr_buffers(void) 49fa0c812cSChristoph Hellwig { 50fa0c812cSChristoph Hellwig return (rand32() % (RAID6_KUNIT_MAX_BUFFERS - (RAID6_MIN_DISKS - 1))) + 51fa0c812cSChristoph Hellwig RAID6_MIN_DISKS; 52fa0c812cSChristoph Hellwig } 53fa0c812cSChristoph Hellwig 54*8cf0a6c4SChristoph Hellwig /* Generate a random alignment that is a multiple of 64. */ 55*8cf0a6c4SChristoph Hellwig static unsigned int random_alignment(unsigned int max_alignment) 56*8cf0a6c4SChristoph Hellwig { 57*8cf0a6c4SChristoph Hellwig if (max_alignment == 0) 58*8cf0a6c4SChristoph Hellwig return 0; 59*8cf0a6c4SChristoph Hellwig return (rand32() % (max_alignment + 1)) & ~63; 60*8cf0a6c4SChristoph Hellwig } 61*8cf0a6c4SChristoph Hellwig 623626738bSChristoph Hellwig static void makedata(int start, int stop) 633626738bSChristoph Hellwig { 643626738bSChristoph Hellwig int i; 653626738bSChristoph Hellwig 66562bcbfcSChristoph Hellwig for (i = start; i <= stop; i++) 67fa0c812cSChristoph Hellwig prandom_bytes_state(&rng, test_buffers[i], test_buflen); 683626738bSChristoph Hellwig } 693626738bSChristoph Hellwig 70fa0c812cSChristoph Hellwig static char member_type(unsigned int nr_buffers, int d) 713626738bSChristoph Hellwig { 72fa0c812cSChristoph Hellwig if (d == nr_buffers - 2) 733626738bSChristoph Hellwig return 'P'; 74fa0c812cSChristoph Hellwig if (d == nr_buffers - 1) 753626738bSChristoph Hellwig return 'Q'; 763626738bSChristoph Hellwig return 'D'; 773626738bSChristoph Hellwig } 783626738bSChristoph Hellwig 79fa0c812cSChristoph Hellwig static void test_recover_one(struct kunit *test, unsigned int nr_buffers, 80fa0c812cSChristoph Hellwig unsigned int len, int faila, int failb) 813626738bSChristoph Hellwig { 822175395fSChristoph Hellwig const struct test_args *ta = test->param_value; 83fa0c812cSChristoph Hellwig void *dataptrs[RAID6_KUNIT_MAX_BUFFERS]; 84d67c2571SChristoph Hellwig int i; 852175395fSChristoph Hellwig 86fa0c812cSChristoph Hellwig if (faila > failb) 87fa0c812cSChristoph Hellwig swap(faila, failb); 88fa0c812cSChristoph Hellwig 89d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 90fa0c812cSChristoph Hellwig memset(test_recov_buffers[i], 0xf0, test_buflen); 913626738bSChristoph Hellwig 92*8cf0a6c4SChristoph Hellwig memcpy(dataptrs, aligned_buffers, sizeof(dataptrs)); 93d67c2571SChristoph Hellwig dataptrs[faila] = test_recov_buffers[0]; 94d67c2571SChristoph Hellwig dataptrs[failb] = test_recov_buffers[1]; 953626738bSChristoph Hellwig 96fa0c812cSChristoph Hellwig if (failb == nr_buffers - 1) { 973626738bSChristoph Hellwig /* 983626738bSChristoph Hellwig * We don't implement the data+Q failure scenario, since it 993626738bSChristoph Hellwig * is equivalent to a RAID-5 failure (XOR, then recompute Q). 1003626738bSChristoph Hellwig */ 101fa0c812cSChristoph Hellwig if (WARN_ON_ONCE(faila != nr_buffers - 2)) 102562bcbfcSChristoph Hellwig return; 1033626738bSChristoph Hellwig 1043626738bSChristoph Hellwig /* P+Q failure. Just rebuild the syndrome. */ 105fa0c812cSChristoph Hellwig ta->gen->gen_syndrome(nr_buffers, len, dataptrs); 106fa0c812cSChristoph Hellwig } else if (failb == nr_buffers - 2) { 1073626738bSChristoph Hellwig /* data+P failure. */ 108fa0c812cSChristoph Hellwig ta->recov->datap(nr_buffers, len, faila, dataptrs); 1093626738bSChristoph Hellwig } else { 1103626738bSChristoph Hellwig /* data+data failure. */ 111fa0c812cSChristoph Hellwig ta->recov->data2(nr_buffers, len, faila, failb, dataptrs); 1123626738bSChristoph Hellwig } 1133626738bSChristoph Hellwig 114*8cf0a6c4SChristoph Hellwig KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[faila], dataptrs[faila], 115fa0c812cSChristoph Hellwig len, 116fa0c812cSChristoph Hellwig "faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n", 117fa0c812cSChristoph Hellwig faila, member_type(nr_buffers, faila), 118fa0c812cSChristoph Hellwig nr_buffers, len, 119fa0c812cSChristoph Hellwig failb, member_type(nr_buffers, failb)); 120*8cf0a6c4SChristoph Hellwig KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[failb], dataptrs[failb], 121fa0c812cSChristoph Hellwig len, 122fa0c812cSChristoph Hellwig "failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n", 123fa0c812cSChristoph Hellwig failb, member_type(nr_buffers, failb), 124fa0c812cSChristoph Hellwig nr_buffers, len, 125fa0c812cSChristoph Hellwig faila, member_type(nr_buffers, faila)); 126fa0c812cSChristoph Hellwig } 127fa0c812cSChristoph Hellwig 128fa0c812cSChristoph Hellwig static void test_recover(struct kunit *test, unsigned int nr_buffers, 129fa0c812cSChristoph Hellwig unsigned int len) 130fa0c812cSChristoph Hellwig { 131fa0c812cSChristoph Hellwig unsigned int nr_data = nr_buffers - 2; 132fa0c812cSChristoph Hellwig int iterations, i; 133fa0c812cSChristoph Hellwig 134fa0c812cSChristoph Hellwig /* Test P+Q recovery */ 135fa0c812cSChristoph Hellwig test_recover_one(test, nr_buffers, len, nr_data, nr_buffers - 1); 136fa0c812cSChristoph Hellwig 137fa0c812cSChristoph Hellwig /* Test data+P recovery */ 138fa0c812cSChristoph Hellwig for (i = 0; i < nr_buffers - 2; i++) 139fa0c812cSChristoph Hellwig test_recover_one(test, nr_buffers, len, i, nr_data); 140fa0c812cSChristoph Hellwig 141fa0c812cSChristoph Hellwig /* Double data failure is impossible with a single data disk */ 142fa0c812cSChristoph Hellwig if (nr_data == 1) 143fa0c812cSChristoph Hellwig return; 144fa0c812cSChristoph Hellwig 145fa0c812cSChristoph Hellwig /* Test data+data recovery using random sampling */ 146fa0c812cSChristoph Hellwig iterations = nr_buffers * 2; /* should provide good enough coverage */ 147fa0c812cSChristoph Hellwig for (i = 0; i < iterations; i++) { 148fa0c812cSChristoph Hellwig int faila = rand32() % nr_data, failb; 149fa0c812cSChristoph Hellwig 150fa0c812cSChristoph Hellwig do { 151fa0c812cSChristoph Hellwig failb = rand32() % nr_data; 152fa0c812cSChristoph Hellwig } while (failb == faila); 153fa0c812cSChristoph Hellwig 154fa0c812cSChristoph Hellwig test_recover_one(test, nr_buffers, len, faila, failb); 155fa0c812cSChristoph Hellwig } 156fa0c812cSChristoph Hellwig } 157fa0c812cSChristoph Hellwig 158fa0c812cSChristoph Hellwig /* Simulate rmw run */ 159fa0c812cSChristoph Hellwig static void test_rmw_one(struct kunit *test, unsigned int nr_buffers, 160fa0c812cSChristoph Hellwig unsigned int len, int p1, int p2) 161fa0c812cSChristoph Hellwig { 162fa0c812cSChristoph Hellwig const struct test_args *ta = test->param_value; 163fa0c812cSChristoph Hellwig 164*8cf0a6c4SChristoph Hellwig ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers); 165fa0c812cSChristoph Hellwig makedata(p1, p2); 166*8cf0a6c4SChristoph Hellwig ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers); 167fa0c812cSChristoph Hellwig test_recover(test, nr_buffers, len); 168fa0c812cSChristoph Hellwig } 169fa0c812cSChristoph Hellwig 170fa0c812cSChristoph Hellwig static void test_rmw(struct kunit *test, unsigned int nr_buffers, 171fa0c812cSChristoph Hellwig unsigned int len) 172fa0c812cSChristoph Hellwig { 173fa0c812cSChristoph Hellwig int iterations = nr_buffers / 2, i; 174fa0c812cSChristoph Hellwig 175fa0c812cSChristoph Hellwig for (i = 0; i < iterations; i++) { 176fa0c812cSChristoph Hellwig int p1 = rand32() % (nr_buffers - 2); 177fa0c812cSChristoph Hellwig int p2 = rand32() % (nr_buffers - 2); 178fa0c812cSChristoph Hellwig 179fa0c812cSChristoph Hellwig if (p2 < p1) 180fa0c812cSChristoph Hellwig swap(p1, p2); 181fa0c812cSChristoph Hellwig test_rmw_one(test, nr_buffers, len, p1, p2); 182fa0c812cSChristoph Hellwig } 183fa0c812cSChristoph Hellwig } 184fa0c812cSChristoph Hellwig 185fa0c812cSChristoph Hellwig static void raid6_test_one(struct kunit *test) 186fa0c812cSChristoph Hellwig { 187fa0c812cSChristoph Hellwig const struct test_args *ta = test->param_value; 188fa0c812cSChristoph Hellwig unsigned int nr_buffers = random_nr_buffers(); 189fa0c812cSChristoph Hellwig unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES); 190*8cf0a6c4SChristoph Hellwig unsigned int max_alignment; 191*8cf0a6c4SChristoph Hellwig int i; 192fa0c812cSChristoph Hellwig 193fa0c812cSChristoph Hellwig /* Nuke syndromes */ 194fa0c812cSChristoph Hellwig memset(test_buffers[nr_buffers - 2], 0xee, test_buflen); 195fa0c812cSChristoph Hellwig memset(test_buffers[nr_buffers - 1], 0xee, test_buflen); 196fa0c812cSChristoph Hellwig 197*8cf0a6c4SChristoph Hellwig /* 198*8cf0a6c4SChristoph Hellwig * If we're not using the entire buffer size, inject randomize alignment 199*8cf0a6c4SChristoph Hellwig * into the buffer. 200*8cf0a6c4SChristoph Hellwig */ 201*8cf0a6c4SChristoph Hellwig max_alignment = RAID6_KUNIT_MAX_BYTES - len; 202*8cf0a6c4SChristoph Hellwig if (rand32() % 2 == 0) { 203*8cf0a6c4SChristoph Hellwig /* Use random alignments mod 64 */ 204*8cf0a6c4SChristoph Hellwig for (i = 0; i < nr_buffers; i++) 205*8cf0a6c4SChristoph Hellwig aligned_buffers[i] = test_buffers[i] + 206*8cf0a6c4SChristoph Hellwig random_alignment(max_alignment); 207*8cf0a6c4SChristoph Hellwig } else { 208*8cf0a6c4SChristoph Hellwig /* Go up to the guard page, to catch buffer overreads */ 209*8cf0a6c4SChristoph Hellwig unsigned int align = test_buflen - len; 210*8cf0a6c4SChristoph Hellwig 211*8cf0a6c4SChristoph Hellwig for (i = 0; i < nr_buffers; i++) 212*8cf0a6c4SChristoph Hellwig aligned_buffers[i] = test_buffers[i] + align; 213*8cf0a6c4SChristoph Hellwig } 214*8cf0a6c4SChristoph Hellwig 215fa0c812cSChristoph Hellwig /* Generate assumed good syndrome */ 216*8cf0a6c4SChristoph Hellwig ta->gen->gen_syndrome(nr_buffers, len, aligned_buffers); 217fa0c812cSChristoph Hellwig 218fa0c812cSChristoph Hellwig test_recover(test, nr_buffers, len); 219fa0c812cSChristoph Hellwig 220fa0c812cSChristoph Hellwig if (ta->gen->xor_syndrome) 221fa0c812cSChristoph Hellwig test_rmw(test, nr_buffers, len); 2223626738bSChristoph Hellwig } 2233626738bSChristoph Hellwig 2243626738bSChristoph Hellwig static void raid6_test(struct kunit *test) 2253626738bSChristoph Hellwig { 226fa0c812cSChristoph Hellwig int i; 2273626738bSChristoph Hellwig 228fa0c812cSChristoph Hellwig for (i = 0; i < RAID6_KUNIT_NUM_TEST_ITERS; i++) 229fa0c812cSChristoph Hellwig raid6_test_one(test); 2303626738bSChristoph Hellwig } 2313626738bSChristoph Hellwig 2322175395fSChristoph Hellwig static const void *raid6_gen_params(struct kunit *test, const void *prev, 2332175395fSChristoph Hellwig char *desc) 2342175395fSChristoph Hellwig { 2352175395fSChristoph Hellwig if (!prev) { 2362175395fSChristoph Hellwig memset(&args, 0, sizeof(args)); 2372175395fSChristoph Hellwig next_algo: 2382175395fSChristoph Hellwig args.recov_idx = 0; 2392175395fSChristoph Hellwig args.gen = raid6_algo_find(args.gen_idx); 2402175395fSChristoph Hellwig if (!args.gen) 2412175395fSChristoph Hellwig return NULL; 2423626738bSChristoph Hellwig } 2432175395fSChristoph Hellwig 2442175395fSChristoph Hellwig if (args.recov) 2452175395fSChristoph Hellwig args.recov_idx++; 2462175395fSChristoph Hellwig args.recov = raid6_recov_algo_find(args.recov_idx); 2472175395fSChristoph Hellwig if (!args.recov) { 2482175395fSChristoph Hellwig args.gen_idx++; 2492175395fSChristoph Hellwig goto next_algo; 2503626738bSChristoph Hellwig } 2512175395fSChristoph Hellwig 2522175395fSChristoph Hellwig snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s", 2532175395fSChristoph Hellwig args.gen->name, args.recov->name); 2542175395fSChristoph Hellwig return &args; 2553626738bSChristoph Hellwig } 2563626738bSChristoph Hellwig 2573626738bSChristoph Hellwig static struct kunit_case raid6_test_cases[] = { 2582175395fSChristoph Hellwig KUNIT_CASE_PARAM(raid6_test, raid6_gen_params), 2593626738bSChristoph Hellwig {}, 2603626738bSChristoph Hellwig }; 2613626738bSChristoph Hellwig 2623626738bSChristoph Hellwig static int raid6_suite_init(struct kunit_suite *suite) 2633626738bSChristoph Hellwig { 264d67c2571SChristoph Hellwig int i; 265d67c2571SChristoph Hellwig 2663626738bSChristoph Hellwig prandom_seed_state(&rng, RAID6_KUNIT_SEED); 267d67c2571SChristoph Hellwig 268d67c2571SChristoph Hellwig /* 269d67c2571SChristoph Hellwig * Allocate the test buffer using vmalloc() with a page-aligned length 270d67c2571SChristoph Hellwig * so that it is immediately followed by a guard page. This allows 271d67c2571SChristoph Hellwig * buffer overreads to be detected, even in assembly code. 272d67c2571SChristoph Hellwig */ 273fa0c812cSChristoph Hellwig test_buflen = round_up(RAID6_KUNIT_MAX_BYTES, PAGE_SIZE); 274d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) { 275fa0c812cSChristoph Hellwig test_recov_buffers[i] = vmalloc(test_buflen); 276d67c2571SChristoph Hellwig if (!test_recov_buffers[i]) 277d67c2571SChristoph Hellwig goto out_free_recov_buffers; 278d67c2571SChristoph Hellwig } 279fa0c812cSChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) { 280fa0c812cSChristoph Hellwig test_buffers[i] = vmalloc(test_buflen); 281d67c2571SChristoph Hellwig if (!test_buffers[i]) 282d67c2571SChristoph Hellwig goto out_free_buffers; 283d67c2571SChristoph Hellwig } 284d67c2571SChristoph Hellwig 285fa0c812cSChristoph Hellwig makedata(0, RAID6_KUNIT_MAX_BUFFERS - 1); 286d67c2571SChristoph Hellwig 2873626738bSChristoph Hellwig return 0; 288d67c2571SChristoph Hellwig 289d67c2571SChristoph Hellwig out_free_buffers: 290fa0c812cSChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) 291d67c2571SChristoph Hellwig vfree(test_buffers[i]); 292d67c2571SChristoph Hellwig memset(test_buffers, 0, sizeof(test_buffers)); 293d67c2571SChristoph Hellwig out_free_recov_buffers: 294d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 295d67c2571SChristoph Hellwig vfree(test_recov_buffers[i]); 296d67c2571SChristoph Hellwig memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); 297d67c2571SChristoph Hellwig return -ENOMEM; 298d67c2571SChristoph Hellwig } 299d67c2571SChristoph Hellwig 300d67c2571SChristoph Hellwig static void raid6_suite_exit(struct kunit_suite *suite) 301d67c2571SChristoph Hellwig { 302d67c2571SChristoph Hellwig int i; 303d67c2571SChristoph Hellwig 304fa0c812cSChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) 305d67c2571SChristoph Hellwig vfree(test_buffers[i]); 306d67c2571SChristoph Hellwig memset(test_buffers, 0, sizeof(test_buffers)); 307d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 308d67c2571SChristoph Hellwig vfree(test_recov_buffers[i]); 309d67c2571SChristoph Hellwig memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); 3103626738bSChristoph Hellwig } 3113626738bSChristoph Hellwig 3123626738bSChristoph Hellwig static struct kunit_suite raid6_test_suite = { 3133626738bSChristoph Hellwig .name = "raid6", 3143626738bSChristoph Hellwig .test_cases = raid6_test_cases, 3153626738bSChristoph Hellwig .suite_init = raid6_suite_init, 316d67c2571SChristoph Hellwig .suite_exit = raid6_suite_exit, 3173626738bSChristoph Hellwig }; 3183626738bSChristoph Hellwig kunit_test_suite(raid6_test_suite); 3193626738bSChristoph Hellwig 3203626738bSChristoph Hellwig MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions"); 3213626738bSChristoph Hellwig MODULE_LICENSE("GPL"); 322