projects/linux·blob master · refs

477 lines · 11.0 KiB

1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * tools/testing/selftests/kvm/lib/test_util.c
4 *
5 * Copyright (C) 2020, Google LLC.
6 */
7#include <stdio.h>
8#include <stdarg.h>
9#include <assert.h>
10#include <ctype.h>
11#include <limits.h>
12#include <stdlib.h>
13#include <time.h>
14#include <sys/stat.h>
15#include <sys/syscall.h>
16#include <linux/mman.h>
17#include "linux/kernel.h"
18
19#include "test_util.h"
20#include "kvm_syscalls.h"
21
22sigjmp_buf expect_sigbus_jmpbuf;
23
24void __attribute__((used)) expect_sigbus_handler(int signum)
25{
26 siglongjmp(expect_sigbus_jmpbuf, 1);
27}
28
29/*
30 * Random number generator that is usable from guest code. This is the
31 * Park-Miller LCG using standard constants.
32 */
33
34struct kvm_random_state new_kvm_random_state(u32 seed)
35{
36 struct kvm_random_state s = {.seed = seed};
37 return s;
38}
39
40u32 kvm_random_u32(struct kvm_random_state *state)
41{
42 state->seed = (u64)state->seed * 48271 % ((u32)(1 << 31) - 1);
43 return state->seed;
44}
45
46/* Returns a random u32 in the inclusive range [min, max] */
47u32 kvm_random_u32_in_range(struct kvm_random_state *state, u32 min, u32 max)
48{
49 u32 value, range;
50
51 TEST_ASSERT(min <= max, "PEBKAC, min = 0x%x, max = 0x%x", min, max);
52
53 value = kvm_random_u32(state);
54
55 range = max - min;
56 if (range == UINT_MAX)
57 return value;
58
59 return min + (value % (range + 1));
60}
61
62/* Returns a random u64 in the inclusive range [min, max] */
63u64 kvm_random_u64_in_range(struct kvm_random_state *state, u64 min, u64 max)
64{
65 u64 value, range;
66
67 TEST_ASSERT(min <= max, "PEBKAC, min = 0x%lx, max = 0x%lx", min, max);
68
69 value = kvm_random_u64(state);
70
71 range = max - min;
72 if (range == ULLONG_MAX)
73 return value;
74
75 return min + (value % (range + 1));
76}
77
78/*
79 * Parses "[0-9]+[kmgt]?".
80 */
81size_t parse_size(const char *size)
82{
83 size_t base;
84 char *scale;
85 int shift = 0;
86
87 TEST_ASSERT(size && isdigit(size[0]), "Need at least one digit in '%s'", size);
88
89 base = strtoull(size, &scale, 0);
90
91 TEST_ASSERT(base != ULLONG_MAX, "Overflow parsing size!");
92
93 switch (tolower(*scale)) {
94 case 't':
95 shift = 40;
96 break;
97 case 'g':
98 shift = 30;
99 break;
100 case 'm':
101 shift = 20;
102 break;
103 case 'k':
104 shift = 10;
105 break;
106 case 'b':
107 case '\0':
108 shift = 0;
109 break;
110 default:
111 TEST_ASSERT(false, "Unknown size letter %c", *scale);
112 }
113
114 TEST_ASSERT((base << shift) >> shift == base, "Overflow scaling size!");
115
116 return base << shift;
117}
118
119s64 timespec_to_ns(struct timespec ts)
120{
121 return (s64)ts.tv_nsec + 1000000000LL * (s64)ts.tv_sec;
122}
123
124struct timespec timespec_add_ns(struct timespec ts, s64 ns)
125{
126 struct timespec res;
127
128 res.tv_nsec = ts.tv_nsec + ns;
129 res.tv_sec = ts.tv_sec + res.tv_nsec / 1000000000LL;
130 res.tv_nsec %= 1000000000LL;
131
132 return res;
133}
134
135struct timespec timespec_add(struct timespec ts1, struct timespec ts2)
136{
137 s64 ns1 = timespec_to_ns(ts1);
138 s64 ns2 = timespec_to_ns(ts2);
139 return timespec_add_ns((struct timespec){0}, ns1 + ns2);
140}
141
142struct timespec timespec_sub(struct timespec ts1, struct timespec ts2)
143{
144 s64 ns1 = timespec_to_ns(ts1);
145 s64 ns2 = timespec_to_ns(ts2);
146 return timespec_add_ns((struct timespec){0}, ns1 - ns2);
147}
148
149struct timespec timespec_elapsed(struct timespec start)
150{
151 struct timespec end;
152
153 clock_gettime(CLOCK_MONOTONIC, &end);
154 return timespec_sub(end, start);
155}
156
157struct timespec timespec_div(struct timespec ts, int divisor)
158{
159 s64 ns = timespec_to_ns(ts) / divisor;
160
161 return timespec_add_ns((struct timespec){0}, ns);
162}
163
164void print_skip(const char *fmt, ...)
165{
166 va_list ap;
167
168 assert(fmt);
169 va_start(ap, fmt);
170 vprintf(fmt, ap);
171 va_end(ap);
172 puts(", skipping test");
173}
174
175static bool test_sysfs_path(const char *path)
176{
177 struct stat statbuf;
178 int ret;
179
180 ret = stat(path, &statbuf);
181 TEST_ASSERT(ret == 0 || (ret == -1 && errno == ENOENT),
182 "Error in stat()ing '%s'", path);
183
184 return ret == 0;
185}
186
187bool thp_configured(void)
188{
189 return test_sysfs_path("/sys/kernel/mm/transparent_hugepage");
190}
191
192static size_t get_sysfs_val(const char *path)
193{
194 size_t size;
195 FILE *f;
196 int ret;
197
198 f = fopen(path, "r");
199 TEST_ASSERT(f, "Error opening '%s'", path);
200
201 ret = fscanf(f, "%ld", &size);
202 TEST_ASSERT(ret > 0, "Error reading '%s'", path);
203
204 /* Re-scan the input stream to verify the entire file was read. */
205 ret = fscanf(f, "%ld", &size);
206 TEST_ASSERT(ret < 1, "Error reading '%s'", path);
207
208 fclose(f);
209 return size;
210}
211
212size_t get_trans_hugepagesz(void)
213{
214 TEST_ASSERT(thp_configured(), "THP is not configured in host kernel");
215
216 return get_sysfs_val("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size");
217}
218
219bool is_numa_balancing_enabled(void)
220{
221 if (!test_sysfs_path("/proc/sys/kernel/numa_balancing"))
222 return false;
223 return get_sysfs_val("/proc/sys/kernel/numa_balancing") == 1;
224}
225
226size_t get_def_hugetlb_pagesz(void)
227{
228 char buf[64];
229 const char *hugepagesize = "Hugepagesize:";
230 const char *hugepages_total = "HugePages_Total:";
231 FILE *f;
232
233 f = fopen("/proc/meminfo", "r");
234 TEST_ASSERT(f != NULL, "Error in opening /proc/meminfo");
235
236 while (fgets(buf, sizeof(buf), f) != NULL) {
237 if (strstr(buf, hugepages_total) == buf) {
238 unsigned long long total = strtoull(buf + strlen(hugepages_total), NULL, 10);
239 if (!total) {
240 fprintf(stderr, "HUGETLB is not enabled in /proc/sys/vm/nr_hugepages\n");
241 exit(KSFT_SKIP);
242 }
243 }
244 if (strstr(buf, hugepagesize) == buf) {
245 fclose(f);
246 return strtoull(buf + strlen(hugepagesize), NULL, 10) << 10;
247 }
248 }
249
250 if (feof(f)) {
251 fprintf(stderr, "HUGETLB is not configured in host kernel");
252 exit(KSFT_SKIP);
253 }
254
255 TEST_FAIL("Error in reading /proc/meminfo");
256}
257
258#define ANON_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS)
259#define ANON_HUGE_FLAGS (ANON_FLAGS | MAP_HUGETLB)
260
261const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(u32 i)
262{
263 static const struct vm_mem_backing_src_alias aliases[] = {
264 [VM_MEM_SRC_ANONYMOUS] = {
265 .name = "anonymous",
266 .flag = ANON_FLAGS,
267 },
268 [VM_MEM_SRC_ANONYMOUS_THP] = {
269 .name = "anonymous_thp",
270 .flag = ANON_FLAGS,
271 },
272 [VM_MEM_SRC_ANONYMOUS_HUGETLB] = {
273 .name = "anonymous_hugetlb",
274 .flag = ANON_HUGE_FLAGS,
275 },
276 [VM_MEM_SRC_ANONYMOUS_HUGETLB_16KB] = {
277 .name = "anonymous_hugetlb_16kb",
278 .flag = ANON_HUGE_FLAGS | MAP_HUGE_16KB,
279 },
280 [VM_MEM_SRC_ANONYMOUS_HUGETLB_64KB] = {
281 .name = "anonymous_hugetlb_64kb",
282 .flag = ANON_HUGE_FLAGS | MAP_HUGE_64KB,
283 },
284 [VM_MEM_SRC_ANONYMOUS_HUGETLB_512KB] = {
285 .name = "anonymous_hugetlb_512kb",
286 .flag = ANON_HUGE_FLAGS | MAP_HUGE_512KB,
287 },
288 [VM_MEM_SRC_ANONYMOUS_HUGETLB_1MB] = {
289 .name = "anonymous_hugetlb_1mb",
290 .flag = ANON_HUGE_FLAGS | MAP_HUGE_1MB,
291 },
292 [VM_MEM_SRC_ANONYMOUS_HUGETLB_2MB] = {
293 .name = "anonymous_hugetlb_2mb",
294 .flag = ANON_HUGE_FLAGS | MAP_HUGE_2MB,
295 },
296 [VM_MEM_SRC_ANONYMOUS_HUGETLB_8MB] = {
297 .name = "anonymous_hugetlb_8mb",
298 .flag = ANON_HUGE_FLAGS | MAP_HUGE_8MB,
299 },
300 [VM_MEM_SRC_ANONYMOUS_HUGETLB_16MB] = {
301 .name = "anonymous_hugetlb_16mb",
302 .flag = ANON_HUGE_FLAGS | MAP_HUGE_16MB,
303 },
304 [VM_MEM_SRC_ANONYMOUS_HUGETLB_32MB] = {
305 .name = "anonymous_hugetlb_32mb",
306 .flag = ANON_HUGE_FLAGS | MAP_HUGE_32MB,
307 },
308 [VM_MEM_SRC_ANONYMOUS_HUGETLB_256MB] = {
309 .name = "anonymous_hugetlb_256mb",
310 .flag = ANON_HUGE_FLAGS | MAP_HUGE_256MB,
311 },
312 [VM_MEM_SRC_ANONYMOUS_HUGETLB_512MB] = {
313 .name = "anonymous_hugetlb_512mb",
314 .flag = ANON_HUGE_FLAGS | MAP_HUGE_512MB,
315 },
316 [VM_MEM_SRC_ANONYMOUS_HUGETLB_1GB] = {
317 .name = "anonymous_hugetlb_1gb",
318 .flag = ANON_HUGE_FLAGS | MAP_HUGE_1GB,
319 },
320 [VM_MEM_SRC_ANONYMOUS_HUGETLB_2GB] = {
321 .name = "anonymous_hugetlb_2gb",
322 .flag = ANON_HUGE_FLAGS | MAP_HUGE_2GB,
323 },
324 [VM_MEM_SRC_ANONYMOUS_HUGETLB_16GB] = {
325 .name = "anonymous_hugetlb_16gb",
326 .flag = ANON_HUGE_FLAGS | MAP_HUGE_16GB,
327 },
328 [VM_MEM_SRC_SHMEM] = {
329 .name = "shmem",
330 .flag = MAP_SHARED,
331 },
332 [VM_MEM_SRC_SHARED_HUGETLB] = {
333 .name = "shared_hugetlb",
334 /*
335 * No MAP_HUGETLB, we use MFD_HUGETLB instead. Since
336 * we're using "file backed" memory, we need to specify
337 * this when the FD is created, not when the area is
338 * mapped.
339 */
340 .flag = MAP_SHARED,
341 },
342 };
343 _Static_assert(ARRAY_SIZE(aliases) == NUM_SRC_TYPES,
344 "Missing new backing src types?");
345
346 TEST_ASSERT(i < NUM_SRC_TYPES, "Backing src type ID %d too big", i);
347
348 return &aliases[i];
349}
350
351#define MAP_HUGE_PAGE_SIZE(x) (1ULL << ((x >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK))
352
353size_t get_backing_src_pagesz(u32 i)
354{
355 u32 flag = vm_mem_backing_src_alias(i)->flag;
356
357 switch (i) {
358 case VM_MEM_SRC_ANONYMOUS:
359 case VM_MEM_SRC_SHMEM:
360 return getpagesize();
361 case VM_MEM_SRC_ANONYMOUS_THP:
362 return get_trans_hugepagesz();
363 case VM_MEM_SRC_ANONYMOUS_HUGETLB:
364 case VM_MEM_SRC_SHARED_HUGETLB:
365 return get_def_hugetlb_pagesz();
366 default:
367 return MAP_HUGE_PAGE_SIZE(flag);
368 }
369}
370
371bool is_backing_src_hugetlb(u32 i)
372{
373 return !!(vm_mem_backing_src_alias(i)->flag & MAP_HUGETLB);
374}
375
376static void print_available_backing_src_types(const char *prefix)
377{
378 int i;
379
380 printf("%sAvailable backing src types:\n", prefix);
381
382 for (i = 0; i < NUM_SRC_TYPES; i++)
383 printf("%s %s\n", prefix, vm_mem_backing_src_alias(i)->name);
384}
385
386void backing_src_help(const char *flag)
387{
388 printf(" %s: specify the type of memory that should be used to\n"
389 " back the guest data region. (default: %s)\n",
390 flag, vm_mem_backing_src_alias(DEFAULT_VM_MEM_SRC)->name);
391 print_available_backing_src_types(" ");
392}
393
394enum vm_mem_backing_src_type parse_backing_src_type(const char *type_name)
395{
396 int i;
397
398 for (i = 0; i < NUM_SRC_TYPES; i++)
399 if (!strcmp(type_name, vm_mem_backing_src_alias(i)->name))
400 return i;
401
402 print_available_backing_src_types("");
403 TEST_FAIL("Unknown backing src type: %s", type_name);
404 return -1;
405}
406
407long get_run_delay(void)
408{
409 char path[64];
410 long val[2];
411 FILE *fp;
412
413 sprintf(path, "/proc/%ld/schedstat", (long)kvm_gettid());
414 fp = fopen(path, "r");
415 /* Return MIN_RUN_DELAY_NS upon failure just to be safe */
416 if (fscanf(fp, "%ld %ld ", &val[0], &val[1]) < 2)
417 val[1] = MIN_RUN_DELAY_NS;
418 fclose(fp);
419
420 return val[1];
421}
422
423int atoi_paranoid(const char *num_str)
424{
425 char *end_ptr;
426 long num;
427
428 errno = 0;
429 num = strtol(num_str, &end_ptr, 0);
430 TEST_ASSERT(!errno, "strtol(\"%s\") failed", num_str);
431 TEST_ASSERT(num_str != end_ptr,
432 "strtol(\"%s\") didn't find a valid integer.", num_str);
433 TEST_ASSERT(*end_ptr == '\0',
434 "strtol(\"%s\") failed to parse trailing characters \"%s\".",
435 num_str, end_ptr);
436 TEST_ASSERT(num >= INT_MIN && num <= INT_MAX,
437 "%ld not in range of [%d, %d]", num, INT_MIN, INT_MAX);
438
439 return num;
440}
441
442char *strdup_printf(const char *fmt, ...)
443{
444 va_list ap;
445 char *str;
446
447 va_start(ap, fmt);
448 TEST_ASSERT(vasprintf(&str, fmt, ap) >= 0, "vasprintf() failed");
449 va_end(ap);
450
451 return str;
452}
453
454#define CLOCKSOURCE_PATH "/sys/devices/system/clocksource/clocksource0/current_clocksource"
455
456char *sys_get_cur_clocksource(void)
457{
458 char *clk_name;
459 struct stat st;
460 FILE *fp;
461
462 fp = fopen(CLOCKSOURCE_PATH, "r");
463 TEST_ASSERT(fp, "failed to open clocksource file, errno: %d", errno);
464
465 TEST_ASSERT(!fstat(fileno(fp), &st), "failed to stat clocksource file, errno: %d",
466 errno);
467
468 clk_name = malloc(st.st_size);
469 TEST_ASSERT(clk_name, "failed to allocate buffer to read file");
470
471 TEST_ASSERT(fgets(clk_name, st.st_size, fp), "failed to read clocksource file: %d",
472 ferror(fp));
473
474 fclose(fp);
475
476 return clk_name;
477}