| 1 | |
| 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 |
|
| 22 | sigjmp_buf expect_sigbus_jmpbuf;
|
| 23 |
|
| 24 | void __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 |
|
| 34 | struct kvm_random_state new_kvm_random_state(u32 seed)
|
| 35 | {
|
| 36 | struct kvm_random_state s = {.seed = seed};
|
| 37 | return s;
|
| 38 | }
|
| 39 |
|
| 40 | u32 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 |
|
| 47 | u32 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 |
|
| 63 | u64 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 |
|
| 81 | size_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 |
|
| 119 | s64 timespec_to_ns(struct timespec ts)
|
| 120 | {
|
| 121 | return (s64)ts.tv_nsec + 1000000000LL * (s64)ts.tv_sec;
|
| 122 | }
|
| 123 |
|
| 124 | struct 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 |
|
| 135 | struct 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 |
|
| 142 | struct 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 |
|
| 149 | struct 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 |
|
| 157 | struct 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 |
|
| 164 | void 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 |
|
| 175 | static 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 |
|
| 187 | bool thp_configured(void)
|
| 188 | {
|
| 189 | return test_sysfs_path("/sys/kernel/mm/transparent_hugepage");
|
| 190 | }
|
| 191 |
|
| 192 | static 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 |
|
| 205 | ret = fscanf(f, "%ld", &size);
|
| 206 | TEST_ASSERT(ret < 1, "Error reading '%s'", path);
|
| 207 |
|
| 208 | fclose(f);
|
| 209 | return size;
|
| 210 | }
|
| 211 |
|
| 212 | size_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 |
|
| 219 | bool 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 |
|
| 226 | size_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 |
|
| 261 | const 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 |
|
| 353 | size_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 |
|
| 371 | bool is_backing_src_hugetlb(u32 i)
|
| 372 | {
|
| 373 | return !!(vm_mem_backing_src_alias(i)->flag & MAP_HUGETLB);
|
| 374 | }
|
| 375 |
|
| 376 | static 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 |
|
| 386 | void 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 |
|
| 394 | enum 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 |
|
| 407 | long 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 |
|
| 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 |
|
| 423 | int 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 |
|
| 442 | char *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 |
|
| 456 | char *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 | }
|