diff --git a/include/zephyr/posix/posix_types.h b/include/zephyr/posix/posix_types.h index 511697cfab9..a2191f0fece 100644 --- a/include/zephyr/posix/posix_types.h +++ b/include/zephyr/posix/posix_types.h @@ -55,6 +55,7 @@ typedef struct pthread_attr pthread_attr_t; BUILD_ASSERT(sizeof(pthread_attr_t) >= sizeof(struct pthread_attr)); typedef uint32_t pthread_t; +typedef uint32_t pthread_spinlock_t; /* Semaphore */ typedef struct k_sem sem_t; diff --git a/include/zephyr/posix/pthread.h b/include/zephyr/posix/pthread.h index e0822307083..5b807880775 100644 --- a/include/zephyr/posix/pthread.h +++ b/include/zephyr/posix/pthread.h @@ -25,6 +25,10 @@ extern "C" { #define PTHREAD_CREATE_DETACHED 0 #define PTHREAD_CREATE_JOINABLE 1 +/* Pthread resource visibility */ +#define PTHREAD_PROCESS_PRIVATE 0 +#define PTHREAD_PROCESS_SHARED 1 + /* Pthread cancellation */ #define _PTHREAD_CANCEL_POS 0 #define PTHREAD_CANCEL_ENABLE (0U << _PTHREAD_CANCEL_POS) @@ -495,6 +499,45 @@ int pthread_setname_np(pthread_t thread, const char *name); */ int pthread_getname_np(pthread_t thread, char *name, size_t len); +#ifdef CONFIG_PTHREAD_IPC + +/** + * @brief Destroy a pthread_spinlock_t. + * + * See IEEE 1003.1 + */ +int pthread_spin_destroy(pthread_spinlock_t *lock); + +/** + * @brief Initialize a thread_spinlock_t. + * + * See IEEE 1003.1 + */ +int pthread_spin_init(pthread_spinlock_t *lock, int pshared); + +/** + * @brief Lock a previously initialized thread_spinlock_t. + * + * See IEEE 1003.1 + */ +int pthread_spin_lock(pthread_spinlock_t *lock); + +/** + * @brief Attempt to lock a previously initialized thread_spinlock_t. + * + * See IEEE 1003.1 + */ +int pthread_spin_trylock(pthread_spinlock_t *lock); + +/** + * @brief Unlock a previously locked thread_spinlock_t. + * + * See IEEE 1003.1 + */ +int pthread_spin_unlock(pthread_spinlock_t *lock); + +#endif + #ifdef __cplusplus } #endif diff --git a/lib/posix/CMakeLists.txt b/lib/posix/CMakeLists.txt index cd6caf6b25e..eb0c697d79b 100644 --- a/lib/posix/CMakeLists.txt +++ b/lib/posix/CMakeLists.txt @@ -26,6 +26,7 @@ zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread_mutex.c) zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread_barrier.c) zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread.c) zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread_sched.c) +zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread_spinlock.c) zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK clock.c) zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK nanosleep.c) zephyr_library_sources_ifdef(CONFIG_POSIX_CLOCK sleep.c) diff --git a/lib/posix/Kconfig b/lib/posix/Kconfig index 7f510210d9c..e6d22d90c4a 100644 --- a/lib/posix/Kconfig +++ b/lib/posix/Kconfig @@ -74,6 +74,13 @@ config MAX_PTHREAD_BARRIER_COUNT help Maximum number of simultaneously active keys in a POSIX application. +config MAX_PTHREAD_SPINLOCK_COUNT + int "Maximum simultaneously active spinlocks in a POSIX application" + default 5 + range 0 255 + help + Maximum number of simultaneously active spinlocks in a POSIX application. + config SEM_VALUE_MAX int "Maximum semaphore limit" default 32767 diff --git a/lib/posix/pthread_spinlock.c b/lib/posix/pthread_spinlock.c new file mode 100644 index 00000000000..16233eead03 --- /dev/null +++ b/lib/posix/pthread_spinlock.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2023, Meta + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "posix_internal.h" + +#include +#include +#include +#include + +union _spinlock_storage { + struct k_spinlock lock; + uint8_t byte; +}; +#if !defined(CONFIG_SMP) && !defined(CONFIG_SPIN_VALIDATE) +BUILD_ASSERT(sizeof(struct k_spinlock) == 0, + "please remove the _spinlock_storage workaround if, at some point, k_spinlock is no " + "longer zero bytes when CONFIG_SMP=n && CONFIG_SPIN_VALIDATE=n"); +#endif + +static union _spinlock_storage posix_spinlock_pool[CONFIG_MAX_PTHREAD_SPINLOCK_COUNT]; +static k_spinlock_key_t posix_spinlock_key[CONFIG_MAX_PTHREAD_SPINLOCK_COUNT]; +SYS_BITARRAY_DEFINE_STATIC(posix_spinlock_bitarray, CONFIG_MAX_PTHREAD_SPINLOCK_COUNT); + +/* + * We reserve the MSB to mark a pthread_spinlock_t as initialized (from the + * perspective of the application). With a linear space, this means that + * the theoretical pthread_spinlock_t range is [0,2147483647]. + */ +BUILD_ASSERT(CONFIG_MAX_PTHREAD_SPINLOCK_COUNT < PTHREAD_OBJ_MASK_INIT, + "CONFIG_MAX_PTHREAD_SPINLOCK_COUNT is too high"); + +static inline size_t posix_spinlock_to_offset(struct k_spinlock *l) +{ + return (union _spinlock_storage *)l - posix_spinlock_pool; +} + +static inline size_t to_posix_spinlock_idx(pthread_spinlock_t lock) +{ + return mark_pthread_obj_uninitialized(lock); +} + +static struct k_spinlock *get_posix_spinlock(pthread_spinlock_t *lock) +{ + size_t bit; + int actually_initialized; + + if (lock == NULL) { + return NULL; + } + + /* if the provided spinlock does not claim to be initialized, its invalid */ + bit = to_posix_spinlock_idx(*lock); + if (!is_pthread_obj_initialized(*lock)) { + return NULL; + } + + /* Mask off the MSB to get the actual bit index */ + if (sys_bitarray_test_bit(&posix_spinlock_bitarray, bit, &actually_initialized) < 0) { + return NULL; + } + + if (actually_initialized == 0) { + /* The spinlock claims to be initialized but is actually not */ + return NULL; + } + + return (struct k_spinlock *)&posix_spinlock_pool[bit]; +} + +int pthread_spin_init(pthread_spinlock_t *lock, int pshared) +{ + int ret; + size_t bit; + + if (lock == NULL || + !(pshared == PTHREAD_PROCESS_PRIVATE || pshared == PTHREAD_PROCESS_SHARED)) { + /* not specified as part of POSIX but this is the Linux behavior */ + return EINVAL; + } + + ret = sys_bitarray_alloc(&posix_spinlock_bitarray, 1, &bit); + if (ret < 0) { + return ENOMEM; + } + + *lock = mark_pthread_obj_initialized(bit); + + return 0; +} + +int pthread_spin_destroy(pthread_spinlock_t *lock) +{ + int err; + size_t bit; + struct k_spinlock *l; + + l = get_posix_spinlock(lock); + if (l == NULL) { + /* not specified as part of POSIX but this is the Linux behavior */ + return EINVAL; + } + + bit = posix_spinlock_to_offset(l); + err = sys_bitarray_free(&posix_spinlock_bitarray, 1, bit); + __ASSERT_NO_MSG(err == 0); + + return 0; +} + +int pthread_spin_lock(pthread_spinlock_t *lock) +{ + size_t bit; + struct k_spinlock *l; + + l = get_posix_spinlock(lock); + if (l == NULL) { + /* not specified as part of POSIX but this is the Linux behavior */ + return EINVAL; + } + + bit = posix_spinlock_to_offset(l); + posix_spinlock_key[bit] = k_spin_lock(l); + + return 0; +} + +int pthread_spin_trylock(pthread_spinlock_t *lock) +{ + size_t bit; + struct k_spinlock *l; + + l = get_posix_spinlock(lock); + if (l == NULL) { + /* not specified as part of POSIX but this is the Linux behavior */ + return EINVAL; + } + + bit = posix_spinlock_to_offset(l); + return k_spin_trylock(l, &posix_spinlock_key[bit]); +} + +int pthread_spin_unlock(pthread_spinlock_t *lock) +{ + size_t bit; + struct k_spinlock *l; + + l = get_posix_spinlock(lock); + if (l == NULL) { + /* not specified as part of POSIX but this is the Linux behavior */ + return EINVAL; + } + + bit = posix_spinlock_to_offset(l); + k_spin_unlock(l, posix_spinlock_key[bit]); + + return 0; +}