xref: /linux/tools/testing/selftests/mm/transhuge-stress.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1 /*
2  * Stress test for transparent huge pages, memory compaction and migration.
3  *
4  * Authors: Konstantin Khlebnikov <koct9i@gmail.com>
5  *
6  * This is free and unencumbered software released into the public domain.
7  */
8 
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <stdint.h>
12 #include <err.h>
13 #include <time.h>
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <string.h>
17 #include <sys/mman.h>
18 #include "vm_util.h"
19 #include "kselftest.h"
20 #include "thp_settings.h"
21 
22 int backing_fd = -1;
23 int mmap_flags = MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE;
24 #define PROT_RW (PROT_READ | PROT_WRITE)
25 
26 int main(int argc, char **argv)
27 {
28 	size_t ram, len;
29 	void *ptr, *p;
30 	struct timespec start, a, b;
31 	int i = 0;
32 	char *name = NULL;
33 	double s;
34 	uint8_t *map;
35 	size_t map_len;
36 	int pagemap_fd;
37 	int duration = 0;
38 
39 	ksft_print_header();
40 
41 	if (!thp_is_enabled())
42 		ksft_exit_skip("Transparent Hugepages not available\n");
43 
44 	ram = sysconf(_SC_PHYS_PAGES);
45 	if (ram > SIZE_MAX / psize() / 4)
46 		ram = SIZE_MAX / 4;
47 	else
48 		ram *= psize();
49 	len = ram;
50 
51 	while (++i < argc) {
52 		if (!strcmp(argv[i], "-h"))
53 			ksft_exit_fail_msg("usage: %s [-f <filename>] [-d <duration>] [size in MiB]\n",
54 					   argv[0]);
55 		else if (!strcmp(argv[i], "-f"))
56 			name = argv[++i];
57 		else if (!strcmp(argv[i], "-d"))
58 			duration = atoi(argv[++i]);
59 		else
60 			len = atoll(argv[i]) << 20;
61 	}
62 
63 	ksft_set_plan(1);
64 
65 	if (name) {
66 		backing_fd = open(name, O_RDWR);
67 		if (backing_fd == -1)
68 			ksft_exit_fail_msg("open %s\n", name);
69 		mmap_flags = MAP_SHARED;
70 	}
71 
72 	warnx("allocate %zd transhuge pages, using %zd MiB virtual memory"
73 	      " and %zd MiB of ram", len >> HPAGE_SHIFT, len >> 20,
74 	      ram >> (20 + HPAGE_SHIFT - pshift() - 1));
75 
76 	pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
77 	if (pagemap_fd < 0)
78 		ksft_exit_fail_msg("open pagemap\n");
79 
80 	len -= len % HPAGE_SIZE;
81 	ptr = mmap(NULL, len + HPAGE_SIZE, PROT_RW, mmap_flags, backing_fd, 0);
82 	if (ptr == MAP_FAILED)
83 		ksft_exit_fail_msg("initial mmap");
84 	ptr += HPAGE_SIZE - (uintptr_t)ptr % HPAGE_SIZE;
85 
86 	if (madvise(ptr, len, MADV_HUGEPAGE))
87 		ksft_exit_fail_msg("MADV_HUGEPAGE");
88 
89 	map_len = ram >> (HPAGE_SHIFT - 1);
90 	map = malloc(map_len);
91 	if (!map)
92 		ksft_exit_fail_msg("map malloc\n");
93 
94 	clock_gettime(CLOCK_MONOTONIC, &start);
95 
96 	while (1) {
97 		int nr_succeed = 0, nr_failed = 0, nr_pages = 0;
98 
99 		memset(map, 0, map_len);
100 
101 		clock_gettime(CLOCK_MONOTONIC, &a);
102 		for (p = ptr; p < ptr + len; p += HPAGE_SIZE) {
103 			int64_t pfn;
104 
105 			pfn = allocate_transhuge(p, pagemap_fd);
106 
107 			if (pfn < 0) {
108 				nr_failed++;
109 			} else {
110 				size_t idx = pfn >> (HPAGE_SHIFT - pshift());
111 
112 				nr_succeed++;
113 				if (idx >= map_len) {
114 					map = realloc(map, idx + 1);
115 					if (!map)
116 						ksft_exit_fail_msg("map realloc\n");
117 					memset(map + map_len, 0, idx + 1 - map_len);
118 					map_len = idx + 1;
119 				}
120 				if (!map[idx])
121 					nr_pages++;
122 				map[idx] = 1;
123 			}
124 
125 			/* split transhuge page, keep last page */
126 			if (madvise(p, HPAGE_SIZE - psize(), MADV_DONTNEED))
127 				ksft_exit_fail_msg("MADV_DONTNEED");
128 		}
129 		clock_gettime(CLOCK_MONOTONIC, &b);
130 		s = b.tv_sec - a.tv_sec + (b.tv_nsec - a.tv_nsec) / 1000000000.;
131 
132 		ksft_print_msg("%.3f s/loop, %.3f ms/page, %10.3f MiB/s\t"
133 			       "%4d succeed, %4d failed, %4d different pages\n",
134 			       s, s * 1000 / (len >> HPAGE_SHIFT), len / s / (1 << 20),
135 			       nr_succeed, nr_failed, nr_pages);
136 
137 		if (duration > 0 && b.tv_sec - start.tv_sec >= duration) {
138 			ksft_test_result_pass("Completed\n");
139 			ksft_finished();
140 		}
141 	}
142 }
143