Skip to content

kernel + posix: add k_spin_trylock(), pthread_spin_lock(), etc #59911

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/services/portability/posix.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ as POSIX.1-2017).
POSIX_FILE_LOCKING,
POSIX_SIGNALS,
POSIX_SINGLE_PROCESS,
POSIX_SPIN_LOCKS,yes
POSIX_THREADS_BASE,yes
XSI_THREAD_MUTEX_EXT,yes
XSI_THREADS_EXT,yes
Expand Down Expand Up @@ -93,6 +94,7 @@ Zephyr.
_POSIX_REALTIME_SIGNALS,
_POSIX_SEMAPHORES,yes
_POSIX_SHARED_MEMORY_OBJECTS,
_POSIX_SPIN_LOCKS,yes
_POSIX_SYNCHRONIZED_IO,
_POSIX_THREAD_ATTR_STACKADDR,yes
_POSIX_THREAD_ATTR_STACKSIZE,yes
Expand Down Expand Up @@ -385,6 +387,16 @@ required for error and event handling.
igsuspend(),
sigwait()

.. csv-table:: POSIX_SPIN_LOCKS
:header: API, Supported
:widths: 50,10

pthread_spin_destroy(),yes
pthread_spin_init(),yes
pthread_spin_lock(),yes
pthread_spin_trylock(),yes
pthread_spin_unlock(),yes


POSIX_DEVICE_IO
+++++++++++++++
Expand Down
1 change: 1 addition & 0 deletions include/zephyr/posix/posix_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
43 changes: 43 additions & 0 deletions include/zephyr/posix/pthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
75 changes: 60 additions & 15 deletions include/zephyr/spinlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
#ifndef ZEPHYR_INCLUDE_SPINLOCK_H_
#define ZEPHYR_INCLUDE_SPINLOCK_H_

#include <errno.h>
#include <stdbool.h>

#include <zephyr/arch/cpu.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/time_units.h>
#include <stdbool.h>
#include <zephyr/arch/cpu.h>

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -105,6 +107,28 @@ bool z_spin_lock_mem_coherent(struct k_spinlock *l);
*/
typedef struct z_spinlock_key k_spinlock_key_t;

static ALWAYS_INLINE void z_spinlock_validate_pre(struct k_spinlock *l)
{
ARG_UNUSED(l);
#ifdef CONFIG_SPIN_VALIDATE
__ASSERT(z_spin_lock_valid(l), "Invalid spinlock %p", l);
#ifdef CONFIG_KERNEL_COHERENCE
__ASSERT_NO_MSG(z_spin_lock_mem_coherent(l));
#endif
#endif
}

static ALWAYS_INLINE void z_spinlock_validate_post(struct k_spinlock *l)
{
ARG_UNUSED(l);
#ifdef CONFIG_SPIN_VALIDATE
z_spin_lock_set_owner(l);
#if defined(CONFIG_SPIN_LOCK_TIME_LIMIT) && (CONFIG_SPIN_LOCK_TIME_LIMIT != 0)
l->lock_time = sys_clock_cycle_get_32();
#endif /* CONFIG_SPIN_LOCK_TIME_LIMIT */
#endif /* CONFIG_SPIN_VALIDATE */
}

/**
* @brief Lock a spinlock
*
Expand Down Expand Up @@ -144,28 +168,49 @@ static ALWAYS_INLINE k_spinlock_key_t k_spin_lock(struct k_spinlock *l)
*/
k.key = arch_irq_lock();

#ifdef CONFIG_SPIN_VALIDATE
__ASSERT(z_spin_lock_valid(l), "Recursive spinlock %p", l);
# ifdef CONFIG_KERNEL_COHERENCE
__ASSERT_NO_MSG(z_spin_lock_mem_coherent(l));
# endif
#endif

z_spinlock_validate_pre(l);
#ifdef CONFIG_SMP
while (!atomic_cas(&l->locked, 0, 1)) {
arch_spin_relax();
}
#endif
z_spinlock_validate_post(l);

#ifdef CONFIG_SPIN_VALIDATE
z_spin_lock_set_owner(l);
#if defined(CONFIG_SPIN_LOCK_TIME_LIMIT) && (CONFIG_SPIN_LOCK_TIME_LIMIT != 0)
l->lock_time = sys_clock_cycle_get_32();
#endif /* CONFIG_SPIN_LOCK_TIME_LIMIT */
#endif/* CONFIG_SPIN_VALIDATE */
return k;
}

/**
* @brief Attempt to lock a spinlock
*
* This routine makes one attempt to lock @p l. If it is successful, then
* it will store the key into @p k.
*
* @param[in] l A pointer to the spinlock to lock
* @param[out] k A pointer to the spinlock key
* @retval 0 on success
* @retval -EBUSY if another thread holds the lock
*
* @see k_spin_lock
* @see k_spin_unlock
*/
static ALWAYS_INLINE int k_spin_trylock(struct k_spinlock *l, k_spinlock_key_t *k)
{
int key = arch_irq_lock();

z_spinlock_validate_pre(l);
#ifdef CONFIG_SMP
if (!atomic_cas(&l->locked, 0, 1)) {
arch_irq_unlock(key);
return -EBUSY;
}
#endif
z_spinlock_validate_post(l);

k->key = key;

return 0;
}

/**
* @brief Unlock a spin lock
*
Expand Down
1 change: 1 addition & 0 deletions lib/posix/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions lib/posix/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
161 changes: 161 additions & 0 deletions lib/posix/pthread_spinlock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* Copyright (c) 2023, Meta
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "posix_internal.h"

#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/posix/pthread.h>
#include <zephyr/sys/bitarray.h>

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;
}
Loading