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> 11769d603fSChristoph Hellwig #include "../algos.h" 123626738bSChristoph Hellwig 133626738bSChristoph Hellwig MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); 143626738bSChristoph Hellwig 153626738bSChristoph Hellwig #define RAID6_KUNIT_SEED 42 16d67c2571SChristoph Hellwig #define RAID6_KUNIT_MAX_FAILURES 2 173626738bSChristoph Hellwig 183626738bSChristoph Hellwig #define NDISKS 16 /* Including P and Q */ 193626738bSChristoph Hellwig 203626738bSChristoph Hellwig static struct rnd_state rng; 21d67c2571SChristoph Hellwig static void *test_buffers[NDISKS]; 22d67c2571SChristoph Hellwig static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES]; 233626738bSChristoph Hellwig 242175395fSChristoph Hellwig struct test_args { 252175395fSChristoph Hellwig unsigned int recov_idx; 262175395fSChristoph Hellwig const struct raid6_recov_calls *recov; 272175395fSChristoph Hellwig unsigned int gen_idx; 282175395fSChristoph Hellwig const struct raid6_calls *gen; 292175395fSChristoph Hellwig }; 302175395fSChristoph Hellwig 312175395fSChristoph Hellwig static struct test_args args; 322175395fSChristoph Hellwig 333626738bSChristoph Hellwig static void makedata(int start, int stop) 343626738bSChristoph Hellwig { 353626738bSChristoph Hellwig int i; 363626738bSChristoph Hellwig 37*562bcbfcSChristoph Hellwig for (i = start; i <= stop; i++) 38d67c2571SChristoph Hellwig prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE); 393626738bSChristoph Hellwig } 403626738bSChristoph Hellwig 413626738bSChristoph Hellwig static char member_type(int d) 423626738bSChristoph Hellwig { 433626738bSChristoph Hellwig switch (d) { 443626738bSChristoph Hellwig case NDISKS-2: 453626738bSChristoph Hellwig return 'P'; 463626738bSChristoph Hellwig case NDISKS-1: 473626738bSChristoph Hellwig return 'Q'; 483626738bSChristoph Hellwig default: 493626738bSChristoph Hellwig return 'D'; 503626738bSChristoph Hellwig } 513626738bSChristoph Hellwig } 523626738bSChristoph Hellwig 532175395fSChristoph Hellwig static void test_recover(struct kunit *test, int faila, int failb) 543626738bSChristoph Hellwig { 552175395fSChristoph Hellwig const struct test_args *ta = test->param_value; 56*562bcbfcSChristoph Hellwig void *dataptrs[NDISKS]; 57d67c2571SChristoph Hellwig int i; 582175395fSChristoph Hellwig 59d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 60d67c2571SChristoph Hellwig memset(test_recov_buffers[i], 0xf0, PAGE_SIZE); 613626738bSChristoph Hellwig 62*562bcbfcSChristoph Hellwig memcpy(dataptrs, test_buffers, sizeof(dataptrs)); 63d67c2571SChristoph Hellwig dataptrs[faila] = test_recov_buffers[0]; 64d67c2571SChristoph Hellwig dataptrs[failb] = test_recov_buffers[1]; 653626738bSChristoph Hellwig 663626738bSChristoph Hellwig if (failb == NDISKS - 1) { 673626738bSChristoph Hellwig /* 683626738bSChristoph Hellwig * We don't implement the data+Q failure scenario, since it 693626738bSChristoph Hellwig * is equivalent to a RAID-5 failure (XOR, then recompute Q). 703626738bSChristoph Hellwig */ 713626738bSChristoph Hellwig if (faila != NDISKS - 2) 72*562bcbfcSChristoph Hellwig return; 733626738bSChristoph Hellwig 743626738bSChristoph Hellwig /* P+Q failure. Just rebuild the syndrome. */ 752175395fSChristoph Hellwig ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs); 763626738bSChristoph Hellwig } else if (failb == NDISKS - 2) { 773626738bSChristoph Hellwig /* data+P failure. */ 782175395fSChristoph Hellwig ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs); 793626738bSChristoph Hellwig } else { 803626738bSChristoph Hellwig /* data+data failure. */ 812175395fSChristoph Hellwig ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs); 823626738bSChristoph Hellwig } 833626738bSChristoph Hellwig 84d67c2571SChristoph Hellwig KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0], 85d67c2571SChristoph Hellwig PAGE_SIZE, 862175395fSChristoph Hellwig "faila miscompared: %3d[%c] (failb=%3d[%c])\n", 873626738bSChristoph Hellwig faila, member_type(faila), 883626738bSChristoph Hellwig failb, member_type(failb)); 89d67c2571SChristoph Hellwig KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1], 90d67c2571SChristoph Hellwig PAGE_SIZE, 912175395fSChristoph Hellwig "failb miscompared: %3d[%c] (faila=%3d[%c])\n", 923626738bSChristoph Hellwig failb, member_type(failb), 933626738bSChristoph Hellwig faila, member_type(faila)); 943626738bSChristoph Hellwig } 953626738bSChristoph Hellwig 963626738bSChristoph Hellwig static void raid6_test(struct kunit *test) 973626738bSChristoph Hellwig { 982175395fSChristoph Hellwig const struct test_args *ta = test->param_value; 993626738bSChristoph Hellwig int i, j, p1, p2; 1003626738bSChristoph Hellwig 1013626738bSChristoph Hellwig /* Nuke syndromes */ 102d67c2571SChristoph Hellwig memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE); 103d67c2571SChristoph Hellwig memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE); 1043626738bSChristoph Hellwig 1053626738bSChristoph Hellwig /* Generate assumed good syndrome */ 106*562bcbfcSChristoph Hellwig ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, test_buffers); 1073626738bSChristoph Hellwig 1083626738bSChristoph Hellwig for (i = 0; i < NDISKS - 1; i++) 1093626738bSChristoph Hellwig for (j = i + 1; j < NDISKS; j++) 1102175395fSChristoph Hellwig test_recover(test, i, j); 1113626738bSChristoph Hellwig 1122175395fSChristoph Hellwig if (!ta->gen->xor_syndrome) 1132175395fSChristoph Hellwig return; 1143626738bSChristoph Hellwig 1152175395fSChristoph Hellwig for (p1 = 0; p1 < NDISKS - 2; p1++) { 1163626738bSChristoph Hellwig for (p2 = p1; p2 < NDISKS - 2; p2++) { 1173626738bSChristoph Hellwig /* Simulate rmw run */ 1182175395fSChristoph Hellwig ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, 119*562bcbfcSChristoph Hellwig test_buffers); 1203626738bSChristoph Hellwig makedata(p1, p2); 1212175395fSChristoph Hellwig ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE, 122*562bcbfcSChristoph Hellwig test_buffers); 1233626738bSChristoph Hellwig 1243626738bSChristoph Hellwig for (i = 0; i < NDISKS - 1; i++) 1253626738bSChristoph Hellwig for (j = i + 1; j < NDISKS; j++) 1262175395fSChristoph Hellwig test_recover(test, i, j); 1272175395fSChristoph Hellwig } 1282175395fSChristoph Hellwig } 1293626738bSChristoph Hellwig } 1303626738bSChristoph Hellwig 1312175395fSChristoph Hellwig static const void *raid6_gen_params(struct kunit *test, const void *prev, 1322175395fSChristoph Hellwig char *desc) 1332175395fSChristoph Hellwig { 1342175395fSChristoph Hellwig if (!prev) { 1352175395fSChristoph Hellwig memset(&args, 0, sizeof(args)); 1362175395fSChristoph Hellwig next_algo: 1372175395fSChristoph Hellwig args.recov_idx = 0; 1382175395fSChristoph Hellwig args.gen = raid6_algo_find(args.gen_idx); 1392175395fSChristoph Hellwig if (!args.gen) 1402175395fSChristoph Hellwig return NULL; 1413626738bSChristoph Hellwig } 1422175395fSChristoph Hellwig 1432175395fSChristoph Hellwig if (args.recov) 1442175395fSChristoph Hellwig args.recov_idx++; 1452175395fSChristoph Hellwig args.recov = raid6_recov_algo_find(args.recov_idx); 1462175395fSChristoph Hellwig if (!args.recov) { 1472175395fSChristoph Hellwig args.gen_idx++; 1482175395fSChristoph Hellwig goto next_algo; 1493626738bSChristoph Hellwig } 1502175395fSChristoph Hellwig 1512175395fSChristoph Hellwig snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s", 1522175395fSChristoph Hellwig args.gen->name, args.recov->name); 1532175395fSChristoph Hellwig return &args; 1543626738bSChristoph Hellwig } 1553626738bSChristoph Hellwig 1563626738bSChristoph Hellwig static struct kunit_case raid6_test_cases[] = { 1572175395fSChristoph Hellwig KUNIT_CASE_PARAM(raid6_test, raid6_gen_params), 1583626738bSChristoph Hellwig {}, 1593626738bSChristoph Hellwig }; 1603626738bSChristoph Hellwig 1613626738bSChristoph Hellwig static int raid6_suite_init(struct kunit_suite *suite) 1623626738bSChristoph Hellwig { 163d67c2571SChristoph Hellwig int i; 164d67c2571SChristoph Hellwig 1653626738bSChristoph Hellwig prandom_seed_state(&rng, RAID6_KUNIT_SEED); 166d67c2571SChristoph Hellwig 167d67c2571SChristoph Hellwig /* 168d67c2571SChristoph Hellwig * Allocate the test buffer using vmalloc() with a page-aligned length 169d67c2571SChristoph Hellwig * so that it is immediately followed by a guard page. This allows 170d67c2571SChristoph Hellwig * buffer overreads to be detected, even in assembly code. 171d67c2571SChristoph Hellwig */ 172d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) { 173d67c2571SChristoph Hellwig test_recov_buffers[i] = vmalloc(PAGE_SIZE); 174d67c2571SChristoph Hellwig if (!test_recov_buffers[i]) 175d67c2571SChristoph Hellwig goto out_free_recov_buffers; 176d67c2571SChristoph Hellwig } 177d67c2571SChristoph Hellwig for (i = 0; i < NDISKS; i++) { 178d67c2571SChristoph Hellwig test_buffers[i] = vmalloc(PAGE_SIZE); 179d67c2571SChristoph Hellwig if (!test_buffers[i]) 180d67c2571SChristoph Hellwig goto out_free_buffers; 181d67c2571SChristoph Hellwig } 182d67c2571SChristoph Hellwig 1833626738bSChristoph Hellwig makedata(0, NDISKS - 1); 184d67c2571SChristoph Hellwig 1853626738bSChristoph Hellwig return 0; 186d67c2571SChristoph Hellwig 187d67c2571SChristoph Hellwig out_free_buffers: 188d67c2571SChristoph Hellwig for (i = 0; i < NDISKS; i++) 189d67c2571SChristoph Hellwig vfree(test_buffers[i]); 190d67c2571SChristoph Hellwig memset(test_buffers, 0, sizeof(test_buffers)); 191d67c2571SChristoph Hellwig out_free_recov_buffers: 192d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 193d67c2571SChristoph Hellwig vfree(test_recov_buffers[i]); 194d67c2571SChristoph Hellwig memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); 195d67c2571SChristoph Hellwig return -ENOMEM; 196d67c2571SChristoph Hellwig } 197d67c2571SChristoph Hellwig 198d67c2571SChristoph Hellwig static void raid6_suite_exit(struct kunit_suite *suite) 199d67c2571SChristoph Hellwig { 200d67c2571SChristoph Hellwig int i; 201d67c2571SChristoph Hellwig 202d67c2571SChristoph Hellwig for (i = 0; i < NDISKS; i++) 203d67c2571SChristoph Hellwig vfree(test_buffers[i]); 204d67c2571SChristoph Hellwig memset(test_buffers, 0, sizeof(test_buffers)); 205d67c2571SChristoph Hellwig for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) 206d67c2571SChristoph Hellwig vfree(test_recov_buffers[i]); 207d67c2571SChristoph Hellwig memset(test_recov_buffers, 0, sizeof(test_recov_buffers)); 2083626738bSChristoph Hellwig } 2093626738bSChristoph Hellwig 2103626738bSChristoph Hellwig static struct kunit_suite raid6_test_suite = { 2113626738bSChristoph Hellwig .name = "raid6", 2123626738bSChristoph Hellwig .test_cases = raid6_test_cases, 2133626738bSChristoph Hellwig .suite_init = raid6_suite_init, 214d67c2571SChristoph Hellwig .suite_exit = raid6_suite_exit, 2153626738bSChristoph Hellwig }; 2163626738bSChristoph Hellwig kunit_test_suite(raid6_test_suite); 2173626738bSChristoph Hellwig 2183626738bSChristoph Hellwig MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions"); 2193626738bSChristoph Hellwig MODULE_LICENSE("GPL"); 220