xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/process_madvise/process_madvise01.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2023 SUSE LLC Andrea Cervesato <[email protected]>
4  */
5 
6 /*\
7  * [Description]
8  *
9  * Allocate anonymous memory pages inside child and reclaim it with
10  * MADV_PAGEOUT. Then check if memory pages have been swapped out by looking
11  * at smaps information.
12  *
13  * The advice might be ignored for some pages in the range when it is
14  * not applicable, so test passes if swap memory increases after
15  * reclaiming memory with MADV_PAGEOUT.
16  */
17 
18 #define _GNU_SOURCE
19 
20 #include <sys/mman.h>
21 #include "tst_test.h"
22 #include "lapi/mmap.h"
23 #include "lapi/syscalls.h"
24 #include "process_madvise.h"
25 
26 #define MEM_CHILD	(1 * TST_MB)
27 
28 static void **data_ptr;
29 
child_alloc(void)30 static void child_alloc(void)
31 {
32 	char data[MEM_CHILD];
33 	struct addr_mapping map_before;
34 	struct addr_mapping map_after;
35 
36 	memset(data, 'a', MEM_CHILD);
37 
38 	tst_res(TINFO, "Allocate memory: %d bytes", MEM_CHILD);
39 
40 	*data_ptr = SAFE_MMAP(NULL, MEM_CHILD,
41 			PROT_READ | PROT_WRITE,
42 			MAP_SHARED | MAP_ANONYMOUS, -1, 0);
43 
44 	memset(*data_ptr, 'a', MEM_CHILD);
45 
46 	memset(&map_before, 0, sizeof(struct addr_mapping));
47 	read_address_mapping((unsigned long)*data_ptr, &map_before);
48 
49 	TST_CHECKPOINT_WAKE_AND_WAIT(0);
50 
51 	memset(&map_after, 0, sizeof(struct addr_mapping));
52 	read_address_mapping((unsigned long)*data_ptr, &map_after);
53 
54 	if (memcmp(*data_ptr, data, MEM_CHILD) != 0) {
55 		tst_res(TFAIL, "Dirty memory after reclaiming it");
56 		return;
57 	}
58 
59 	SAFE_MUNMAP(*data_ptr, MEM_CHILD);
60 	*data_ptr = NULL;
61 
62 	TST_EXP_EXPR(map_before.swap < map_after.swap,
63 		"Most of the memory has been swapped out: %dkB out of %dkB",
64 		map_after.swap - map_before.swap,
65 		MEM_CHILD / TST_KB);
66 }
67 
setup(void)68 static void setup(void)
69 {
70 	data_ptr = SAFE_MMAP(NULL, sizeof(void *),
71 			PROT_READ | PROT_WRITE,
72 			MAP_SHARED | MAP_ANONYMOUS, -1, 0);
73 }
74 
cleanup(void)75 static void cleanup(void)
76 {
77 	if (*data_ptr)
78 		SAFE_MUNMAP(*data_ptr, MEM_CHILD);
79 
80 	if (data_ptr)
81 		SAFE_MUNMAP(data_ptr, sizeof(void *));
82 }
83 
run(void)84 static void run(void)
85 {
86 	int ret;
87 	int pidfd;
88 	pid_t pid_alloc;
89 	struct iovec vec;
90 
91 	pid_alloc = SAFE_FORK();
92 	if (!pid_alloc) {
93 		child_alloc();
94 		return;
95 	}
96 
97 	TST_CHECKPOINT_WAIT(0);
98 
99 	tst_res(TINFO, "Reclaim memory using MADV_PAGEOUT");
100 
101 	pidfd = SAFE_PIDFD_OPEN(pid_alloc, 0);
102 
103 	vec.iov_base = *data_ptr;
104 	vec.iov_len = MEM_CHILD;
105 
106 	ret = tst_syscall(__NR_process_madvise, pidfd, &vec, 1UL,
107 			MADV_PAGEOUT, 0UL);
108 
109 	if (ret == -1)
110 		tst_brk(TBROK | TERRNO, "process_madvise failed");
111 
112 	if (ret != MEM_CHILD)
113 		tst_brk(TBROK, "process_madvise reclaimed only %d bytes", ret);
114 
115 	TST_CHECKPOINT_WAKE(0);
116 }
117 
118 static struct tst_test test = {
119 	.setup = setup,
120 	.cleanup = cleanup,
121 	.test_all = run,
122 	.forks_child = 1,
123 	.min_kver = "5.10",
124 	.needs_checkpoints = 1,
125 	.needs_root = 1,
126 	.min_swap_avail = MEM_CHILD / TST_MB,
127 	.needs_kconfigs = (const char *[]) {
128 		"CONFIG_SWAP=y",
129 		NULL
130 	},
131 };
132