1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * MADV_POPULATE_READ and MADV_POPULATE_WRITE tests 4 * 5 * Copyright 2021, Red Hat, Inc. 6 * 7 * Author(s): David Hildenbrand <david@redhat.com> 8 */ 9 #define _GNU_SOURCE 10 #include <stdlib.h> 11 #include <string.h> 12 #include <stdbool.h> 13 #include <stdint.h> 14 #include <unistd.h> 15 #include <errno.h> 16 #include <fcntl.h> 17 #include <linux/mman.h> 18 #include <sys/mman.h> 19 20 #include "../kselftest.h" 21 #include "vm_util.h" 22 23 /* 24 * For now, we're using 2 MiB of private anonymous memory for all tests. 25 */ 26 #define SIZE (2 * 1024 * 1024) 27 28 static size_t pagesize; 29 30 static void sense_support(void) 31 { 32 char *addr; 33 int ret; 34 35 addr = mmap(0, pagesize, PROT_READ | PROT_WRITE, 36 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 37 if (!addr) 38 ksft_exit_fail_msg("mmap failed\n"); 39 40 ret = madvise(addr, pagesize, MADV_POPULATE_READ); 41 if (ret) 42 ksft_exit_skip("MADV_POPULATE_READ is not available\n"); 43 44 ret = madvise(addr, pagesize, MADV_POPULATE_WRITE); 45 if (ret) 46 ksft_exit_skip("MADV_POPULATE_WRITE is not available\n"); 47 48 munmap(addr, pagesize); 49 } 50 51 static void test_prot_read(void) 52 { 53 char *addr; 54 int ret; 55 56 ksft_print_msg("[RUN] %s\n", __func__); 57 58 addr = mmap(0, SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 59 if (addr == MAP_FAILED) 60 ksft_exit_fail_msg("mmap failed\n"); 61 62 ret = madvise(addr, SIZE, MADV_POPULATE_READ); 63 ksft_test_result(!ret, "MADV_POPULATE_READ with PROT_READ\n"); 64 65 ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 66 ksft_test_result(ret == -1 && errno == EINVAL, 67 "MADV_POPULATE_WRITE with PROT_READ\n"); 68 69 munmap(addr, SIZE); 70 } 71 72 static void test_prot_write(void) 73 { 74 char *addr; 75 int ret; 76 77 ksft_print_msg("[RUN] %s\n", __func__); 78 79 addr = mmap(0, SIZE, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 80 if (addr == MAP_FAILED) 81 ksft_exit_fail_msg("mmap failed\n"); 82 83 ret = madvise(addr, SIZE, MADV_POPULATE_READ); 84 ksft_test_result(ret == -1 && errno == EINVAL, 85 "MADV_POPULATE_READ with PROT_WRITE\n"); 86 87 ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 88 ksft_test_result(!ret, "MADV_POPULATE_WRITE with PROT_WRITE\n"); 89 90 munmap(addr, SIZE); 91 } 92 93 static void test_holes(void) 94 { 95 char *addr; 96 int ret; 97 98 ksft_print_msg("[RUN] %s\n", __func__); 99 100 addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 101 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 102 if (addr == MAP_FAILED) 103 ksft_exit_fail_msg("mmap failed\n"); 104 ret = munmap(addr + pagesize, pagesize); 105 if (ret) 106 ksft_exit_fail_msg("munmap failed\n"); 107 108 /* Hole in the middle */ 109 ret = madvise(addr, SIZE, MADV_POPULATE_READ); 110 ksft_test_result(ret == -1 && errno == ENOMEM, 111 "MADV_POPULATE_READ with holes in the middle\n"); 112 ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 113 ksft_test_result(ret == -1 && errno == ENOMEM, 114 "MADV_POPULATE_WRITE with holes in the middle\n"); 115 116 /* Hole at end */ 117 ret = madvise(addr, 2 * pagesize, MADV_POPULATE_READ); 118 ksft_test_result(ret == -1 && errno == ENOMEM, 119 "MADV_POPULATE_READ with holes at the end\n"); 120 ret = madvise(addr, 2 * pagesize, MADV_POPULATE_WRITE); 121 ksft_test_result(ret == -1 && errno == ENOMEM, 122 "MADV_POPULATE_WRITE with holes at the end\n"); 123 124 /* Hole at beginning */ 125 ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_READ); 126 ksft_test_result(ret == -1 && errno == ENOMEM, 127 "MADV_POPULATE_READ with holes at the beginning\n"); 128 ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_WRITE); 129 ksft_test_result(ret == -1 && errno == ENOMEM, 130 "MADV_POPULATE_WRITE with holes at the beginning\n"); 131 132 munmap(addr, SIZE); 133 } 134 135 static bool range_is_populated(char *start, ssize_t size) 136 { 137 int fd = open("/proc/self/pagemap", O_RDONLY); 138 bool ret = true; 139 140 if (fd < 0) 141 ksft_exit_fail_msg("opening pagemap failed\n"); 142 for (; size > 0 && ret; size -= pagesize, start += pagesize) 143 if (!pagemap_is_populated(fd, start)) 144 ret = false; 145 close(fd); 146 return ret; 147 } 148 149 static bool range_is_not_populated(char *start, ssize_t size) 150 { 151 int fd = open("/proc/self/pagemap", O_RDONLY); 152 bool ret = true; 153 154 if (fd < 0) 155 ksft_exit_fail_msg("opening pagemap failed\n"); 156 for (; size > 0 && ret; size -= pagesize, start += pagesize) 157 if (pagemap_is_populated(fd, start)) 158 ret = false; 159 close(fd); 160 return ret; 161 } 162 163 static void test_populate_read(void) 164 { 165 char *addr; 166 int ret; 167 168 ksft_print_msg("[RUN] %s\n", __func__); 169 170 addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 171 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 172 if (addr == MAP_FAILED) 173 ksft_exit_fail_msg("mmap failed\n"); 174 ksft_test_result(range_is_not_populated(addr, SIZE), 175 "range initially not populated\n"); 176 177 ret = madvise(addr, SIZE, MADV_POPULATE_READ); 178 ksft_test_result(!ret, "MADV_POPULATE_READ\n"); 179 ksft_test_result(range_is_populated(addr, SIZE), 180 "range is populated\n"); 181 182 munmap(addr, SIZE); 183 } 184 185 static void test_populate_write(void) 186 { 187 char *addr; 188 int ret; 189 190 ksft_print_msg("[RUN] %s\n", __func__); 191 192 addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 193 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 194 if (addr == MAP_FAILED) 195 ksft_exit_fail_msg("mmap failed\n"); 196 ksft_test_result(range_is_not_populated(addr, SIZE), 197 "range initially not populated\n"); 198 199 ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 200 ksft_test_result(!ret, "MADV_POPULATE_WRITE\n"); 201 ksft_test_result(range_is_populated(addr, SIZE), 202 "range is populated\n"); 203 204 munmap(addr, SIZE); 205 } 206 207 static bool range_is_softdirty(char *start, ssize_t size) 208 { 209 int fd = open("/proc/self/pagemap", O_RDONLY); 210 bool ret = true; 211 212 if (fd < 0) 213 ksft_exit_fail_msg("opening pagemap failed\n"); 214 for (; size > 0 && ret; size -= pagesize, start += pagesize) 215 if (!pagemap_is_softdirty(fd, start)) 216 ret = false; 217 close(fd); 218 return ret; 219 } 220 221 static bool range_is_not_softdirty(char *start, ssize_t size) 222 { 223 int fd = open("/proc/self/pagemap", O_RDONLY); 224 bool ret = true; 225 226 if (fd < 0) 227 ksft_exit_fail_msg("opening pagemap failed\n"); 228 for (; size > 0 && ret; size -= pagesize, start += pagesize) 229 if (pagemap_is_softdirty(fd, start)) 230 ret = false; 231 close(fd); 232 return ret; 233 } 234 235 static void test_softdirty(void) 236 { 237 char *addr; 238 int ret; 239 240 ksft_print_msg("[RUN] %s\n", __func__); 241 242 addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 243 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 244 if (addr == MAP_FAILED) 245 ksft_exit_fail_msg("mmap failed\n"); 246 247 /* Clear any softdirty bits. */ 248 clear_softdirty(); 249 ksft_test_result(range_is_not_softdirty(addr, SIZE), 250 "range is not softdirty\n"); 251 252 /* Populating READ should set softdirty. */ 253 ret = madvise(addr, SIZE, MADV_POPULATE_READ); 254 ksft_test_result(!ret, "MADV_POPULATE_READ\n"); 255 ksft_test_result(range_is_not_softdirty(addr, SIZE), 256 "range is not softdirty\n"); 257 258 /* Populating WRITE should set softdirty. */ 259 ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 260 ksft_test_result(!ret, "MADV_POPULATE_WRITE\n"); 261 ksft_test_result(range_is_softdirty(addr, SIZE), 262 "range is softdirty\n"); 263 264 munmap(addr, SIZE); 265 } 266 267 static int system_has_softdirty(void) 268 { 269 /* 270 * There is no way to check if the kernel supports soft-dirty, other 271 * than by writing to a page and seeing if the bit was set. But the 272 * tests are intended to check that the bit gets set when it should, so 273 * doing that check would turn a potentially legitimate fail into a 274 * skip. Fortunately, we know for sure that arm64 does not support 275 * soft-dirty. So for now, let's just use the arch as a corse guide. 276 */ 277 #if defined(__aarch64__) 278 return 0; 279 #else 280 return 1; 281 #endif 282 } 283 284 int main(int argc, char **argv) 285 { 286 int nr_tests = 16; 287 int err; 288 289 pagesize = getpagesize(); 290 291 if (system_has_softdirty()) 292 nr_tests += 5; 293 294 ksft_print_header(); 295 ksft_set_plan(nr_tests); 296 297 sense_support(); 298 test_prot_read(); 299 test_prot_write(); 300 test_holes(); 301 test_populate_read(); 302 test_populate_write(); 303 if (system_has_softdirty()) 304 test_softdirty(); 305 306 err = ksft_get_fail_cnt(); 307 if (err) 308 ksft_exit_fail_msg("%d out of %d tests failed\n", 309 err, ksft_test_num()); 310 return ksft_exit_pass(); 311 } 312