Statistics
| Branch: | Revision:

root / coroutine-ucontext.c @ feature-archipelago

History | View | Annotate | Download (5.6 kB)

1 00dccaf1 Kevin Wolf
/*
2 00dccaf1 Kevin Wolf
 * ucontext coroutine initialization code
3 00dccaf1 Kevin Wolf
 *
4 00dccaf1 Kevin Wolf
 * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
5 00dccaf1 Kevin Wolf
 * Copyright (C) 2011  Kevin Wolf <kwolf@redhat.com>
6 00dccaf1 Kevin Wolf
 *
7 00dccaf1 Kevin Wolf
 * This library is free software; you can redistribute it and/or
8 00dccaf1 Kevin Wolf
 * modify it under the terms of the GNU Lesser General Public
9 00dccaf1 Kevin Wolf
 * License as published by the Free Software Foundation; either
10 00dccaf1 Kevin Wolf
 * version 2.0 of the License, or (at your option) any later version.
11 00dccaf1 Kevin Wolf
 *
12 00dccaf1 Kevin Wolf
 * This library is distributed in the hope that it will be useful,
13 00dccaf1 Kevin Wolf
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 00dccaf1 Kevin Wolf
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 00dccaf1 Kevin Wolf
 * Lesser General Public License for more details.
16 00dccaf1 Kevin Wolf
 *
17 00dccaf1 Kevin Wolf
 * You should have received a copy of the GNU Lesser General Public
18 00dccaf1 Kevin Wolf
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 00dccaf1 Kevin Wolf
 */
20 00dccaf1 Kevin Wolf
21 00dccaf1 Kevin Wolf
/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */
22 00dccaf1 Kevin Wolf
#ifdef _FORTIFY_SOURCE
23 00dccaf1 Kevin Wolf
#undef _FORTIFY_SOURCE
24 00dccaf1 Kevin Wolf
#endif
25 00dccaf1 Kevin Wolf
#include <stdlib.h>
26 00dccaf1 Kevin Wolf
#include <setjmp.h>
27 00dccaf1 Kevin Wolf
#include <stdint.h>
28 00dccaf1 Kevin Wolf
#include <pthread.h>
29 00dccaf1 Kevin Wolf
#include <ucontext.h>
30 00dccaf1 Kevin Wolf
#include "qemu-common.h"
31 737e150e Paolo Bonzini
#include "block/coroutine_int.h"
32 00dccaf1 Kevin Wolf
33 3f4349dc Kevin Wolf
#ifdef CONFIG_VALGRIND_H
34 3f4349dc Kevin Wolf
#include <valgrind/valgrind.h>
35 3f4349dc Kevin Wolf
#endif
36 3f4349dc Kevin Wolf
37 00dccaf1 Kevin Wolf
typedef struct {
38 00dccaf1 Kevin Wolf
    Coroutine base;
39 00dccaf1 Kevin Wolf
    void *stack;
40 6ab7e546 Peter Maydell
    sigjmp_buf env;
41 3f4349dc Kevin Wolf
42 3f4349dc Kevin Wolf
#ifdef CONFIG_VALGRIND_H
43 3f4349dc Kevin Wolf
    unsigned int valgrind_stack_id;
44 3f4349dc Kevin Wolf
#endif
45 3f4349dc Kevin Wolf
46 00dccaf1 Kevin Wolf
} CoroutineUContext;
47 00dccaf1 Kevin Wolf
48 00dccaf1 Kevin Wolf
/**
49 00dccaf1 Kevin Wolf
 * Per-thread coroutine bookkeeping
50 00dccaf1 Kevin Wolf
 */
