Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * MADV_POPULATE_READ and MADV_POPULATE_WRITE tests
0004  *
0005  * Copyright 2021, Red Hat, Inc.
0006  *
0007  * Author(s): David Hildenbrand <david@redhat.com>
0008  */
0009 #define _GNU_SOURCE
0010 #include <stdlib.h>
0011 #include <string.h>
0012 #include <stdbool.h>
0013 #include <stdint.h>
0014 #include <unistd.h>
0015 #include <errno.h>
0016 #include <fcntl.h>
0017 #include <linux/mman.h>
0018 #include <sys/mman.h>
0019 
0020 #include "../kselftest.h"
0021 #include "vm_util.h"
0022 
0023 /*
0024  * For now, we're using 2 MiB of private anonymous memory for all tests.
0025  */
0026 #define SIZE (2 * 1024 * 1024)
0027 
0028 static size_t pagesize;
0029 
0030 static bool pagemap_is_populated(int fd, char *start)
0031 {
0032     uint64_t entry = pagemap_get_entry(fd, start);
0033 
0034     /* Present or swapped. */
0035     return entry & 0xc000000000000000ull;
0036 }
0037 
0038 static void sense_support(void)
0039 {
0040     char *addr;
0041     int ret;
0042 
0043     addr = mmap(0, pagesize, PROT_READ | PROT_WRITE,
0044             MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
0045     if (!addr)
0046         ksft_exit_fail_msg("mmap failed\n");
0047 
0048     ret = madvise(addr, pagesize, MADV_POPULATE_READ);
0049     if (ret)
0050         ksft_exit_skip("MADV_POPULATE_READ is not available\n");
0051 
0052     ret = madvise(addr, pagesize, MADV_POPULATE_WRITE);
0053     if (ret)
0054         ksft_exit_skip("MADV_POPULATE_WRITE is not available\n");
0055 
0056     munmap(addr, pagesize);
0057 }
0058 
0059 static void test_prot_read(void)
0060 {
0061     char *addr;
0062     int ret;
0063 
0064     ksft_print_msg("[RUN] %s\n", __func__);
0065 
0066     addr = mmap(0, SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
0067     if (addr == MAP_FAILED)
0068         ksft_exit_fail_msg("mmap failed\n");
0069 
0070     ret = madvise(addr, SIZE, MADV_POPULATE_READ);
0071     ksft_test_result(!ret, "MADV_POPULATE_READ with PROT_READ\n");
0072 
0073     ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
0074     ksft_test_result(ret == -1 && errno == EINVAL,
0075              "MADV_POPULATE_WRITE with PROT_READ\n");
0076 
0077     munmap(addr, SIZE);
0078 }
0079 
0080 static void test_prot_write(void)
0081 {
0082     char *addr;
0083     int ret;
0084 
0085     ksft_print_msg("[RUN] %s\n", __func__);
0086 
0087     addr = mmap(0, SIZE, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
0088     if (addr == MAP_FAILED)
0089         ksft_exit_fail_msg("mmap failed\n");
0090 
0091     ret = madvise(addr, SIZE, MADV_POPULATE_READ);
0092     ksft_test_result(ret == -1 && errno == EINVAL,
0093              "MADV_POPULATE_READ with PROT_WRITE\n");
0094 
0095     ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
0096     ksft_test_result(!ret, "MADV_POPULATE_WRITE with PROT_WRITE\n");
0097 
0098     munmap(addr, SIZE);
0099 }
0100 
0101 static void test_holes(void)
0102 {
0103     char *addr;
0104     int ret;
0105 
0106     ksft_print_msg("[RUN] %s\n", __func__);
0107 
0108     addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
0109             MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
0110     if (addr == MAP_FAILED)
0111         ksft_exit_fail_msg("mmap failed\n");
0112     ret = munmap(addr + pagesize, pagesize);
0113     if (ret)
0114         ksft_exit_fail_msg("munmap failed\n");
0115 
0116     /* Hole in the middle */
0117     ret = madvise(addr, SIZE, MADV_POPULATE_READ);
0118     ksft_test_result(ret == -1 && errno == ENOMEM,
0119              "MADV_POPULATE_READ with holes in the middle\n");
0120     ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
0121     ksft_test_result(ret == -1 && errno == ENOMEM,
0122              "MADV_POPULATE_WRITE with holes in the middle\n");
0123 
0124     /* Hole at end */
0125     ret = madvise(addr, 2 * pagesize, MADV_POPULATE_READ);
0126     ksft_test_result(ret == -1 && errno == ENOMEM,
0127              "MADV_POPULATE_READ with holes at the end\n");
0128     ret = madvise(addr, 2 * pagesize, MADV_POPULATE_WRITE);
0129     ksft_test_result(ret == -1 && errno == ENOMEM,
0130              "MADV_POPULATE_WRITE with holes at the end\n");
0131 
0132     /* Hole at beginning */
0133     ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_READ);
0134     ksft_test_result(ret == -1 && errno == ENOMEM,
0135              "MADV_POPULATE_READ with holes at the beginning\n");
0136     ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_WRITE);
0137     ksft_test_result(ret == -1 && errno == ENOMEM,
0138              "MADV_POPULATE_WRITE with holes at the beginning\n");
0139 
0140     munmap(addr, SIZE);
0141 }
0142 
0143 static bool range_is_populated(char *start, ssize_t size)
0144 {
0145     int fd = open("/proc/self/pagemap", O_RDONLY);
0146     bool ret = true;
0147 
0148     if (fd < 0)
0149         ksft_exit_fail_msg("opening pagemap failed\n");
0150     for (; size > 0 && ret; size -= pagesize, start += pagesize)
0151         if (!pagemap_is_populated(fd, start))
0152             ret = false;
0153     close(fd);
0154     return ret;
0155 }
0156 
0157 static bool range_is_not_populated(char *start, ssize_t size)
0158 {
0159     int fd = open("/proc/self/pagemap", O_RDONLY);
0160     bool ret = true;
0161 
0162     if (fd < 0)
0163         ksft_exit_fail_msg("opening pagemap failed\n");
0164     for (; size > 0 && ret; size -= pagesize, start += pagesize)
0165         if (pagemap_is_populated(fd, start))
0166             ret = false;
0167     close(fd);
0168     return ret;
0169 }
0170 
0171 static void test_populate_read(void)
0172 {
0173     char *addr;
0174     int ret;
0175 
0176     ksft_print_msg("[RUN] %s\n", __func__);
0177 
0178     addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
0179             MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
0180     if (addr == MAP_FAILED)
0181         ksft_exit_fail_msg("mmap failed\n");
0182     ksft_test_result(range_is_not_populated(addr, SIZE),
0183              "range initially not populated\n");
0184 
0185     ret = madvise(addr, SIZE, MADV_POPULATE_READ);
0186     ksft_test_result(!ret, "MADV_POPULATE_READ\n");
0187     ksft_test_result(range_is_populated(addr, SIZE),
0188              "range is populated\n");
0189 
0190     munmap(addr, SIZE);
0191 }
0192 
0193 static void test_populate_write(void)
0194 {
0195     char *addr;
0196     int ret;
0197 
0198     ksft_print_msg("[RUN] %s\n", __func__);
0199 
0200     addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
0201             MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
0202     if (addr == MAP_FAILED)
0203         ksft_exit_fail_msg("mmap failed\n");
0204     ksft_test_result(range_is_not_populated(addr, SIZE),
0205              "range initially not populated\n");
0206 
0207     ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
0208     ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");
0209     ksft_test_result(range_is_populated(addr, SIZE),
0210              "range is populated\n");
0211 
0212     munmap(addr, SIZE);
0213 }
0214 
0215 static bool range_is_softdirty(char *start, ssize_t size)
0216 {
0217     int fd = open("/proc/self/pagemap", O_RDONLY);
0218     bool ret = true;
0219 
0220     if (fd < 0)
0221         ksft_exit_fail_msg("opening pagemap failed\n");
0222     for (; size > 0 && ret; size -= pagesize, start += pagesize)
0223         if (!pagemap_is_softdirty(fd, start))
0224             ret = false;
0225     close(fd);
0226     return ret;
0227 }
0228 
0229 static bool range_is_not_softdirty(char *start, ssize_t size)
0230 {
0231     int fd = open("/proc/self/pagemap", O_RDONLY);
0232     bool ret = true;
0233 
0234     if (fd < 0)
0235         ksft_exit_fail_msg("opening pagemap failed\n");
0236     for (; size > 0 && ret; size -= pagesize, start += pagesize)
0237         if (pagemap_is_softdirty(fd, start))
0238             ret = false;
0239     close(fd);
0240     return ret;
0241 }
0242 
0243 static void test_softdirty(void)
0244 {
0245     char *addr;
0246     int ret;
0247 
0248     ksft_print_msg("[RUN] %s\n", __func__);
0249 
0250     addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
0251             MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
0252     if (addr == MAP_FAILED)
0253         ksft_exit_fail_msg("mmap failed\n");
0254 
0255     /* Clear any softdirty bits. */
0256     clear_softdirty();
0257     ksft_test_result(range_is_not_softdirty(addr, SIZE),
0258              "range is not softdirty\n");
0259 
0260     /* Populating READ should set softdirty. */
0261     ret = madvise(addr, SIZE, MADV_POPULATE_READ);
0262     ksft_test_result(!ret, "MADV_POPULATE_READ\n");
0263     ksft_test_result(range_is_not_softdirty(addr, SIZE),
0264              "range is not softdirty\n");
0265 
0266     /* Populating WRITE should set softdirty. */
0267     ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
0268     ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");
0269     ksft_test_result(range_is_softdirty(addr, SIZE),
0270              "range is softdirty\n");
0271 
0272     munmap(addr, SIZE);
0273 }
0274 
0275 int main(int argc, char **argv)
0276 {
0277     int err;
0278 
0279     pagesize = getpagesize();
0280 
0281     ksft_print_header();
0282     ksft_set_plan(21);
0283 
0284     sense_support();
0285     test_prot_read();
0286     test_prot_write();
0287     test_holes();
0288     test_populate_read();
0289     test_populate_write();
0290     test_softdirty();
0291 
0292     err = ksft_get_fail_cnt();
0293     if (err)
0294         ksft_exit_fail_msg("%d out of %d tests failed\n",
0295                    err, ksft_test_num());
0296     return ksft_exit_pass();
0297 }