mrelease_test.c 4.4 KB

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