166 lines
5.6 KiB
C
166 lines
5.6 KiB
C
|
/*
|
||
|
* QEMU Thread Local Storage for coroutines
|
||
|
*
|
||
|
* Copyright Red Hat
|
||
|
*
|
||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||
|
*
|
||
|
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||
|
* See the COPYING.LIB file in the top-level directory.
|
||
|
*
|
||
|
* It is forbidden to access Thread Local Storage in coroutines because
|
||
|
* compiler optimizations may cause values to be cached across coroutine
|
||
|
* re-entry. Coroutines can run in more than one thread through the course of
|
||
|
* their life, leading bugs when stale TLS values from the wrong thread are
|
||
|
* used as a result of compiler optimization.
|
||
|
*
|
||
|
* An example is:
|
||
|
*
|
||
|
* ..code-block:: c
|
||
|
* :caption: A coroutine that may see the wrong TLS value
|
||
|
*
|
||
|
* static __thread AioContext *current_aio_context;
|
||
|
* ...
|
||
|
* static void coroutine_fn foo(void)
|
||
|
* {
|
||
|
* aio_notify(current_aio_context);
|
||
|
* qemu_coroutine_yield();
|
||
|
* aio_notify(current_aio_context); // <-- may be stale after yielding!
|
||
|
* }
|
||
|
*
|
||
|
* This header provides macros for safely defining variables in Thread Local
|
||
|
* Storage:
|
||
|
*
|
||
|
* ..code-block:: c
|
||
|
* :caption: A coroutine that safely uses TLS
|
||
|
*
|
||
|
* QEMU_DEFINE_STATIC_CO_TLS(AioContext *, current_aio_context)
|
||
|
* ...
|
||
|
* static void coroutine_fn foo(void)
|
||
|
* {
|
||
|
* aio_notify(get_current_aio_context());
|
||
|
* qemu_coroutine_yield();
|
||
|
* aio_notify(get_current_aio_context()); // <-- safe
|
||
|
* }
|
||
|
*/
|
||
|
|
||
|
#ifndef QEMU_COROUTINE_TLS_H
|
||
|
#define QEMU_COROUTINE_TLS_H
|
||
|
|
||
|
/*
|
||
|
* To stop the compiler from caching TLS values we define accessor functions
|
||
|
* with __attribute__((noinline)) plus asm volatile("") to prevent
|
||
|
* optimizations that override noinline.
|
||
|
*
|
||
|
* The compiler can still analyze noinline code and make optimizations based on
|
||
|
* that knowledge, so an inline asm output operand is used to prevent
|
||
|
* optimizations that make assumptions about the address of the TLS variable.
|
||
|
*
|
||
|
* This is fragile and ultimately needs to be solved by a mechanism that is
|
||
|
* guaranteed to work by the compiler (e.g. stackless coroutines), but for now
|
||
|
* we use this approach to prevent issues.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* QEMU_DECLARE_CO_TLS:
|
||
|
* @type: the variable's C type
|
||
|
* @var: the variable name
|
||
|
*
|
||
|
* Declare an extern variable in Thread Local Storage from a header file:
|
||
|
*
|
||
|
* .. code-block:: c
|
||
|
* :caption: Declaring an extern variable in Thread Local Storage
|
||
|
*
|
||
|
* QEMU_DECLARE_CO_TLS(int, my_count)
|
||
|
* ...
|
||
|
* int c = get_my_count();
|
||
|
* set_my_count(c + 1);
|
||
|
* *get_ptr_my_count() = 0;
|
||
|
*
|
||
|
* This is a coroutine-safe replacement for the __thread keyword and is
|
||
|
* equivalent to the following code:
|
||
|
*
|
||
|
* .. code-block:: c
|
||
|
* :caption: Declaring a TLS variable using __thread
|
||
|
*
|
||
|
* extern __thread int my_count;
|
||
|
* ...
|
||
|
* int c = my_count;
|
||
|
* my_count = c + 1;
|
||
|
* *(&my_count) = 0;
|
||
|
*/
|
||
|
#define QEMU_DECLARE_CO_TLS(type, var) \
|
||
|
__attribute__((noinline)) type get_##var(void); \
|
||
|
__attribute__((noinline)) void set_##var(type v); \
|
||
|
__attribute__((noinline)) type *get_ptr_##var(void);
|
||
|
|
||
|
/**
|
||
|
* QEMU_DEFINE_CO_TLS:
|
||
|
* @type: the variable's C type
|
||
|
* @var: the variable name
|
||
|
*
|
||
|
* Define a variable in Thread Local Storage that was previously declared from
|
||
|
* a header file with QEMU_DECLARE_CO_TLS():
|
||
|
*
|
||
|
* .. code-block:: c
|
||
|
* :caption: Defining a variable in Thread Local Storage
|
||
|
*
|
||
|
* QEMU_DEFINE_CO_TLS(int, my_count)
|
||
|
*
|
||
|
* This is a coroutine-safe replacement for the __thread keyword and is
|
||
|
* equivalent to the following code:
|
||
|
*
|
||
|
* .. code-block:: c
|
||
|
* :caption: Defining a TLS variable using __thread
|
||
|
*
|
||
|
* __thread int my_count;
|
||
|
*/
|
||
|
#define QEMU_DEFINE_CO_TLS(type, var) \
|
||
|
static __thread type co_tls_##var; \
|
||
|
type get_##var(void) { asm volatile(""); return co_tls_##var; } \
|
||
|
void set_##var(type v) { asm volatile(""); co_tls_##var = v; } \
|
||
|
type *get_ptr_##var(void) \
|
||
|
{ type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; }
|
||
|
|
||
|
/**
|
||
|
* QEMU_DEFINE_STATIC_CO_TLS:
|
||
|
* @type: the variable's C type
|
||
|
* @var: the variable name
|
||
|
*
|
||
|
* Define a static variable in Thread Local Storage:
|
||
|
*
|
||
|
* .. code-block:: c
|
||
|
* :caption: Defining a static variable in Thread Local Storage
|
||
|
*
|
||
|
* QEMU_DEFINE_STATIC_CO_TLS(int, my_count)
|
||
|
* ...
|
||
|
* int c = get_my_count();
|
||
|
* set_my_count(c + 1);
|
||
|
* *get_ptr_my_count() = 0;
|
||
|
*
|
||
|
* This is a coroutine-safe replacement for the __thread keyword and is
|
||
|
* equivalent to the following code:
|
||
|
*
|
||
|
* .. code-block:: c
|
||
|
* :caption: Defining a static TLS variable using __thread
|
||
|
*
|
||
|
* static __thread int my_count;
|
||
|
* ...
|
||
|
* int c = my_count;
|
||
|
* my_count = c + 1;
|
||
|
* *(&my_count) = 0;
|
||
|
*/
|
||
|
#define QEMU_DEFINE_STATIC_CO_TLS(type, var) \
|
||
|
static __thread type co_tls_##var; \
|
||
|
static __attribute__((noinline, unused)) \
|
||
|
type get_##var(void) \
|
||
|
{ asm volatile(""); return co_tls_##var; } \
|
||
|
static __attribute__((noinline, unused)) \
|
||
|
void set_##var(type v) \
|
||
|
{ asm volatile(""); co_tls_##var = v; } \
|
||
|
static __attribute__((noinline, unused)) \
|
||
|
type *get_ptr_##var(void) \
|
||
|
{ type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; }
|
||
|
|
||
|
#endif /* QEMU_COROUTINE_TLS_H */
|