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