qemu

Форк
0
/
coroutine-sigaltstack.c 
302 строки · 8.8 Кб
1
/*
2
 * sigaltstack coroutine initialization code
3
 *
4
 * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
5
 * Copyright (C) 2011  Kevin Wolf <kwolf@redhat.com>
6
 * Copyright (C) 2012  Alex Barcelo <abarcelo@ac.upc.edu>
7
** This file is partly based on pth_mctx.c, from the GNU Portable Threads
8
**  Copyright (c) 1999-2006 Ralf S. Engelschall <rse@engelschall.com>
9
 *
10
 * This library is free software; you can redistribute it and/or
11
 * modify it under the terms of the GNU Lesser General Public
12
 * License as published by the Free Software Foundation; either
13
 * version 2.1 of the License, or (at your option) any later version.
14
 *
15
 * This library is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
 * Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public
21
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
22
 */
23

24
/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */
25
#undef _FORTIFY_SOURCE
26
#define _FORTIFY_SOURCE 0
27

28
#include "qemu/osdep.h"
29
#include <pthread.h>
30
#include "qemu/coroutine_int.h"
31

32
#ifdef CONFIG_SAFESTACK
33
#error "SafeStack is not compatible with code run in alternate signal stacks"
34
#endif
35

36
typedef struct {
37
    Coroutine base;
38
    void *stack;
39
    size_t stack_size;
40
    sigjmp_buf env;
41
} CoroutineSigAltStack;
42

43
/**
44
 * Per-thread coroutine bookkeeping
45
 */
46
typedef struct {
47
    /** Currently executing coroutine */
48
    Coroutine *current;
49

50
    /** The default coroutine */
51
    CoroutineSigAltStack leader;
52

53
    /** Information for the signal handler (trampoline) */
54
    sigjmp_buf tr_reenter;
55
    volatile sig_atomic_t tr_called;
56
    void *tr_handler;
57
} CoroutineThreadState;
58

59
static pthread_key_t thread_state_key;
60

61
static CoroutineThreadState *coroutine_get_thread_state(void)
62
{
63
    CoroutineThreadState *s = pthread_getspecific(thread_state_key);
64

65
    if (!s) {
66
        s = g_malloc0(sizeof(*s));
67
        s->current = &s->leader.base;
68
        pthread_setspecific(thread_state_key, s);
69
    }
70
    return s;
71
}
72

73
static void qemu_coroutine_thread_cleanup(void *opaque)
74
{
75
    CoroutineThreadState *s = opaque;
76

77
    g_free(s);
78
}
79

80
static void __attribute__((constructor)) coroutine_init(void)
81
{
82
    int ret;
83

84
    ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup);
85
    if (ret != 0) {
86
        fprintf(stderr, "unable to create leader key: %s\n", strerror(errno));
87
        abort();
88
    }
89
}
90

91
/* "boot" function
92
 * This is what starts the coroutine, is called from the trampoline
93
 * (from the signal handler when it is not signal handling, read ahead
94
 * for more information).
95
 */
96
static void coroutine_bootstrap(CoroutineSigAltStack *self, Coroutine *co)
97
{
98
    /* Initialize longjmp environment and switch back the caller */
99
    if (!sigsetjmp(self->env, 0)) {
100
        siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
101
    }
102

103
    while (true) {
104
        co->entry(co->entry_arg);
105
        qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE);
106
    }
107
}
108

109
/*
110
 * This is used as the signal handler. This is called with the brand new stack
111
 * (thanks to sigaltstack). We have to return, given that this is a signal
112
 * handler and the sigmask and some other things are changed.
113
 */
114
static void coroutine_trampoline(int signal)
115
{
116
    CoroutineSigAltStack *self;
117
    Coroutine *co;
118
    CoroutineThreadState *coTS;
119

120
    /* Get the thread specific information */
121
    coTS = coroutine_get_thread_state();
122
    self = coTS->tr_handler;
123
    coTS->tr_called = 1;
124
    co = &self->base;
125

126
    /*
127
     * Here we have to do a bit of a ping pong between the caller, given that
128
     * this is a signal handler and we have to do a return "soon". Then the
129
     * caller can reestablish everything and do a siglongjmp here again.
130
     */
131
    if (!sigsetjmp(coTS->tr_reenter, 0)) {
132
        return;
133
    }
134

135
    /*
136
     * Ok, the caller has siglongjmp'ed back to us, so now prepare
137
     * us for the real machine state switching. We have to jump
138
     * into another function here to get a new stack context for
139
     * the auto variables (which have to be auto-variables
140
     * because the start of the thread happens later). Else with
141
     * PIC (i.e. Position Independent Code which is used when PTH
142
     * is built as a shared library) most platforms would
143
     * horrible core dump as experience showed.
144
     */
145
    coroutine_bootstrap(self, co);
146
}
147

