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