File Index
Files and directories
Sign in
FILE

madv_populate.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/mm/madv_populate.c C
  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 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			 "read 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			 "read 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			 "write 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			 "write 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			 "cleared 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, "softdirty MADV_POPULATE_READ\n");
255	ksft_test_result(range_is_not_softdirty(addr, SIZE),
256			 "range is not softdirty after MADV_POPULATE_READ\n");
257
258	/* Populating WRITE should set softdirty. */
259	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
260	ksft_test_result(!ret, "softdirty MADV_POPULATE_WRITE\n");
261	ksft_test_result(range_is_softdirty(addr, SIZE),
262			 "range is softdirty after MADV_POPULATE_WRITE \n");
263
264	munmap(addr, SIZE);
265}
266
267int main(int argc, char **argv)
268{
269	int nr_tests = 16;
270	int err;
271
272	pagesize = getpagesize();
273
274	if (softdirty_supported())
275		nr_tests += 5;
276
277	ksft_print_header();
278	ksft_set_plan(nr_tests);
279
280	sense_support();
281	test_prot_read();
282	test_prot_write();
283	test_holes();
284	test_populate_read();
285	test_populate_write();
286	if (softdirty_supported())
287		test_softdirty();
288
289	err = ksft_get_fail_cnt();
290	if (err)
291		ksft_exit_fail_msg("%d out of %d tests failed\n",
292				   err, ksft_test_num());
293	ksft_exit_pass();
294}
~/ Read-only file preview.