Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: mm: add pagemap ioctl tests

Add pagemap ioctl tests. Add several different types of tests to judge
the correction of the interface.

Link: https://lkml.kernel.org/r/20230821141518.870589-7-usama.anjum@collabora.com
Signed-off-by: Muhammad Usama Anjum <usama.anjum@collabora.com>
Cc: Alex Sierra <alex.sierra@amd.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Andrei Vagin <avagin@gmail.com>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Gustavo A. R. Silva <gustavoars@kernel.org>
Cc: "Liam R. Howlett" <Liam.Howlett@oracle.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Michal Miroslaw <emmir@google.com>
Cc: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Cc: Mike Rapoport (IBM) <rppt@kernel.org>
Cc: Nadav Amit <namit@vmware.com>
Cc: Pasha Tatashin <pasha.tatashin@soleen.com>
Cc: Paul Gofman <pgofman@codeweavers.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Yang Shi <shy828301@gmail.com>
Cc: Yun Zhou <yun.zhou@windriver.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Muhammad Usama Anjum and committed by
Andrew Morton
46fd75d4 18825b8a

+1669 -1
+2
tools/testing/selftests/mm/.gitignore
··· 18 18 mremap_test 19 19 on-fault-limit 20 20 transhuge-stress 21 + pagemap_ioctl 22 + *.tmp* 21 23 protection_keys 22 24 protection_keys_32 23 25 protection_keys_64
+2 -1
tools/testing/selftests/mm/Makefile
··· 33 33 MAKEFLAGS += --no-builtin-rules 34 34 35 35 CFLAGS = -Wall -I $(top_srcdir) $(EXTRA_CFLAGS) $(KHDR_INCLUDES) 36 - LDLIBS = -lrt -lpthread 36 + LDLIBS = -lrt -lpthread -lm 37 37 38 38 TEST_GEN_FILES = cow 39 39 TEST_GEN_FILES += compaction_test ··· 60 60 TEST_GEN_FILES += mremap_dontunmap 61 61 TEST_GEN_FILES += mremap_test 62 62 TEST_GEN_FILES += on-fault-limit 63 + TEST_GEN_PROGS += pagemap_ioctl 63 64 TEST_GEN_FILES += thuge-gen 64 65 TEST_GEN_FILES += transhuge-stress 65 66 TEST_GEN_FILES += uffd-stress
+1
tools/testing/selftests/mm/config
··· 1 1 CONFIG_SYSVIPC=y 2 2 CONFIG_USERFAULTFD=y 3 + CONFIG_PTE_MARKER_UFFD_WP=y 3 4 CONFIG_TEST_VMALLOC=m 4 5 CONFIG_DEVICE_PRIVATE=y 5 6 CONFIG_TEST_HMM=m
+1660
tools/testing/selftests/mm/pagemap_ioctl.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #define _GNU_SOURCE 3 + #include <stdio.h> 4 + #include <fcntl.h> 5 + #include <string.h> 6 + #include <sys/mman.h> 7 + #include <errno.h> 8 + #include <malloc.h> 9 + #include "vm_util.h" 10 + #include "../kselftest.h" 11 + #include <linux/types.h> 12 + #include <linux/memfd.h> 13 + #include <linux/userfaultfd.h> 14 + #include <linux/fs.h> 15 + #include <sys/ioctl.h> 16 + #include <sys/stat.h> 17 + #include <math.h> 18 + #include <asm/unistd.h> 19 + #include <pthread.h> 20 + #include <sys/resource.h> 21 + #include <assert.h> 22 + #include <sys/ipc.h> 23 + #include <sys/shm.h> 24 + 25 + #define PAGEMAP_BITS_ALL (PAGE_IS_WPALLOWED | PAGE_IS_WRITTEN | \ 26 + PAGE_IS_FILE | PAGE_IS_PRESENT | \ 27 + PAGE_IS_SWAPPED | PAGE_IS_PFNZERO | \ 28 + PAGE_IS_HUGE) 29 + #define PAGEMAP_NON_WRITTEN_BITS (PAGE_IS_WPALLOWED | PAGE_IS_FILE | \ 30 + PAGE_IS_PRESENT | PAGE_IS_SWAPPED | \ 31 + PAGE_IS_PFNZERO | PAGE_IS_HUGE) 32 + 33 + #define TEST_ITERATIONS 100 34 + #define PAGEMAP "/proc/self/pagemap" 35 + int pagemap_fd; 36 + int uffd; 37 + int page_size; 38 + int hpage_size; 39 + 40 + #define LEN(region) ((region.end - region.start)/page_size) 41 + 42 + static long pagemap_ioctl(void *start, int len, void *vec, int vec_len, int flag, 43 + int max_pages, long required_mask, long anyof_mask, long excluded_mask, 44 + long return_mask) 45 + { 46 + struct pm_scan_arg arg; 47 + 48 + arg.start = (uintptr_t)start; 49 + arg.end = (uintptr_t)(start + len); 50 + arg.vec = (uintptr_t)vec; 51 + arg.vec_len = vec_len; 52 + arg.flags = flag; 53 + arg.size = sizeof(struct pm_scan_arg); 54 + arg.max_pages = max_pages; 55 + arg.category_mask = required_mask; 56 + arg.category_anyof_mask = anyof_mask; 57 + arg.category_inverted = excluded_mask; 58 + arg.return_mask = return_mask; 59 + 60 + return ioctl(pagemap_fd, PAGEMAP_SCAN, &arg); 61 + } 62 + 63 + static long pagemap_ioc(void *start, int len, void *vec, int vec_len, int flag, 64 + int max_pages, long required_mask, long anyof_mask, long excluded_mask, 65 + long return_mask, long *walk_end) 66 + { 67 + struct pm_scan_arg arg; 68 + int ret; 69 + 70 + arg.start = (uintptr_t)start; 71 + arg.end = (uintptr_t)(start + len); 72 + arg.vec = (uintptr_t)vec; 73 + arg.vec_len = vec_len; 74 + arg.flags = flag; 75 + arg.size = sizeof(struct pm_scan_arg); 76 + arg.max_pages = max_pages; 77 + arg.category_mask = required_mask; 78 + arg.category_anyof_mask = anyof_mask; 79 + arg.category_inverted = excluded_mask; 80 + arg.return_mask = return_mask; 81 + 82 + ret = ioctl(pagemap_fd, PAGEMAP_SCAN, &arg); 83 + 84 + if (walk_end) 85 + *walk_end = arg.walk_end; 86 + 87 + return ret; 88 + } 89 + 90 + 91 + int init_uffd(void) 92 + { 93 + struct uffdio_api uffdio_api; 94 + 95 + uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY); 96 + if (uffd == -1) 97 + ksft_exit_fail_msg("uffd syscall failed\n"); 98 + 99 + uffdio_api.api = UFFD_API; 100 + uffdio_api.features = UFFD_FEATURE_WP_UNPOPULATED | UFFD_FEATURE_WP_ASYNC | 101 + UFFD_FEATURE_WP_HUGETLBFS_SHMEM; 102 + if (ioctl(uffd, UFFDIO_API, &uffdio_api)) 103 + ksft_exit_fail_msg("UFFDIO_API\n"); 104 + 105 + if (!(uffdio_api.api & UFFDIO_REGISTER_MODE_WP) || 106 + !(uffdio_api.features & UFFD_FEATURE_WP_UNPOPULATED) || 107 + !(uffdio_api.features & UFFD_FEATURE_WP_ASYNC) || 108 + !(uffdio_api.features & UFFD_FEATURE_WP_HUGETLBFS_SHMEM)) 109 + ksft_exit_fail_msg("UFFDIO_API error %llu\n", uffdio_api.api); 110 + 111 + return 0; 112 + } 113 + 114 + int wp_init(void *lpBaseAddress, int dwRegionSize) 115 + { 116 + struct uffdio_register uffdio_register; 117 + struct uffdio_writeprotect wp; 118 + 119 + uffdio_register.range.start = (unsigned long)lpBaseAddress; 120 + uffdio_register.range.len = dwRegionSize; 121 + uffdio_register.mode = UFFDIO_REGISTER_MODE_WP; 122 + if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) 123 + ksft_exit_fail_msg("ioctl(UFFDIO_REGISTER) %d %s\n", errno, strerror(errno)); 124 + 125 + if (!(uffdio_register.ioctls & UFFDIO_WRITEPROTECT)) 126 + ksft_exit_fail_msg("ioctl set is incorrect\n"); 127 + 128 + wp.range.start = (unsigned long)lpBaseAddress; 129 + wp.range.len = dwRegionSize; 130 + wp.mode = UFFDIO_WRITEPROTECT_MODE_WP; 131 + 132 + if (ioctl(uffd, UFFDIO_WRITEPROTECT, &wp)) 133 + ksft_exit_fail_msg("ioctl(UFFDIO_WRITEPROTECT)\n"); 134 + 135 + return 0; 136 + } 137 + 138 + int wp_free(void *lpBaseAddress, int dwRegionSize) 139 + { 140 + struct uffdio_register uffdio_register; 141 + 142 + uffdio_register.range.start = (unsigned long)lpBaseAddress; 143 + uffdio_register.range.len = dwRegionSize; 144 + uffdio_register.mode = UFFDIO_REGISTER_MODE_WP; 145 + if (ioctl(uffd, UFFDIO_UNREGISTER, &uffdio_register.range)) 146 + ksft_exit_fail_msg("ioctl unregister failure\n"); 147 + return 0; 148 + } 149 + 150 + int wp_addr_range(void *lpBaseAddress, int dwRegionSize) 151 + { 152 + if (pagemap_ioctl(lpBaseAddress, dwRegionSize, NULL, 0, 153 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 154 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN) < 0) 155 + ksft_exit_fail_msg("error %d %d %s\n", 1, errno, strerror(errno)); 156 + 157 + return 0; 158 + } 159 + 160 + void *gethugetlb_mem(int size, int *shmid) 161 + { 162 + char *mem; 163 + 164 + if (shmid) { 165 + *shmid = shmget(2, size, SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W); 166 + if (*shmid < 0) 167 + return NULL; 168 + 169 + mem = shmat(*shmid, 0, 0); 170 + if (mem == (char *)-1) { 171 + shmctl(*shmid, IPC_RMID, NULL); 172 + ksft_exit_fail_msg("Shared memory attach failure\n"); 173 + } 174 + } else { 175 + mem = mmap(NULL, size, PROT_READ | PROT_WRITE, 176 + MAP_ANONYMOUS | MAP_HUGETLB | MAP_PRIVATE, -1, 0); 177 + if (mem == MAP_FAILED) 178 + return NULL; 179 + } 180 + 181 + return mem; 182 + } 183 + 184 + int userfaultfd_tests(void) 185 + { 186 + int mem_size, vec_size, written, num_pages = 16; 187 + char *mem, *vec; 188 + 189 + mem_size = num_pages * page_size; 190 + mem = mmap(NULL, mem_size, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); 191 + if (mem == MAP_FAILED) 192 + ksft_exit_fail_msg("error nomem\n"); 193 + 194 + wp_init(mem, mem_size); 195 + 196 + /* Change protection of pages differently */ 197 + mprotect(mem, mem_size/8, PROT_READ|PROT_WRITE); 198 + mprotect(mem + 1 * mem_size/8, mem_size/8, PROT_READ); 199 + mprotect(mem + 2 * mem_size/8, mem_size/8, PROT_READ|PROT_WRITE); 200 + mprotect(mem + 3 * mem_size/8, mem_size/8, PROT_READ); 201 + mprotect(mem + 4 * mem_size/8, mem_size/8, PROT_READ|PROT_WRITE); 202 + mprotect(mem + 5 * mem_size/8, mem_size/8, PROT_NONE); 203 + mprotect(mem + 6 * mem_size/8, mem_size/8, PROT_READ|PROT_WRITE); 204 + mprotect(mem + 7 * mem_size/8, mem_size/8, PROT_READ); 205 + 206 + wp_addr_range(mem + (mem_size/16), mem_size - 2 * (mem_size/8)); 207 + wp_addr_range(mem, mem_size); 208 + 209 + vec_size = mem_size/page_size; 210 + vec = malloc(sizeof(struct page_region) * vec_size); 211 + 212 + written = pagemap_ioctl(mem, mem_size, vec, 1, PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 213 + vec_size - 2, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 214 + if (written < 0) 215 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 216 + 217 + ksft_test_result(written == 0, "%s all new pages must not be written (dirty)\n", __func__); 218 + 219 + wp_free(mem, mem_size); 220 + munmap(mem, mem_size); 221 + free(vec); 222 + return 0; 223 + } 224 + 225 + int get_reads(struct page_region *vec, int vec_size) 226 + { 227 + int i, sum = 0; 228 + 229 + for (i = 0; i < vec_size; i++) 230 + sum += LEN(vec[i]); 231 + 232 + return sum; 233 + } 234 + 235 + int sanity_tests_sd(void) 236 + { 237 + int mem_size, vec_size, ret, ret2, ret3, i, num_pages = 1000, total_pages = 0; 238 + int total_writes, total_reads, reads, count; 239 + struct page_region *vec, *vec2; 240 + char *mem, *m[2]; 241 + long walk_end; 242 + 243 + vec_size = num_pages/2; 244 + mem_size = num_pages * page_size; 245 + 246 + vec = malloc(sizeof(struct page_region) * vec_size); 247 + if (!vec) 248 + ksft_exit_fail_msg("error nomem\n"); 249 + 250 + vec2 = malloc(sizeof(struct page_region) * vec_size); 251 + if (!vec2) 252 + ksft_exit_fail_msg("error nomem\n"); 253 + 254 + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 255 + if (mem == MAP_FAILED) 256 + ksft_exit_fail_msg("error nomem\n"); 257 + 258 + wp_init(mem, mem_size); 259 + wp_addr_range(mem, mem_size); 260 + 261 + /* 1. wrong operation */ 262 + ksft_test_result(pagemap_ioctl(mem, 0, vec, vec_size, 0, 263 + 0, PAGEMAP_BITS_ALL, 0, 0, PAGEMAP_BITS_ALL) == 0, 264 + "%s Zero range size is valid\n", __func__); 265 + 266 + ksft_test_result(pagemap_ioctl(mem, mem_size, NULL, vec_size, 0, 267 + 0, PAGEMAP_BITS_ALL, 0, 0, PAGEMAP_BITS_ALL) < 0, 268 + "%s output buffer must be specified with size\n", __func__); 269 + 270 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, 0, 0, 271 + 0, PAGEMAP_BITS_ALL, 0, 0, PAGEMAP_BITS_ALL) == 0, 272 + "%s output buffer can be 0\n", __func__); 273 + 274 + ksft_test_result(pagemap_ioctl(mem, mem_size, 0, 0, 0, 275 + 0, PAGEMAP_BITS_ALL, 0, 0, PAGEMAP_BITS_ALL) == 0, 276 + "%s output buffer can be 0\n", __func__); 277 + 278 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, -1, 279 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN) < 0, 280 + "%s wrong flag specified\n", __func__); 281 + 282 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 283 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC | 0xFF, 284 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN) < 0, 285 + "%s flag has extra bits specified\n", __func__); 286 + 287 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 288 + 0, 0, 0, 0, PAGE_IS_WRITTEN) >= 0, 289 + "%s no selection mask is specified\n", __func__); 290 + 291 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 292 + 0, PAGE_IS_WRITTEN, PAGE_IS_WRITTEN, 0, 0) == 0, 293 + "%s no return mask is specified\n", __func__); 294 + 295 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 296 + 0, PAGE_IS_WRITTEN, 0, 0, 0x1000) < 0, 297 + "%s wrong return mask specified\n", __func__); 298 + 299 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 300 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 301 + 0, 0xFFF, PAGE_IS_WRITTEN, 0, PAGE_IS_WRITTEN) < 0, 302 + "%s mixture of correct and wrong flag\n", __func__); 303 + 304 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 305 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 306 + 0, 0, 0, PAGEMAP_BITS_ALL, PAGE_IS_WRITTEN) >= 0, 307 + "%s PAGEMAP_BITS_ALL can be specified with PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC\n", 308 + __func__); 309 + 310 + /* 2. Clear area with larger vec size */ 311 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 312 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 0, 313 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 314 + ksft_test_result(ret >= 0, "%s Clear area with larger vec size\n", __func__); 315 + 316 + /* 3. Repeated pattern of written and non-written pages */ 317 + for (i = 0; i < mem_size; i += 2 * page_size) 318 + mem[i]++; 319 + 320 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, PAGE_IS_WRITTEN, 0, 321 + 0, PAGE_IS_WRITTEN); 322 + if (ret < 0) 323 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 324 + 325 + ksft_test_result(ret == mem_size/(page_size * 2), 326 + "%s Repeated pattern of written and non-written pages\n", __func__); 327 + 328 + /* 4. Repeated pattern of written and non-written pages in parts */ 329 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 330 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 331 + num_pages/2 - 2, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 332 + if (ret < 0) 333 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 334 + 335 + ret2 = pagemap_ioctl(mem, mem_size, vec, 2, 0, 0, PAGE_IS_WRITTEN, 0, 0, 336 + PAGE_IS_WRITTEN); 337 + if (ret2 < 0) 338 + ksft_exit_fail_msg("error %d %d %s\n", ret2, errno, strerror(errno)); 339 + 340 + ret3 = pagemap_ioctl(mem, mem_size, vec, vec_size, 341 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 342 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 343 + if (ret3 < 0) 344 + ksft_exit_fail_msg("error %d %d %s\n", ret3, errno, strerror(errno)); 345 + 346 + ksft_test_result((ret + ret3) == num_pages/2 && ret2 == 2, 347 + "%s Repeated pattern of written and non-written pages in parts %d %d %d\n", 348 + __func__, ret, ret3, ret2); 349 + 350 + /* 5. Repeated pattern of written and non-written pages max_pages */ 351 + for (i = 0; i < mem_size; i += 2 * page_size) 352 + mem[i]++; 353 + mem[(mem_size/page_size - 1) * page_size]++; 354 + 355 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 356 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 357 + num_pages/2, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 358 + if (ret < 0) 359 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 360 + 361 + ret2 = pagemap_ioctl(mem, mem_size, vec, vec_size, 362 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 363 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 364 + if (ret2 < 0) 365 + ksft_exit_fail_msg("error %d %d %s\n", ret2, errno, strerror(errno)); 366 + 367 + ksft_test_result(ret == num_pages/2 && ret2 == 1, 368 + "%s Repeated pattern of written and non-written pages max_pages\n", 369 + __func__); 370 + 371 + /* 6. only get 2 dirty pages and clear them as well */ 372 + vec_size = mem_size/page_size; 373 + memset(mem, -1, mem_size); 374 + 375 + /* get and clear second and third pages */ 376 + ret = pagemap_ioctl(mem + page_size, 2 * page_size, vec, 1, 377 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 378 + 2, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 379 + if (ret < 0) 380 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 381 + 382 + ret2 = pagemap_ioctl(mem, mem_size, vec2, vec_size, 0, 0, 383 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 384 + if (ret2 < 0) 385 + ksft_exit_fail_msg("error %d %d %s\n", ret2, errno, strerror(errno)); 386 + 387 + ksft_test_result(ret == 1 && LEN(vec[0]) == 2 && 388 + vec[0].start == (uintptr_t)(mem + page_size) && 389 + ret2 == 2 && LEN(vec2[0]) == 1 && vec2[0].start == (uintptr_t)mem && 390 + LEN(vec2[1]) == vec_size - 3 && 391 + vec2[1].start == (uintptr_t)(mem + 3 * page_size), 392 + "%s only get 2 written pages and clear them as well\n", __func__); 393 + 394 + wp_free(mem, mem_size); 395 + munmap(mem, mem_size); 396 + 397 + /* 7. Two regions */ 398 + m[0] = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 399 + if (m[0] == MAP_FAILED) 400 + ksft_exit_fail_msg("error nomem\n"); 401 + m[1] = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 402 + if (m[1] == MAP_FAILED) 403 + ksft_exit_fail_msg("error nomem\n"); 404 + 405 + wp_init(m[0], mem_size); 406 + wp_init(m[1], mem_size); 407 + wp_addr_range(m[0], mem_size); 408 + wp_addr_range(m[1], mem_size); 409 + 410 + memset(m[0], 'a', mem_size); 411 + memset(m[1], 'b', mem_size); 412 + 413 + wp_addr_range(m[0], mem_size); 414 + 415 + ret = pagemap_ioctl(m[1], mem_size, vec, 1, 0, 0, PAGE_IS_WRITTEN, 0, 0, 416 + PAGE_IS_WRITTEN); 417 + if (ret < 0) 418 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 419 + 420 + ksft_test_result(ret == 1 && LEN(vec[0]) == mem_size/page_size, 421 + "%s Two regions\n", __func__); 422 + 423 + wp_free(m[0], mem_size); 424 + wp_free(m[1], mem_size); 425 + munmap(m[0], mem_size); 426 + munmap(m[1], mem_size); 427 + 428 + free(vec); 429 + free(vec2); 430 + 431 + /* 8. Smaller vec */ 432 + mem_size = 1050 * page_size; 433 + vec_size = mem_size/(page_size*2); 434 + 435 + vec = malloc(sizeof(struct page_region) * vec_size); 436 + if (!vec) 437 + ksft_exit_fail_msg("error nomem\n"); 438 + 439 + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 440 + if (mem == MAP_FAILED) 441 + ksft_exit_fail_msg("error nomem\n"); 442 + 443 + wp_init(mem, mem_size); 444 + wp_addr_range(mem, mem_size); 445 + 446 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 447 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 0, 448 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 449 + if (ret < 0) 450 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 451 + 452 + for (i = 0; i < mem_size/page_size; i += 2) 453 + mem[i * page_size]++; 454 + 455 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 456 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 457 + mem_size/(page_size*5), PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 458 + if (ret < 0) 459 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 460 + 461 + total_pages += ret; 462 + 463 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 464 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 465 + mem_size/(page_size*5), PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 466 + if (ret < 0) 467 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 468 + 469 + total_pages += ret; 470 + 471 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 472 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 473 + mem_size/(page_size*5), PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 474 + if (ret < 0) 475 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 476 + 477 + total_pages += ret; 478 + 479 + ksft_test_result(total_pages == mem_size/(page_size*2), "%s Smaller max_pages\n", __func__); 480 + 481 + free(vec); 482 + wp_free(mem, mem_size); 483 + munmap(mem, mem_size); 484 + total_pages = 0; 485 + 486 + /* 9. Smaller vec */ 487 + mem_size = 10000 * page_size; 488 + vec_size = 50; 489 + 490 + vec = malloc(sizeof(struct page_region) * vec_size); 491 + if (!vec) 492 + ksft_exit_fail_msg("error nomem\n"); 493 + 494 + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 495 + if (mem == MAP_FAILED) 496 + ksft_exit_fail_msg("error nomem\n"); 497 + 498 + wp_init(mem, mem_size); 499 + wp_addr_range(mem, mem_size); 500 + 501 + for (count = 0; count < TEST_ITERATIONS; count++) { 502 + total_writes = total_reads = 0; 503 + walk_end = (long)mem; 504 + 505 + for (i = 0; i < mem_size; i += page_size) { 506 + if (rand() % 2) { 507 + mem[i]++; 508 + total_writes++; 509 + } 510 + } 511 + 512 + while (total_reads < total_writes) { 513 + ret = pagemap_ioc((void *)walk_end, mem_size-(walk_end - (long)mem), vec, 514 + vec_size, PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 515 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 516 + if (ret < 0) 517 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 518 + 519 + if (ret > vec_size) 520 + break; 521 + 522 + reads = get_reads(vec, ret); 523 + total_reads += reads; 524 + } 525 + 526 + if (total_reads != total_writes) 527 + break; 528 + } 529 + 530 + ksft_test_result(count == TEST_ITERATIONS, "Smaller vec\n"); 531 + 532 + free(vec); 533 + wp_free(mem, mem_size); 534 + munmap(mem, mem_size); 535 + 536 + /* 10. Walk_end tester */ 537 + vec_size = 1000; 538 + mem_size = vec_size * page_size; 539 + 540 + vec = malloc(sizeof(struct page_region) * vec_size); 541 + if (!vec) 542 + ksft_exit_fail_msg("error nomem\n"); 543 + 544 + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 545 + if (mem == MAP_FAILED) 546 + ksft_exit_fail_msg("error nomem\n"); 547 + 548 + wp_init(mem, mem_size); 549 + wp_addr_range(mem, mem_size); 550 + 551 + memset(mem, 0, mem_size); 552 + 553 + ret = pagemap_ioc(mem, 0, vec, vec_size, 0, 554 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 555 + if (ret < 0) 556 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 557 + ksft_test_result(ret == 0 && walk_end == (long)mem, 558 + "Walk_end: Same start and end address\n"); 559 + 560 + ret = pagemap_ioc(mem, 0, vec, vec_size, PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 561 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 562 + if (ret < 0) 563 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 564 + ksft_test_result(ret == 0 && walk_end == (long)mem, 565 + "Walk_end: Same start and end with WP\n"); 566 + 567 + ret = pagemap_ioc(mem, 0, vec, 0, PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 568 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 569 + if (ret < 0) 570 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 571 + ksft_test_result(ret == 0 && walk_end == (long)mem, 572 + "Walk_end: Same start and end with 0 output buffer\n"); 573 + 574 + ret = pagemap_ioc(mem, mem_size, vec, vec_size, 0, 575 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 576 + if (ret < 0) 577 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 578 + ksft_test_result(ret == 1 && walk_end == (long)(mem + mem_size), 579 + "Walk_end: Big vec\n"); 580 + 581 + ret = pagemap_ioc(mem, mem_size, vec, 1, 0, 582 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 583 + if (ret < 0) 584 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 585 + ksft_test_result(ret == 1 && walk_end == (long)(mem + mem_size), 586 + "Walk_end: vec of minimum length\n"); 587 + 588 + ret = pagemap_ioc(mem, mem_size, vec, 1, 0, 589 + vec_size, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 590 + if (ret < 0) 591 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 592 + ksft_test_result(ret == 1 && walk_end == (long)(mem + mem_size), 593 + "Walk_end: Max pages specified\n"); 594 + 595 + ret = pagemap_ioc(mem, mem_size, vec, vec_size, 0, 596 + vec_size/2, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 597 + if (ret < 0) 598 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 599 + ksft_test_result(ret == 1 && walk_end == (long)(mem + mem_size/2), 600 + "Walk_end: Half max pages\n"); 601 + 602 + ret = pagemap_ioc(mem, mem_size, vec, vec_size, 0, 603 + 1, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 604 + if (ret < 0) 605 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 606 + ksft_test_result(ret == 1 && walk_end == (long)(mem + page_size), 607 + "Walk_end: 1 max page\n"); 608 + 609 + ret = pagemap_ioc(mem, mem_size, vec, vec_size, 0, 610 + -1, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 611 + if (ret < 0) 612 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 613 + ksft_test_result(ret == 1 && walk_end == (long)(mem + mem_size), 614 + "Walk_end: max pages\n"); 615 + 616 + wp_addr_range(mem, mem_size); 617 + for (i = 0; i < mem_size; i += 2 * page_size) 618 + mem[i]++; 619 + 620 + ret = pagemap_ioc(mem, mem_size, vec, vec_size, 0, 621 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 622 + if (ret < 0) 623 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 624 + ksft_test_result(ret == vec_size/2 && walk_end == (long)(mem + mem_size), 625 + "Walk_end sparse: Big vec\n"); 626 + 627 + ret = pagemap_ioc(mem, mem_size, vec, 1, 0, 628 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 629 + if (ret < 0) 630 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 631 + ksft_test_result(ret == 1 && walk_end == (long)(mem + page_size * 2), 632 + "Walk_end sparse: vec of minimum length\n"); 633 + 634 + ret = pagemap_ioc(mem, mem_size, vec, 1, 0, 635 + vec_size, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 636 + if (ret < 0) 637 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 638 + ksft_test_result(ret == 1 && walk_end == (long)(mem + page_size * 2), 639 + "Walk_end sparse: Max pages specified\n"); 640 + 641 + ret = pagemap_ioc(mem, mem_size, vec, vec_size/2, 0, 642 + vec_size, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 643 + if (ret < 0) 644 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 645 + ksft_test_result(ret == vec_size/2 && walk_end == (long)(mem + mem_size), 646 + "Walk_end sparse: Max pages specified\n"); 647 + 648 + ret = pagemap_ioc(mem, mem_size, vec, vec_size, 0, 649 + vec_size, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 650 + if (ret < 0) 651 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 652 + ksft_test_result(ret == vec_size/2 && walk_end == (long)(mem + mem_size), 653 + "Walk_end sparse: Max pages specified\n"); 654 + 655 + ret = pagemap_ioc(mem, mem_size, vec, vec_size, 0, 656 + vec_size/2, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 657 + if (ret < 0) 658 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 659 + ksft_test_result(ret == vec_size/2 && walk_end == (long)(mem + mem_size), 660 + "Walk_endsparse : Half max pages\n"); 661 + 662 + ret = pagemap_ioc(mem, mem_size, vec, vec_size, 0, 663 + 1, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN, &walk_end); 664 + if (ret < 0) 665 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 666 + ksft_test_result(ret == 1 && walk_end == (long)(mem + page_size * 2), 667 + "Walk_end: 1 max page\n"); 668 + 669 + free(vec); 670 + wp_free(mem, mem_size); 671 + munmap(mem, mem_size); 672 + 673 + return 0; 674 + } 675 + 676 + int base_tests(char *prefix, char *mem, int mem_size, int skip) 677 + { 678 + int vec_size, written; 679 + struct page_region *vec, *vec2; 680 + 681 + if (skip) { 682 + ksft_test_result_skip("%s all new pages must not be written (dirty)\n", prefix); 683 + ksft_test_result_skip("%s all pages must be written (dirty)\n", prefix); 684 + ksft_test_result_skip("%s all pages dirty other than first and the last one\n", 685 + prefix); 686 + ksft_test_result_skip("%s PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC\n", prefix); 687 + ksft_test_result_skip("%s only middle page dirty\n", prefix); 688 + ksft_test_result_skip("%s only two middle pages dirty\n", prefix); 689 + return 0; 690 + } 691 + 692 + vec_size = mem_size/page_size; 693 + vec = malloc(sizeof(struct page_region) * vec_size); 694 + vec2 = malloc(sizeof(struct page_region) * vec_size); 695 + 696 + /* 1. all new pages must be not be written (dirty) */ 697 + written = pagemap_ioctl(mem, mem_size, vec, 1, PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 698 + vec_size - 2, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 699 + if (written < 0) 700 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 701 + 702 + ksft_test_result(written == 0, "%s all new pages must not be written (dirty)\n", prefix); 703 + 704 + /* 2. all pages must be written */ 705 + memset(mem, -1, mem_size); 706 + 707 + written = pagemap_ioctl(mem, mem_size, vec, 1, 0, 0, PAGE_IS_WRITTEN, 0, 0, 708 + PAGE_IS_WRITTEN); 709 + if (written < 0) 710 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 711 + 712 + ksft_test_result(written == 1 && LEN(vec[0]) == mem_size/page_size, 713 + "%s all pages must be written (dirty)\n", prefix); 714 + 715 + /* 3. all pages dirty other than first and the last one */ 716 + written = pagemap_ioctl(mem, mem_size, vec, 1, PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 717 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 718 + if (written < 0) 719 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 720 + 721 + memset(mem + page_size, 0, mem_size - (2 * page_size)); 722 + 723 + written = pagemap_ioctl(mem, mem_size, vec, 1, PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 724 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 725 + if (written < 0) 726 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 727 + 728 + ksft_test_result(written == 1 && LEN(vec[0]) >= vec_size - 2 && LEN(vec[0]) <= vec_size, 729 + "%s all pages dirty other than first and the last one\n", prefix); 730 + 731 + written = pagemap_ioctl(mem, mem_size, vec, 1, 0, 0, 732 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 733 + if (written < 0) 734 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 735 + 736 + ksft_test_result(written == 0, 737 + "%s PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC\n", prefix); 738 + 739 + /* 4. only middle page dirty */ 740 + written = pagemap_ioctl(mem, mem_size, vec, 1, PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 741 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 742 + if (written < 0) 743 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 744 + 745 + mem[vec_size/2 * page_size]++; 746 + 747 + written = pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, PAGE_IS_WRITTEN, 748 + 0, 0, PAGE_IS_WRITTEN); 749 + if (written < 0) 750 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 751 + 752 + ksft_test_result(written == 1 && LEN(vec[0]) >= 1, 753 + "%s only middle page dirty\n", prefix); 754 + 755 + /* 5. only two middle pages dirty and walk over only middle pages */ 756 + written = pagemap_ioctl(mem, mem_size, vec, 1, PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 757 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN | PAGE_IS_HUGE); 758 + if (written < 0) 759 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 760 + 761 + mem[vec_size/2 * page_size]++; 762 + mem[(vec_size/2 + 1) * page_size]++; 763 + 764 + written = pagemap_ioctl(&mem[vec_size/2 * page_size], 2 * page_size, vec, 1, 0, 765 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN | PAGE_IS_HUGE); 766 + if (written < 0) 767 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 768 + 769 + ksft_test_result(written == 1 && vec[0].start == (uintptr_t)(&mem[vec_size/2 * page_size]) 770 + && LEN(vec[0]) == 2, 771 + "%s only two middle pages dirty\n", prefix); 772 + 773 + free(vec); 774 + free(vec2); 775 + return 0; 776 + } 777 + 778 + void *gethugepage(int map_size) 779 + { 780 + int ret; 781 + char *map; 782 + 783 + map = memalign(hpage_size, map_size); 784 + if (!map) 785 + ksft_exit_fail_msg("memalign failed %d %s\n", errno, strerror(errno)); 786 + 787 + ret = madvise(map, map_size, MADV_HUGEPAGE); 788 + if (ret) 789 + return NULL; 790 + 791 + memset(map, 0, map_size); 792 + 793 + return map; 794 + } 795 + 796 + int hpage_unit_tests(void) 797 + { 798 + char *map; 799 + int ret, ret2; 800 + size_t num_pages = 10; 801 + int map_size = hpage_size * num_pages; 802 + int vec_size = map_size/page_size; 803 + struct page_region *vec, *vec2; 804 + 805 + vec = malloc(sizeof(struct page_region) * vec_size); 806 + vec2 = malloc(sizeof(struct page_region) * vec_size); 807 + if (!vec || !vec2) 808 + ksft_exit_fail_msg("malloc failed\n"); 809 + 810 + map = gethugepage(map_size); 811 + if (map) { 812 + wp_init(map, map_size); 813 + wp_addr_range(map, map_size); 814 + 815 + /* 1. all new huge page must not be written (dirty) */ 816 + ret = pagemap_ioctl(map, map_size, vec, vec_size, 817 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 0, 818 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 819 + if (ret < 0) 820 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 821 + 822 + ksft_test_result(ret == 0, "%s all new huge page must not be written (dirty)\n", 823 + __func__); 824 + 825 + /* 2. all the huge page must not be written */ 826 + ret = pagemap_ioctl(map, map_size, vec, vec_size, 0, 0, 827 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 828 + if (ret < 0) 829 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 830 + 831 + ksft_test_result(ret == 0, "%s all the huge page must not be written\n", __func__); 832 + 833 + /* 3. all the huge page must be written and clear dirty as well */ 834 + memset(map, -1, map_size); 835 + ret = pagemap_ioctl(map, map_size, vec, vec_size, 836 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 837 + 0, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 838 + if (ret < 0) 839 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 840 + 841 + ksft_test_result(ret == 1 && vec[0].start == (uintptr_t)map && 842 + LEN(vec[0]) == vec_size && vec[0].categories == PAGE_IS_WRITTEN, 843 + "%s all the huge page must be written and clear\n", __func__); 844 + 845 + /* 4. only middle page written */ 846 + wp_free(map, map_size); 847 + free(map); 848 + map = gethugepage(map_size); 849 + wp_init(map, map_size); 850 + wp_addr_range(map, map_size); 851 + map[vec_size/2 * page_size]++; 852 + 853 + ret = pagemap_ioctl(map, map_size, vec, vec_size, 0, 0, 854 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 855 + if (ret < 0) 856 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 857 + 858 + ksft_test_result(ret == 1 && LEN(vec[0]) > 0, 859 + "%s only middle page written\n", __func__); 860 + 861 + wp_free(map, map_size); 862 + free(map); 863 + } else { 864 + ksft_test_result_skip("%s all new huge page must be written\n", __func__); 865 + ksft_test_result_skip("%s all the huge page must not be written\n", __func__); 866 + ksft_test_result_skip("%s all the huge page must be written and clear\n", __func__); 867 + ksft_test_result_skip("%s only middle page written\n", __func__); 868 + } 869 + 870 + /* 5. clear first half of huge page */ 871 + map = gethugepage(map_size); 872 + if (map) { 873 + wp_init(map, map_size); 874 + wp_addr_range(map, map_size); 875 + 876 + memset(map, 0, map_size); 877 + 878 + wp_addr_range(map, map_size/2); 879 + 880 + ret = pagemap_ioctl(map, map_size, vec, vec_size, 0, 0, 881 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 882 + if (ret < 0) 883 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 884 + 885 + ksft_test_result(ret == 1 && LEN(vec[0]) == vec_size/2 && 886 + vec[0].start == (uintptr_t)(map + map_size/2), 887 + "%s clear first half of huge page\n", __func__); 888 + wp_free(map, map_size); 889 + free(map); 890 + } else { 891 + ksft_test_result_skip("%s clear first half of huge page\n", __func__); 892 + } 893 + 894 + /* 6. clear first half of huge page with limited buffer */ 895 + map = gethugepage(map_size); 896 + if (map) { 897 + wp_init(map, map_size); 898 + wp_addr_range(map, map_size); 899 + 900 + memset(map, 0, map_size); 901 + 902 + ret = pagemap_ioctl(map, map_size, vec, vec_size, 903 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 904 + vec_size/2, PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 905 + if (ret < 0) 906 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 907 + 908 + ret = pagemap_ioctl(map, map_size, vec, vec_size, 0, 0, 909 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 910 + if (ret < 0) 911 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 912 + 913 + ksft_test_result(ret == 1 && LEN(vec[0]) == vec_size/2 && 914 + vec[0].start == (uintptr_t)(map + map_size/2), 915 + "%s clear first half of huge page with limited buffer\n", 916 + __func__); 917 + wp_free(map, map_size); 918 + free(map); 919 + } else { 920 + ksft_test_result_skip("%s clear first half of huge page with limited buffer\n", 921 + __func__); 922 + } 923 + 924 + /* 7. clear second half of huge page */ 925 + map = gethugepage(map_size); 926 + if (map) { 927 + wp_init(map, map_size); 928 + wp_addr_range(map, map_size); 929 + 930 + memset(map, -1, map_size); 931 + 932 + ret = pagemap_ioctl(map + map_size/2, map_size/2, vec, vec_size, 933 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, vec_size/2, 934 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 935 + if (ret < 0) 936 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 937 + 938 + ret = pagemap_ioctl(map, map_size, vec, vec_size, 0, 0, 939 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 940 + if (ret < 0) 941 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 942 + 943 + ksft_test_result(ret == 1 && LEN(vec[0]) == vec_size/2, 944 + "%s clear second half huge page\n", __func__); 945 + wp_free(map, map_size); 946 + free(map); 947 + } else { 948 + ksft_test_result_skip("%s clear second half huge page\n", __func__); 949 + } 950 + 951 + /* 8. get half huge page */ 952 + map = gethugepage(map_size); 953 + if (map) { 954 + wp_init(map, map_size); 955 + wp_addr_range(map, map_size); 956 + 957 + memset(map, -1, map_size); 958 + usleep(100); 959 + 960 + ret = pagemap_ioctl(map, map_size, vec, 1, 961 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 962 + hpage_size/(2*page_size), PAGE_IS_WRITTEN, 0, 0, 963 + PAGE_IS_WRITTEN); 964 + if (ret < 0) 965 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 966 + 967 + ksft_test_result(ret == 1 && LEN(vec[0]) == hpage_size/(2*page_size), 968 + "%s get half huge page\n", __func__); 969 + 970 + ret2 = pagemap_ioctl(map, map_size, vec, vec_size, 0, 0, 971 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN); 972 + if (ret2 < 0) 973 + ksft_exit_fail_msg("error %d %d %s\n", ret2, errno, strerror(errno)); 974 + 975 + ksft_test_result(ret2 == 1 && LEN(vec[0]) == (map_size - hpage_size/2)/page_size, 976 + "%s get half huge page\n", __func__); 977 + 978 + wp_free(map, map_size); 979 + free(map); 980 + } else { 981 + ksft_test_result_skip("%s get half huge page\n", __func__); 982 + ksft_test_result_skip("%s get half huge page\n", __func__); 983 + } 984 + 985 + free(vec); 986 + free(vec2); 987 + return 0; 988 + } 989 + 990 + int unmapped_region_tests(void) 991 + { 992 + void *start = (void *)0x10000000; 993 + int written, len = 0x00040000; 994 + int vec_size = len / page_size; 995 + struct page_region *vec = malloc(sizeof(struct page_region) * vec_size); 996 + 997 + /* 1. Get written pages */ 998 + written = pagemap_ioctl(start, len, vec, vec_size, 0, 0, 999 + PAGEMAP_NON_WRITTEN_BITS, 0, 0, PAGEMAP_NON_WRITTEN_BITS); 1000 + if (written < 0) 1001 + ksft_exit_fail_msg("error %d %d %s\n", written, errno, strerror(errno)); 1002 + 1003 + ksft_test_result(written >= 0, "%s Get status of pages\n", __func__); 1004 + 1005 + free(vec); 1006 + return 0; 1007 + } 1008 + 1009 + static void test_simple(void) 1010 + { 1011 + int i; 1012 + char *map; 1013 + struct page_region vec; 1014 + 1015 + map = aligned_alloc(page_size, page_size); 1016 + if (!map) 1017 + ksft_exit_fail_msg("aligned_alloc failed\n"); 1018 + 1019 + wp_init(map, page_size); 1020 + wp_addr_range(map, page_size); 1021 + 1022 + for (i = 0 ; i < TEST_ITERATIONS; i++) { 1023 + if (pagemap_ioctl(map, page_size, &vec, 1, 0, 0, 1024 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN) == 1) { 1025 + ksft_print_msg("written bit was 1, but should be 0 (i=%d)\n", i); 1026 + break; 1027 + } 1028 + 1029 + wp_addr_range(map, page_size); 1030 + /* Write something to the page to get the written bit enabled on the page */ 1031 + map[0]++; 1032 + 1033 + if (pagemap_ioctl(map, page_size, &vec, 1, 0, 0, 1034 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN) == 0) { 1035 + ksft_print_msg("written bit was 0, but should be 1 (i=%d)\n", i); 1036 + break; 1037 + } 1038 + 1039 + wp_addr_range(map, page_size); 1040 + } 1041 + wp_free(map, page_size); 1042 + free(map); 1043 + 1044 + ksft_test_result(i == TEST_ITERATIONS, "Test %s\n", __func__); 1045 + } 1046 + 1047 + int sanity_tests(void) 1048 + { 1049 + int mem_size, vec_size, ret, fd, i, buf_size; 1050 + struct page_region *vec; 1051 + char *mem, *fmem; 1052 + struct stat sbuf; 1053 + char *tmp_buf; 1054 + 1055 + /* 1. wrong operation */ 1056 + mem_size = 10 * page_size; 1057 + vec_size = mem_size / page_size; 1058 + 1059 + vec = malloc(sizeof(struct page_region) * vec_size); 1060 + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 1061 + if (mem == MAP_FAILED || vec == MAP_FAILED) 1062 + ksft_exit_fail_msg("error nomem\n"); 1063 + 1064 + wp_init(mem, mem_size); 1065 + wp_addr_range(mem, mem_size); 1066 + 1067 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 1068 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 1069 + 0, PAGEMAP_BITS_ALL, 0, 0, PAGEMAP_BITS_ALL) >= 0, 1070 + "%s WP op can be specified with !PAGE_IS_WRITTEN\n", __func__); 1071 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, 1072 + PAGEMAP_BITS_ALL, 0, 0, PAGEMAP_BITS_ALL) >= 0, 1073 + "%s required_mask specified\n", __func__); 1074 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, 1075 + 0, PAGEMAP_BITS_ALL, 0, PAGEMAP_BITS_ALL) >= 0, 1076 + "%s anyof_mask specified\n", __func__); 1077 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, 1078 + 0, 0, PAGEMAP_BITS_ALL, PAGEMAP_BITS_ALL) >= 0, 1079 + "%s excluded_mask specified\n", __func__); 1080 + ksft_test_result(pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, 1081 + PAGEMAP_BITS_ALL, PAGEMAP_BITS_ALL, 0, 1082 + PAGEMAP_BITS_ALL) >= 0, 1083 + "%s required_mask and anyof_mask specified\n", __func__); 1084 + wp_free(mem, mem_size); 1085 + munmap(mem, mem_size); 1086 + 1087 + /* 2. Get sd and present pages with anyof_mask */ 1088 + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 1089 + if (mem == MAP_FAILED) 1090 + ksft_exit_fail_msg("error nomem\n"); 1091 + wp_init(mem, mem_size); 1092 + wp_addr_range(mem, mem_size); 1093 + 1094 + memset(mem, 0, mem_size); 1095 + 1096 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, 1097 + 0, PAGEMAP_BITS_ALL, 0, PAGEMAP_BITS_ALL); 1098 + ksft_test_result(ret >= 0 && vec[0].start == (uintptr_t)mem && LEN(vec[0]) == vec_size && 1099 + (vec[0].categories & (PAGE_IS_WRITTEN | PAGE_IS_PRESENT)) == 1100 + (PAGE_IS_WRITTEN | PAGE_IS_PRESENT), 1101 + "%s Get sd and present pages with anyof_mask\n", __func__); 1102 + 1103 + /* 3. Get sd and present pages with required_mask */ 1104 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, 1105 + PAGEMAP_BITS_ALL, 0, 0, PAGEMAP_BITS_ALL); 1106 + ksft_test_result(ret >= 0 && vec[0].start == (uintptr_t)mem && LEN(vec[0]) == vec_size && 1107 + (vec[0].categories & (PAGE_IS_WRITTEN | PAGE_IS_PRESENT)) == 1108 + (PAGE_IS_WRITTEN | PAGE_IS_PRESENT), 1109 + "%s Get all the pages with required_mask\n", __func__); 1110 + 1111 + /* 4. Get sd and present pages with required_mask and anyof_mask */ 1112 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, 1113 + PAGE_IS_WRITTEN, PAGE_IS_PRESENT, 0, PAGEMAP_BITS_ALL); 1114 + ksft_test_result(ret >= 0 && vec[0].start == (uintptr_t)mem && LEN(vec[0]) == vec_size && 1115 + (vec[0].categories & (PAGE_IS_WRITTEN | PAGE_IS_PRESENT)) == 1116 + (PAGE_IS_WRITTEN | PAGE_IS_PRESENT), 1117 + "%s Get sd and present pages with required_mask and anyof_mask\n", 1118 + __func__); 1119 + 1120 + /* 5. Don't get sd pages */ 1121 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, 1122 + PAGE_IS_WRITTEN, 0, PAGE_IS_WRITTEN, PAGEMAP_BITS_ALL); 1123 + ksft_test_result(ret == 0, "%s Don't get sd pages\n", __func__); 1124 + 1125 + /* 6. Don't get present pages */ 1126 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 0, 0, 1127 + PAGE_IS_PRESENT, 0, PAGE_IS_PRESENT, PAGEMAP_BITS_ALL); 1128 + ksft_test_result(ret == 0, "%s Don't get present pages\n", __func__); 1129 + 1130 + wp_free(mem, mem_size); 1131 + munmap(mem, mem_size); 1132 + 1133 + /* 8. Find written present pages with return mask */ 1134 + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 1135 + if (mem == MAP_FAILED) 1136 + ksft_exit_fail_msg("error nomem\n"); 1137 + wp_init(mem, mem_size); 1138 + wp_addr_range(mem, mem_size); 1139 + 1140 + memset(mem, 0, mem_size); 1141 + 1142 + ret = pagemap_ioctl(mem, mem_size, vec, vec_size, 1143 + PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC, 0, 1144 + 0, PAGEMAP_BITS_ALL, 0, PAGE_IS_WRITTEN); 1145 + ksft_test_result(ret >= 0 && vec[0].start == (uintptr_t)mem && LEN(vec[0]) == vec_size && 1146 + vec[0].categories == PAGE_IS_WRITTEN, 1147 + "%s Find written present pages with return mask\n", __func__); 1148 + wp_free(mem, mem_size); 1149 + munmap(mem, mem_size); 1150 + 1151 + /* 9. Memory mapped file */ 1152 + fd = open(__FILE__, O_RDONLY); 1153 + if (fd < 0) 1154 + ksft_exit_fail_msg("%s Memory mapped file\n"); 1155 + 1156 + ret = stat(__FILE__, &sbuf); 1157 + if (ret < 0) 1158 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 1159 + 1160 + fmem = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 1161 + if (fmem == MAP_FAILED) 1162 + ksft_exit_fail_msg("error nomem %ld %s\n", errno, strerror(errno)); 1163 + 1164 + tmp_buf = malloc(sbuf.st_size); 1165 + memcpy(tmp_buf, fmem, sbuf.st_size); 1166 + 1167 + ret = pagemap_ioctl(fmem, sbuf.st_size, vec, vec_size, 0, 0, 1168 + 0, PAGEMAP_NON_WRITTEN_BITS, 0, PAGEMAP_NON_WRITTEN_BITS); 1169 + 1170 + ksft_test_result(ret >= 0 && vec[0].start == (uintptr_t)fmem && 1171 + LEN(vec[0]) == ceilf((float)sbuf.st_size/page_size) && 1172 + (vec[0].categories & PAGE_IS_FILE), 1173 + "%s Memory mapped file\n", __func__); 1174 + 1175 + munmap(fmem, sbuf.st_size); 1176 + close(fd); 1177 + 1178 + /* 10. Create and read/write to a memory mapped file */ 1179 + buf_size = page_size * 10; 1180 + 1181 + fd = open(__FILE__".tmp2", O_RDWR | O_CREAT, 0666); 1182 + if (fd < 0) 1183 + ksft_exit_fail_msg("Read/write to memory: %s\n", 1184 + strerror(errno)); 1185 + 1186 + for (i = 0; i < buf_size; i++) 1187 + if (write(fd, "c", 1) < 0) 1188 + ksft_exit_fail_msg("Create and read/write to a memory mapped file\n"); 1189 + 1190 + fmem = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); 1191 + if (fmem == MAP_FAILED) 1192 + ksft_exit_fail_msg("error nomem %ld %s\n", errno, strerror(errno)); 1193 + 1194 + wp_init(fmem, buf_size); 1195 + wp_addr_range(fmem, buf_size); 1196 + 1197 + for (i = 0; i < buf_size; i++) 1198 + fmem[i] = 'z'; 1199 + 1200 + msync(fmem, buf_size, MS_SYNC); 1201 + 1202 + ret = pagemap_ioctl(fmem, buf_size, vec, vec_size, 0, 0, 1203 + PAGE_IS_WRITTEN, PAGE_IS_PRESENT | PAGE_IS_SWAPPED | PAGE_IS_FILE, 0, 1204 + PAGEMAP_BITS_ALL); 1205 + 1206 + ksft_test_result(ret >= 0 && vec[0].start == (uintptr_t)fmem && 1207 + LEN(vec[0]) == (buf_size/page_size) && 1208 + (vec[0].categories & PAGE_IS_WRITTEN), 1209 + "%s Read/write to memory\n", __func__); 1210 + 1211 + wp_free(fmem, buf_size); 1212 + munmap(fmem, buf_size); 1213 + close(fd); 1214 + 1215 + free(vec); 1216 + return 0; 1217 + } 1218 + 1219 + int mprotect_tests(void) 1220 + { 1221 + int ret; 1222 + char *mem, *mem2; 1223 + struct page_region vec; 1224 + int pagemap_fd = open("/proc/self/pagemap", O_RDONLY); 1225 + 1226 + if (pagemap_fd < 0) { 1227 + fprintf(stderr, "open() failed\n"); 1228 + exit(1); 1229 + } 1230 + 1231 + /* 1. Map two pages */ 1232 + mem = mmap(0, 2 * page_size, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 1233 + if (mem == MAP_FAILED) 1234 + ksft_exit_fail_msg("error nomem\n"); 1235 + wp_init(mem, 2 * page_size); 1236 + wp_addr_range(mem, 2 * page_size); 1237 + 1238 + /* Populate both pages. */ 1239 + memset(mem, 1, 2 * page_size); 1240 + 1241 + ret = pagemap_ioctl(mem, 2 * page_size, &vec, 1, 0, 0, PAGE_IS_WRITTEN, 1242 + 0, 0, PAGE_IS_WRITTEN); 1243 + if (ret < 0) 1244 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 1245 + 1246 + ksft_test_result(ret == 1 && LEN(vec) == 2, "%s Both pages written\n", __func__); 1247 + 1248 + /* 2. Start tracking */ 1249 + wp_addr_range(mem, 2 * page_size); 1250 + 1251 + ksft_test_result(pagemap_ioctl(mem, 2 * page_size, &vec, 1, 0, 0, 1252 + PAGE_IS_WRITTEN, 0, 0, PAGE_IS_WRITTEN) == 0, 1253 + "%s Both pages are not written (dirty)\n", __func__); 1254 + 1255 + /* 3. Remap the second page */ 1256 + mem2 = mmap(mem + page_size, page_size, PROT_READ|PROT_WRITE, 1257 + MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0); 1258 + if (mem2 == MAP_FAILED) 1259 + ksft_exit_fail_msg("error nomem\n"); 1260 + wp_init(mem2, page_size); 1261 + wp_addr_range(mem2, page_size); 1262 + 1263 + /* Protect + unprotect. */ 1264 + mprotect(mem, page_size, PROT_NONE); 1265 + mprotect(mem, 2 * page_size, PROT_READ); 1266 + mprotect(mem, 2 * page_size, PROT_READ|PROT_WRITE); 1267 + 1268 + /* Modify both pages. */ 1269 + memset(mem, 2, 2 * page_size); 1270 + 1271 + /* Protect + unprotect. */ 1272 + mprotect(mem, page_size, PROT_NONE); 1273 + mprotect(mem, page_size, PROT_READ); 1274 + mprotect(mem, page_size, PROT_READ|PROT_WRITE); 1275 + 1276 + ret = pagemap_ioctl(mem, 2 * page_size, &vec, 1, 0, 0, PAGE_IS_WRITTEN, 1277 + 0, 0, PAGE_IS_WRITTEN); 1278 + if (ret < 0) 1279 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 1280 + 1281 + ksft_test_result(ret == 1 && LEN(vec) == 2, 1282 + "%s Both pages written after remap and mprotect\n", __func__); 1283 + 1284 + /* 4. Clear and make the pages written */ 1285 + wp_addr_range(mem, 2 * page_size); 1286 + 1287 + memset(mem, 'A', 2 * page_size); 1288 + 1289 + ret = pagemap_ioctl(mem, 2 * page_size, &vec, 1, 0, 0, PAGE_IS_WRITTEN, 1290 + 0, 0, PAGE_IS_WRITTEN); 1291 + if (ret < 0) 1292 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 1293 + 1294 + ksft_test_result(ret == 1 && LEN(vec) == 2, 1295 + "%s Clear and make the pages written\n", __func__); 1296 + 1297 + wp_free(mem, 2 * page_size); 1298 + munmap(mem, 2 * page_size); 1299 + return 0; 1300 + } 1301 + 1302 + /* transact test */ 1303 + static const unsigned int nthreads = 6, pages_per_thread = 32, access_per_thread = 8; 1304 + static pthread_barrier_t start_barrier, end_barrier; 1305 + static unsigned int extra_thread_faults; 1306 + static unsigned int iter_count = 1000; 1307 + static volatile int finish; 1308 + 1309 + static ssize_t get_dirty_pages_reset(char *mem, unsigned int count, 1310 + int reset, int page_size) 1311 + { 1312 + struct pm_scan_arg arg = {0}; 1313 + struct page_region rgns[256]; 1314 + int i, j, cnt, ret; 1315 + 1316 + arg.size = sizeof(struct pm_scan_arg); 1317 + arg.start = (uintptr_t)mem; 1318 + arg.max_pages = count; 1319 + arg.end = (uintptr_t)(mem + count * page_size); 1320 + arg.vec = (uintptr_t)rgns; 1321 + arg.vec_len = sizeof(rgns) / sizeof(*rgns); 1322 + if (reset) 1323 + arg.flags |= PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC; 1324 + arg.category_mask = PAGE_IS_WRITTEN; 1325 + arg.return_mask = PAGE_IS_WRITTEN; 1326 + 1327 + ret = ioctl(pagemap_fd, PAGEMAP_SCAN, &arg); 1328 + if (ret < 0) 1329 + ksft_exit_fail_msg("ioctl failed\n"); 1330 + 1331 + cnt = 0; 1332 + for (i = 0; i < ret; ++i) { 1333 + if (rgns[i].categories != PAGE_IS_WRITTEN) 1334 + ksft_exit_fail_msg("wrong flags\n"); 1335 + 1336 + for (j = 0; j < LEN(rgns[i]); ++j) 1337 + cnt++; 1338 + } 1339 + 1340 + return cnt; 1341 + } 1342 + 1343 + void *thread_proc(void *mem) 1344 + { 1345 + int *m = mem; 1346 + long curr_faults, faults; 1347 + struct rusage r; 1348 + unsigned int i; 1349 + int ret; 1350 + 1351 + if (getrusage(RUSAGE_THREAD, &r)) 1352 + ksft_exit_fail_msg("getrusage\n"); 1353 + 1354 + curr_faults = r.ru_minflt; 1355 + 1356 + while (!finish) { 1357 + ret = pthread_barrier_wait(&start_barrier); 1358 + if (ret && ret != PTHREAD_BARRIER_SERIAL_THREAD) 1359 + ksft_exit_fail_msg("pthread_barrier_wait\n"); 1360 + 1361 + for (i = 0; i < access_per_thread; ++i) 1362 + __atomic_add_fetch(m + i * (0x1000 / sizeof(*m)), 1, __ATOMIC_SEQ_CST); 1363 + 1364 + ret = pthread_barrier_wait(&end_barrier); 1365 + if (ret && ret != PTHREAD_BARRIER_SERIAL_THREAD) 1366 + ksft_exit_fail_msg("pthread_barrier_wait\n"); 1367 + 1368 + if (getrusage(RUSAGE_THREAD, &r)) 1369 + ksft_exit_fail_msg("getrusage\n"); 1370 + 1371 + faults = r.ru_minflt - curr_faults; 1372 + if (faults < access_per_thread) 1373 + ksft_exit_fail_msg("faults < access_per_thread"); 1374 + 1375 + __atomic_add_fetch(&extra_thread_faults, faults - access_per_thread, 1376 + __ATOMIC_SEQ_CST); 1377 + curr_faults = r.ru_minflt; 1378 + } 1379 + 1380 + return NULL; 1381 + } 1382 + 1383 + static void transact_test(int page_size) 1384 + { 1385 + unsigned int i, count, extra_pages; 1386 + pthread_t th; 1387 + char *mem; 1388 + int ret, c; 1389 + 1390 + if (pthread_barrier_init(&start_barrier, NULL, nthreads + 1)) 1391 + ksft_exit_fail_msg("pthread_barrier_init\n"); 1392 + 1393 + if (pthread_barrier_init(&end_barrier, NULL, nthreads + 1)) 1394 + ksft_exit_fail_msg("pthread_barrier_init\n"); 1395 + 1396 + mem = mmap(NULL, 0x1000 * nthreads * pages_per_thread, PROT_READ | PROT_WRITE, 1397 + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 1398 + if (mem == MAP_FAILED) 1399 + ksft_exit_fail_msg("Error mmap %s.\n", strerror(errno)); 1400 + 1401 + wp_init(mem, 0x1000 * nthreads * pages_per_thread); 1402 + wp_addr_range(mem, 0x1000 * nthreads * pages_per_thread); 1403 + 1404 + memset(mem, 0, 0x1000 * nthreads * pages_per_thread); 1405 + 1406 + count = get_dirty_pages_reset(mem, nthreads * pages_per_thread, 1, page_size); 1407 + ksft_test_result(count > 0, "%s count %d\n", __func__, count); 1408 + count = get_dirty_pages_reset(mem, nthreads * pages_per_thread, 1, page_size); 1409 + ksft_test_result(count == 0, "%s count %d\n", __func__, count); 1410 + 1411 + finish = 0; 1412 + for (i = 0; i < nthreads; ++i) 1413 + pthread_create(&th, NULL, thread_proc, mem + 0x1000 * i * pages_per_thread); 1414 + 1415 + extra_pages = 0; 1416 + for (i = 0; i < iter_count; ++i) { 1417 + count = 0; 1418 + 1419 + ret = pthread_barrier_wait(&start_barrier); 1420 + if (ret && ret != PTHREAD_BARRIER_SERIAL_THREAD) 1421 + ksft_exit_fail_msg("pthread_barrier_wait\n"); 1422 + 1423 + count = get_dirty_pages_reset(mem, nthreads * pages_per_thread, 1, 1424 + page_size); 1425 + 1426 + ret = pthread_barrier_wait(&end_barrier); 1427 + if (ret && ret != PTHREAD_BARRIER_SERIAL_THREAD) 1428 + ksft_exit_fail_msg("pthread_barrier_wait\n"); 1429 + 1430 + if (count > nthreads * access_per_thread) 1431 + ksft_exit_fail_msg("Too big count %d expected %d, iter %d\n", 1432 + count, nthreads * access_per_thread, i); 1433 + 1434 + c = get_dirty_pages_reset(mem, nthreads * pages_per_thread, 1, page_size); 1435 + count += c; 1436 + 1437 + if (c > nthreads * access_per_thread) { 1438 + ksft_test_result_fail(" %s count > nthreads\n", __func__); 1439 + return; 1440 + } 1441 + 1442 + if (count != nthreads * access_per_thread) { 1443 + /* 1444 + * The purpose of the test is to make sure that no page updates are lost 1445 + * when the page updates and read-resetting soft dirty flags are performed 1446 + * in parallel. However, it is possible that the application will get the 1447 + * soft dirty flags twice on the two consecutive read-resets. This seems 1448 + * unavoidable as soft dirty flag is handled in software through page faults 1449 + * in kernel. While the updating the flags is supposed to be synchronized 1450 + * between page fault handling and read-reset, it is possible that 1451 + * read-reset happens after page fault PTE update but before the application 1452 + * re-executes write instruction. So read-reset gets the flag, clears write 1453 + * access and application gets page fault again for the same write. 1454 + */ 1455 + if (count < nthreads * access_per_thread) { 1456 + ksft_test_result_fail("Lost update, iter %d, %d vs %d.\n", i, count, 1457 + nthreads * access_per_thread); 1458 + return; 1459 + } 1460 + 1461 + extra_pages += count - nthreads * access_per_thread; 1462 + } 1463 + } 1464 + 1465 + pthread_barrier_wait(&start_barrier); 1466 + finish = 1; 1467 + pthread_barrier_wait(&end_barrier); 1468 + 1469 + ksft_test_result_pass("%s Extra pages %u (%.1lf%%), extra thread faults %d.\n", __func__, 1470 + extra_pages, 1471 + 100.0 * extra_pages / (iter_count * nthreads * access_per_thread), 1472 + extra_thread_faults); 1473 + } 1474 + 1475 + int main(void) 1476 + { 1477 + int mem_size, shmid, buf_size, fd, i, ret; 1478 + char *mem, *map, *fmem; 1479 + struct stat sbuf; 1480 + 1481 + ksft_print_header(); 1482 + ksft_set_plan(115); 1483 + 1484 + page_size = getpagesize(); 1485 + hpage_size = read_pmd_pagesize(); 1486 + 1487 + pagemap_fd = open(PAGEMAP, O_RDONLY); 1488 + if (pagemap_fd < 0) 1489 + return -EINVAL; 1490 + 1491 + if (init_uffd()) 1492 + ksft_exit_fail_msg("uffd init failed\n"); 1493 + 1494 + /* 1. Sanity testing */ 1495 + sanity_tests_sd(); 1496 + 1497 + /* 2. Normal page testing */ 1498 + mem_size = 10 * page_size; 1499 + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 1500 + if (mem == MAP_FAILED) 1501 + ksft_exit_fail_msg("error nomem\n"); 1502 + wp_init(mem, mem_size); 1503 + wp_addr_range(mem, mem_size); 1504 + 1505 + base_tests("Page testing:", mem, mem_size, 0); 1506 + 1507 + wp_free(mem, mem_size); 1508 + munmap(mem, mem_size); 1509 + 1510 + /* 3. Large page testing */ 1511 + mem_size = 512 * 10 * page_size; 1512 + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 1513 + if (mem == MAP_FAILED) 1514 + ksft_exit_fail_msg("error nomem\n"); 1515 + wp_init(mem, mem_size); 1516 + wp_addr_range(mem, mem_size); 1517 + 1518 + base_tests("Large Page testing:", mem, mem_size, 0); 1519 + 1520 + wp_free(mem, mem_size); 1521 + munmap(mem, mem_size); 1522 + 1523 + /* 4. Huge page testing */ 1524 + map = gethugepage(hpage_size); 1525 + if (map) { 1526 + wp_init(map, hpage_size); 1527 + wp_addr_range(map, hpage_size); 1528 + base_tests("Huge page testing:", map, hpage_size, 0); 1529 + wp_free(map, hpage_size); 1530 + free(map); 1531 + } else { 1532 + base_tests("Huge page testing:", NULL, 0, 1); 1533 + } 1534 + 1535 + /* 5. SHM Hugetlb page testing */ 1536 + mem_size = 2*1024*1024; 1537 + mem = gethugetlb_mem(mem_size, &shmid); 1538 + if (mem) { 1539 + wp_init(mem, mem_size); 1540 + wp_addr_range(mem, mem_size); 1541 + 1542 + base_tests("Hugetlb shmem testing:", mem, mem_size, 0); 1543 + 1544 + wp_free(mem, mem_size); 1545 + shmctl(shmid, IPC_RMID, NULL); 1546 + } else { 1547 + base_tests("Hugetlb shmem testing:", NULL, 0, 1); 1548 + } 1549 + 1550 + /* 6. Hugetlb page testing */ 1551 + mem = gethugetlb_mem(mem_size, NULL); 1552 + if (mem) { 1553 + wp_init(mem, mem_size); 1554 + wp_addr_range(mem, mem_size); 1555 + 1556 + base_tests("Hugetlb mem testing:", mem, mem_size, 0); 1557 + 1558 + wp_free(mem, mem_size); 1559 + } else { 1560 + base_tests("Hugetlb mem testing:", NULL, 0, 1); 1561 + } 1562 + 1563 + /* 7. File Hugetlb testing */ 1564 + mem_size = 2*1024*1024; 1565 + fd = memfd_create("uffd-test", MFD_HUGETLB | MFD_NOEXEC_SEAL); 1566 + mem = mmap(NULL, mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 1567 + if (mem) { 1568 + wp_init(mem, mem_size); 1569 + wp_addr_range(mem, mem_size); 1570 + 1571 + base_tests("Hugetlb shmem testing:", mem, mem_size, 0); 1572 + 1573 + wp_free(mem, mem_size); 1574 + shmctl(shmid, IPC_RMID, NULL); 1575 + } else { 1576 + base_tests("Hugetlb shmem testing:", NULL, 0, 1); 1577 + } 1578 + close(fd); 1579 + 1580 + /* 8. File memory testing */ 1581 + buf_size = page_size * 10; 1582 + 1583 + fd = open(__FILE__".tmp0", O_RDWR | O_CREAT, 0777); 1584 + if (fd < 0) 1585 + ksft_exit_fail_msg("Create and read/write to a memory mapped file: %s\n", 1586 + strerror(errno)); 1587 + 1588 + for (i = 0; i < buf_size; i++) 1589 + if (write(fd, "c", 1) < 0) 1590 + ksft_exit_fail_msg("Create and read/write to a memory mapped file\n"); 1591 + 1592 + ret = stat(__FILE__".tmp0", &sbuf); 1593 + if (ret < 0) 1594 + ksft_exit_fail_msg("error %d %d %s\n", ret, errno, strerror(errno)); 1595 + 1596 + fmem = mmap(NULL, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); 1597 + if (fmem == MAP_FAILED) 1598 + ksft_exit_fail_msg("error nomem %ld %s\n", errno, strerror(errno)); 1599 + 1600 + wp_init(fmem, sbuf.st_size); 1601 + wp_addr_range(fmem, sbuf.st_size); 1602 + 1603 + base_tests("File memory testing:", fmem, sbuf.st_size, 0); 1604 + 1605 + wp_free(fmem, sbuf.st_size); 1606 + munmap(fmem, sbuf.st_size); 1607 + close(fd); 1608 + 1609 + /* 9. File memory testing */ 1610 + buf_size = page_size * 10; 1611 + 1612 + fd = memfd_create(__FILE__".tmp00", MFD_NOEXEC_SEAL); 1613 + if (fd < 0) 1614 + ksft_exit_fail_msg("Create and read/write to a memory mapped file: %s\n", 1615 + strerror(errno)); 1616 + 1617 + if (ftruncate(fd, buf_size)) 1618 + ksft_exit_fail_msg("Error ftruncate\n"); 1619 + 1620 + for (i = 0; i < buf_size; i++) 1621 + if (write(fd, "c", 1) < 0) 1622 + ksft_exit_fail_msg("Create and read/write to a memory mapped file\n"); 1623 + 1624 + fmem = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); 1625 + if (fmem == MAP_FAILED) 1626 + ksft_exit_fail_msg("error nomem %ld %s\n", errno, strerror(errno)); 1627 + 1628 + wp_init(fmem, buf_size); 1629 + wp_addr_range(fmem, buf_size); 1630 + 1631 + base_tests("File anonymous memory testing:", fmem, buf_size, 0); 1632 + 1633 + wp_free(fmem, buf_size); 1634 + munmap(fmem, buf_size); 1635 + close(fd); 1636 + 1637 + /* 10. Huge page tests */ 1638 + hpage_unit_tests(); 1639 + 1640 + /* 11. Iterative test */ 1641 + test_simple(); 1642 + 1643 + /* 12. Mprotect test */ 1644 + mprotect_tests(); 1645 + 1646 + /* 13. Transact test */ 1647 + transact_test(page_size); 1648 + 1649 + /* 14. Sanity testing */ 1650 + sanity_tests(); 1651 + 1652 + /*15. Unmapped address test */ 1653 + unmapped_region_tests(); 1654 + 1655 + /* 16. Userfaultfd tests */ 1656 + userfaultfd_tests(); 1657 + 1658 + close(pagemap_fd); 1659 + return ksft_exit_pass(); 1660 + }
+4
tools/testing/selftests/mm/run_vmtests.sh
··· 56 56 memory protection key tests 57 57 - soft_dirty 58 58 test soft dirty page bit semantics 59 + - pagemap 60 + test pagemap_scan IOCTL 59 61 - cow 60 62 test copy-on-write semantics 61 63 - thp ··· 343 341 then 344 342 CATEGORY="soft_dirty" run_test ./soft-dirty 345 343 fi 344 + 345 + CATEGORY="pagemap" run_test ./pagemap_ioctl 346 346 347 347 # COW tests 348 348 CATEGORY="cow" run_test ./cow