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> 11*fa0c812cSChristoph 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 17*fa0c812cSChristoph Hellwig #define RAID6_KUNIT_NUM_TEST_ITERS 10 18*fa0c812cSChristoph Hellwig #define RAID6_KUNIT_MAX_BUFFERS 64 /* Including P and Q */ 19d67c2571SChristoph Hellwig #define RAID6_KUNIT_MAX_FAILURES 2 20*fa0c812cSChristoph Hellwig #define RAID6_KUNIT_MAX_BYTES PAGE_SIZE 213626738bSChristoph Hellwig 223626738bSChristoph Hellwig static struct rnd_state rng; 23*fa0c812cSChristoph Hellwig static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS]; 24d67c2571SChristoph Hellwig static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; 25*fa0c812cSChristoph Hellwig static size_t test_buflen; 263626738bSChristoph Hellwig 272175395fSChristoph Hellwig struct test_args { 282175395fSChristoph Hellwig unsigned int recov_idx; 292175395fSChristoph Hellwig const struct raid6_recov_calls *recov; 302175395fSChristoph Hellwig unsigned int gen_idx; 312175395fSChristoph Hellwig const struct raid6_calls *gen; 322175395fSChristoph Hellwig }; 332175395fSChristoph Hellwig 342175395fSChristoph Hellwig static struct test_args args; 352175395fSChristoph Hellwig 36*fa0c812cSChristoph Hellwig static u32 rand32(void) 37*fa0c812cSChristoph Hellwig { 38*fa0c812cSChristoph Hellwig return prandom_u32_state(&rng); 39*fa0c812cSChristoph Hellwig } 40*fa0c812cSChristoph Hellwig 41*fa0c812cSChristoph Hellwig /* Generate a random length that is a multiple of 512. */ 42*fa0c812cSChristoph Hellwig static unsigned int random_length(unsigned int max_length) 43*fa0c812cSChristoph Hellwig { 44*fa0c812cSChristoph Hellwig return round_up((rand32() % max_length) + 1, 512); 45*fa0c812cSChristoph Hellwig } 46*fa0c812cSChristoph Hellwig 47*fa0c812cSChristoph Hellwig static unsigned int random_nr_buffers(void) 48*fa0c812cSChristoph Hellwig { 49*fa0c812cSChristoph Hellwig return (rand32() % (RAID6_KUNIT_MAX_BUFFERS - (RAID6_MIN_DISKS - 1))) + 50*fa0c812cSChristoph Hellwig RAID6_MIN_DISKS; 51*fa0c812cSChristoph Hellwig } 52*fa0c812cSChristoph Hellwig 533626738bSChristoph Hellwig static void makedata(int start, int stop) 543626738bSChristoph Hellwig { 553626738bSChristoph Hellwig int i; 563626738bSChristoph Hellwig 57562bcbfcSChristoph Hellwig for (i = start; i <= stop; i++) 58*fa0c812cSChristoph Hellwig prandom_bytes_state(&rng, test_buffers[i], test_buflen); 593626738bSChristoph Hellwig } 603626738bSChristoph Hellwig 61*fa0c812cSChristoph Hellwig static char member_type(unsigned int nr_buffers, int d) 623626738bSChristoph Hellwig { 63*fa0c812cSChristoph Hellwig if (d == nr_buffers - 2) 643626738bSChristoph Hellwig return 'P'; 65*fa0c812cSChristoph Hellwig if (d == nr_buffers - 1) 663626738bSChristoph Hellwig return 'Q'; 673626738bSChristoph Hellwig return 'D'; 683626738bSChristoph Hellwig } 693626738bSChristoph Hellwig 70*fa0c812cSChristoph Hellwig static void test_recover_one(struct kunit *test, unsigned int nr_buffers, 71*fa0c812cSChristoph Hellwig unsigned int len, int faila, int failb) 723626738bSChristoph Hellwig { 732175395fSChristoph Hellwig const struct test_args *ta = test->param_value; 74*fa0c812cSChristoph Hellwig void *dataptrs[RAID6_KUNIT_MAX_BUFFERS]; 75d67c2571SChristoph Hellwig int i; 762175395fSChristoph Hellwig 77*fa0c812cSChristoph Hellwig if (faila > failb) 78*fa0c812cSChristoph Hellwig swap(faila, failb); 79*fa0c812cSChristoph Hellwig 80d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 81*fa0c812cSChristoph Hellwig memset(test_recov_buffers[i], 0xf0, test_buflen); 823626738bSChristoph Hellwig 83562bcbfcSChristoph Hellwig memcpy(dataptrs, test_buffers, sizeof(dataptrs)); 84d67c2571SChristoph Hellwig dataptrs[faila] = test_recov_buffers[0]; 85d67c2571SChristoph Hellwig dataptrs[failb] = test_recov_buffers[1]; 863626738bSChristoph Hellwig 87*fa0c812cSChristoph Hellwig if (failb == nr_buffers - 1) { 883626738bSChristoph Hellwig /* 893626738bSChristoph Hellwig * We don't implement the data+Q failure scenario, since it 903626738bSChristoph Hellwig * is equivalent to a RAID-5 failure (XOR, then recompute Q). 913626738bSChristoph Hellwig */ 92*fa0c812cSChristoph Hellwig if (WARN_ON_ONCE(faila != nr_buffers - 2)) 93562bcbfcSChristoph Hellwig return; 943626738bSChristoph Hellwig 953626738bSChristoph Hellwig /* P+Q failure. Just rebuild the syndrome. */ 96*fa0c812cSChristoph Hellwig ta->gen->gen_syndrome(nr_buffers, len, dataptrs); 97*fa0c812cSChristoph Hellwig } else if (failb == nr_buffers - 2) { 983626738bSChristoph Hellwig /* data+P failure. */ 99*fa0c812cSChristoph Hellwig ta->recov->datap(nr_buffers, len, faila, dataptrs); 1003626738bSChristoph Hellwig } else { 1013626738bSChristoph Hellwig /* data+data failure. */ 102*fa0c812cSChristoph Hellwig ta->recov->data2(nr_buffers, len, faila, failb, dataptrs); 1033626738bSChristoph Hellwig } 1043626738bSChristoph Hellwig 105d67c2571SChristoph Hellwig KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0], 106*fa0c812cSChristoph Hellwig len, 107*fa0c812cSChristoph Hellwig "faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n", 108*fa0c812cSChristoph Hellwig faila, member_type(nr_buffers, faila), 109*fa0c812cSChristoph Hellwig nr_buffers, len, 110*fa0c812cSChristoph Hellwig failb, member_type(nr_buffers, failb)); 111d67c2571SChristoph Hellwig KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1], 112*fa0c812cSChristoph Hellwig len, 113*fa0c812cSChristoph Hellwig "failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n", 114*fa0c812cSChristoph Hellwig failb, member_type(nr_buffers, failb), 115*fa0c812cSChristoph Hellwig nr_buffers, len, 116*fa0c812cSChristoph Hellwig faila, member_type(nr_buffers, faila)); 117*fa0c812cSChristoph Hellwig } 118*fa0c812cSChristoph Hellwig 119*fa0c812cSChristoph Hellwig static void test_recover(struct kunit *test, unsigned int nr_buffers, 120*fa0c812cSChristoph Hellwig unsigned int len) 121*fa0c812cSChristoph Hellwig { 122*fa0c812cSChristoph Hellwig unsigned int nr_data = nr_buffers - 2; 123*fa0c812cSChristoph Hellwig int iterations, i; 124*fa0c812cSChristoph Hellwig 125*fa0c812cSChristoph Hellwig /* Test P+Q recovery */ 126*fa0c812cSChristoph Hellwig test_recover_one(test, nr_buffers, len, nr_data, nr_buffers - 1); 127*fa0c812cSChristoph Hellwig 128*fa0c812cSChristoph Hellwig /* Test data+P recovery */ 129*fa0c812cSChristoph Hellwig for (i = 0; i < nr_buffers - 2; i++) 130*fa0c812cSChristoph Hellwig test_recover_one(test, nr_buffers, len, i, nr_data); 131*fa0c812cSChristoph Hellwig 132*fa0c812cSChristoph Hellwig /* Double data failure is impossible with a single data disk */ 133*fa0c812cSChristoph Hellwig if (nr_data == 1) 134*fa0c812cSChristoph Hellwig return; 135*fa0c812cSChristoph Hellwig 136*fa0c812cSChristoph Hellwig /* Test data+data recovery using random sampling */ 137*fa0c812cSChristoph Hellwig iterations = nr_buffers * 2; /* should provide good enough coverage */ 138*fa0c812cSChristoph Hellwig for (i = 0; i < iterations; i++) { 139*fa0c812cSChristoph Hellwig int faila = rand32() % nr_data, failb; 140*fa0c812cSChristoph Hellwig 141*fa0c812cSChristoph Hellwig do { 142*fa0c812cSChristoph Hellwig failb = rand32() % nr_data; 143*fa0c812cSChristoph Hellwig } while (failb == faila); 144*fa0c812cSChristoph Hellwig 145*fa0c812cSChristoph Hellwig test_recover_one(test, nr_buffers, len, faila, failb); 146*fa0c812cSChristoph Hellwig } 147*fa0c812cSChristoph Hellwig } 148*fa0c812cSChristoph Hellwig 149*fa0c812cSChristoph Hellwig /* Simulate rmw run */ 150*fa0c812cSChristoph Hellwig static void test_rmw_one(struct kunit *test, unsigned int nr_buffers, 151*fa0c812cSChristoph Hellwig unsigned int len, int p1, int p2) 152*fa0c812cSChristoph Hellwig { 153*fa0c812cSChristoph Hellwig const struct test_args *ta = test->param_value; 154*fa0c812cSChristoph Hellwig 155*fa0c812cSChristoph Hellwig ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers); 156*fa0c812cSChristoph Hellwig makedata(p1, p2); 157*fa0c812cSChristoph Hellwig ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers); 158*fa0c812cSChristoph Hellwig test_recover(test, nr_buffers, len); 159*fa0c812cSChristoph Hellwig } 160*fa0c812cSChristoph Hellwig 161*fa0c812cSChristoph Hellwig static void test_rmw(struct kunit *test, unsigned int nr_buffers, 162*fa0c812cSChristoph Hellwig unsigned int len) 163*fa0c812cSChristoph Hellwig { 164*fa0c812cSChristoph Hellwig int iterations = nr_buffers / 2, i; 165*fa0c812cSChristoph Hellwig 166*fa0c812cSChristoph Hellwig for (i = 0; i < iterations; i++) { 167*fa0c812cSChristoph Hellwig int p1 = rand32() % (nr_buffers - 2); 168*fa0c812cSChristoph Hellwig int p2 = rand32() % (nr_buffers - 2); 169*fa0c812cSChristoph Hellwig 170*fa0c812cSChristoph Hellwig if (p2 < p1) 171*fa0c812cSChristoph Hellwig swap(p1, p2); 172*fa0c812cSChristoph Hellwig test_rmw_one(test, nr_buffers, len, p1, p2); 173*fa0c812cSChristoph Hellwig } 174*fa0c812cSChristoph Hellwig } 175*fa0c812cSChristoph Hellwig 176*fa0c812cSChristoph Hellwig static void raid6_test_one(struct kunit *test) 177*fa0c812cSChristoph Hellwig { 178*fa0c812cSChristoph Hellwig const struct test_args *ta = test->param_value; 179*fa0c812cSChristoph Hellwig unsigned int nr_buffers = random_nr_buffers(); 180*fa0c812cSChristoph Hellwig unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES); 181*fa0c812cSChristoph Hellwig 182*fa0c812cSChristoph Hellwig /* Nuke syndromes */ 183*fa0c812cSChristoph Hellwig memset(test_buffers[nr_buffers - 2], 0xee, test_buflen); 184*fa0c812cSChristoph Hellwig memset(test_buffers[nr_buffers - 1], 0xee, test_buflen); 185*fa0c812cSChristoph Hellwig 186*fa0c812cSChristoph Hellwig /* Generate assumed good syndrome */ 187*fa0c812cSChristoph Hellwig ta->gen->gen_syndrome(nr_buffers, len, test_buffers); 188*fa0c812cSChristoph Hellwig 189*fa0c812cSChristoph Hellwig test_recover(test, nr_buffers, len); 190*fa0c812cSChristoph Hellwig 191*fa0c812cSChristoph Hellwig if (ta->gen->xor_syndrome) 192*fa0c812cSChristoph Hellwig test_rmw(test, nr_buffers, len); 1933626738bSChristoph Hellwig } 1943626738bSChristoph Hellwig 1953626738bSChristoph Hellwig static void raid6_test(struct kunit *test) 1963626738bSChristoph Hellwig { 197*fa0c812cSChristoph Hellwig int i; 1983626738bSChristoph Hellwig 199*fa0c812cSChristoph Hellwig for (i = 0; i < RAID6_KUNIT_NUM_TEST_ITERS; i++) 200*fa0c812cSChristoph Hellwig raid6_test_one(test); 2013626738bSChristoph Hellwig } 2023626738bSChristoph Hellwig 2032175395fSChristoph Hellwig static const void *raid6_gen_params(struct kunit *test, const void *prev, 2042175395fSChristoph Hellwig char *desc) 2052175395fSChristoph Hellwig { 2062175395fSChristoph Hellwig if (!prev) { 2072175395fSChristoph Hellwig memset(&args, 0, sizeof(args)); 2082175395fSChristoph Hellwig next_algo: 2092175395fSChristoph Hellwig args.recov_idx = 0; 2102175395fSChristoph Hellwig args.gen = raid6_algo_find(args.gen_idx); 2112175395fSChristoph Hellwig if (!args.gen) 2122175395fSChristoph Hellwig return NULL; 2133626738bSChristoph Hellwig } 2142175395fSChristoph Hellwig 2152175395fSChristoph Hellwig if (args.recov) 2162175395fSChristoph Hellwig args.recov_idx++; 2172175395fSChristoph Hellwig args.recov = raid6_recov_algo_find(args.recov_idx); 2182175395fSChristoph Hellwig if (!args.recov) { 2192175395fSChristoph Hellwig args.gen_idx++; 2202175395fSChristoph Hellwig goto next_algo; 2213626738bSChristoph Hellwig } 2222175395fSChristoph Hellwig 2232175395fSChristoph Hellwig snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s", 2242175395fSChristoph Hellwig args.gen->name, args.recov->name); 2252175395fSChristoph Hellwig return &args; 2263626738bSChristoph Hellwig } 2273626738bSChristoph Hellwig 2283626738bSChristoph Hellwig static struct kunit_case raid6_test_cases[] = { 2292175395fSChristoph Hellwig KUNIT_CASE_PARAM(raid6_test, raid6_gen_params), 2303626738bSChristoph Hellwig {}, 2313626738bSChristoph Hellwig }; 2323626738bSChristoph Hellwig 2333626738bSChristoph Hellwig static int raid6_suite_init(struct kunit_suite *suite) 2343626738bSChristoph Hellwig { 235d67c2571SChristoph Hellwig int i; 236d67c2571SChristoph Hellwig 2373626738bSChristoph Hellwig prandom_seed_state(&rng, RAID6_KUNIT_SEED); 238d67c2571SChristoph Hellwig 239d67c2571SChristoph Hellwig /* 240d67c2571SChristoph Hellwig * Allocate the test buffer using vmalloc() with a page-aligned length 241d67c2571SChristoph Hellwig * so that it is immediately followed by a guard page. This allows 242d67c2571SChristoph Hellwig * buffer overreads to be detected, even in assembly code. 243d67c2571SChristoph Hellwig */ 244*fa0c812cSChristoph Hellwig test_buflen = round_up(RAID6_KUNIT_MAX_BYTES, PAGE_SIZE); 245d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) { 246*fa0c812cSChristoph Hellwig test_recov_buffers[i] = vmalloc(test_buflen); 247d67c2571SChristoph Hellwig if (!test_recov_buffers[i]) 248d67c2571SChristoph Hellwig goto out_free_recov_buffers; 249d67c2571SChristoph Hellwig } 250*fa0c812cSChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) { 251*fa0c812cSChristoph Hellwig test_buffers[i] = vmalloc(test_buflen); 252d67c2571SChristoph Hellwig if (!test_buffers[i]) 253d67c2571SChristoph Hellwig goto out_free_buffers; 254d67c2571SChristoph Hellwig } 255d67c2571SChristoph Hellwig 256*fa0c812cSChristoph Hellwig makedata(0, RAID6_KUNIT_MAX_BUFFERS - 1); 257d67c2571SChristoph Hellwig 2583626738bSChristoph Hellwig return 0; 259d67c2571SChristoph Hellwig 260d67c2571SChristoph Hellwig out_free_buffers: 261*fa0c812cSChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) 262d67c2571SChristoph Hellwig vfree(test_buffers[i]); 263d67c2571SChristoph Hellwig memset(test_buffers, 0, sizeof(test_buffers)); 264d67c2571SChristoph Hellwig out_free_recov_buffers: 265d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 266d67c2571SChristoph Hellwig vfree(test_recov_buffers[i]); 267d67c2571SChristoph Hellwig memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); 268d67c2571SChristoph Hellwig return -ENOMEM; 269d67c2571SChristoph Hellwig } 270d67c2571SChristoph Hellwig 271d67c2571SChristoph Hellwig static void raid6_suite_exit(struct kunit_suite *suite) 272d67c2571SChristoph Hellwig { 273d67c2571SChristoph Hellwig int i; 274d67c2571SChristoph Hellwig 275*fa0c812cSChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) 276d67c2571SChristoph Hellwig vfree(test_buffers[i]); 277d67c2571SChristoph Hellwig memset(test_buffers, 0, sizeof(test_buffers)); 278d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 279d67c2571SChristoph Hellwig vfree(test_recov_buffers[i]); 280d67c2571SChristoph Hellwig memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); 2813626738bSChristoph Hellwig } 2823626738bSChristoph Hellwig 2833626738bSChristoph Hellwig static struct kunit_suite raid6_test_suite = { 2843626738bSChristoph Hellwig .name = "raid6", 2853626738bSChristoph Hellwig .test_cases = raid6_test_cases, 2863626738bSChristoph Hellwig .suite_init = raid6_suite_init, 287d67c2571SChristoph Hellwig .suite_exit = raid6_suite_exit, 2883626738bSChristoph Hellwig }; 2893626738bSChristoph Hellwig kunit_test_suite(raid6_test_suite); 2903626738bSChristoph Hellwig 2913626738bSChristoph Hellwig MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions"); 2923626738bSChristoph Hellwig MODULE_LICENSE("GPL"); 293