brintos

brintos / linux-shallow public Read only

0
0
Text · 4.4 KiB · 100370a Raw
185 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright 2022 Google LLC4 */5#define _GNU_SOURCE6#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 102419 20static 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 */51static 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 66static 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 77int 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;102retry:103	/*104	 * Pipe for the child to signal when it's done allocating105	 * memory106	 */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 reap126	 */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 memory155		 * which causes it to spend more time on cleanup and increases156		 * 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}185