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