0001
0002
0003
0004
0005
0006
0007
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
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
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
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
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
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
0256 clear_softdirty();
0257 ksft_test_result(range_is_not_softdirty(addr, SIZE),
0258 "range is not softdirty\n");
0259
0260
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
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 }