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 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
|
// SPDX-License-Identifier: GPL-2.0-only
#include <kunit/test.h>
#include <linux/crash_core.h> // For struct crash_mem and struct range if defined there
// Helper to create and initialize crash_mem
static struct crash_mem *create_crash_mem(struct kunit *test, unsigned int max_ranges,
unsigned int nr_initial_ranges,
const struct range *initial_ranges)
{
struct crash_mem *mem;
size_t alloc_size;
// Check if max_ranges can even hold initial_ranges
if (max_ranges < nr_initial_ranges) {
kunit_err(test, "max_ranges (%u) < nr_initial_ranges (%u)\n",
max_ranges, nr_initial_ranges);
return NULL;
}
alloc_size = sizeof(struct crash_mem) + (size_t)max_ranges * sizeof(struct range);
mem = kunit_kzalloc(test, alloc_size, GFP_KERNEL);
if (!mem) {
kunit_err(test, "Failed to allocate crash_mem\n");
return NULL;
}
mem->max_nr_ranges = max_ranges;
mem->nr_ranges = nr_initial_ranges;
if (initial_ranges && nr_initial_ranges > 0) {
memcpy(mem->ranges, initial_ranges,
nr_initial_ranges * sizeof(struct range));
}
return mem;
}
// Helper to compare ranges for assertions
static void assert_ranges_equal(struct kunit *test,
const struct range *actual_ranges,
unsigned int actual_nr_ranges,
const struct range *expected_ranges,
unsigned int expected_nr_ranges,
const char *case_name)
{
unsigned int i;
KUNIT_ASSERT_EQ_MSG(test, expected_nr_ranges, actual_nr_ranges,
"%s: Number of ranges mismatch.", case_name);
for (i = 0; i < expected_nr_ranges; i++) {
KUNIT_ASSERT_EQ_MSG(test, expected_ranges[i].start, actual_ranges[i].start,
"%s: Range %u start mismatch.", case_name, i);
KUNIT_ASSERT_EQ_MSG(test, expected_ranges[i].end, actual_ranges[i].end,
"%s: Range %u end mismatch.", case_name, i);
}
}
// Structure for test parameters
struct exclude_test_param {
const char *description;
unsigned long long exclude_start;
unsigned long long exclude_end;
unsigned int initial_max_ranges;
const struct range *initial_ranges;
unsigned int initial_nr_ranges;
const struct range *expected_ranges;
unsigned int expected_nr_ranges;
int expected_ret;
};
static void run_exclude_test_case(struct kunit *test, const struct exclude_test_param *params)
{
struct crash_mem *mem;
int ret;
kunit_info(test, "%s", params->description);
mem = create_crash_mem(test, params->initial_max_ranges,
params->initial_nr_ranges, params->initial_ranges);
if (!mem)
return; // Error already logged by create_crash_mem or kunit_kzalloc
ret = crash_exclude_mem_range(mem, params->exclude_start, params->exclude_end);
KUNIT_ASSERT_EQ_MSG(test, params->expected_ret, ret,
"%s: Return value mismatch.", params->description);
if (params->expected_ret == 0) {
assert_ranges_equal(test, mem->ranges, mem->nr_ranges,
params->expected_ranges, params->expected_nr_ranges,
params->description);
} else {
// If an error is expected, nr_ranges might still be relevant to check
// depending on the exact point of failure. For ENOMEM on split,
// nr_ranges shouldn't have changed.
KUNIT_ASSERT_EQ_MSG(test, params->initial_nr_ranges,
mem->nr_ranges,
"%s: Number of ranges mismatch on error.",
params->description);
}
}
/*
* Test Strategy 1: One to-be-excluded range A and one existing range B.
*
* Exhaust all possibilities of the position of A regarding B.
*/
static const struct range single_range_b = { .start = 100, .end = 199 };
static const struct exclude_test_param exclude_single_range_test_data[] = {
{
.description = "1.1: A is left of B, no overlap",
.exclude_start = 10, .exclude_end = 50,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
.expected_ret = 0,
},
{
.description = "1.2: A's right boundary touches B's left boundary",
.exclude_start = 10, .exclude_end = 99,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
.expected_ret = 0,
},
{
.description = "1.3: A overlaps B's left part",
.exclude_start = 50, .exclude_end = 149,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = (const struct range[]){{ .start = 150, .end = 199 }},
.expected_nr_ranges = 1,
.expected_ret = 0,
},
{
.description = "1.4: A is completely inside B",
.exclude_start = 120, .exclude_end = 179,
.initial_max_ranges = 2, // Needs space for split
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = (const struct range[]){
{ .start = 100, .end = 119 },
{ .start = 180, .end = 199 }
},
.expected_nr_ranges = 2,
.expected_ret = 0,
},
{
.description = "1.5: A overlaps B's right part",
.exclude_start = 150, .exclude_end = 249,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = (const struct range[]){{ .start = 100, .end = 149 }},
.expected_nr_ranges = 1,
.expected_ret = 0,
},
{
.description = "1.6: A's left boundary touches B's right boundary",
.exclude_start = 200, .exclude_end = 250,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
.expected_ret = 0,
},
{
.description = "1.7: A is right of B, no overlap",
.exclude_start = 250, .exclude_end = 300,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
.expected_ret = 0,
},
{
.description = "1.8: A completely covers B and extends beyond",
.exclude_start = 50, .exclude_end = 250,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = NULL, .expected_nr_ranges = 0,
.expected_ret = 0,
},
{
.description = "1.9: A covers B and extends to the left",
.exclude_start = 50, .exclude_end = 199, // A ends exactly where B ends
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = NULL, .expected_nr_ranges = 0,
.expected_ret = 0,
},
{
.description = "1.10: A covers B and extends to the right",
.exclude_start = 100, .exclude_end = 250, // A starts exactly where B starts
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = NULL, .expected_nr_ranges = 0,
.expected_ret = 0,
},
{
.description = "1.11: A is identical to B",
.exclude_start = 100, .exclude_end = 199,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = NULL, .expected_nr_ranges = 0,
.expected_ret = 0,
},
{
.description = "1.12: A is a point, left of B, no overlap",
.exclude_start = 10, .exclude_end = 10,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
.expected_ret = 0,
},
{
.description = "1.13: A is a point, at start of B",
.exclude_start = 100, .exclude_end = 100,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = (const struct range[]){{ .start = 101, .end = 199 }},
.expected_nr_ranges = 1,
.expected_ret = 0,
},
{
.description = "1.14: A is a point, in middle of B (causes split)",
.exclude_start = 150, .exclude_end = 150,
.initial_max_ranges = 2, // Needs space for split
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = (const struct range[]){
{ .start = 100, .end = 149 },
{ .start = 151, .end = 199 }
},
.expected_nr_ranges = 2,
.expected_ret = 0,
},
{
.description = "1.15: A is a point, at end of B",
.exclude_start = 199, .exclude_end = 199,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = (const struct range[]){{ .start = 100, .end = 198 }},
.expected_nr_ranges = 1,
.expected_ret = 0,
},
{
.description = "1.16: A is a point, right of B, no overlap",
.exclude_start = 250, .exclude_end = 250,
.initial_max_ranges = 1,
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
.expected_ret = 0,
},
// ENOMEM case for single range split
{
.description = "1.17: A completely inside B (split), no space (ENOMEM)",
.exclude_start = 120, .exclude_end = 179,
.initial_max_ranges = 1, // Not enough for split
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
.expected_ranges = NULL, // Not checked on error by assert_ranges_equal for content
.expected_nr_ranges = 1, // Should remain unchanged
.expected_ret = -ENOMEM,
},
};
static void exclude_single_range_test(struct kunit *test)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(exclude_single_range_test_data); i++) {
kunit_log(KERN_INFO, test, "Running: %s", exclude_single_range_test_data[i].description);
run_exclude_test_case(test, &exclude_single_range_test_data[i]);
// KUnit will stop on first KUNIT_ASSERT failure within run_exclude_test_case
}
}
/*
* Test Strategy 2: Regression test.
*/
static const struct exclude_test_param exclude_range_regression_test_data[] = {
// Test data from commit a2e9a95d2190
{
.description = "2.1: exclude low 1M",
.exclude_start = 0, .exclude_end = (1 << 20) - 1,
.initial_max_ranges = 3,
.initial_ranges = (const struct range[]){
{ .start = 0, .end = 0x3efff },
{ .start = 0x3f000, .end = 0x3ffff },
{ .start = 0x40000, .end = 0x9ffff }
},
.initial_nr_ranges = 3,
.expected_nr_ranges = 0,
.expected_ret = 0,
},
// Test data from https://lore.kernel.org/all/ZXrY7QbXAlxydsSC@MiWiFi-R3L-srv/T/#u
{
.description = "2.2: when range out of bound",
.exclude_start = 100, .exclude_end = 200,
.initial_max_ranges = 3,
.initial_ranges = (const struct range[]){
{ .start = 1, .end = 299 },
{ .start = 401, .end = 1000 },
{ .start = 1001, .end = 2000 }
},
.initial_nr_ranges = 3,
.expected_ranges = NULL, // Not checked on error by assert_ranges_equal for content
.expected_nr_ranges = 3, // Should remain unchanged
.expected_ret = -ENOMEM
},
};
static void exclude_range_regression_test(struct kunit *test)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(exclude_range_regression_test_data); i++) {
kunit_log(KERN_INFO, test, "Running: %s", exclude_range_regression_test_data[i].description);
run_exclude_test_case(test, &exclude_range_regression_test_data[i]);
// KUnit will stop on first KUNIT_ASSERT failure within run_exclude_test_case
}
}
/*
* KUnit Test Suite
*/
static struct kunit_case crash_exclude_mem_range_test_cases[] = {
KUNIT_CASE(exclude_single_range_test),
KUNIT_CASE(exclude_range_regression_test),
{}
};
static struct kunit_suite crash_exclude_mem_range_suite = {
.name = "crash_exclude_mem_range_tests",
.test_cases = crash_exclude_mem_range_test_cases,
// .init and .exit can be NULL if not needed globally for the suite
};
kunit_test_suite(crash_exclude_mem_range_suite);
MODULE_DESCRIPTION("crash dump KUnit test suite");
MODULE_LICENSE("GPL");
|