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