xref: /linux/tools/testing/selftests/mm/mrelease_test.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
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