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