xref: /linux/lib/raid/xor/tests/xor_kunit.c (revision 440d6635b20037bc9ad46b20817d7b61cef0fc1b)
1*af53e85eSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0-or-later
2*af53e85eSChristoph Hellwig /*
3*af53e85eSChristoph Hellwig  * Unit test the XOR library functions.
4*af53e85eSChristoph Hellwig  *
5*af53e85eSChristoph Hellwig  * Copyright 2024 Google LLC
6*af53e85eSChristoph Hellwig  * Copyright 2026 Christoph Hellwig
7*af53e85eSChristoph Hellwig  *
8*af53e85eSChristoph Hellwig  * Based on the CRC tests by Eric Biggers <ebiggers@google.com>.
9*af53e85eSChristoph Hellwig  */
10*af53e85eSChristoph Hellwig #include <kunit/test.h>
11*af53e85eSChristoph Hellwig #include <linux/prandom.h>
12*af53e85eSChristoph Hellwig #include <linux/string_choices.h>
13*af53e85eSChristoph Hellwig #include <linux/vmalloc.h>
14*af53e85eSChristoph Hellwig #include <linux/raid/xor.h>
15*af53e85eSChristoph Hellwig 
16*af53e85eSChristoph Hellwig #define XOR_KUNIT_SEED			42
17*af53e85eSChristoph Hellwig #define XOR_KUNIT_MAX_BYTES		16384
18*af53e85eSChristoph Hellwig #define XOR_KUNIT_MAX_BUFFERS		64
19*af53e85eSChristoph Hellwig #define XOR_KUNIT_NUM_TEST_ITERS	1000
20*af53e85eSChristoph Hellwig 
21*af53e85eSChristoph Hellwig static struct rnd_state rng;
22*af53e85eSChristoph Hellwig static void *test_buffers[XOR_KUNIT_MAX_BUFFERS];
23*af53e85eSChristoph Hellwig static void *test_dest;
24*af53e85eSChristoph Hellwig static void *test_ref;
25*af53e85eSChristoph Hellwig static size_t test_buflen;
26*af53e85eSChristoph Hellwig 
27*af53e85eSChristoph Hellwig static u32 rand32(void)
28*af53e85eSChristoph Hellwig {
29*af53e85eSChristoph Hellwig 	return prandom_u32_state(&rng);
30*af53e85eSChristoph Hellwig }
31*af53e85eSChristoph Hellwig 
32*af53e85eSChristoph Hellwig /* Reference implementation using dumb byte-wise XOR */
33*af53e85eSChristoph Hellwig static void xor_ref(void *dest, void **srcs, unsigned int src_cnt,
34*af53e85eSChristoph Hellwig 		unsigned int bytes)
35*af53e85eSChristoph Hellwig {
36*af53e85eSChristoph Hellwig 	unsigned int off, idx;
37*af53e85eSChristoph Hellwig 	u8 *d = dest;
38*af53e85eSChristoph Hellwig 
39*af53e85eSChristoph Hellwig 	for (off = 0; off < bytes; off++) {
40*af53e85eSChristoph Hellwig 		for (idx = 0; idx < src_cnt; idx++) {
41*af53e85eSChristoph Hellwig 			u8 *src = srcs[idx];
42*af53e85eSChristoph Hellwig 
43*af53e85eSChristoph Hellwig 			d[off] ^= src[off];
44*af53e85eSChristoph Hellwig 		}
45*af53e85eSChristoph Hellwig 	}
46*af53e85eSChristoph Hellwig }
47*af53e85eSChristoph Hellwig 
48*af53e85eSChristoph Hellwig /* Generate a random length that is a multiple of 512. */
49*af53e85eSChristoph Hellwig static unsigned int random_length(unsigned int max_length)
50*af53e85eSChristoph Hellwig {
51*af53e85eSChristoph Hellwig 	return round_up((rand32() % max_length) + 1, 512);
52*af53e85eSChristoph Hellwig }
53*af53e85eSChristoph Hellwig 
54*af53e85eSChristoph Hellwig /* Generate a random alignment that is a multiple of 64. */
55*af53e85eSChristoph Hellwig static unsigned int random_alignment(unsigned int max_alignment)
56*af53e85eSChristoph Hellwig {
57*af53e85eSChristoph Hellwig 	return ((rand32() % max_alignment) + 1) & ~63;
58*af53e85eSChristoph Hellwig }
59*af53e85eSChristoph Hellwig 
60*af53e85eSChristoph Hellwig static void xor_generate_random_data(void)
61*af53e85eSChristoph Hellwig {
62*af53e85eSChristoph Hellwig 	int i;
63*af53e85eSChristoph Hellwig 
64*af53e85eSChristoph Hellwig 	prandom_bytes_state(&rng, test_dest, test_buflen);
65*af53e85eSChristoph Hellwig 	memcpy(test_ref, test_dest, test_buflen);
66*af53e85eSChristoph Hellwig 	for (i = 0; i < XOR_KUNIT_MAX_BUFFERS; i++)
67*af53e85eSChristoph Hellwig 		prandom_bytes_state(&rng, test_buffers[i], test_buflen);
68*af53e85eSChristoph Hellwig }
69*af53e85eSChristoph Hellwig 
70*af53e85eSChristoph Hellwig /* Test that xor_gen gives the same result as a reference implementation. */
71*af53e85eSChristoph Hellwig static void xor_test(struct kunit *test)
72*af53e85eSChristoph Hellwig {
73*af53e85eSChristoph Hellwig 	void *aligned_buffers[XOR_KUNIT_MAX_BUFFERS];
74*af53e85eSChristoph Hellwig 	size_t i;
75*af53e85eSChristoph Hellwig 
76*af53e85eSChristoph Hellwig 	for (i = 0; i < XOR_KUNIT_NUM_TEST_ITERS; i++) {
77*af53e85eSChristoph Hellwig 		unsigned int nr_buffers =
78*af53e85eSChristoph Hellwig 			(rand32() % XOR_KUNIT_MAX_BUFFERS) + 1;
79*af53e85eSChristoph Hellwig 		unsigned int len = random_length(XOR_KUNIT_MAX_BYTES);
80*af53e85eSChristoph Hellwig 		unsigned int max_alignment, align = 0;
81*af53e85eSChristoph Hellwig 		void *buffers;
82*af53e85eSChristoph Hellwig 
83*af53e85eSChristoph Hellwig 		if (rand32() % 8 == 0)
84*af53e85eSChristoph Hellwig 			/* Refresh the data occasionally. */
85*af53e85eSChristoph Hellwig 			xor_generate_random_data();
86*af53e85eSChristoph Hellwig 
87*af53e85eSChristoph Hellwig 		/*
88*af53e85eSChristoph Hellwig 		 * If we're not using the entire buffer size, inject randomize
89*af53e85eSChristoph Hellwig 		 * alignment into the buffer.
90*af53e85eSChristoph Hellwig 		 */
91*af53e85eSChristoph Hellwig 		max_alignment = XOR_KUNIT_MAX_BYTES - len;
92*af53e85eSChristoph Hellwig 		if (max_alignment == 0) {
93*af53e85eSChristoph Hellwig 			buffers = test_buffers;
94*af53e85eSChristoph Hellwig 		} else if (rand32() % 2 == 0) {
95*af53e85eSChristoph Hellwig 			/* Use random alignments mod 64 */
96*af53e85eSChristoph Hellwig 			int j;
97*af53e85eSChristoph Hellwig 
98*af53e85eSChristoph Hellwig 			for (j = 0; j < nr_buffers; j++)
99*af53e85eSChristoph Hellwig 				aligned_buffers[j] = test_buffers[j] +
100*af53e85eSChristoph Hellwig 					random_alignment(max_alignment);
101*af53e85eSChristoph Hellwig 			buffers = aligned_buffers;
102*af53e85eSChristoph Hellwig 			align = random_alignment(max_alignment);
103*af53e85eSChristoph Hellwig 		} else {
104*af53e85eSChristoph Hellwig 			/* Go up to the guard page, to catch buffer overreads */
105*af53e85eSChristoph Hellwig 			int j;
106*af53e85eSChristoph Hellwig 
107*af53e85eSChristoph Hellwig 			align = test_buflen - len;
108*af53e85eSChristoph Hellwig 			for (j = 0; j < nr_buffers; j++)
109*af53e85eSChristoph Hellwig 				aligned_buffers[j] = test_buffers[j] + align;
110*af53e85eSChristoph Hellwig 			buffers = aligned_buffers;
111*af53e85eSChristoph Hellwig 		}
112*af53e85eSChristoph Hellwig 
113*af53e85eSChristoph Hellwig 		/*
114*af53e85eSChristoph Hellwig 		 * Compute the XOR, and verify that it equals the XOR computed
115*af53e85eSChristoph Hellwig 		 * by a simple byte-at-a-time reference implementation.
116*af53e85eSChristoph Hellwig 		 */
117*af53e85eSChristoph Hellwig 		xor_ref(test_ref + align, buffers, nr_buffers, len);
118*af53e85eSChristoph Hellwig 		xor_gen(test_dest + align, buffers, nr_buffers, len);
119*af53e85eSChristoph Hellwig 		KUNIT_EXPECT_MEMEQ_MSG(test, test_ref + align,
120*af53e85eSChristoph Hellwig 				test_dest + align, len,
121*af53e85eSChristoph Hellwig 				"Wrong result with buffers=%u, len=%u, unaligned=%s, at_end=%s",
122*af53e85eSChristoph Hellwig 				nr_buffers, len,
123*af53e85eSChristoph Hellwig 				str_yes_no(max_alignment),
124*af53e85eSChristoph Hellwig 				str_yes_no(align + len == test_buflen));
125*af53e85eSChristoph Hellwig 	}
126*af53e85eSChristoph Hellwig }
127*af53e85eSChristoph Hellwig 
128*af53e85eSChristoph Hellwig static struct kunit_case xor_test_cases[] = {
129*af53e85eSChristoph Hellwig 	KUNIT_CASE(xor_test),
130*af53e85eSChristoph Hellwig 	{},
131*af53e85eSChristoph Hellwig };
132*af53e85eSChristoph Hellwig 
133*af53e85eSChristoph Hellwig static int xor_suite_init(struct kunit_suite *suite)
134*af53e85eSChristoph Hellwig {
135*af53e85eSChristoph Hellwig 	int i;
136*af53e85eSChristoph Hellwig 
137*af53e85eSChristoph Hellwig 	/*
138*af53e85eSChristoph Hellwig 	 * Allocate the test buffer using vmalloc() with a page-aligned length
139*af53e85eSChristoph Hellwig 	 * so that it is immediately followed by a guard page.  This allows
140*af53e85eSChristoph Hellwig 	 * buffer overreads to be detected, even in assembly code.
141*af53e85eSChristoph Hellwig 	 */
142*af53e85eSChristoph Hellwig 	test_buflen = round_up(XOR_KUNIT_MAX_BYTES, PAGE_SIZE);
143*af53e85eSChristoph Hellwig 	test_ref = vmalloc(test_buflen);
144*af53e85eSChristoph Hellwig 	if (!test_ref)
145*af53e85eSChristoph Hellwig 		return -ENOMEM;
146*af53e85eSChristoph Hellwig 	test_dest = vmalloc(test_buflen);
147*af53e85eSChristoph Hellwig 	if (!test_dest)
148*af53e85eSChristoph Hellwig 		goto out_free_ref;
149*af53e85eSChristoph Hellwig 	for (i = 0; i < XOR_KUNIT_MAX_BUFFERS; i++) {
150*af53e85eSChristoph Hellwig 		test_buffers[i] = vmalloc(test_buflen);
151*af53e85eSChristoph Hellwig 		if (!test_buffers[i])
152*af53e85eSChristoph Hellwig 			goto out_free_buffers;
153*af53e85eSChristoph Hellwig 	}
154*af53e85eSChristoph Hellwig 
155*af53e85eSChristoph Hellwig 	prandom_seed_state(&rng, XOR_KUNIT_SEED);
156*af53e85eSChristoph Hellwig 	xor_generate_random_data();
157*af53e85eSChristoph Hellwig 	return 0;
158*af53e85eSChristoph Hellwig 
159*af53e85eSChristoph Hellwig out_free_buffers:
160*af53e85eSChristoph Hellwig 	while (--i >= 0)
161*af53e85eSChristoph Hellwig 		vfree(test_buffers[i]);
162*af53e85eSChristoph Hellwig 	vfree(test_dest);
163*af53e85eSChristoph Hellwig out_free_ref:
164*af53e85eSChristoph Hellwig 	vfree(test_ref);
165*af53e85eSChristoph Hellwig 	return -ENOMEM;
166*af53e85eSChristoph Hellwig }
167*af53e85eSChristoph Hellwig 
168*af53e85eSChristoph Hellwig static void xor_suite_exit(struct kunit_suite *suite)
169*af53e85eSChristoph Hellwig {
170*af53e85eSChristoph Hellwig 	int i;
171*af53e85eSChristoph Hellwig 
172*af53e85eSChristoph Hellwig 	vfree(test_ref);
173*af53e85eSChristoph Hellwig 	vfree(test_dest);
174*af53e85eSChristoph Hellwig 	for (i = 0; i < XOR_KUNIT_MAX_BUFFERS; i++)
175*af53e85eSChristoph Hellwig 		vfree(test_buffers[i]);
176*af53e85eSChristoph Hellwig }
177*af53e85eSChristoph Hellwig 
178*af53e85eSChristoph Hellwig static struct kunit_suite xor_test_suite = {
179*af53e85eSChristoph Hellwig 	.name		= "xor",
180*af53e85eSChristoph Hellwig 	.test_cases	= xor_test_cases,
181*af53e85eSChristoph Hellwig 	.suite_init	= xor_suite_init,
182*af53e85eSChristoph Hellwig 	.suite_exit	= xor_suite_exit,
183*af53e85eSChristoph Hellwig };
184*af53e85eSChristoph Hellwig kunit_test_suite(xor_test_suite);
185*af53e85eSChristoph Hellwig 
186*af53e85eSChristoph Hellwig MODULE_DESCRIPTION("Unit test for the XOR library functions");
187*af53e85eSChristoph Hellwig MODULE_LICENSE("GPL");
188