51 00dccaf1 Kevin Wolf
typedef struct {
52 00dccaf1 Kevin Wolf
    /** Currently executing coroutine */
53 00dccaf1 Kevin Wolf
    Coroutine *current;
54 00dccaf1 Kevin Wolf
55 00dccaf1 Kevin Wolf
    /** The default coroutine */
56 00dccaf1 Kevin Wolf
    CoroutineUContext leader;
57 00dccaf1 Kevin Wolf
} CoroutineThreadState;
58 00dccaf1 Kevin Wolf
59 00dccaf1 Kevin Wolf
static pthread_key_t thread_state_key;
60 00dccaf1 Kevin Wolf
61 00dccaf1 Kevin Wolf
/*
62 00dccaf1 Kevin Wolf
 * va_args to makecontext() must be type 'int', so passing
63 00dccaf1 Kevin Wolf
 * the pointer we need may require several int args. This
64 00dccaf1 Kevin Wolf
 * union is a quick hack to let us do that
65 00dccaf1 Kevin Wolf
 */
66 00dccaf1 Kevin Wolf
union cc_arg {
67 00dccaf1 Kevin Wolf
    void *p;
68 00dccaf1 Kevin Wolf
    int i[2];
69 00dccaf1 Kevin Wolf
};
70 00dccaf1 Kevin Wolf
71 00dccaf1 Kevin Wolf
static CoroutineThreadState *coroutine_get_thread_state(void)
72 00dccaf1 Kevin Wolf
{
73 00dccaf1 Kevin Wolf
    CoroutineThreadState *s = pthread_getspecific(thread_state_key);
74 00dccaf1 Kevin Wolf
75 00dccaf1 Kevin Wolf
    if (!s) {
76 7267c094 Anthony Liguori
        s = g_malloc0(sizeof(*s));
77 00dccaf1 Kevin Wolf
        s->current = &s->leader.base;
78 00dccaf1 Kevin Wolf
        pthread_setspecific(thread_state_key, s);
79 00dccaf1 Kevin Wolf
    }
80 00dccaf1 Kevin Wolf
    return s;
81 00dccaf1 Kevin Wolf
}
82 00dccaf1 Kevin Wolf
83 00dccaf1 Kevin Wolf
static void qemu_coroutine_thread_cleanup(void *opaque)
84 00dccaf1 Kevin Wolf
{
85 00dccaf1 Kevin Wolf
    CoroutineThreadState *s = opaque;
86 39a7a362 Avi Kivity
87 39a7a362 Avi Kivity
    g_free(s);
88 39a7a362 Avi Kivity
}
89 39a7a362 Avi Kivity
90 00dccaf1 Kevin Wolf
static void __attribute__((constructor)) coroutine_init(void)
91 00dccaf1 Kevin Wolf
{
92 00dccaf1 Kevin Wolf
    int ret;
93 00dccaf1 Kevin Wolf
94 00dccaf1 Kevin Wolf
    ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup);
95 00dccaf1 Kevin Wolf
    if (ret != 0) {
96 00dccaf1 Kevin Wolf
        fprintf(stderr, "unable to create leader key: %s\n", strerror(errno));
97 00dccaf1 Kevin Wolf
        abort();
98 00dccaf1 Kevin Wolf
    }
99 00dccaf1 Kevin Wolf
}
100 00dccaf1 Kevin Wolf
101 00dccaf1 Kevin Wolf
static void coroutine_trampoline(int i0, int i1)
102 00dccaf1 Kevin Wolf
{
103 00dccaf1 Kevin Wolf
    union cc_arg arg;
104 00dccaf1 Kevin Wolf
    CoroutineUContext *self;
105 00dccaf1 Kevin Wolf
    Coroutine *co;
106 00dccaf1 Kevin Wolf
107 00dccaf1 Kevin Wolf
    arg.i[0] = i0;
108 00dccaf1 Kevin Wolf
    arg.i[1] = i1;
109 00dccaf1 Kevin Wolf
    self = arg.p;
110 00dccaf1 Kevin Wolf
    co = &self->base;
111 00dccaf1 Kevin Wolf
112 00dccaf1 Kevin Wolf
    /* Initialize longjmp environment and switch back the caller */
113 6ab7e546 Peter Maydell
    if (!sigsetjmp(self->env, 0)) {
114 6ab7e546 Peter Maydell
        siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
115 00dccaf1 Kevin Wolf
    }
116 00dccaf1 Kevin Wolf
117 00dccaf1 Kevin Wolf
    while (true) {
118 00dccaf1 Kevin Wolf
        co->entry(co->entry_arg);
119 00dccaf1 Kevin Wolf
        qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE);
120 00dccaf1 Kevin Wolf
    }
