mrelease_test.c 4.4 KB

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