1 // SPDX-FileCopyrightText: 2012 Mathieu Desnoyers <mathieu.desnoyers (at) efficios.com> 2 // 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 /* 6 * Userspace RCU library - test program (fork) 7 */ 8 9 #include <stdio.h> 10 #include <pthread.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <sys/types.h> 14 #include <sys/wait.h> 15 #include <unistd.h> 16 #include <stdio.h> 17 #include <sched.h> 18 #include <errno.h> 19 20 #include <urcu/assert.h> 21 #include <urcu/arch.h> 22 #include <urcu/tls-compat.h> 23 24 #ifndef DYNAMIC_LINK_TEST 25 #define _LGPL_SOURCE 26 #else 27 #define rcu_debug_yield_read() 28 #endif 29 #include <urcu.h> 30 31 #include "tap.h" 32 33 /* We generate children 3 levels deep */ 34 #define FORK_DEPTH 3 35 /* Each generation spawns 10 children */ 36 #define NR_FORK 10 37 38 #define NR_TESTS NR_FORK 39 40 static int fork_generation; 41 42 /* 43 * Only print diagnostic for top level parent process, else the console 44 * has trouble formatting the tap output. 45 */ 46 #define diag_gen0(...) \ 47 do { \ 48 if (!fork_generation) \ 49 diag(__VA_ARGS__); \ 50 } while (0) 51 52 struct test_node { 53 int somedata; 54 struct rcu_head head; 55 }; 56 57 static void cb(struct rcu_head *head) 58 { 59 struct test_node *node; 60 61 diag_gen0("rcu callback invoked in pid: %d", (int) getpid()); 62 node = caa_container_of(head, struct test_node, head); 63 free(node); 64 } 65 66 static void test_rcu(void) 67 { 68 struct test_node *node; 69 70 rcu_register_thread(); 71 72 synchronize_rcu(); 73 74 rcu_read_lock(); 75 rcu_read_unlock(); 76 77 node = (struct test_node *) malloc(sizeof(*node)); 78 urcu_posix_assert(node); 79 80 call_rcu(&node->head, cb); 81 82 synchronize_rcu(); 83 84 rcu_unregister_thread(); 85 } 86 87 /* 88 * Return 0 if child, > 0 if parent, < 0 on error. 89 */ 90 static int do_fork(const char *execname) 91 { 92 pid_t pid; 93 94 diag_gen0("%s parent pid: %d, before fork", 95 execname, (int) getpid()); 96 97 call_rcu_before_fork(); 98 pid = fork(); 99 if (pid == 0) { 100 /* child */ 101 fork_generation++; 102 tap_disable(); 103 104 call_rcu_after_fork_child(); 105 diag_gen0("%s child pid: %d, after fork", 106 execname, (int) getpid()); 107 test_rcu(); 108 diag_gen0("%s child pid: %d, after rcu test", 109 execname, (int) getpid()); 110 if (fork_generation >= FORK_DEPTH) 111 exit(EXIT_SUCCESS); 112 return 0; 113 } else if (pid > 0) { 114 int status; 115 116 /* parent */ 117 call_rcu_after_fork_parent(); 118 diag_gen0("%s parent pid: %d, after fork", 119 execname, (int) getpid()); 120 test_rcu(); 121 diag_gen0("%s parent pid: %d, after rcu test", 122 execname, (int) getpid()); 123 for (;;) { 124 pid = wait(&status); 125 if (pid < 0) { 126 if (!fork_generation) 127 perror("wait"); 128 return -1; 129 } 130 if (WIFEXITED(status)) { 131 diag_gen0("child %u exited normally with status %u", 132 pid, WEXITSTATUS(status)); 133 if (WEXITSTATUS(status)) 134 return -1; 135 break; 136 } else if (WIFSIGNALED(status)) { 137 diag_gen0("child %u was terminated by signal %u", 138 pid, WTERMSIG(status)); 139 return -1; 140 } else { 141 continue; 142 } 143 } 144 return 1; 145 } else { 146 if (!fork_generation) 147 perror("fork"); 148 return -1; 149 } 150 } 151 152 int main(int argc __attribute__((unused)), char **argv) 153 { 154 unsigned int i; 155 156 plan_tests(NR_TESTS); 157 158 #if 0 159 /* pthread_atfork does not work with malloc/free in callbacks */ 160 ret = pthread_atfork(call_rcu_before_fork, 161 call_rcu_after_fork_parent, 162 call_rcu_after_fork_child); 163 if (ret) { 164 errno = ret; 165 perror("pthread_atfork"); 166 exit(EXIT_FAILURE); 167 } 168 #endif 169 170 restart: 171 for (i = 0; i < NR_FORK; i++) { 172 int ret; 173 174 test_rcu(); 175 synchronize_rcu(); 176 ret = do_fork(argv[0]); 177 if (!fork_generation) { 178 ok(ret >= 0, "child status %d", ret); 179 } 180 if (ret == 0) { /* child */ 181 goto restart; 182 } else if (ret < 0) { 183 goto error; 184 } else { 185 /* else parent, continue. */ 186 } 187 } 188 if (!fork_generation) { 189 return exit_status(); 190 } else { 191 exit(EXIT_SUCCESS); 192 } 193 194 error: 195 if (!fork_generation) { 196 return exit_status(); 197 } else { 198 exit(EXIT_FAILURE); 199 } 200 } 201