121 00dccaf1 Kevin Wolf
}
122 00dccaf1 Kevin Wolf
123 40239784 Paolo Bonzini
Coroutine *qemu_coroutine_new(void)
124 00dccaf1 Kevin Wolf
{
125 00dccaf1 Kevin Wolf
    const size_t stack_size = 1 << 20;
126 00dccaf1 Kevin Wolf
    CoroutineUContext *co;
127 00dccaf1 Kevin Wolf
    ucontext_t old_uc, uc;
128 6ab7e546 Peter Maydell
    sigjmp_buf old_env;
129 32b74677 malc
    union cc_arg arg = {0};
130 00dccaf1 Kevin Wolf
131 6ab7e546 Peter Maydell
    /* The ucontext functions preserve signal masks which incurs a
132 6ab7e546 Peter Maydell
     * system call overhead.  sigsetjmp(buf, 0)/siglongjmp() does not
133 6ab7e546 Peter Maydell
     * preserve signal masks but only works on the current stack.
134 6ab7e546 Peter Maydell
     * Since we need a way to create and switch to a new stack, use
135 6ab7e546 Peter Maydell
     * the ucontext functions for that but sigsetjmp()/siglongjmp() for
136 6ab7e546 Peter Maydell
     * everything else.
137 00dccaf1 Kevin Wolf
     */
138 00dccaf1 Kevin Wolf
139 00dccaf1 Kevin Wolf
    if (getcontext(&uc) == -1) {
140 00dccaf1 Kevin Wolf
        abort();
141 00dccaf1 Kevin Wolf
    }
142 00dccaf1 Kevin Wolf
143 7267c094 Anthony Liguori
    co = g_malloc0(sizeof(*co));
144 7267c094 Anthony Liguori
    co->stack = g_malloc(stack_size);
145 00dccaf1 Kevin Wolf
    co->base.entry_arg = &old_env; /* stash away our jmp_buf */
146 00dccaf1 Kevin Wolf
147 00dccaf1 Kevin Wolf
    uc.uc_link = &old_uc;
148 00dccaf1 Kevin Wolf
    uc.uc_stack.ss_sp = co->stack;
149 00dccaf1 Kevin Wolf
    uc.uc_stack.ss_size = stack_size;
150 00dccaf1 Kevin Wolf
    uc.uc_stack.ss_flags = 0;
151 00dccaf1 Kevin Wolf
152 3f4349dc Kevin Wolf
#ifdef CONFIG_VALGRIND_H
153 3f4349dc Kevin Wolf
    co->valgrind_stack_id =
154 3f4349dc Kevin Wolf
        VALGRIND_STACK_REGISTER(co->stack, co->stack + stack_size);
155 3f4349dc Kevin Wolf
#endif
156 3f4349dc Kevin Wolf
157 00dccaf1 Kevin Wolf
    arg.p = co;
158 00dccaf1 Kevin Wolf
159 00dccaf1 Kevin Wolf
    makecontext(&uc, (void (*)(void))coroutine_trampoline,
160 00dccaf1 Kevin Wolf
                2, arg.i[0], arg.i[1]);
161 00dccaf1 Kevin Wolf
162 6ab7e546 Peter Maydell
    /* swapcontext() in, siglongjmp() back out */
163 6ab7e546 Peter Maydell
    if (!sigsetjmp(old_env, 0)) {
164 00dccaf1 Kevin Wolf
        swapcontext(&old_uc, &uc);
165 00dccaf1 Kevin Wolf
    }
166 00dccaf1 Kevin Wolf
    return &co->base;
167 00dccaf1 Kevin Wolf
}
168 00dccaf1 Kevin Wolf
169 3f4349dc Kevin Wolf
#ifdef CONFIG_VALGRIND_H
170 cc6e3ca9 Gerd Hoffmann
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
171 3f4349dc Kevin Wolf
/* Work around an unused variable in the valgrind.h macro... */
172 e6f53fd5 Markus Armbruster
#pragma GCC diagnostic push
173 3f4349dc Kevin Wolf
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
174 06d71fa1 Peter Maydell
#endif
175 3f4349dc Kevin Wolf
static inline void valgrind_stack_deregister(CoroutineUContext *co)
176 3f4349dc Kevin Wolf
{
177 3f4349dc Kevin Wolf
    VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id);
