linux/tools/testing/selftests/kvm/lib/test_util.c
<<
>>
Prefs
   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
   8#include <assert.h>
   9#include <ctype.h>
  10#include <limits.h>
  11#include <stdlib.h>
  12#include <time.h>
  13#include <sys/stat.h>
  14#include <sys/syscall.h>
  15#include <linux/mman.h>
  16#include "linux/kernel.h"
  17
  18#include "test_util.h"
  19
  20/*
  21 * Parses "[0-9]+[kmgt]?".
  22 */
  23size_t parse_size(const char *size)
  24{
  25        size_t base;
  26        char *scale;
  27        int shift = 0;
  28
  29        TEST_ASSERT(size && isdigit(size[0]), "Need at least one digit in '%s'", size);
  30
  31        base = strtoull(size, &scale, 0);
  32
  33        TEST_ASSERT(base != ULLONG_MAX, "Overflow parsing size!");
  34
  35        switch (tolower(*scale)) {
  36        case 't':
  37                shift = 40;
  38                break;
  39        case 'g':
  40                shift = 30;
  41                break;
  42        case 'm':
  43                shift = 20;
  44                break;
  45        case 'k':
  46                shift = 10;
  47                break;
  48        case 'b':
  49        case '\0':
  50                shift = 0;
  51                break;
  52        default:
  53                TEST_ASSERT(false, "Unknown size letter %c", *scale);
  54        }
  55
  56        TEST_ASSERT((base << shift) >> shift == base, "Overflow scaling size!");
  57
  58        return base << shift;
  59}
  60
  61int64_t timespec_to_ns(struct timespec ts)
  62{
  63        return (int64_t)ts.tv_nsec + 1000000000LL * (int64_t)ts.tv_sec;
  64}
  65
  66struct timespec timespec_add_ns(struct timespec ts, int64_t ns)
  67{
  68        struct timespec res;
  69
  70        res.tv_nsec = ts.tv_nsec + ns;
  71        res.tv_sec = ts.tv_sec + res.tv_nsec / 1000000000LL;
  72        res.tv_nsec %= 1000000000LL;
  73
  74        return res;
  75}
  76
  77struct timespec timespec_add(struct timespec ts1, struct timespec ts2)
  78{
  79        int64_t ns1 = timespec_to_ns(ts1);
  80        int64_t ns2 = timespec_to_ns(ts2);
  81        return timespec_add_ns((struct timespec){0}, ns1 + ns2);
  82}
  83
  84struct timespec timespec_sub(struct timespec ts1, struct timespec ts2)
  85{
  86        int64_t ns1 = timespec_to_ns(ts1);
  87        int64_t ns2 = timespec_to_ns(ts2);
  88        return timespec_add_ns((struct timespec){0}, ns1 - ns2);
  89}
  90
  91struct timespec timespec_elapsed(struct timespec start)
  92{
  93        struct timespec end;
  94
  95        clock_gettime(CLOCK_MONOTONIC, &end);
  96        return timespec_sub(end, start);
  97}
  98
  99struct timespec timespec_div(struct timespec ts, int divisor)
 100{
 101        int64_t ns = timespec_to_ns(ts) / divisor;
 102
 103        return timespec_add_ns((struct timespec){0}, ns);
 104}
 105
 106void print_skip(const char *fmt, ...)
 107{
 108        va_list ap;
 109
 110        assert(fmt);
 111        va_start(ap, fmt);
 112        vprintf(fmt, ap);
 113        va_end(ap);
 114        puts(", skipping test");
 115}
 116
 117bool thp_configured(void)
 118{
 119        int ret;
 120        struct stat statbuf;
 121
 122        ret = stat("/sys/kernel/mm/transparent_hugepage", &statbuf);
 123        TEST_ASSERT(ret == 0 || (ret == -1 && errno == ENOENT),
 124                    "Error in stating /sys/kernel/mm/transparent_hugepage");
 125
 126        return ret == 0;
 127}
 128
 129size_t get_trans_hugepagesz(void)
 130{
 131        size_t size;
 132        FILE *f;
 133        int ret;
 134
 135        TEST_ASSERT(thp_configured(), "THP is not configured in host kernel");
 136
 137        f = fopen("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", "r");
 138        TEST_ASSERT(f != NULL, "Error in opening transparent_hugepage/hpage_pmd_size");
 139
 140        ret = fscanf(f, "%ld", &size);
 141        ret = fscanf(f, "%ld", &size);
 142        TEST_ASSERT(ret < 1, "Error reading transparent_hugepage/hpage_pmd_size");
 143        fclose(f);
 144
 145        return size;
 146}
 147
 148size_t get_def_hugetlb_pagesz(void)
 149{
 150        char buf[64];
 151        const char *tag = "Hugepagesize:";
 152        FILE *f;
 153
 154        f = fopen("/proc/meminfo", "r");
 155        TEST_ASSERT(f != NULL, "Error in opening /proc/meminfo");
 156
 157        while (fgets(buf, sizeof(buf), f) != NULL) {
 158                if (strstr(buf, tag) == buf) {
 159                        fclose(f);
 160                        return strtoull(buf + strlen(tag), NULL, 10) << 10;
 161                }
 162        }
 163
 164        if (feof(f))
 165                TEST_FAIL("HUGETLB is not configured in host kernel");
 166        else
 167                TEST_FAIL("Error in reading /proc/meminfo");
 168
 169        fclose(f);
 170        return 0;
 171}
 172
 173#define ANON_FLAGS      (MAP_PRIVATE | MAP_ANONYMOUS)
 174#define ANON_HUGE_FLAGS (ANON_FLAGS | MAP_HUGETLB)
 175
 176const struct vm_mem_backing_src_alias *vm_mem_backing_src_alias(uint32_t i)
 177{
 178        static const struct vm_mem_backing_src_alias aliases[] = {
 179                [VM_MEM_SRC_ANONYMOUS] = {
 180                        .name = "anonymous",
 181                        .flag = ANON_FLAGS,
 182                },
 183                [VM_MEM_SRC_ANONYMOUS_THP] = {
 184                        .name = "anonymous_thp",
 185                        .flag = ANON_FLAGS,
 186                },
 187                [VM_MEM_SRC_ANONYMOUS_HUGETLB] = {
 188                        .name = "anonymous_hugetlb",
 189                        .flag = ANON_HUGE_FLAGS,
 190                },
 191                [VM_MEM_SRC_ANONYMOUS_HUGETLB_16KB] = {
 192                        .name = "anonymous_hugetlb_16kb",
 193                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_16KB,
 194                },
 195                [VM_MEM_SRC_ANONYMOUS_HUGETLB_64KB] = {
 196                        .name = "anonymous_hugetlb_64kb",
 197                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_64KB,
 198                },
 199                [VM_MEM_SRC_ANONYMOUS_HUGETLB_512KB] = {
 200                        .name = "anonymous_hugetlb_512kb",
 201                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_512KB,
 202                },
 203                [VM_MEM_SRC_ANONYMOUS_HUGETLB_1MB] = {
 204                        .name = "anonymous_hugetlb_1mb",
 205                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_1MB,
 206                },
 207                [VM_MEM_SRC_ANONYMOUS_HUGETLB_2MB] = {
 208                        .name = "anonymous_hugetlb_2mb",
 209                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_2MB,
 210                },
 211                [VM_MEM_SRC_ANONYMOUS_HUGETLB_8MB] = {
 212                        .name = "anonymous_hugetlb_8mb",
 213                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_8MB,
 214                },
 215                [VM_MEM_SRC_ANONYMOUS_HUGETLB_16MB] = {
 216                        .name = "anonymous_hugetlb_16mb",
 217                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_16MB,
 218                },
 219                [VM_MEM_SRC_ANONYMOUS_HUGETLB_32MB] = {
 220                        .name = "anonymous_hugetlb_32mb",
 221                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_32MB,
 222                },
 223                [VM_MEM_SRC_ANONYMOUS_HUGETLB_256MB] = {
 224                        .name = "anonymous_hugetlb_256mb",
 225                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_256MB,
 226                },
 227                [VM_MEM_SRC_ANONYMOUS_HUGETLB_512MB] = {
 228                        .name = "anonymous_hugetlb_512mb",
 229                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_512MB,
 230                },
 231                [VM_MEM_SRC_ANONYMOUS_HUGETLB_1GB] = {
 232                        .name = "anonymous_hugetlb_1gb",
 233                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_1GB,
 234                },
 235                [VM_MEM_SRC_ANONYMOUS_HUGETLB_2GB] = {
 236                        .name = "anonymous_hugetlb_2gb",
 237                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_2GB,
 238                },
 239                [VM_MEM_SRC_ANONYMOUS_HUGETLB_16GB] = {
 240                        .name = "anonymous_hugetlb_16gb",
 241                        .flag = ANON_HUGE_FLAGS | MAP_HUGE_16GB,
 242                },
 243                [VM_MEM_SRC_SHMEM] = {
 244                        .name = "shmem",
 245                        .flag = MAP_SHARED,
 246                },
 247                [VM_MEM_SRC_SHARED_HUGETLB] = {
 248                        .name = "shared_hugetlb",
 249                        /*
 250                         * No MAP_HUGETLB, we use MFD_HUGETLB instead. Since
 251                         * we're using "file backed" memory, we need to specify
 252                         * this when the FD is created, not when the area is
 253                         * mapped.
 254                         */
 255                        .flag = MAP_SHARED,
 256                },
 257        };
 258        _Static_assert(ARRAY_SIZE(aliases) == NUM_SRC_TYPES,
 259                       "Missing new backing src types?");
 260
 261        TEST_ASSERT(i < NUM_SRC_TYPES, "Backing src type ID %d too big", i);
 262
 263        return &aliases[i];
 264}
 265
 266#define MAP_HUGE_PAGE_SIZE(x) (1ULL << ((x >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK))
 267
 268size_t get_backing_src_pagesz(uint32_t i)
 269{
 270        uint32_t flag = vm_mem_backing_src_alias(i)->flag;
 271
 272        switch (i) {
 273        case VM_MEM_SRC_ANONYMOUS:
 274        case VM_MEM_SRC_SHMEM:
 275                return getpagesize();
 276        case VM_MEM_SRC_ANONYMOUS_THP:
 277                return get_trans_hugepagesz();
 278        case VM_MEM_SRC_ANONYMOUS_HUGETLB:
 279        case VM_MEM_SRC_SHARED_HUGETLB:
 280                return get_def_hugetlb_pagesz();
 281        default:
 282                return MAP_HUGE_PAGE_SIZE(flag);
 283        }
 284}
 285
 286static void print_available_backing_src_types(const char *prefix)
 287{
 288        int i;
 289
 290        printf("%sAvailable backing src types:\n", prefix);
 291
 292        for (i = 0; i < NUM_SRC_TYPES; i++)
 293                printf("%s    %s\n", prefix, vm_mem_backing_src_alias(i)->name);
 294}
 295
 296void backing_src_help(const char *flag)
 297{
 298        printf(" %s: specify the type of memory that should be used to\n"
 299               "     back the guest data region. (default: %s)\n",
 300               flag, vm_mem_backing_src_alias(DEFAULT_VM_MEM_SRC)->name);
 301        print_available_backing_src_types("     ");
 302}
 303
 304enum vm_mem_backing_src_type parse_backing_src_type(const char *type_name)
 305{
 306        int i;
 307
 308        for (i = 0; i < NUM_SRC_TYPES; i++)
 309                if (!strcmp(type_name, vm_mem_backing_src_alias(i)->name))
 310                        return i;
 311
 312        print_available_backing_src_types("");
 313        TEST_FAIL("Unknown backing src type: %s", type_name);
 314        return -1;
 315}
 316
 317long get_run_delay(void)
 318{
 319        char path[64];
 320        long val[2];
 321        FILE *fp;
 322
 323        sprintf(path, "/proc/%ld/schedstat", syscall(SYS_gettid));
 324        fp = fopen(path, "r");
 325        /* Return MIN_RUN_DELAY_NS upon failure just to be safe */
 326        if (fscanf(fp, "%ld %ld ", &val[0], &val[1]) < 2)
 327                val[1] = MIN_RUN_DELAY_NS;
 328        fclose(fp);
 329
 330        return val[1];
 331}
 332