1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Helper functions to sync execution between parent and child processes.
4  *
5  * Copyright 2018, Thiago Jung Bauermann, IBM Corporation.
6  */
7 #include <stdio.h>
8 #include <stdbool.h>
9 #include <semaphore.h>
10 
11 /*
12  * Information in a shared memory location for synchronization between child and
13  * parent.
14  */
15 struct child_sync {
16 	/* The parent waits on this semaphore. */
17 	sem_t sem_parent;
18 
19 	/* If true, the child should give up as well. */
20 	bool parent_gave_up;
21 
22 	/* The child waits on this semaphore. */
23 	sem_t sem_child;
24 
25 	/* If true, the parent should give up as well. */
26 	bool child_gave_up;
27 };
28 
29 #define CHILD_FAIL_IF(x, sync)						\
30 	do {								\
31 		if (x) {						\
32 			fprintf(stderr,					\
33 				"[FAIL] Test FAILED on line %d\n", __LINE__); \
34 			(sync)->child_gave_up = true;			\
35 			prod_parent(sync);				\
36 			return 1;					\
37 		}							\
38 	} while (0)
39 
40 #define PARENT_FAIL_IF(x, sync)						\
41 	do {								\
42 		if (x) {						\
43 			fprintf(stderr,					\
44 				"[FAIL] Test FAILED on line %d\n", __LINE__); \
45 			(sync)->parent_gave_up = true;			\
46 			prod_child(sync);				\
47 			return 1;					\
48 		}							\
49 	} while (0)
50 
51 #define PARENT_SKIP_IF_UNSUPPORTED(x, sync)				\
52 	do {								\
53 		if ((x) == -1 && (errno == ENODEV || errno == EINVAL)) { \
54 			(sync)->parent_gave_up = true;			\
55 			prod_child(sync);				\
56 			SKIP_IF(1);					\
57 		}							\
58 	} while (0)
59 
init_child_sync(struct child_sync * sync)60 int init_child_sync(struct child_sync *sync)
61 {
62 	int ret;
63 
64 	ret = sem_init(&sync->sem_parent, 1, 0);
65 	if (ret) {
66 		perror("Semaphore initialization failed");
67 		return 1;
68 	}
69 
70 	ret = sem_init(&sync->sem_child, 1, 0);
71 	if (ret) {
72 		perror("Semaphore initialization failed");
73 		return 1;
74 	}
75 
76 	return 0;
77 }
78 
destroy_child_sync(struct child_sync * sync)79 void destroy_child_sync(struct child_sync *sync)
80 {
81 	sem_destroy(&sync->sem_parent);
82 	sem_destroy(&sync->sem_child);
83 }
84 
wait_child(struct child_sync * sync)85 int wait_child(struct child_sync *sync)
86 {
87 	int ret;
88 
89 	/* Wait until the child prods us. */
90 	ret = sem_wait(&sync->sem_parent);
91 	if (ret) {
92 		perror("Error waiting for child");
93 		return 1;
94 	}
95 
96 	return sync->child_gave_up;
97 }
98 
prod_child(struct child_sync * sync)99 int prod_child(struct child_sync *sync)
100 {
101 	int ret;
102 
103 	/* Unblock the child now. */
104 	ret = sem_post(&sync->sem_child);
105 	if (ret) {
106 		perror("Error prodding child");
107 		return 1;
108 	}
109 
110 	return 0;
111 }
112 
wait_parent(struct child_sync * sync)113 int wait_parent(struct child_sync *sync)
114 {
115 	int ret;
116 
117 	/* Wait until the parent prods us. */
118 	ret = sem_wait(&sync->sem_child);
119 	if (ret) {
120 		perror("Error waiting for parent");
121 		return 1;
122 	}
123 
124 	return sync->parent_gave_up;
125 }
126 
prod_parent(struct child_sync * sync)127 int prod_parent(struct child_sync *sync)
128 {
129 	int ret;
130 
131 	/* Unblock the parent now. */
132 	ret = sem_post(&sync->sem_parent);
133 	if (ret) {
134 		perror("Error prodding parent");
135 		return 1;
136 	}
137 
138 	return 0;
139 }
140