From: Michael Jeanson Date: Tue, 6 Apr 2021 22:32:51 +0000 (-0400) Subject: Move futex wrapper to 'common/compat/' X-Git-Tag: v2.13.0-rc1~119 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=58f8447848dfbfa79367607af7845547d47a08d4;p=lttng-ust.git Move futex wrapper to 'common/compat/' Change-Id: I9f1a31162f789bde101e08c5effa65449d631946 Signed-off-by: Michael Jeanson Signed-off-by: Mathieu Desnoyers --- diff --git a/src/common/Makefile.am b/src/common/Makefile.am index e450f8c8..b847001e 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -45,6 +45,7 @@ noinst_HEADERS += \ noinst_LTLIBRARIES = \ libcounter.la \ + libcompat.la \ libmsgpack.la \ libringbuffer.la \ libsnprintf.la \ @@ -74,6 +75,11 @@ 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 \ @@ -137,6 +143,7 @@ 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 new file mode 100644 index 00000000..c87d7134 --- /dev/null +++ b/src/common/compat/futex.c @@ -0,0 +1,141 @@ +/* + * 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 new file mode 100644 index 00000000..446c7f1c --- /dev/null +++ b/src/common/compat/futex.h @@ -0,0 +1,178 @@ +/* + * 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, int32_t val3) +{ + 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 77c91067..5612cb64 100644 --- a/src/lib/lttng-ust/Makefile.am +++ b/src/lib/lttng-ust/Makefile.am @@ -97,9 +97,7 @@ liblttng_ust_runtime_la_SOURCES = \ rculfhash-internal.h \ rculfhash-mm-chunk.c \ rculfhash-mm-mmap.c \ - rculfhash-mm-order.c \ - compat_futex.c \ - futex.h + rculfhash-mm-order.c if HAVE_PERF_EVENT liblttng_ust_runtime_la_SOURCES += \ diff --git a/src/lib/lttng-ust/compat_futex.c b/src/lib/lttng-ust/compat_futex.c deleted file mode 100644 index adf1f440..00000000 --- a/src/lib/lttng-ust/compat_futex.c +++ /dev/null @@ -1,140 +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 "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/lib/lttng-ust/futex.h b/src/lib/lttng-ust/futex.h deleted file mode 100644 index 18c46a13..00000000 --- a/src/lib/lttng-ust/futex.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * - * Copyright 2011-2012 Mathieu Desnoyers - * - * Userspace RCU - sys_futex/compat_futex header. - */ - -#ifndef _LTTNG_UST_FUTEX_H -#define _LTTNG_UST_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, int32_t val3) -{ - 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 /* _LTTNG_UST_FUTEX_H */ diff --git a/src/lib/lttng-ust/lttng-ust-comm.c b/src/lib/lttng-ust/lttng-ust-comm.c index 6f5be120..083de26d 100644 --- a/src/lib/lttng-ust/lttng-ust-comm.c +++ b/src/lib/lttng-ust/lttng-ust-comm.c @@ -25,7 +25,6 @@ #include #include #include -#include "futex.h" #include #include @@ -39,6 +38,7 @@ #include #include #include +#include "common/compat/futex.h" #include "common/ustcomm.h" #include "common/ust-fd.h" #include "common/logging.h"