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 #ifndef MADV_POPULATE_READ 24 #define MADV_POPULATE_READ 22 25 #endif /* MADV_POPULATE_READ */ 26 #ifndef MADV_POPULATE_WRITE 27 #define MADV_POPULATE_WRITE 23 28 #endif /* MADV_POPULATE_WRITE */ 29 30 /* 31 * For now, we're using 2 MiB of private anonymous memory for all tests. 32 */ 33 #define SIZE (2 * 1024 * 1024) 34 35 static size_t pagesize; 36 37 static void sense_support(void) 38 { 39 char *addr; 40 int ret; 41 42 addr = mmap(0, pagesize, PROT_READ | PROT_WRITE, 43 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 44 if (!addr) 45 ksft_exit_fail_msg("mmap failed\n"); 46 47 ret = madvise(addr, pagesize, MADV_POPULATE_READ); 48 if (ret) 49 ksft_exit_skip("MADV_POPULATE_READ is not available\n"); 50 51 ret = madvise(addr, pagesize, MADV_POPULATE_WRITE); 52 if (ret) 53 ksft_exit_skip("MADV_POPULATE_WRITE is not available\n"); 54 55 munmap(addr, pagesize); 56 } 57 58 static void test_prot_read(void) 59 { 60 char *addr; 61 int ret; 62 63 ksft_print_msg("[RUN] %s\n", __func__); 64 65 addr = mmap(0, SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 66 if (addr == MAP_FAILED) 67 ksft_exit_fail_msg("mmap failed\n"); 68 69 ret = madvise(addr, SIZE, MADV_POPULATE_READ); 70 ksft_test_result(!ret, "MADV_POPULATE_READ with PROT_READ\n"); 71 72 ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 73 ksft_test_result(ret == -1 && errno == EINVAL, 74 "MADV_POPULATE_WRITE with PROT_READ\n"); 75 76 munmap(addr, SIZE); 77 } 78 79 static void test_prot_write(void) 80 { 81 char *addr; 82 int ret; 83 84 ksft_print_msg("[RUN] %s\n", __func__); 85 86 addr = mmap(0, SIZE, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 87 if (addr == MAP_FAILED) 88 ksft_exit_fail_msg("mmap failed\n"); 89 90 ret = madvise(addr, SIZE, MADV_POPULATE_READ); 91 ksft_test_result(ret == -1 && errno == EINVAL, 92 "MADV_POPULATE_READ with PROT_WRITE\n"); 93 94 ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 95 ksft_test_result(!ret, "MADV_POPULATE_WRITE with PROT_WRITE\n"); 96 97 munmap(addr, SIZE); 98 } 99 100 static void test_holes(void) 101 { 102 char *addr; 103 int ret; 104 105 ksft_print_msg("[RUN] %s\n", __func__); 106 107 addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 108 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 109 if (addr == MAP_FAILED) 110 ksft_exit_fail_msg("mmap failed\n"); 111 ret = munmap(addr + pagesize, pagesize); 112 if (ret) 113 ksft_exit_fail_msg("munmap failed\n"); 114 115 /* Hole in the middle */ 116 ret = madvise(addr, SIZE, MADV_POPULATE_READ); 117 ksft_test_result(ret == -1 && errno == ENOMEM, 118 "MADV_POPULATE_READ with holes in the middle\n"); 119 ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 120 ksft_test_result(ret == -1 && errno == ENOMEM, 121 "MADV_POPULATE_WRITE with holes in the middle\n"); 122 123 /* Hole at end */ 124 ret = madvise(addr, 2 * pagesize, MADV_POPULATE_READ); 125 ksft_test_result(ret == -1 && errno == ENOMEM, 126 "MADV_POPULATE_READ with holes at the end\n"); 127 ret = madvise(addr, 2 * pagesize, MADV_POPULATE_WRITE); 128 ksft_test_result(ret == -1 && errno == ENOMEM, 129 "MADV_POPULATE_WRITE with holes at the end\n"); 130 131 /* Hole at beginning */ 132 ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_READ); 133 ksft_test_result(ret == -1 && errno == ENOMEM, 134 "MADV_POPULATE_READ with holes at the beginning\n"); 135 ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_WRITE); 136 ksft_test_result(ret == -1 && errno == ENOMEM, 137 "MADV_POPULATE_WRITE with holes at the beginning\n"); 138 139 munmap(addr, SIZE); 140 } 141 142 static bool range_is_populated(char *start, ssize_t size) 143 { 144 int fd = open("/proc/self/pagemap", O_RDONLY); 145 bool ret = true; 146 147 if (fd < 0) 148 ksft_exit_fail_msg("opening pagemap failed\n"); 149 for (; size > 0 && ret; size -= pagesize, start += pagesize) 150 if (!pagemap_is_populated(fd, start)) 151 ret = false; 152 close(fd); 153 return ret; 154 } 155 156 static bool range_is_not_populated(char *start, ssize_t size) 157 { 158 int fd = open("/proc/self/pagemap", O_RDONLY); 159 bool ret = true; 160 161 if (fd < 0) 162 ksft_exit_fail_msg("opening pagemap failed\n"); 163 for (; size > 0 && ret; size -= pagesize, start += pagesize) 164 if (pagemap_is_populated(fd, start)) 165 ret = false; 166 close(fd); 167 return ret; 168 } 169 170 static void test_populate_read(void) 171 { 172 char *addr; 173 int ret; 174 175 ksft_print_msg("[RUN] %s\n", __func__); 176 177 addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 178 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 179 if (addr == MAP_FAILED) 180 ksft_exit_fail_msg("mmap failed\n"); 181 ksft_test_result(range_is_not_populated(addr, SIZE), 182 "range initially not populated\n"); 183 184 ret = madvise(addr, SIZE, MADV_POPULATE_READ); 185 ksft_test_result(!ret, "MADV_POPULATE_READ\n"); 186 ksft_test_result(range_is_populated(addr, SIZE), 187 "range is populated\n"); 188 189 munmap(addr, SIZE); 190 } 191 192 static void test_populate_write(void) 193 { 194 char *addr; 195 int ret; 196 197 ksft_print_msg("[RUN] %s\n", __func__); 198 199 addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 200 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 201 if (addr == MAP_FAILED) 202 ksft_exit_fail_msg("mmap failed\n"); 203 ksft_test_result(range_is_not_populated(addr, SIZE), 204 "range initially not populated\n"); 205 206 ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 207 ksft_test_result(!ret, "MADV_POPULATE_WRITE\n"); 208 ksft_test_result(range_is_populated(addr, SIZE), 209 "range is populated\n"); 210 211 munmap(addr, SIZE); 212 } 213 214 static bool range_is_softdirty(char *start, ssize_t size) 215 { 216 int fd = open("/proc/self/pagemap", O_RDONLY); 217 bool ret = true; 218 219 if (fd < 0) 220 ksft_exit_fail_msg("opening pagemap failed\n"); 221 for (; size > 0 && ret; size -= pagesize, start += pagesize) 222 if (!pagemap_is_softdirty(fd, start)) 223 ret = false; 224 close(fd); 225 return ret; 226 } 227 228 static bool range_is_not_softdirty(char *start, ssize_t size) 229 { 230 int fd = open("/proc/self/pagemap", O_RDONLY); 231 bool ret = true; 232 233 if (fd < 0) 234 ksft_exit_fail_msg("opening pagemap failed\n"); 235 for (; size > 0 && ret; size -= pagesize, start += pagesize) 236 if (pagemap_is_softdirty(fd, start)) 237 ret = false; 238 close(fd); 239 return ret; 240 } 241 242 static void test_softdirty(void) 243 { 244 char *addr; 245 int ret; 246 247 ksft_print_msg("[RUN] %s\n", __func__); 248 249 addr = mmap(0, SIZE, PROT_READ | PROT_WRITE, 250 MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 251 if (addr == MAP_FAILED) 252 ksft_exit_fail_msg("mmap failed\n"); 253 254 /* Clear any softdirty bits. */ 255 clear_softdirty(); 256 ksft_test_result(range_is_not_softdirty(addr, SIZE), 257 "range is not softdirty\n"); 258 259 /* Populating READ should set softdirty. */ 260 ret = madvise(addr, SIZE, MADV_POPULATE_READ); 261 ksft_test_result(!ret, "MADV_POPULATE_READ\n"); 262 ksft_test_result(range_is_not_softdirty(addr, SIZE), 263 "range is not softdirty\n"); 264 265 /* Populating WRITE should set softdirty. */ 266 ret = madvise(addr, SIZE, MADV_POPULATE_WRITE); 267 ksft_test_result(!ret, "MADV_POPULATE_WRITE\n"); 268 ksft_test_result(range_is_softdirty(addr, SIZE), 269 "range is softdirty\n"); 270 271 munmap(addr, SIZE); 272 } 273 274 int main(int argc, char **argv) 275 { 276 int err; 277 278 pagesize = getpagesize(); 279 280 ksft_print_header(); 281 ksft_set_plan(21); 282 283 sense_support(); 284 test_prot_read(); 285 test_prot_write(); 286 test_holes(); 287 test_populate_read(); 288 test_populate_write(); 289 test_softdirty(); 290 291 err = ksft_get_fail_cnt(); 292 if (err) 293 ksft_exit_fail_msg("%d out of %d tests failed\n", 294 err, ksft_test_num()); 295 return ksft_exit_pass(); 296 } 297