brintos

brintos / linux-shallow public Read only

0
0
Text · 7.7 KiB · ef7d911 Raw
312 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * MADV_POPULATE_READ and MADV_POPULATE_WRITE tests4 *5 * Copyright 2021, Red Hat, Inc.6 *7 * Author(s): David Hildenbrand <david@redhat.com>8 */9#define _GNU_SOURCE10#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 void sense_support(void)31{32	char *addr;33	int ret;34 35	addr = mmap(0, pagesize, PROT_READ | PROT_WRITE,36		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);37	if (!addr)38		ksft_exit_fail_msg("mmap failed\n");39 40	ret = madvise(addr, pagesize, MADV_POPULATE_READ);41	if (ret)42		ksft_exit_skip("MADV_POPULATE_READ is not available\n");43 44	ret = madvise(addr, pagesize, MADV_POPULATE_WRITE);45	if (ret)46		ksft_exit_skip("MADV_POPULATE_WRITE is not available\n");47 48	munmap(addr, pagesize);49}50 51static void test_prot_read(void)52{53	char *addr;54	int ret;55 56	ksft_print_msg("[RUN] %s\n", __func__);57 58	addr = mmap(0, SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);59	if (addr == MAP_FAILED)60		ksft_exit_fail_msg("mmap failed\n");61 62	ret = madvise(addr, SIZE, MADV_POPULATE_READ);63	ksft_test_result(!ret, "MADV_POPULATE_READ with PROT_READ\n");64 65	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);66	ksft_test_result(ret == -1 && errno == EINVAL,67			 "MADV_POPULATE_WRITE with PROT_READ\n");68 69	munmap(addr, SIZE);70}71 72static void test_prot_write(void)73{74	char *addr;75	int ret;76 77	ksft_print_msg("[RUN] %s\n", __func__);78 79	addr = mmap(0, SIZE, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);80	if (addr == MAP_FAILED)81		ksft_exit_fail_msg("mmap failed\n");82 83	ret = madvise(addr, SIZE, MADV_POPULATE_READ);84	ksft_test_result(ret == -1 && errno == EINVAL,85			 "MADV_POPULATE_READ with PROT_WRITE\n");86 87	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);88	ksft_test_result(!ret, "MADV_POPULATE_WRITE with PROT_WRITE\n");89 90	munmap(addr, SIZE);91}92 93static void test_holes(void)94{95	char *addr;96	int ret;97 98	ksft_print_msg("[RUN] %s\n", __func__);99 100	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,101		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);102	if (addr == MAP_FAILED)103		ksft_exit_fail_msg("mmap failed\n");104	ret = munmap(addr + pagesize, pagesize);105	if (ret)106		ksft_exit_fail_msg("munmap failed\n");107 108	/* Hole in the middle */109	ret = madvise(addr, SIZE, MADV_POPULATE_READ);110	ksft_test_result(ret == -1 && errno == ENOMEM,111			 "MADV_POPULATE_READ with holes in the middle\n");112	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);113	ksft_test_result(ret == -1 && errno == ENOMEM,114			 "MADV_POPULATE_WRITE with holes in the middle\n");115 116	/* Hole at end */117	ret = madvise(addr, 2 * pagesize, MADV_POPULATE_READ);118	ksft_test_result(ret == -1 && errno == ENOMEM,119			 "MADV_POPULATE_READ with holes at the end\n");120	ret = madvise(addr, 2 * pagesize, MADV_POPULATE_WRITE);121	ksft_test_result(ret == -1 && errno == ENOMEM,122			 "MADV_POPULATE_WRITE with holes at the end\n");123 124	/* Hole at beginning */125	ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_READ);126	ksft_test_result(ret == -1 && errno == ENOMEM,127			 "MADV_POPULATE_READ with holes at the beginning\n");128	ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_WRITE);129	ksft_test_result(ret == -1 && errno == ENOMEM,130			 "MADV_POPULATE_WRITE with holes at the beginning\n");131 132	munmap(addr, SIZE);133}134 135static bool range_is_populated(char *start, ssize_t size)136{137	int fd = open("/proc/self/pagemap", O_RDONLY);138	bool ret = true;139 140	if (fd < 0)141		ksft_exit_fail_msg("opening pagemap failed\n");142	for (; size > 0 && ret; size -= pagesize, start += pagesize)143		if (!pagemap_is_populated(fd, start))144			ret = false;145	close(fd);146	return ret;147}148 149static bool range_is_not_populated(char *start, ssize_t size)150{151	int fd = open("/proc/self/pagemap", O_RDONLY);152	bool ret = true;153 154	if (fd < 0)155		ksft_exit_fail_msg("opening pagemap failed\n");156	for (; size > 0 && ret; size -= pagesize, start += pagesize)157		if (pagemap_is_populated(fd, start))158			ret = false;159	close(fd);160	return ret;161}162 163static void test_populate_read(void)164{165	char *addr;166	int ret;167 168	ksft_print_msg("[RUN] %s\n", __func__);169 170	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,171		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);172	if (addr == MAP_FAILED)173		ksft_exit_fail_msg("mmap failed\n");174	ksft_test_result(range_is_not_populated(addr, SIZE),175			 "range initially not populated\n");176 177	ret = madvise(addr, SIZE, MADV_POPULATE_READ);178	ksft_test_result(!ret, "MADV_POPULATE_READ\n");179	ksft_test_result(range_is_populated(addr, SIZE),180			 "range is populated\n");181 182	munmap(addr, SIZE);183}184 185static void test_populate_write(void)186{187	char *addr;188	int ret;189 190	ksft_print_msg("[RUN] %s\n", __func__);191 192	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,193		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);194	if (addr == MAP_FAILED)195		ksft_exit_fail_msg("mmap failed\n");196	ksft_test_result(range_is_not_populated(addr, SIZE),197			 "range initially not populated\n");198 199	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);200	ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");201	ksft_test_result(range_is_populated(addr, SIZE),202			 "range is populated\n");203 204	munmap(addr, SIZE);205}206 207static bool range_is_softdirty(char *start, ssize_t size)208{209	int fd = open("/proc/self/pagemap", O_RDONLY);210	bool ret = true;211 212	if (fd < 0)213		ksft_exit_fail_msg("opening pagemap failed\n");214	for (; size > 0 && ret; size -= pagesize, start += pagesize)215		if (!pagemap_is_softdirty(fd, start))216			ret = false;217	close(fd);218	return ret;219}220 221static bool range_is_not_softdirty(char *start, ssize_t size)222{223	int fd = open("/proc/self/pagemap", O_RDONLY);224	bool ret = true;225 226	if (fd < 0)227		ksft_exit_fail_msg("opening pagemap failed\n");228	for (; size > 0 && ret; size -= pagesize, start += pagesize)229		if (pagemap_is_softdirty(fd, start))230			ret = false;231	close(fd);232	return ret;233}234 235static void test_softdirty(void)236{237	char *addr;238	int ret;239 240	ksft_print_msg("[RUN] %s\n", __func__);241 242	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,243		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);244	if (addr == MAP_FAILED)245		ksft_exit_fail_msg("mmap failed\n");246 247	/* Clear any softdirty bits. */248	clear_softdirty();249	ksft_test_result(range_is_not_softdirty(addr, SIZE),250			 "range is not softdirty\n");251 252	/* Populating READ should set softdirty. */253	ret = madvise(addr, SIZE, MADV_POPULATE_READ);254	ksft_test_result(!ret, "MADV_POPULATE_READ\n");255	ksft_test_result(range_is_not_softdirty(addr, SIZE),256			 "range is not softdirty\n");257 258	/* Populating WRITE should set softdirty. */259	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);260	ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");261	ksft_test_result(range_is_softdirty(addr, SIZE),262			 "range is softdirty\n");263 264	munmap(addr, SIZE);265}266 267static int system_has_softdirty(void)268{269	/*270	 * There is no way to check if the kernel supports soft-dirty, other271	 * than by writing to a page and seeing if the bit was set. But the272	 * tests are intended to check that the bit gets set when it should, so273	 * doing that check would turn a potentially legitimate fail into a274	 * skip. Fortunately, we know for sure that arm64 does not support275	 * soft-dirty. So for now, let's just use the arch as a corse guide.276	 */277#if defined(__aarch64__)278	return 0;279#else280	return 1;281#endif282}283 284int main(int argc, char **argv)285{286	int nr_tests = 16;287	int err;288 289	pagesize = getpagesize();290 291	if (system_has_softdirty())292		nr_tests += 5;293 294	ksft_print_header();295	ksft_set_plan(nr_tests);296 297	sense_support();298	test_prot_read();299	test_prot_write();300	test_holes();301	test_populate_read();302	test_populate_write();303	if (system_has_softdirty())304		test_softdirty();305 306	err = ksft_get_fail_cnt();307	if (err)308		ksft_exit_fail_msg("%d out of %d tests failed\n",309				   err, ksft_test_num());310	ksft_exit_pass();311}312