]> Git Repo - J-linux.git/blob - tools/testing/selftests/mm/mrelease_test.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / tools / testing / selftests / mm / mrelease_test.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2022 Google LLC
4  */
5 #define _GNU_SOURCE
6 #include <errno.h>
7 #include <stdbool.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <sys/syscall.h>
11 #include <sys/wait.h>
12 #include <unistd.h>
13 #include <asm-generic/unistd.h>
14 #include "vm_util.h"
15 #include "../kselftest.h"
16
17 #define MB(x) (x << 20)
18 #define MAX_SIZE_MB 1024
19
20 static int alloc_noexit(unsigned long nr_pages, int pipefd)
21 {
22         int ppid = getppid();
23         int timeout = 10; /* 10sec timeout to get killed */
24         unsigned long i;
25         char *buf;
26
27         buf = (char *)mmap(NULL, nr_pages * psize(), PROT_READ | PROT_WRITE,
28                            MAP_PRIVATE | MAP_ANON, 0, 0);
29         if (buf == MAP_FAILED)
30                 ksft_exit_fail_msg("mmap failed, halting the test: %s\n", strerror(errno));
31
32         for (i = 0; i < nr_pages; i++)
33                 *((unsigned long *)(buf + (i * psize()))) = i;
34
35         /* Signal the parent that the child is ready */
36         if (write(pipefd, "", 1) < 0)
37                 ksft_exit_fail_msg("write: %s\n", strerror(errno));
38
39         /* Wait to be killed (when reparenting happens) */
40         while (getppid() == ppid && timeout > 0) {
41                 sleep(1);
42                 timeout--;
43         }
44
45         munmap(buf, nr_pages * psize());
46
47         return (timeout > 0) ? KSFT_PASS : KSFT_FAIL;
48 }
49
50 /* The process_mrelease calls in this test are expected to fail */
51 static void run_negative_tests(int pidfd)
52 {
53         /* Test invalid flags. Expect to fail with EINVAL error code. */
54         if (!syscall(__NR_process_mrelease, pidfd, (unsigned int)-1) ||
55                         errno != EINVAL) {
56                 ksft_exit_fail_msg("process_mrelease with wrong flags: %s\n", strerror(errno));
57         }
58         /*
59          * Test reaping while process is alive with no pending SIGKILL.
60          * Expect to fail with EINVAL error code.
61          */
62         if (!syscall(__NR_process_mrelease, pidfd, 0) || errno != EINVAL)
63                 ksft_exit_fail_msg("process_mrelease on a live process: %s\n", strerror(errno));
64 }
65
66 static int child_main(int pipefd[], size_t size)
67 {
68         int res;
69
70         /* Allocate and fault-in memory and wait to be killed */
71         close(pipefd[0]);
72         res = alloc_noexit(MB(size) / psize(), pipefd[1]);
73         close(pipefd[1]);
74         return res;
75 }
76
77 int main(void)
78 {
79         int pipefd[2], pidfd;
80         bool success, retry;
81         size_t size;
82         pid_t pid;
83         char byte;
84         int res;
85
86         ksft_print_header();
87         ksft_set_plan(1);
88
89         /* Test a wrong pidfd */
90         if (!syscall(__NR_process_mrelease, -1, 0) || errno != EBADF) {
91                 if (errno == ENOSYS) {
92                         ksft_test_result_skip("process_mrelease not implemented\n");
93                         ksft_finished();
94                 } else {
95                         ksft_exit_fail_msg("process_mrelease with wrong pidfd: %s",
96                                            strerror(errno));
97                 }
98         }
99
100         /* Start the test with 1MB child memory allocation */
101         size = 1;
102 retry:
103         /*
104          * Pipe for the child to signal when it's done allocating
105          * memory
106          */
107         if (pipe(pipefd))
108                 ksft_exit_fail_msg("pipe: %s\n", strerror(errno));
109
110         pid = fork();
111         if (pid < 0) {
112                 close(pipefd[0]);
113                 close(pipefd[1]);
114                 ksft_exit_fail_msg("fork: %s\n", strerror(errno));
115         }
116
117         if (pid == 0) {
118                 /* Child main routine */
119                 res = child_main(pipefd, size);
120                 exit(res);
121         }
122
123         /*
124          * Parent main routine:
125          * Wait for the child to finish allocations, then kill and reap
126          */
127         close(pipefd[1]);
128         /* Block until the child is ready */
129         res = read(pipefd[0], &byte, 1);
130         close(pipefd[0]);
131         if (res < 0) {
132                 if (!kill(pid, SIGKILL))
133                         waitpid(pid, NULL, 0);
134                 ksft_exit_fail_msg("read: %s\n", strerror(errno));
135         }
136
137         pidfd = syscall(__NR_pidfd_open, pid, 0);
138         if (pidfd < 0) {
139                 if (!kill(pid, SIGKILL))
140                         waitpid(pid, NULL, 0);
141                 ksft_exit_fail_msg("pidfd_open: %s\n", strerror(errno));
142         }
143
144         /* Run negative tests which require a live child */
145         run_negative_tests(pidfd);
146
147         if (kill(pid, SIGKILL))
148                 ksft_exit_fail_msg("kill: %s\n", strerror(errno));
149
150         success = (syscall(__NR_process_mrelease, pidfd, 0) == 0);
151         if (!success) {
152                 /*
153                  * If we failed to reap because the child exited too soon,
154                  * before we could call process_mrelease. Double child's memory
155                  * which causes it to spend more time on cleanup and increases
156                  * our chances of reaping its memory before it exits.
157                  * Retry until we succeed or reach MAX_SIZE_MB.
158                  */
159                 if (errno == ESRCH) {
160                         retry = (size <= MAX_SIZE_MB);
161                 } else {
162                         waitpid(pid, NULL, 0);
163                         ksft_exit_fail_msg("process_mrelease: %s\n", strerror(errno));
164                 }
165         }
166
167         /* Cleanup to prevent zombies */
168         if (waitpid(pid, NULL, 0) < 0)
169                 ksft_exit_fail_msg("waitpid: %s\n", strerror(errno));
170
171         close(pidfd);
172
173         if (!success) {
174                 if (retry) {
175                         size *= 2;
176                         goto retry;
177                 }
178                 ksft_exit_fail_msg("All process_mrelease attempts failed!\n");
179         }
180
181         ksft_test_result_pass("Success reaping a child with %zuMB of memory allocations\n",
182                               size);
183         ksft_finished();
184 }
This page took 0.039797 seconds and 4 git commands to generate.