• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   */
parse_size(const char * size)23  size_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  
timespec_to_ns(struct timespec ts)61  int64_t timespec_to_ns(struct timespec ts)
62  {
63  	return (int64_t)ts.tv_nsec + 1000000000LL * (int64_t)ts.tv_sec;
64  }
65  
timespec_add_ns(struct timespec ts,int64_t ns)66  struct 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  
timespec_add(struct timespec ts1,struct timespec ts2)77  struct 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  
timespec_sub(struct timespec ts1,struct timespec ts2)84  struct 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  
timespec_elapsed(struct timespec start)91  struct 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  
timespec_div(struct timespec ts,int divisor)99  struct 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  
print_skip(const char * fmt,...)106  void 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  
thp_configured(void)117  bool 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  
get_trans_hugepagesz(void)129  size_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  
get_def_hugetlb_pagesz(void)148  size_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  
vm_mem_backing_src_alias(uint32_t i)176  const 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  
get_backing_src_pagesz(uint32_t i)268  size_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  
print_available_backing_src_types(const char * prefix)286  static 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  
backing_src_help(const char * flag)296  void 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  
parse_backing_src_type(const char * type_name)304  enum 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  
get_run_delay(void)317  long 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