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