From: Michael Jeanson Date: Thu, 22 Apr 2021 16:48:52 +0000 (-0400) Subject: Make futex compat internal to liblttng-ust X-Git-Tag: v2.13.0-rc1~11 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=08ba2503b86d1506117c077c99eb1c38973a48f3;p=lttng-ust.git Make futex compat internal to liblttng-ust This compat header originated in userspace RCU where it's used across multiple shared objects hence the need to have the mutexes as public weak symbols, in our case it's only used internally by liblttng-ust so we can hide them. If we end up using this compat header in another library in this project we will have to use the same scheme, but in the meantime, don't expose those symbols in the ABI. Move the code to the liblttng-ust directory to make sure it's not used by other libraries. Change-Id: Ibfaef9448eeaf6247b42f71d6b6d8de234d79a3c Signed-off-by: Michael Jeanson Signed-off-by: Mathieu Desnoyers --- diff --git a/src/common/Makefile.am b/src/common/Makefile.am index b5f8473b..f4c134a7 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -49,7 +49,6 @@ noinst_HEADERS += \ noinst_LTLIBRARIES = \ libcounter.la \ - libcompat.la \ libmsgpack.la \ libringbuffer.la \ libsnprintf.la \ @@ -77,11 +76,6 @@ endif libcounter_la_CFLAGS = -DUST_COMPONENT="libcounter" $(AM_CFLAGS) -# compat -libcompat_la_SOURCES = \ - compat/futex.c \ - compat/futex.h - # msgpack libmsgpack_la_SOURCES = \ msgpack/msgpack.c \ @@ -154,7 +148,6 @@ libcommon_la_SOURCES = \ patient.c libcommon_la_LIBADD = \ - libcompat.la \ libmsgpack.la \ libsnprintf.la diff --git a/src/common/compat/futex.c b/src/common/compat/futex.c deleted file mode 100644 index c87d7134..00000000 --- a/src/common/compat/futex.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * - * Copyright (c) 2009 Mathieu Desnoyers - * - * Userspace RCU library - sys_futex compatibility code - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "common/compat/futex.h" - -/* - * Using attribute "weak" for __lttng_ust_compat_futex_lock and - * __lttng_ust_compat_futex_cond. Those are globally visible by the entire - * program, even though many shared objects may have their own version. - * The first version that gets loaded will be used by the entire program - * (executable and all shared objects). - */ - -__attribute__((weak)) -pthread_mutex_t __lttng_ust_compat_futex_lock = PTHREAD_MUTEX_INITIALIZER; -__attribute__((weak)) -pthread_cond_t __lttng_ust_compat_futex_cond = PTHREAD_COND_INITIALIZER; - -/* - * _NOT SIGNAL-SAFE_. pthread_cond is not signal-safe anyway. Though. - * For now, timeout, uaddr2 and val3 are unused. - * Waiter will relinquish the CPU until woken up. - */ - -int lttng_ust_compat_futex_noasync(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) -{ - int ret = 0, lockret; - - /* - * Check if NULL. Don't let users expect that they are taken into - * account. - */ - assert(!timeout); - assert(!uaddr2); - assert(!val3); - - /* - * memory barriers to serialize with the previous uaddr modification. - */ - cmm_smp_mb(); - - lockret = pthread_mutex_lock(&__lttng_ust_compat_futex_lock); - if (lockret) { - errno = lockret; - ret = -1; - goto end; - } - switch (op) { - case FUTEX_WAIT: - /* - * Wait until *uaddr is changed to something else than "val". - * Comparing *uaddr content against val figures out which - * thread has been awakened. - */ - while (CMM_LOAD_SHARED(*uaddr) == val) - pthread_cond_wait(&__lttng_ust_compat_futex_cond, - &__lttng_ust_compat_futex_lock); - break; - case FUTEX_WAKE: - /* - * Each wake is sending a broadcast, thus attempting wakeup of - * all awaiting threads, independently of their respective - * uaddr. - */ - pthread_cond_broadcast(&__lttng_ust_compat_futex_cond); - break; - default: - errno = EINVAL; - ret = -1; - } - lockret = pthread_mutex_unlock(&__lttng_ust_compat_futex_lock); - if (lockret) { - errno = lockret; - ret = -1; - } -end: - return ret; -} - -/* - * _ASYNC SIGNAL-SAFE_. - * For now, timeout, uaddr2 and val3 are unused. - * Waiter will busy-loop trying to read the condition. - * It is OK to use compat_futex_async() on a futex address on which - * futex() WAKE operations are also performed. - */ - -int lttng_ust_compat_futex_async(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) -{ - int ret = 0; - - /* - * Check if NULL. Don't let users expect that they are taken into - * account. - */ - assert(!timeout); - assert(!uaddr2); - assert(!val3); - - /* - * Ensure previous memory operations on uaddr have completed. - */ - cmm_smp_mb(); - - switch (op) { - case FUTEX_WAIT: - while (CMM_LOAD_SHARED(*uaddr) == val) { - if (poll(NULL, 0, 10) < 0) { - ret = -1; - /* Keep poll errno. Caller handles EINTR. */ - goto end; - } - } - break; - case FUTEX_WAKE: - break; - default: - errno = EINVAL; - ret = -1; - } -end: - return ret; -} diff --git a/src/common/compat/futex.h b/src/common/compat/futex.h deleted file mode 100644 index 71b98677..00000000 --- a/src/common/compat/futex.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * - * Copyright 2011-2012 Mathieu Desnoyers - * - * Userspace RCU - sys_futex/compat_futex header. - */ - -#ifndef _UST_COMMON_COMPAT_FUTEX_H -#define _UST_COMMON_COMPAT_FUTEX_H - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define FUTEX_WAIT 0 -#define FUTEX_WAKE 1 - -/* - * sys_futex compatibility header. - * Use *only* *either of* futex_noasync OR futex_async on a given address. - * - * futex_noasync cannot be executed in signal handlers, but ensures that - * it will be put in a wait queue even in compatibility mode. - * - * futex_async is signal-handler safe for the wakeup. It uses polling - * on the wait-side in compatibility mode. - * - * BEWARE: sys_futex() FUTEX_WAIT may return early if interrupted - * (returns EINTR). - */ - -extern int lttng_ust_compat_futex_noasync(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) - __attribute__((visibility("hidden"))); - -extern int lttng_ust_compat_futex_async(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) - __attribute__((visibility("hidden"))); - -#if (defined(__linux__) && defined(__NR_futex)) - -#include -#include -#include -#include - -static inline int lttng_ust_futex(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) -{ - return syscall(__NR_futex, uaddr, op, val, timeout, - uaddr2, val3); -} - -static inline int lttng_ust_futex_noasync(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) -{ - int ret; - - ret = lttng_ust_futex(uaddr, op, val, timeout, uaddr2, val3); - if (caa_unlikely(ret < 0 && errno == ENOSYS)) { - /* - * The fallback on ENOSYS is the async-safe version of - * the compat futex implementation, because the - * async-safe compat implementation allows being used - * concurrently with calls to futex(). Indeed, sys_futex - * FUTEX_WAIT, on some architectures (mips and parisc), - * within a given process, spuriously return ENOSYS due - * to signal restart bugs on some kernel versions. - */ - return lttng_ust_compat_futex_async(uaddr, op, val, timeout, - uaddr2, val3); - } - return ret; - -} - -static inline int lttng_ust_futex_async(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) -{ - int ret; - - ret = lttng_ust_futex(uaddr, op, val, timeout, uaddr2, val3); - if (caa_unlikely(ret < 0 && errno == ENOSYS)) { - return lttng_ust_compat_futex_async(uaddr, op, val, timeout, - uaddr2, val3); - } - return ret; -} - -#elif defined(__FreeBSD__) - -#include -#include - -static inline int lttng_ust_futex_async(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, - int32_t *uaddr2 __attribute__((unused)), - int32_t val3 __attribute__((unused))) -{ - int umtx_op; - void *umtx_uaddr = NULL, *umtx_uaddr2 = NULL; - struct _umtx_time umtx_timeout = { - ._flags = UMTX_ABSTIME, - ._clockid = CLOCK_MONOTONIC, - }; - - switch (op) { - case FUTEX_WAIT: - /* On FreeBSD, a "u_int" is a 32-bit integer. */ - umtx_op = UMTX_OP_WAIT_UINT; - if (timeout != NULL) { - umtx_timeout._timeout = *timeout; - umtx_uaddr = (void *) sizeof(umtx_timeout); - umtx_uaddr2 = (void *) &umtx_timeout; - } - break; - case FUTEX_WAKE: - umtx_op = UMTX_OP_WAKE; - break; - default: - errno = EINVAL; - return -1; - } - - return _umtx_op(uaddr, umtx_op, (uint32_t) val, umtx_uaddr, - umtx_uaddr2); -} - -static inline int lttng_ust_futex_noasync(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) -{ - return lttng_ust_futex_async(uaddr, op, val, timeout, uaddr2, val3); -} - -#elif defined(__CYGWIN__) - -/* - * The futex_noasync compat code uses a weak symbol to share state across - * different shared object which is not possible on Windows with the - * Portable Executable format. Use the async compat code for both cases. - */ -static inline int lttng_ust_futex_noasync(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) -{ - return lttng_ust_compat_futex_async(uaddr, op, val, timeout, uaddr2, val3); -} - -static inline int lttng_ust_futex_async(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) -{ - return lttng_ust_compat_futex_async(uaddr, op, val, timeout, uaddr2, val3); -} - -#else - -static inline int lttng_ust_futex_noasync(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) -{ - return lttng_ust_compat_futex_noasync(uaddr, op, val, timeout, uaddr2, val3); -} - -static inline int lttng_ust_futex_async(int32_t *uaddr, int op, int32_t val, - const struct timespec *timeout, int32_t *uaddr2, int32_t val3) -{ - return lttng_ust_compat_futex_async(uaddr, op, val, timeout, uaddr2, val3); -} - -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* _UST_COMMON_COMPAT_FUTEX_H */ diff --git a/src/lib/lttng-ust/Makefile.am b/src/lib/lttng-ust/Makefile.am index 3ef20aa6..73ceb438 100644 --- a/src/lib/lttng-ust/Makefile.am +++ b/src/lib/lttng-ust/Makefile.am @@ -44,6 +44,8 @@ liblttng_ust_runtime_la_SOURCES = \ lttng-ust-statedump.c \ lttng-ust-statedump.h \ lttng-ust-statedump-provider.h \ + futex.c \ + futex.h \ ust_lib.c \ ust_lib.h \ context-internal.h \ diff --git a/src/lib/lttng-ust/futex.c b/src/lib/lttng-ust/futex.c new file mode 100644 index 00000000..cf86dfcd --- /dev/null +++ b/src/lib/lttng-ust/futex.c @@ -0,0 +1,151 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * Copyright (c) 2009 Mathieu Desnoyers + * + * Userspace RCU library - sys_futex compatibility code + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "lib/lttng-ust/futex.h" + +/* + * This compat header originated in userspace RCU where it's used across + * multiple shared objects hence the need to have the mutexes as public weak + * symbols, in our case here, it's only used internally by liblttng-ust so we + * can hide them. If we end up using this compat header in another library in + * this project we will have to use the same scheme, but in the meantime, don't + * expose those symbols in the ABI. + */ + +/* + * This comment will apply if we start using this compat header in multiple + * libraires. + * + * Using attribute "weak" for __lttng_ust_compat_futex_lock and + * __lttng_ust_compat_futex_cond. Those are globally visible by the entire + * program, even though many shared objects may have their own version. + * The first version that gets loaded will be used by the entire program + * (executable and all shared objects). + */ + +static pthread_mutex_t __lttng_ust_compat_futex_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t __lttng_ust_compat_futex_cond = PTHREAD_COND_INITIALIZER; + +/* + * _NOT SIGNAL-SAFE_. pthread_cond is not signal-safe anyway. Though. + * For now, timeout, uaddr2 and val3 are unused. + * Waiter will relinquish the CPU until woken up. + */ + +int lttng_ust_compat_futex_noasync(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) +{ + int ret = 0, lockret; + + /* + * Check if NULL. Don't let users expect that they are taken into + * account. + */ + assert(!timeout); + assert(!uaddr2); + assert(!val3); + + /* + * memory barriers to serialize with the previous uaddr modification. + */ + cmm_smp_mb(); + + lockret = pthread_mutex_lock(&__lttng_ust_compat_futex_lock); + if (lockret) { + errno = lockret; + ret = -1; + goto end; + } + switch (op) { + case FUTEX_WAIT: + /* + * Wait until *uaddr is changed to something else than "val". + * Comparing *uaddr content against val figures out which + * thread has been awakened. + */ + while (CMM_LOAD_SHARED(*uaddr) == val) + pthread_cond_wait(&__lttng_ust_compat_futex_cond, + &__lttng_ust_compat_futex_lock); + break; + case FUTEX_WAKE: + /* + * Each wake is sending a broadcast, thus attempting wakeup of + * all awaiting threads, independently of their respective + * uaddr. + */ + pthread_cond_broadcast(&__lttng_ust_compat_futex_cond); + break; + default: + errno = EINVAL; + ret = -1; + } + lockret = pthread_mutex_unlock(&__lttng_ust_compat_futex_lock); + if (lockret) { + errno = lockret; + ret = -1; + } +end: + return ret; +} + +/* + * _ASYNC SIGNAL-SAFE_. + * For now, timeout, uaddr2 and val3 are unused. + * Waiter will busy-loop trying to read the condition. + * It is OK to use compat_futex_async() on a futex address on which + * futex() WAKE operations are also performed. + */ + +int lttng_ust_compat_futex_async(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) +{ + int ret = 0; + + /* + * Check if NULL. Don't let users expect that they are taken into + * account. + */ + assert(!timeout); + assert(!uaddr2); + assert(!val3); + + /* + * Ensure previous memory operations on uaddr have completed. + */ + cmm_smp_mb(); + + switch (op) { + case FUTEX_WAIT: + while (CMM_LOAD_SHARED(*uaddr) == val) { + if (poll(NULL, 0, 10) < 0) { + ret = -1; + /* Keep poll errno. Caller handles EINTR. */ + goto end; + } + } + break; + case FUTEX_WAKE: + break; + default: + errno = EINVAL; + ret = -1; + } +end: + return ret; +} diff --git a/src/lib/lttng-ust/futex.h b/src/lib/lttng-ust/futex.h new file mode 100644 index 00000000..ef6b349a --- /dev/null +++ b/src/lib/lttng-ust/futex.h @@ -0,0 +1,180 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * Copyright 2011-2012 Mathieu Desnoyers + * + * Userspace RCU - sys_futex/compat_futex header. + */ + +#ifndef _UST_COMMON_COMPAT_FUTEX_H +#define _UST_COMMON_COMPAT_FUTEX_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FUTEX_WAIT 0 +#define FUTEX_WAKE 1 + +/* + * sys_futex compatibility header. + * Use *only* *either of* futex_noasync OR futex_async on a given address. + * + * futex_noasync cannot be executed in signal handlers, but ensures that + * it will be put in a wait queue even in compatibility mode. + * + * futex_async is signal-handler safe for the wakeup. It uses polling + * on the wait-side in compatibility mode. + * + * BEWARE: sys_futex() FUTEX_WAIT may return early if interrupted + * (returns EINTR). + */ + +int lttng_ust_compat_futex_noasync(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) + __attribute__((visibility("hidden"))); + +int lttng_ust_compat_futex_async(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) + __attribute__((visibility("hidden"))); + +#if (defined(__linux__) && defined(__NR_futex)) + +#include +#include +#include +#include + +static inline int lttng_ust_futex(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) +{ + return syscall(__NR_futex, uaddr, op, val, timeout, + uaddr2, val3); +} + +static inline int lttng_ust_futex_noasync(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) +{ + int ret; + + ret = lttng_ust_futex(uaddr, op, val, timeout, uaddr2, val3); + if (caa_unlikely(ret < 0 && errno == ENOSYS)) { + /* + * The fallback on ENOSYS is the async-safe version of + * the compat futex implementation, because the + * async-safe compat implementation allows being used + * concurrently with calls to futex(). Indeed, sys_futex + * FUTEX_WAIT, on some architectures (mips and parisc), + * within a given process, spuriously return ENOSYS due + * to signal restart bugs on some kernel versions. + */ + return lttng_ust_compat_futex_async(uaddr, op, val, timeout, + uaddr2, val3); + } + return ret; + +} + +static inline int lttng_ust_futex_async(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) +{ + int ret; + + ret = lttng_ust_futex(uaddr, op, val, timeout, uaddr2, val3); + if (caa_unlikely(ret < 0 && errno == ENOSYS)) { + return lttng_ust_compat_futex_async(uaddr, op, val, timeout, + uaddr2, val3); + } + return ret; +} + +#elif defined(__FreeBSD__) + +#include +#include + +static inline int lttng_ust_futex_async(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, + int32_t *uaddr2 __attribute__((unused)), + int32_t val3 __attribute__((unused))) +{ + int umtx_op; + void *umtx_uaddr = NULL, *umtx_uaddr2 = NULL; + struct _umtx_time umtx_timeout = { + ._flags = UMTX_ABSTIME, + ._clockid = CLOCK_MONOTONIC, + }; + + switch (op) { + case FUTEX_WAIT: + /* On FreeBSD, a "u_int" is a 32-bit integer. */ + umtx_op = UMTX_OP_WAIT_UINT; + if (timeout != NULL) { + umtx_timeout._timeout = *timeout; + umtx_uaddr = (void *) sizeof(umtx_timeout); + umtx_uaddr2 = (void *) &umtx_timeout; + } + break; + case FUTEX_WAKE: + umtx_op = UMTX_OP_WAKE; + break; + default: + errno = EINVAL; + return -1; + } + + return _umtx_op(uaddr, umtx_op, (uint32_t) val, umtx_uaddr, + umtx_uaddr2); +} + +static inline int lttng_ust_futex_noasync(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) +{ + return lttng_ust_futex_async(uaddr, op, val, timeout, uaddr2, val3); +} + +#elif defined(__CYGWIN__) + +/* + * The futex_noasync compat code uses a weak symbol to share state across + * different shared object which is not possible on Windows with the + * Portable Executable format. Use the async compat code for both cases. + */ +static inline int lttng_ust_futex_noasync(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) +{ + return lttng_ust_compat_futex_async(uaddr, op, val, timeout, uaddr2, val3); +} + +static inline int lttng_ust_futex_async(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) +{ + return lttng_ust_compat_futex_async(uaddr, op, val, timeout, uaddr2, val3); +} + +#else + +static inline int lttng_ust_futex_noasync(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) +{ + return lttng_ust_compat_futex_noasync(uaddr, op, val, timeout, uaddr2, val3); +} + +static inline int lttng_ust_futex_async(int32_t *uaddr, int op, int32_t val, + const struct timespec *timeout, int32_t *uaddr2, int32_t val3) +{ + return lttng_ust_compat_futex_async(uaddr, op, val, timeout, uaddr2, val3); +} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _UST_COMMON_COMPAT_FUTEX_H */ diff --git a/src/lib/lttng-ust/lttng-ust-comm.c b/src/lib/lttng-ust/lttng-ust-comm.c index 6cdce82d..61f0b34a 100644 --- a/src/lib/lttng-ust/lttng-ust-comm.c +++ b/src/lib/lttng-ust/lttng-ust-comm.c @@ -39,7 +39,7 @@ #include #include #include -#include "common/compat/futex.h" +#include "lib/lttng-ust/futex.h" #include "common/ustcomm.h" #include "common/ust-fd.h" #include "common/logging.h"