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 */
15struct 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, msg)			\
52	do {								\
53		if ((x) == -1 && (errno == ENODEV || errno == EINVAL)) { \
54			(sync)->parent_gave_up = true;			\
55			prod_child(sync);				\
56			SKIP_IF_MSG(1, msg);				\
57		}							\
58	} while (0)
59
60int 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
79void destroy_child_sync(struct child_sync *sync)
80{
81	sem_destroy(&sync->sem_parent);
82	sem_destroy(&sync->sem_child);
83}
84
85int 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
99int 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
113int 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
127int 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