xref: /linux/tools/testing/selftests/mm/vm_util.c (revision bd4d67e76f699688d15e6194f1505b8bdfee0dd7)
1baa489faSSeongJae Park // SPDX-License-Identifier: GPL-2.0
2baa489faSSeongJae Park #include <string.h>
3baa489faSSeongJae Park #include <fcntl.h>
4baa489faSSeongJae Park #include "../kselftest.h"
5baa489faSSeongJae Park #include "vm_util.h"
6baa489faSSeongJae Park 
7baa489faSSeongJae Park #define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size"
8baa489faSSeongJae Park #define SMAP_FILE_PATH "/proc/self/smaps"
9baa489faSSeongJae Park #define MAX_LINE_LENGTH 500
10baa489faSSeongJae Park 
11af605d26SPeter Xu unsigned int __page_size;
12af605d26SPeter Xu unsigned int __page_shift;
13af605d26SPeter Xu 
14baa489faSSeongJae Park uint64_t pagemap_get_entry(int fd, char *start)
15baa489faSSeongJae Park {
16baa489faSSeongJae Park 	const unsigned long pfn = (unsigned long)start / getpagesize();
17baa489faSSeongJae Park 	uint64_t entry;
18baa489faSSeongJae Park 	int ret;
19baa489faSSeongJae Park 
20baa489faSSeongJae Park 	ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry));
21baa489faSSeongJae Park 	if (ret != sizeof(entry))
22baa489faSSeongJae Park 		ksft_exit_fail_msg("reading pagemap failed\n");
23baa489faSSeongJae Park 	return entry;
24baa489faSSeongJae Park }
25baa489faSSeongJae Park 
26baa489faSSeongJae Park bool pagemap_is_softdirty(int fd, char *start)
27baa489faSSeongJae Park {
28baa489faSSeongJae Park 	uint64_t entry = pagemap_get_entry(fd, start);
29baa489faSSeongJae Park 
30baa489faSSeongJae Park 	// Check if dirty bit (55th bit) is set
31baa489faSSeongJae Park 	return entry & 0x0080000000000000ull;
32baa489faSSeongJae Park }
33baa489faSSeongJae Park 
34baa489faSSeongJae Park bool pagemap_is_swapped(int fd, char *start)
35baa489faSSeongJae Park {
36baa489faSSeongJae Park 	uint64_t entry = pagemap_get_entry(fd, start);
37baa489faSSeongJae Park 
38baa489faSSeongJae Park 	return entry & 0x4000000000000000ull;
39baa489faSSeongJae Park }
40baa489faSSeongJae Park 
41baa489faSSeongJae Park bool pagemap_is_populated(int fd, char *start)
42baa489faSSeongJae Park {
43baa489faSSeongJae Park 	uint64_t entry = pagemap_get_entry(fd, start);
44baa489faSSeongJae Park 
45baa489faSSeongJae Park 	/* Present or swapped. */
46baa489faSSeongJae Park 	return entry & 0xc000000000000000ull;
47baa489faSSeongJae Park }
48baa489faSSeongJae Park 
49baa489faSSeongJae Park unsigned long pagemap_get_pfn(int fd, char *start)
50baa489faSSeongJae Park {
51baa489faSSeongJae Park 	uint64_t entry = pagemap_get_entry(fd, start);
52baa489faSSeongJae Park 
53baa489faSSeongJae Park 	/* If present (63th bit), PFN is at bit 0 -- 54. */
54baa489faSSeongJae Park 	if (entry & 0x8000000000000000ull)
55baa489faSSeongJae Park 		return entry & 0x007fffffffffffffull;
56baa489faSSeongJae Park 	return -1ul;
57baa489faSSeongJae Park }
58baa489faSSeongJae Park 
59baa489faSSeongJae Park void clear_softdirty(void)
60baa489faSSeongJae Park {
61baa489faSSeongJae Park 	int ret;
62baa489faSSeongJae Park 	const char *ctrl = "4";
63baa489faSSeongJae Park 	int fd = open("/proc/self/clear_refs", O_WRONLY);
64baa489faSSeongJae Park 
65baa489faSSeongJae Park 	if (fd < 0)
66baa489faSSeongJae Park 		ksft_exit_fail_msg("opening clear_refs failed\n");
67baa489faSSeongJae Park 	ret = write(fd, ctrl, strlen(ctrl));
68baa489faSSeongJae Park 	close(fd);
69baa489faSSeongJae Park 	if (ret != strlen(ctrl))
70baa489faSSeongJae Park 		ksft_exit_fail_msg("writing clear_refs failed\n");
71baa489faSSeongJae Park }
72baa489faSSeongJae Park 
73baa489faSSeongJae Park bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len)
74baa489faSSeongJae Park {
75baa489faSSeongJae Park 	while (fgets(buf, len, fp)) {
76baa489faSSeongJae Park 		if (!strncmp(buf, pattern, strlen(pattern)))
77baa489faSSeongJae Park 			return true;
78baa489faSSeongJae Park 	}
79baa489faSSeongJae Park 	return false;
80baa489faSSeongJae Park }
81baa489faSSeongJae Park 
82baa489faSSeongJae Park uint64_t read_pmd_pagesize(void)
83baa489faSSeongJae Park {
84baa489faSSeongJae Park 	int fd;
85baa489faSSeongJae Park 	char buf[20];
86baa489faSSeongJae Park 	ssize_t num_read;
87baa489faSSeongJae Park 
88baa489faSSeongJae Park 	fd = open(PMD_SIZE_FILE_PATH, O_RDONLY);
89baa489faSSeongJae Park 	if (fd == -1)
90d6e61afbSDavid Hildenbrand 		return 0;
91baa489faSSeongJae Park 
92baa489faSSeongJae Park 	num_read = read(fd, buf, 19);
93baa489faSSeongJae Park 	if (num_read < 1) {
94baa489faSSeongJae Park 		close(fd);
95d6e61afbSDavid Hildenbrand 		return 0;
96baa489faSSeongJae Park 	}
97baa489faSSeongJae Park 	buf[num_read] = '\0';
98baa489faSSeongJae Park 	close(fd);
99baa489faSSeongJae Park 
100baa489faSSeongJae Park 	return strtoul(buf, NULL, 10);
101baa489faSSeongJae Park }
102baa489faSSeongJae Park 
103baa489faSSeongJae Park bool __check_huge(void *addr, char *pattern, int nr_hpages,
104baa489faSSeongJae Park 		  uint64_t hpage_size)
105baa489faSSeongJae Park {
106baa489faSSeongJae Park 	uint64_t thp = -1;
107baa489faSSeongJae Park 	int ret;
108baa489faSSeongJae Park 	FILE *fp;
109baa489faSSeongJae Park 	char buffer[MAX_LINE_LENGTH];
110baa489faSSeongJae Park 	char addr_pattern[MAX_LINE_LENGTH];
111baa489faSSeongJae Park 
112baa489faSSeongJae Park 	ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-",
113baa489faSSeongJae Park 		       (unsigned long) addr);
114baa489faSSeongJae Park 	if (ret >= MAX_LINE_LENGTH)
115baa489faSSeongJae Park 		ksft_exit_fail_msg("%s: Pattern is too long\n", __func__);
116baa489faSSeongJae Park 
117baa489faSSeongJae Park 	fp = fopen(SMAP_FILE_PATH, "r");
118baa489faSSeongJae Park 	if (!fp)
119baa489faSSeongJae Park 		ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH);
120baa489faSSeongJae Park 
121baa489faSSeongJae Park 	if (!check_for_pattern(fp, addr_pattern, buffer, sizeof(buffer)))
122baa489faSSeongJae Park 		goto err_out;
123baa489faSSeongJae Park 
124baa489faSSeongJae Park 	/*
125baa489faSSeongJae Park 	 * Fetch the pattern in the same block and check the number of
126baa489faSSeongJae Park 	 * hugepages.
127baa489faSSeongJae Park 	 */
128baa489faSSeongJae Park 	if (!check_for_pattern(fp, pattern, buffer, sizeof(buffer)))
129baa489faSSeongJae Park 		goto err_out;
130baa489faSSeongJae Park 
131baa489faSSeongJae Park 	snprintf(addr_pattern, MAX_LINE_LENGTH, "%s%%9ld kB", pattern);
132baa489faSSeongJae Park 
133baa489faSSeongJae Park 	if (sscanf(buffer, addr_pattern, &thp) != 1)
134baa489faSSeongJae Park 		ksft_exit_fail_msg("Reading smap error\n");
135baa489faSSeongJae Park 
136baa489faSSeongJae Park err_out:
137baa489faSSeongJae Park 	fclose(fp);
138baa489faSSeongJae Park 	return thp == (nr_hpages * (hpage_size >> 10));
139baa489faSSeongJae Park }
140baa489faSSeongJae Park 
141baa489faSSeongJae Park bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size)
142baa489faSSeongJae Park {
143baa489faSSeongJae Park 	return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
144baa489faSSeongJae Park }
145baa489faSSeongJae Park 
146baa489faSSeongJae Park bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size)
147baa489faSSeongJae Park {
148baa489faSSeongJae Park 	return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
149baa489faSSeongJae Park }
150baa489faSSeongJae Park 
151baa489faSSeongJae Park bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size)
152baa489faSSeongJae Park {
153baa489faSSeongJae Park 	return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
154baa489faSSeongJae Park }
155af605d26SPeter Xu 
156af605d26SPeter Xu int64_t allocate_transhuge(void *ptr, int pagemap_fd)
157af605d26SPeter Xu {
158af605d26SPeter Xu 	uint64_t ent[2];
159af605d26SPeter Xu 
160af605d26SPeter Xu 	/* drop pmd */
161af605d26SPeter Xu 	if (mmap(ptr, HPAGE_SIZE, PROT_READ | PROT_WRITE,
162af605d26SPeter Xu 		 MAP_FIXED | MAP_ANONYMOUS |
163af605d26SPeter Xu 		 MAP_NORESERVE | MAP_PRIVATE, -1, 0) != ptr)
164af605d26SPeter Xu 		errx(2, "mmap transhuge");
165af605d26SPeter Xu 
166af605d26SPeter Xu 	if (madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE))
167af605d26SPeter Xu 		err(2, "MADV_HUGEPAGE");
168af605d26SPeter Xu 
169af605d26SPeter Xu 	/* allocate transparent huge page */
170af605d26SPeter Xu 	*(volatile void **)ptr = ptr;
171af605d26SPeter Xu 
172af605d26SPeter Xu 	if (pread(pagemap_fd, ent, sizeof(ent),
173af605d26SPeter Xu 		  (uintptr_t)ptr >> (pshift() - 3)) != sizeof(ent))
174af605d26SPeter Xu 		err(2, "read pagemap");
175af605d26SPeter Xu 
176af605d26SPeter Xu 	if (PAGEMAP_PRESENT(ent[0]) && PAGEMAP_PRESENT(ent[1]) &&
177af605d26SPeter Xu 	    PAGEMAP_PFN(ent[0]) + 1 == PAGEMAP_PFN(ent[1]) &&
178af605d26SPeter Xu 	    !(PAGEMAP_PFN(ent[0]) & ((1 << (HPAGE_SHIFT - pshift())) - 1)))
179af605d26SPeter Xu 		return PAGEMAP_PFN(ent[0]);
180af605d26SPeter Xu 
181af605d26SPeter Xu 	return -1;
182af605d26SPeter Xu }
183*bd4d67e7SPeter Xu 
184*bd4d67e7SPeter Xu unsigned long default_huge_page_size(void)
185*bd4d67e7SPeter Xu {
186*bd4d67e7SPeter Xu 	unsigned long hps = 0;
187*bd4d67e7SPeter Xu 	char *line = NULL;
188*bd4d67e7SPeter Xu 	size_t linelen = 0;
189*bd4d67e7SPeter Xu 	FILE *f = fopen("/proc/meminfo", "r");
190*bd4d67e7SPeter Xu 
191*bd4d67e7SPeter Xu 	if (!f)
192*bd4d67e7SPeter Xu 		return 0;
193*bd4d67e7SPeter Xu 	while (getline(&line, &linelen, f) > 0) {
194*bd4d67e7SPeter Xu 		if (sscanf(line, "Hugepagesize:       %lu kB", &hps) == 1) {
195*bd4d67e7SPeter Xu 			hps <<= 10;
196*bd4d67e7SPeter Xu 			break;
197*bd4d67e7SPeter Xu 		}
198*bd4d67e7SPeter Xu 	}
199*bd4d67e7SPeter Xu 
200*bd4d67e7SPeter Xu 	free(line);
201*bd4d67e7SPeter Xu 	fclose(f);
202*bd4d67e7SPeter Xu 	return hps;
203*bd4d67e7SPeter Xu }
204