1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#ifdef _FORTIFY_SOURCE
26#undef _FORTIFY_SOURCE
27#endif
28#include "qemu/osdep.h"
29#include <pthread.h>
30#include "qemu-common.h"
31#include "qemu/coroutine_int.h"
32
33typedef struct {
34 Coroutine base;
35 void *stack;
36 size_t stack_size;
37 sigjmp_buf env;
38} CoroutineSigAltStack;
39
40
41
42
43typedef struct {
44
45 Coroutine *current;
46
47
48 CoroutineSigAltStack leader;
49
50
51 sigjmp_buf tr_reenter;
52 volatile sig_atomic_t tr_called;
53 void *tr_handler;
54} CoroutineThreadState;
55
56static pthread_key_t thread_state_key;
57
58static CoroutineThreadState *coroutine_get_thread_state(void)
59{
60 CoroutineThreadState *s = pthread_getspecific(thread_state_key);
61
62 if (!s) {
63 s = g_malloc0(sizeof(*s));
64 s->current = &s->leader.base;
65 pthread_setspecific(thread_state_key, s);
66 }
67 return s;
68}
69
70static void qemu_coroutine_thread_cleanup(void *opaque)
71{
72 CoroutineThreadState *s = opaque;
73
74 g_free(s);
75}
76
77static void __attribute__((constructor)) coroutine_init(void)
78{
79 int ret;
80
81 ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup);
82 if (ret != 0) {
83 fprintf(stderr, "unable to create leader key: %s\n", strerror(errno));
84 abort();
85 }
86}
87
88
89
90
91
92
93static void coroutine_bootstrap(CoroutineSigAltStack *self, Coroutine *co)
94{
95
96 if (!sigsetjmp(self->env, 0)) {
97 siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
98 }
99
100 while (true) {
101 co->entry(co->entry_arg);
102 qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE);
103 }
104}
105
106
107
108
109
110
111static void coroutine_trampoline(int signal)
112{
113 CoroutineSigAltStack *self;
114 Coroutine *co;
115 CoroutineThreadState *coTS;
116
117
118 coTS = coroutine_get_thread_state();
119 self = coTS->tr_handler;
120 coTS->tr_called = 1;
121 co = &self->base;
122
123
124
125
126
127
128 if (!sigsetjmp(coTS->tr_reenter, 0)) {
129 return;
130 }
131
132
133
134
135
136
137
138
139
140
141
142 coroutine_bootstrap(self, co);
143}
144
145Coroutine *qemu_coroutine_new(void)
146{
147 CoroutineSigAltStack *co;
148 CoroutineThreadState *coTS;
149 struct sigaction sa;
150 struct sigaction osa;
151 stack_t ss;
152 stack_t oss;
153 sigset_t sigs;
154 sigset_t osigs;
155 sigjmp_buf old_env;
156
157
158
159
160
161
162
163
164
165
166 co = g_malloc0(sizeof(*co));
167 co->stack_size = COROUTINE_STACK_SIZE;
168 co->stack = qemu_alloc_stack(&co->stack_size);
169 co->base.entry_arg = &old_env;
170
171 coTS = coroutine_get_thread_state();
172 coTS->tr_handler = co;
173
174
175
176
177
178
179 sigemptyset(&sigs);
180 sigaddset(&sigs, SIGUSR2);
181 pthread_sigmask(SIG_BLOCK, &sigs, &osigs);
182 sa.sa_handler = coroutine_trampoline;
183 sigfillset(&sa.sa_mask);
184 sa.sa_flags = SA_ONSTACK;
185 if (sigaction(SIGUSR2, &sa, &osa) != 0) {
186 abort();
187 }
188
189
190
191
192 ss.ss_sp = co->stack;
193 ss.ss_size = co->stack_size;
194 ss.ss_flags = 0;
195 if (sigaltstack(&ss, &oss) < 0) {
196 abort();
197 }
198
199
200
201
202
203
204
205
206 coTS->tr_called = 0;
207 pthread_kill(pthread_self(), SIGUSR2);
208 sigfillset(&sigs);
209 sigdelset(&sigs, SIGUSR2);
210 while (!coTS->tr_called) {
211 sigsuspend(&sigs);
212 }
213
214
215
216
217
218
219 sigaltstack(NULL, &ss);
220 ss.ss_flags = SS_DISABLE;
221 if (sigaltstack(&ss, NULL) < 0) {
222 abort();
223 }
224 sigaltstack(NULL, &ss);
225 if (!(oss.ss_flags & SS_DISABLE)) {
226 sigaltstack(&oss, NULL);
227 }
228
229
230
231
232 sigaction(SIGUSR2, &osa, NULL);
233 pthread_sigmask(SIG_SETMASK, &osigs, NULL);
234
235
236
237
238
239
240
241
242 if (!sigsetjmp(old_env, 0)) {
243 siglongjmp(coTS->tr_reenter, 1);
244 }
245
246
247
248
249
250 return &co->base;
251}
252
253void qemu_coroutine_delete(Coroutine *co_)
254{
255 CoroutineSigAltStack *co = DO_UPCAST(CoroutineSigAltStack, base, co_);
256
257 qemu_free_stack(co->stack, co->stack_size);
258 g_free(co);
259}
260
261CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
262 CoroutineAction action)
263{
264 CoroutineSigAltStack *from = DO_UPCAST(CoroutineSigAltStack, base, from_);
265 CoroutineSigAltStack *to = DO_UPCAST(CoroutineSigAltStack, base, to_);
266 CoroutineThreadState *s = coroutine_get_thread_state();
267 int ret;
268
269 s->current = to_;
270
271 ret = sigsetjmp(from->env, 0);
272 if (ret == 0) {
273 siglongjmp(to->env, action);
274 }
275 return ret;
276}
277
278Coroutine *qemu_coroutine_self(void)
279{
280 CoroutineThreadState *s = coroutine_get_thread_state();
281
282 return s->current;
283}
284
285bool qemu_in_coroutine(void)
286{
287 CoroutineThreadState *s = pthread_getspecific(thread_state_key);
288
289 return s && s->current->caller;
290}
291
292