Home | History | Annotate | Line # | Download | only in regression
      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