178 3f4349dc Kevin Wolf
}
179 cc6e3ca9 Gerd Hoffmann
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
180 e6f53fd5 Markus Armbruster
#pragma GCC diagnostic pop
181 3f4349dc Kevin Wolf
#endif
182 06d71fa1 Peter Maydell
#endif
183 3f4349dc Kevin Wolf
184 00dccaf1 Kevin Wolf
void qemu_coroutine_delete(Coroutine *co_)
185 00dccaf1 Kevin Wolf
{
186 00dccaf1 Kevin Wolf
    CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
187 00dccaf1 Kevin Wolf
188 3f4349dc Kevin Wolf
#ifdef CONFIG_VALGRIND_H
189 3f4349dc Kevin Wolf
    valgrind_stack_deregister(co);
190 3f4349dc Kevin Wolf
#endif
191 3f4349dc Kevin Wolf
192 7267c094 Anthony Liguori
    g_free(co->stack);
193 7267c094 Anthony Liguori
    g_free(co);
194 00dccaf1 Kevin Wolf
}
195 00dccaf1 Kevin Wolf
196 00dccaf1 Kevin Wolf
CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
197 00dccaf1 Kevin Wolf
                                      CoroutineAction action)
198 00dccaf1 Kevin Wolf
{
199 00dccaf1 Kevin Wolf
    CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_);
200 00dccaf1 Kevin Wolf
    CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_);
201 00dccaf1 Kevin Wolf
    CoroutineThreadState *s = coroutine_get_thread_state();
202 00dccaf1 Kevin Wolf
    int ret;
203 00dccaf1 Kevin Wolf
204 00dccaf1 Kevin Wolf
    s->current = to_;
205 00dccaf1 Kevin Wolf
206 6ab7e546 Peter Maydell
    ret = sigsetjmp(from->env, 0);
207 00dccaf1 Kevin Wolf
    if (ret == 0) {
208 6ab7e546 Peter Maydell
        siglongjmp(to->env, action);
209 00dccaf1 Kevin Wolf
    }
210 00dccaf1 Kevin Wolf
    return ret;
211 00dccaf1 Kevin Wolf
}
212 00dccaf1 Kevin Wolf
213 00dccaf1 Kevin Wolf
Coroutine *qemu_coroutine_self(void)
214 00dccaf1 Kevin Wolf
{
215 00dccaf1 Kevin Wolf
    CoroutineThreadState *s = coroutine_get_thread_state();
216 00dccaf1 Kevin Wolf
217 00dccaf1 Kevin Wolf
    return s->current;
218 00dccaf1 Kevin Wolf
}
219 00dccaf1 Kevin Wolf
220 00dccaf1 Kevin Wolf
bool qemu_in_coroutine(void)
221 00dccaf1 Kevin Wolf
{
222 00dccaf1 Kevin Wolf
    CoroutineThreadState *s = pthread_getspecific(thread_state_key);
223 00dccaf1 Kevin Wolf
224 00dccaf1 Kevin Wolf
    return s && s->current->caller;
225 00dccaf1 Kevin Wolf
}