148
Coroutine *qemu_coroutine_new(void)
149
{
150
    CoroutineSigAltStack *co;
151
    CoroutineThreadState *coTS;
152
    struct sigaction sa;
153
    struct sigaction osa;
154
    stack_t ss;
155
    stack_t oss;
156
    sigset_t sigs;
157
    sigset_t osigs;
158
    sigjmp_buf old_env;
159
    static pthread_mutex_t sigusr2_mutex = PTHREAD_MUTEX_INITIALIZER;
160

161
    /* The way to manipulate stack is with the sigaltstack function. We
162
     * prepare a stack, with it delivering a signal to ourselves and then
163
     * put sigsetjmp/siglongjmp where needed.
164
     * This has been done keeping coroutine-ucontext as a model and with the
165
     * pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics
166
     * of the coroutines and see pth_mctx.c (from the pth project) for the
167
     * sigaltstack way of manipulating stacks.
168
     */
169

170
    co = g_malloc0(sizeof(*co));
171
    co->stack_size = COROUTINE_STACK_SIZE;
172
    co->stack = qemu_alloc_stack(&co->stack_size);
173
    co->base.entry_arg = &old_env; /* stash away our jmp_buf */
174

175
    coTS = coroutine_get_thread_state();
176
    coTS->tr_handler = co;
177

178
    /*
179
     * Preserve the SIGUSR2 signal state, block SIGUSR2,
180
     * and establish our signal handler. The signal will
181
     * later transfer control onto the signal stack.
182
     */
183
    sigemptyset(&sigs);
184
    sigaddset(&sigs, SIGUSR2);
185
    pthread_sigmask(SIG_BLOCK, &sigs, &osigs);
186
    sa.sa_handler = coroutine_trampoline;
187
    sigfillset(&sa.sa_mask);
188
    sa.sa_flags = SA_ONSTACK;
189

190
    /*
191
     * sigaction() is a process-global operation.  We must not run
192
     * this code in multiple threads at once.
193
     */
194
    pthread_mutex_lock(&sigusr2_mutex);
195
    if (sigaction(SIGUSR2, &sa, &osa) != 0) {
196
        abort();
197
    }
198

199
    /*
200
     * Set the new stack.
201
     */
202
    ss.ss_sp = co->stack;
203
    ss.ss_size = co->stack_size;
204
    ss.ss_flags = 0;
205
    if (sigaltstack(&ss, &oss) < 0) {
206
        abort();
207
    }
208

209
    /*
210
     * Now transfer control onto the signal stack and set it up.
211
     * It will return immediately via "return" after the sigsetjmp()
212
     * was performed. Be careful here with race conditions.  The
213
     * signal can be delivered the first time sigsuspend() is
214
     * called.
215
     */
216
    coTS->tr_called = 0;
217
    pthread_kill(pthread_self(), SIGUSR2);
218
    sigfillset(&sigs);
219
    sigdelset(&sigs, SIGUSR2);
220
    while (!coTS->tr_called) {
221
        sigsuspend(&sigs);
222
    }
223

224
    /*
225
     * Inform the system that we are back off the signal stack by
226
     * removing the alternative signal stack. Be careful here: It
227
     * first has to be disabled, before it can be removed.
228
     */
229
    sigaltstack(NULL, &ss);
230
    ss.ss_flags = SS_DISABLE;
231
    if (sigaltstack(&ss, NULL) < 0) {
232
        abort();
233
    }
234
    sigaltstack(NULL, &ss);
235
    if (!(oss.ss_flags & SS_DISABLE)) {
236
        sigaltstack(&oss, NULL);
237
    }
238

239
    /*
240
     * Restore the old SIGUSR2 signal handler and mask
241
     */
242
    sigaction(SIGUSR2, &osa, NULL);
243
    pthread_mutex_unlock(&sigusr2_mutex);
244

245
    pthread_sigmask(SIG_SETMASK, &osigs, NULL);
246

247
    /*
248
     * Now enter the trampoline again, but this time not as a signal
249
     * handler. Instead we jump into it directly. The functionally
250
     * redundant ping-pong pointer arithmetic is necessary to avoid
251
     * type-conversion warnings related to the `volatile' qualifier and
252
     * the fact that `jmp_buf' usually is an array type.
253
     */
254
    if (!sigsetjmp(old_env, 0)) {
255
        siglongjmp(coTS->tr_reenter, 1);
256
    }
257

258
    /*
259
     * Ok, we returned again, so now we're finished
260
     */
261

262
    return &co->base;
263
}
264

265
void qemu_coroutine_delete(Coroutine *co_)
266
{
267
    CoroutineSigAltStack *co = DO_UPCAST(CoroutineSigAltStack, base, co_);
268

269
    qemu_free_stack(co->stack, co->stack_size);
270
    g_free(co);
271
}
272

273
CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
274
                                      CoroutineAction action)
275
{
276
    CoroutineSigAltStack *from = DO_UPCAST(CoroutineSigAltStack, base, from_);
277
    CoroutineSigAltStack *to = DO_UPCAST(CoroutineSigAltStack, base, to_);
278
    CoroutineThreadState *s = coroutine_get_thread_state();
279
    int ret;
280

281
    s->current = to_;
282

283
    ret = sigsetjmp(from->env, 0);
284
    if (ret == 0) {
285
        siglongjmp(to->env, action);
286
    }
287
    return ret;
288
}
289

290
Coroutine *qemu_coroutine_self(void)
291
{
292
    CoroutineThreadState *s = coroutine_get_thread_state();
293

294
    return s->current;
295
}
296

297
bool qemu_in_coroutine(void)
298
{
299
    CoroutineThreadState *s = pthread_getspecific(thread_state_key);
300

301
    return s && s->current->caller;
302
}
303

304

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.