File Index
Files and directories
Sign in
FILE

test_util.c

File preview with syntax highlighting.

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