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

Configure Feed

Select the types of activity you want to include in your feed.

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