1 // SPDX-License-Identifier: GPL-2.0
5 #include <linux/userfaultfd.h>
6 #include <sys/syscall.h>
8 #include "../kselftest.h"
11 #define PMD_SIZE_FILE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size"
12 #define SMAP_FILE_PATH "/proc/self/smaps"
13 #define MAX_LINE_LENGTH 500
15 unsigned int __page_size;
16 unsigned int __page_shift;
18 uint64_t pagemap_get_entry(int fd, char *start)
20 const unsigned long pfn = (unsigned long)start / getpagesize();
24 ret = pread(fd, &entry, sizeof(entry), pfn * sizeof(entry));
25 if (ret != sizeof(entry))
26 ksft_exit_fail_msg("reading pagemap failed\n");
30 bool pagemap_is_softdirty(int fd, char *start)
32 return pagemap_get_entry(fd, start) & PM_SOFT_DIRTY;
35 bool pagemap_is_swapped(int fd, char *start)
37 return pagemap_get_entry(fd, start) & PM_SWAP;
40 bool pagemap_is_populated(int fd, char *start)
42 return pagemap_get_entry(fd, start) & (PM_PRESENT | PM_SWAP);
45 unsigned long pagemap_get_pfn(int fd, char *start)
47 uint64_t entry = pagemap_get_entry(fd, start);
49 /* If present (63th bit), PFN is at bit 0 -- 54. */
50 if (entry & PM_PRESENT)
51 return entry & 0x007fffffffffffffull;
55 void clear_softdirty(void)
58 const char *ctrl = "4";
59 int fd = open("/proc/self/clear_refs", O_WRONLY);
62 ksft_exit_fail_msg("opening clear_refs failed\n");
63 ret = write(fd, ctrl, strlen(ctrl));
65 if (ret != strlen(ctrl))
66 ksft_exit_fail_msg("writing clear_refs failed\n");
69 bool check_for_pattern(FILE *fp, const char *pattern, char *buf, size_t len)
71 while (fgets(buf, len, fp)) {
72 if (!strncmp(buf, pattern, strlen(pattern)))
78 uint64_t read_pmd_pagesize(void)
84 fd = open(PMD_SIZE_FILE_PATH, O_RDONLY);
88 num_read = read(fd, buf, 19);
96 return strtoul(buf, NULL, 10);
99 bool __check_huge(void *addr, char *pattern, int nr_hpages,
105 char buffer[MAX_LINE_LENGTH];
106 char addr_pattern[MAX_LINE_LENGTH];
108 ret = snprintf(addr_pattern, MAX_LINE_LENGTH, "%08lx-",
109 (unsigned long) addr);
110 if (ret >= MAX_LINE_LENGTH)
111 ksft_exit_fail_msg("%s: Pattern is too long\n", __func__);
113 fp = fopen(SMAP_FILE_PATH, "r");
115 ksft_exit_fail_msg("%s: Failed to open file %s\n", __func__, SMAP_FILE_PATH);
117 if (!check_for_pattern(fp, addr_pattern, buffer, sizeof(buffer)))
121 * Fetch the pattern in the same block and check the number of
124 if (!check_for_pattern(fp, pattern, buffer, sizeof(buffer)))
127 snprintf(addr_pattern, MAX_LINE_LENGTH, "%s%%9ld kB", pattern);
129 if (sscanf(buffer, addr_pattern, &thp) != 1)
130 ksft_exit_fail_msg("Reading smap error\n");
134 return thp == (nr_hpages * (hpage_size >> 10));
137 bool check_huge_anon(void *addr, int nr_hpages, uint64_t hpage_size)
139 return __check_huge(addr, "AnonHugePages: ", nr_hpages, hpage_size);
142 bool check_huge_file(void *addr, int nr_hpages, uint64_t hpage_size)
144 return __check_huge(addr, "FilePmdMapped:", nr_hpages, hpage_size);
147 bool check_huge_shmem(void *addr, int nr_hpages, uint64_t hpage_size)
149 return __check_huge(addr, "ShmemPmdMapped:", nr_hpages, hpage_size);
152 int64_t allocate_transhuge(void *ptr, int pagemap_fd)
157 if (mmap(ptr, HPAGE_SIZE, PROT_READ | PROT_WRITE,
158 MAP_FIXED | MAP_ANONYMOUS |
159 MAP_NORESERVE | MAP_PRIVATE, -1, 0) != ptr)
160 errx(2, "mmap transhuge");
162 if (madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE))
163 err(2, "MADV_HUGEPAGE");
165 /* allocate transparent huge page */
166 *(volatile void **)ptr = ptr;
168 if (pread(pagemap_fd, ent, sizeof(ent),
169 (uintptr_t)ptr >> (pshift() - 3)) != sizeof(ent))
170 err(2, "read pagemap");
172 if (PAGEMAP_PRESENT(ent[0]) && PAGEMAP_PRESENT(ent[1]) &&
173 PAGEMAP_PFN(ent[0]) + 1 == PAGEMAP_PFN(ent[1]) &&
174 !(PAGEMAP_PFN(ent[0]) & ((1 << (HPAGE_SHIFT - pshift())) - 1)))
175 return PAGEMAP_PFN(ent[0]);
180 unsigned long default_huge_page_size(void)
182 unsigned long hps = 0;
185 FILE *f = fopen("/proc/meminfo", "r");
189 while (getline(&line, &linelen, f) > 0) {
190 if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) {
201 /* If `ioctls' non-NULL, the allowed ioctls will be returned into the var */
202 int uffd_register_with_ioctls(int uffd, void *addr, uint64_t len,
203 bool miss, bool wp, bool minor, uint64_t *ioctls)
205 struct uffdio_register uffdio_register = { 0 };
210 mode |= UFFDIO_REGISTER_MODE_MISSING;
212 mode |= UFFDIO_REGISTER_MODE_WP;
214 mode |= UFFDIO_REGISTER_MODE_MINOR;
216 uffdio_register.range.start = (unsigned long)addr;
217 uffdio_register.range.len = len;
218 uffdio_register.mode = mode;
220 if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
223 *ioctls = uffdio_register.ioctls;
228 int uffd_register(int uffd, void *addr, uint64_t len,
229 bool miss, bool wp, bool minor)
231 return uffd_register_with_ioctls(uffd, addr, len,
232 miss, wp, minor, NULL);
235 int uffd_unregister(int uffd, void *addr, uint64_t len)
237 struct uffdio_range range = { .start = (uintptr_t)addr, .len = len };
240 if (ioctl(uffd, UFFDIO_UNREGISTER, &range) == -1)
246 int uffd_open_dev(unsigned int flags)
250 fd = open("/dev/userfaultfd", O_RDWR | O_CLOEXEC);
253 uffd = ioctl(fd, USERFAULTFD_IOC_NEW, flags);
259 int uffd_open_sys(unsigned int flags)
261 #ifdef __NR_userfaultfd
262 return syscall(__NR_userfaultfd, flags);
268 int uffd_open(unsigned int flags)
270 int uffd = uffd_open_sys(flags);
273 uffd = uffd_open_dev(flags);
278 int uffd_get_features(uint64_t *features)
280 struct uffdio_api uffdio_api = { .api = UFFD_API, .features = 0 };
282 * This should by default work in most kernels; the feature list
283 * will be the same no matter what we pass in here.
285 int fd = uffd_open(UFFD_USER_MODE_ONLY);
288 /* Maybe the kernel is older than user-only mode? */
294 if (ioctl(fd, UFFDIO_API, &uffdio_api)) {
299 *features = uffdio_api.features;