xref: /linux/tools/testing/selftests/mm/vm_util.c (revision 617a814f14b8914271f7a70366d72c6196d17663)
1baa489faSSeongJae Park // SPDX-License-Identifier: GPL-2.0
2baa489faSSeongJae Park #include <string.h>
3baa489faSSeongJae Park #include <fcntl.h>
481b1e3f9SDavid Hildenbrand #include <dirent.h>
5c4277cb6SPeter Xu #include <sys/ioctl.h>
6c4277cb6SPeter Xu #include <linux/userfaultfd.h>
7600bca58SAndrei Vagin #include <linux/fs.h>
878391f64SPeter Xu #include <sys/syscall.h>
978391f64SPeter Xu #include <unistd.h>
10baa489faSSeongJae Park #include "../kselftest.h"
11baa489faSSeongJae Park #include "vm_util.h"
12baa489faSSeongJae Park 
13baa489faSSeongJae Park #define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size"
14baa489faSSeongJae Park #define SMAP_FILE_PATH "/proc/self/smaps"
15*391e8697SAlexander Zhu #define STATUS_FILE_PATH "/proc/self/status"
16baa489faSSeongJae Park #define MAX_LINE_LENGTH 500
17baa489faSSeongJae Park 
18af605d26SPeter Xu unsigned int __page_size;
19af605d26SPeter Xu unsigned int __page_shift;
20af605d26SPeter Xu 
21baa489faSSeongJae Park uint64_t pagemap_get_entry(int fd, char *start)
22baa489faSSeongJae Park {
23baa489faSSeongJae Park 	const unsigned long pfn = (unsigned long)start / getpagesize();
24baa489faSSeongJae Park 	uint64_t entry;
25baa489faSSeongJae Park 	int ret;
26baa489faSSeongJae Park 
27baa489faSSeongJae Park 	ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry));
28baa489faSSeongJae Park 	if (ret != sizeof(entry))
29baa489faSSeongJae Park 		ksft_exit_fail_msg("reading pagemap failed\n");
30baa489faSSeongJae Park 	return entry;
31baa489faSSeongJae Park }
32baa489faSSeongJae Park 
33600bca58SAndrei Vagin static uint64_t __pagemap_scan_get_categories(int fd, char *start, struct page_region *r)
34600bca58SAndrei Vagin {
35600bca58SAndrei Vagin 	struct pm_scan_arg arg;
36600bca58SAndrei Vagin 
37600bca58SAndrei Vagin 	arg.start = (uintptr_t)start;
38600bca58SAndrei Vagin 	arg.end = (uintptr_t)(start + psize());
39600bca58SAndrei Vagin 	arg.vec = (uintptr_t)r;
40600bca58SAndrei Vagin 	arg.vec_len = 1;
41600bca58SAndrei Vagin 	arg.flags = 0;
42600bca58SAndrei Vagin 	arg.size = sizeof(struct pm_scan_arg);
43600bca58SAndrei Vagin 	arg.max_pages = 0;
44600bca58SAndrei Vagin 	arg.category_inverted = 0;
45600bca58SAndrei Vagin 	arg.category_mask = 0;
46600bca58SAndrei Vagin 	arg.category_anyof_mask = PAGE_IS_WPALLOWED | PAGE_IS_WRITTEN | PAGE_IS_FILE |
47600bca58SAndrei Vagin 				  PAGE_IS_PRESENT | PAGE_IS_SWAPPED | PAGE_IS_PFNZERO |
48600bca58SAndrei Vagin 				  PAGE_IS_HUGE | PAGE_IS_SOFT_DIRTY;
49600bca58SAndrei Vagin 	arg.return_mask = arg.category_anyof_mask;
50600bca58SAndrei Vagin 
51600bca58SAndrei Vagin 	return ioctl(fd, PAGEMAP_SCAN, &arg);
52600bca58SAndrei Vagin }
53600bca58SAndrei Vagin 
54600bca58SAndrei Vagin static uint64_t pagemap_scan_get_categories(int fd, char *start)
55600bca58SAndrei Vagin {
56600bca58SAndrei Vagin 	struct page_region r;
57600bca58SAndrei Vagin 	long ret;
58600bca58SAndrei Vagin 
59600bca58SAndrei Vagin 	ret = __pagemap_scan_get_categories(fd, start, &r);
60600bca58SAndrei Vagin 	if (ret < 0)
61600bca58SAndrei Vagin 		ksft_exit_fail_msg("PAGEMAP_SCAN failed: %s\n", strerror(errno));
62600bca58SAndrei Vagin 	if (ret == 0)
63600bca58SAndrei Vagin 		return 0;
64600bca58SAndrei Vagin 	return r.categories;
65600bca58SAndrei Vagin }
66600bca58SAndrei Vagin 
67600bca58SAndrei Vagin /* `start` is any valid address. */
68600bca58SAndrei Vagin static bool pagemap_scan_supported(int fd, char *start)
69600bca58SAndrei Vagin {
70600bca58SAndrei Vagin 	static int supported = -1;
71600bca58SAndrei Vagin 	int ret;
72600bca58SAndrei Vagin 
73600bca58SAndrei Vagin 	if (supported != -1)
74600bca58SAndrei Vagin 		return supported;
75600bca58SAndrei Vagin 
76600bca58SAndrei Vagin 	/* Provide an invalid address in order to trigger EFAULT. */
77600bca58SAndrei Vagin 	ret = __pagemap_scan_get_categories(fd, start, (struct page_region *) ~0UL);
78600bca58SAndrei Vagin 	if (ret == 0)
79600bca58SAndrei Vagin 		ksft_exit_fail_msg("PAGEMAP_SCAN succeeded unexpectedly\n");
80600bca58SAndrei Vagin 
81600bca58SAndrei Vagin 	supported = errno == EFAULT;
82600bca58SAndrei Vagin 
83600bca58SAndrei Vagin 	return supported;
84600bca58SAndrei Vagin }
85600bca58SAndrei Vagin 
86600bca58SAndrei Vagin static bool page_entry_is(int fd, char *start, char *desc,
87600bca58SAndrei Vagin 			  uint64_t pagemap_flags, uint64_t pagescan_flags)
88600bca58SAndrei Vagin {
89600bca58SAndrei Vagin 	bool m = pagemap_get_entry(fd, start) & pagemap_flags;
90600bca58SAndrei Vagin 
91600bca58SAndrei Vagin 	if (pagemap_scan_supported(fd, start)) {
92600bca58SAndrei Vagin 		bool s = pagemap_scan_get_categories(fd, start) & pagescan_flags;
93600bca58SAndrei Vagin 
94600bca58SAndrei Vagin 		if (m == s)
95600bca58SAndrei Vagin 			return m;
96600bca58SAndrei Vagin 
97600bca58SAndrei Vagin 		ksft_exit_fail_msg(
98600bca58SAndrei Vagin 			"read and ioctl return unmatched results for %s: %d %d", desc, m, s);
99600bca58SAndrei Vagin 	}
100600bca58SAndrei Vagin 	return m;
101600bca58SAndrei Vagin }
102600bca58SAndrei Vagin 
103baa489faSSeongJae Park bool pagemap_is_softdirty(int fd, char *start)
104baa489faSSeongJae Park {
105600bca58SAndrei Vagin 	return page_entry_is(fd, start, "soft-dirty",
106600bca58SAndrei Vagin 				PM_SOFT_DIRTY, PAGE_IS_SOFT_DIRTY);
107baa489faSSeongJae Park }
108baa489faSSeongJae Park 
109baa489faSSeongJae Park bool pagemap_is_swapped(int fd, char *start)
110baa489faSSeongJae Park {
111600bca58SAndrei Vagin 	return page_entry_is(fd, start, "swap", PM_SWAP, PAGE_IS_SWAPPED);
112baa489faSSeongJae Park }
113baa489faSSeongJae Park 
114baa489faSSeongJae Park bool pagemap_is_populated(int fd, char *start)
115baa489faSSeongJae Park {
116600bca58SAndrei Vagin 	return page_entry_is(fd, start, "populated",
117600bca58SAndrei Vagin 				PM_PRESENT | PM_SWAP,
118600bca58SAndrei Vagin 				PAGE_IS_PRESENT | PAGE_IS_SWAPPED);
119baa489faSSeongJae Park }
120baa489faSSeongJae Park 
121baa489faSSeongJae Park unsigned long pagemap_get_pfn(int fd, char *start)
122baa489faSSeongJae Park {
123baa489faSSeongJae Park 	uint64_t entry = pagemap_get_entry(fd, start);
124baa489faSSeongJae Park 
125baa489faSSeongJae Park 	/* If present (63th bit), PFN is at bit 0 -- 54. */
1269f74696bSPeter Xu 	if (entry & PM_PRESENT)
127baa489faSSeongJae Park 		return entry & 0x007fffffffffffffull;
128baa489faSSeongJae Park 	return -1ul;
129baa489faSSeongJae Park }
130baa489faSSeongJae Park 
131baa489faSSeongJae Park void clear_softdirty(void)
132baa489faSSeongJae Park {
133baa489faSSeongJae Park 	int ret;
134baa489faSSeongJae Park 	const char *ctrl = "4";
135baa489faSSeongJae Park 	int fd = open("/proc/self/clear_refs", O_WRONLY);
136baa489faSSeongJae Park 
137baa489faSSeongJae Park 	if (fd < 0)
138baa489faSSeongJae Park 		ksft_exit_fail_msg("opening clear_refs failed\n");
139baa489faSSeongJae Park 	ret = write(fd, ctrl, strlen(ctrl));
140baa489faSSeongJae Park 	close(fd);
141baa489faSSeongJae Park 	if (ret != strlen(ctrl))
142baa489faSSeongJae Park 		ksft_exit_fail_msg("writing clear_refs failed\n");
143baa489faSSeongJae Park }
144baa489faSSeongJae Park 
145baa489faSSeongJae Park bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len)
146baa489faSSeongJae Park {
147baa489faSSeongJae Park 	while (fgets(buf, len, fp)) {
148baa489faSSeongJae Park 		if (!strncmp(buf, pattern, strlen(pattern)))
149baa489faSSeongJae Park 			return true;
150baa489faSSeongJae Park 	}
151baa489faSSeongJae Park 	return false;
152baa489faSSeongJae Park }
153baa489faSSeongJae Park 
154baa489faSSeongJae Park uint64_t read_pmd_pagesize(void)
155baa489faSSeongJae Park {
156baa489faSSeongJae Park 	int fd;
157baa489faSSeongJae Park 	char buf[20];
158baa489faSSeongJae Park 	ssize_t num_read;
159baa489faSSeongJae Park 
160baa489faSSeongJae Park 	fd = open(PMD_SIZE_FILE_PATH, O_RDONLY);
161baa489faSSeongJae Park 	if (fd == -1)
162d6e61afbSDavid Hildenbrand 		return 0;
163baa489faSSeongJae Park 
164baa489faSSeongJae Park 	num_read = read(fd, buf, 19);
165baa489faSSeongJae Park 	if (num_read < 1) {
166baa489faSSeongJae Park 		close(fd);
167d6e61afbSDavid Hildenbrand 		return 0;
168baa489faSSeongJae Park 	}
169baa489faSSeongJae Park 	buf[num_read] = '\0';
170baa489faSSeongJae Park 	close(fd);
171baa489faSSeongJae Park 
172baa489faSSeongJae Park 	return strtoul(buf, NULL, 10);
173baa489faSSeongJae Park }
174baa489faSSeongJae Park 
175*391e8697SAlexander Zhu unsigned long rss_anon(void)
176*391e8697SAlexander Zhu {
177*391e8697SAlexander Zhu 	unsigned long rss_anon = 0;
178*391e8697SAlexander Zhu 	FILE *fp;
179*391e8697SAlexander Zhu 	char buffer[MAX_LINE_LENGTH];
180*391e8697SAlexander Zhu 
181*391e8697SAlexander Zhu 	fp = fopen(STATUS_FILE_PATH, "r");
182*391e8697SAlexander Zhu 	if (!fp)
183*391e8697SAlexander Zhu 		ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, STATUS_FILE_PATH);
184*391e8697SAlexander Zhu 
185*391e8697SAlexander Zhu 	if (!check_for_pattern(fp, "RssAnon:", buffer, sizeof(buffer)))
186*391e8697SAlexander Zhu 		goto err_out;
187*391e8697SAlexander Zhu 
188*391e8697SAlexander Zhu 	if (sscanf(buffer, "RssAnon:%10lu kB", &rss_anon) != 1)
189*391e8697SAlexander Zhu 		ksft_exit_fail_msg("Reading status error\n");
190*391e8697SAlexander Zhu 
191*391e8697SAlexander Zhu err_out:
192*391e8697SAlexander Zhu 	fclose(fp);
193*391e8697SAlexander Zhu 	return rss_anon;
194*391e8697SAlexander Zhu }
195*391e8697SAlexander Zhu 
196baa489faSSeongJae Park bool __check_huge(void *addr, char *pattern, int nr_hpages,
197baa489faSSeongJae Park 		  uint64_t hpage_size)
198baa489faSSeongJae Park {
199baa489faSSeongJae Park 	uint64_t thp = -1;
200baa489faSSeongJae Park 	int ret;
201baa489faSSeongJae Park 	FILE *fp;
202baa489faSSeongJae Park 	char buffer[MAX_LINE_LENGTH];
203baa489faSSeongJae Park 	char addr_pattern[MAX_LINE_LENGTH];
204baa489faSSeongJae Park 
205baa489faSSeongJae Park 	ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-",
206baa489faSSeongJae Park 		       (unsigned long) addr);
207baa489faSSeongJae Park 	if (ret >= MAX_LINE_LENGTH)
208baa489faSSeongJae Park 		ksft_exit_fail_msg("%s: Pattern is too long\n", __func__);
209baa489faSSeongJae Park 
210baa489faSSeongJae Park 	fp = fopen(SMAP_FILE_PATH, "r");
211baa489faSSeongJae Park 	if (!fp)
212baa489faSSeongJae Park 		ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH);
213baa489faSSeongJae Park 
214baa489faSSeongJae Park 	if (!check_for_pattern(fp, addr_pattern, buffer, sizeof(buffer)))
215baa489faSSeongJae Park 		goto err_out;
216baa489faSSeongJae Park 
217baa489faSSeongJae Park 	/*
218baa489faSSeongJae Park 	 * Fetch the pattern in the same block and check the number of
219baa489faSSeongJae Park 	 * hugepages.
220baa489faSSeongJae Park 	 */
221baa489faSSeongJae Park 	if (!check_for_pattern(fp, pattern, buffer, sizeof(buffer)))
222baa489faSSeongJae Park 		goto err_out;
223baa489faSSeongJae Park 
224baa489faSSeongJae Park 	snprintf(addr_pattern, MAX_LINE_LENGTH, "%s%%9ld kB", pattern);
225baa489faSSeongJae Park 
226baa489faSSeongJae Park 	if (sscanf(buffer, addr_pattern, &thp) != 1)
227baa489faSSeongJae Park 		ksft_exit_fail_msg("Reading smap error\n");
228baa489faSSeongJae Park 
229baa489faSSeongJae Park err_out:
230baa489faSSeongJae Park 	fclose(fp);
231baa489faSSeongJae Park 	return thp == (nr_hpages * (hpage_size >> 10));
232baa489faSSeongJae Park }
233baa489faSSeongJae Park 
234baa489faSSeongJae Park bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size)
235baa489faSSeongJae Park {
236baa489faSSeongJae Park 	return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
237baa489faSSeongJae Park }
238baa489faSSeongJae Park 
239baa489faSSeongJae Park bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size)
240baa489faSSeongJae Park {
241baa489faSSeongJae Park 	return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
242baa489faSSeongJae Park }
243baa489faSSeongJae Park 
244baa489faSSeongJae Park bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size)
245baa489faSSeongJae Park {
246baa489faSSeongJae Park 	return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
247baa489faSSeongJae Park }
248af605d26SPeter Xu 
249af605d26SPeter Xu int64_t allocate_transhuge(void *ptr, int pagemap_fd)
250af605d26SPeter Xu {
251af605d26SPeter Xu 	uint64_t ent[2];
252af605d26SPeter Xu 
253af605d26SPeter Xu 	/* drop pmd */
254af605d26SPeter Xu 	if (mmap(ptr, HPAGE_SIZE, PROT_READ | PROT_WRITE,
255af605d26SPeter Xu 		 MAP_FIXED | MAP_ANONYMOUS |
256af605d26SPeter Xu 		 MAP_NORESERVE | MAP_PRIVATE, -1, 0) != ptr)
257c811b0ceSMuhammad Usama Anjum 		ksft_exit_fail_msg("mmap transhuge\n");
258af605d26SPeter Xu 
259af605d26SPeter Xu 	if (madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE))
260c811b0ceSMuhammad Usama Anjum 		ksft_exit_fail_msg("MADV_HUGEPAGE\n");
261af605d26SPeter Xu 
262af605d26SPeter Xu 	/* allocate transparent huge page */
263af605d26SPeter Xu 	*(volatile void **)ptr = ptr;
264af605d26SPeter Xu 
265af605d26SPeter Xu 	if (pread(pagemap_fd, ent, sizeof(ent),
266af605d26SPeter Xu 		  (uintptr_t)ptr >> (pshift() - 3)) != sizeof(ent))
267c811b0ceSMuhammad Usama Anjum 		ksft_exit_fail_msg("read pagemap\n");
268af605d26SPeter Xu 
269af605d26SPeter Xu 	if (PAGEMAP_PRESENT(ent[0]) && PAGEMAP_PRESENT(ent[1]) &&
270af605d26SPeter Xu 	    PAGEMAP_PFN(ent[0]) + 1 == PAGEMAP_PFN(ent[1]) &&
271af605d26SPeter Xu 	    !(PAGEMAP_PFN(ent[0]) & ((1 << (HPAGE_SHIFT - pshift())) - 1)))
272af605d26SPeter Xu 		return PAGEMAP_PFN(ent[0]);
273af605d26SPeter Xu 
274af605d26SPeter Xu 	return -1;
275af605d26SPeter Xu }
276bd4d67e7SPeter Xu 
277bd4d67e7SPeter Xu unsigned long default_huge_page_size(void)
278bd4d67e7SPeter Xu {
279bd4d67e7SPeter Xu 	unsigned long hps = 0;
280bd4d67e7SPeter Xu 	char *line = NULL;
281bd4d67e7SPeter Xu 	size_t linelen = 0;
282bd4d67e7SPeter Xu 	FILE *f = fopen("/proc/meminfo", "r");
283bd4d67e7SPeter Xu 
284bd4d67e7SPeter Xu 	if (!f)
285bd4d67e7SPeter Xu 		return 0;
286bd4d67e7SPeter Xu 	while (getline(&line, &linelen, f) > 0) {
287bd4d67e7SPeter Xu 		if (sscanf(line, "Hugepagesize:       %lu kB", &hps) == 1) {
288bd4d67e7SPeter Xu 			hps <<= 10;
289bd4d67e7SPeter Xu 			break;
290bd4d67e7SPeter Xu 		}
291bd4d67e7SPeter Xu 	}
292bd4d67e7SPeter Xu 
293bd4d67e7SPeter Xu 	free(line);
294bd4d67e7SPeter Xu 	fclose(f);
295bd4d67e7SPeter Xu 	return hps;
296bd4d67e7SPeter Xu }
297c4277cb6SPeter Xu 
29881b1e3f9SDavid Hildenbrand int detect_hugetlb_page_sizes(size_t sizes[], int max)
29981b1e3f9SDavid Hildenbrand {
30081b1e3f9SDavid Hildenbrand 	DIR *dir = opendir("/sys/kernel/mm/hugepages/");
30181b1e3f9SDavid Hildenbrand 	int count = 0;
30281b1e3f9SDavid Hildenbrand 
30381b1e3f9SDavid Hildenbrand 	if (!dir)
30481b1e3f9SDavid Hildenbrand 		return 0;
30581b1e3f9SDavid Hildenbrand 
30681b1e3f9SDavid Hildenbrand 	while (count < max) {
30781b1e3f9SDavid Hildenbrand 		struct dirent *entry = readdir(dir);
30881b1e3f9SDavid Hildenbrand 		size_t kb;
30981b1e3f9SDavid Hildenbrand 
31081b1e3f9SDavid Hildenbrand 		if (!entry)
31181b1e3f9SDavid Hildenbrand 			break;
31281b1e3f9SDavid Hildenbrand 		if (entry->d_type != DT_DIR)
31381b1e3f9SDavid Hildenbrand 			continue;
31481b1e3f9SDavid Hildenbrand 		if (sscanf(entry->d_name, "hugepages-%zukB", &kb) != 1)
31581b1e3f9SDavid Hildenbrand 			continue;
31681b1e3f9SDavid Hildenbrand 		sizes[count++] = kb * 1024;
31781b1e3f9SDavid Hildenbrand 		ksft_print_msg("[INFO] detected hugetlb page size: %zu KiB\n",
31881b1e3f9SDavid Hildenbrand 			       kb);
31981b1e3f9SDavid Hildenbrand 	}
32081b1e3f9SDavid Hildenbrand 	closedir(dir);
32181b1e3f9SDavid Hildenbrand 	return count;
32281b1e3f9SDavid Hildenbrand }
32381b1e3f9SDavid Hildenbrand 
324c3315502SPeter Xu /* If `ioctls' non-NULL, the allowed ioctls will be returned into the var */
325c3315502SPeter Xu int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len,
326c3315502SPeter Xu 			      bool miss, bool wp, bool minor, uint64_t *ioctls)
327c4277cb6SPeter Xu {
328c4277cb6SPeter Xu 	struct uffdio_register uffdio_register = { 0 };
329c4277cb6SPeter Xu 	uint64_t mode = 0;
330c4277cb6SPeter Xu 	int ret = 0;
331c4277cb6SPeter Xu 
332c4277cb6SPeter Xu 	if (miss)
333c4277cb6SPeter Xu 		mode |= UFFDIO_REGISTER_MODE_MISSING;
334c4277cb6SPeter Xu 	if (wp)
335c4277cb6SPeter Xu 		mode |= UFFDIO_REGISTER_MODE_WP;
336c4277cb6SPeter Xu 	if (minor)
337c4277cb6SPeter Xu 		mode |= UFFDIO_REGISTER_MODE_MINOR;
338c4277cb6SPeter Xu 
339c4277cb6SPeter Xu 	uffdio_register.range.start = (unsigned long)addr;
340c4277cb6SPeter Xu 	uffdio_register.range.len = len;
341c4277cb6SPeter Xu 	uffdio_register.mode = mode;
342c4277cb6SPeter Xu 
343c4277cb6SPeter Xu 	if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
344c4277cb6SPeter Xu 		ret = -errno;
345c3315502SPeter Xu 	else if (ioctls)
346c3315502SPeter Xu 		*ioctls = uffdio_register.ioctls;
347c4277cb6SPeter Xu 
348c4277cb6SPeter Xu 	return ret;
349c4277cb6SPeter Xu }
350c4277cb6SPeter Xu 
351c3315502SPeter Xu int uffd_register(int uffd, void *addr, uint64_t len,
352c3315502SPeter Xu 		  bool miss, bool wp, bool minor)
353c3315502SPeter Xu {
354c3315502SPeter Xu 	return uffd_register_with_ioctls(uffd, addr, len,
355c3315502SPeter Xu 					 miss, wp, minor, NULL);
356c3315502SPeter Xu }
357c3315502SPeter Xu 
358c4277cb6SPeter Xu int uffd_unregister(int uffd, void *addr, uint64_t len)
359c4277cb6SPeter Xu {
360c4277cb6SPeter Xu 	struct uffdio_range range = { .start = (uintptr_t)addr, .len = len };
361c4277cb6SPeter Xu 	int ret = 0;
362c4277cb6SPeter Xu 
363c4277cb6SPeter Xu 	if (ioctl(uffd, UFFDIO_UNREGISTER, &range) == -1)
364c4277cb6SPeter Xu 		ret = -errno;
365c4277cb6SPeter Xu 
366c4277cb6SPeter Xu 	return ret;
367c4277cb6SPeter Xu }
368c8b90731SBreno Leitao 
369c8b90731SBreno Leitao unsigned long get_free_hugepages(void)
370c8b90731SBreno Leitao {
371c8b90731SBreno Leitao 	unsigned long fhp = 0;
372c8b90731SBreno Leitao 	char *line = NULL;
373c8b90731SBreno Leitao 	size_t linelen = 0;
374c8b90731SBreno Leitao 	FILE *f = fopen("/proc/meminfo", "r");
375c8b90731SBreno Leitao 
376c8b90731SBreno Leitao 	if (!f)
377c8b90731SBreno Leitao 		return fhp;
378c8b90731SBreno Leitao 	while (getline(&line, &linelen, f) > 0) {
379c8b90731SBreno Leitao 		if (sscanf(line, "HugePages_Free:      %lu", &fhp) == 1)
380c8b90731SBreno Leitao 			break;
381c8b90731SBreno Leitao 	}
382c8b90731SBreno Leitao 
383c8b90731SBreno Leitao 	free(line);
384c8b90731SBreno Leitao 	fclose(f);
385c8b90731SBreno Leitao 	return fhp;
386c8b90731SBreno Leitao }
387