diff --git a/arch/xtensa/core/atomic.S b/arch/xtensa/core/atomic.S index 13082bb1c54..c7c3d7777af 100644 --- a/arch/xtensa/core/atomic.S +++ b/arch/xtensa/core/atomic.S @@ -22,8 +22,11 @@ */ .global atomic_clear .type atomic_clear,@function + .global atomic_ptr_clear + .type atomic_ptr_clear,@function .align 4 atomic_clear: +atomic_ptr_clear: ENTRY(48) movi a4, 0 .L_LoopClear: @@ -54,8 +57,11 @@ atomic_clear: */ .global atomic_set .type atomic_set,@function + .global atomic_ptr_set + .type atomic_ptr_set,@function .align 4 atomic_set: +atomic_ptr_set: ENTRY(48) .L_LoopSet: l32ai a4, a2, 0 @@ -81,8 +87,11 @@ atomic_set: */ .global atomic_get .type atomic_get,@function + .global atomic_ptr_get + .type atomic_ptr_get,@function .align 4 atomic_get: +atomic_ptr_get: ENTRY(48) l32ai a2, a2, 0 RET(48) @@ -388,8 +397,11 @@ atomic_xor: */ .global atomic_cas .type atomic_cas,@function + .global atomic_ptr_cas + .type atomic_ptr_cas,@function .align 4 atomic_cas: +atomic_ptr_cas: ENTRY(48) l32ai a5, a2, 0 beq a5, a3, 2f diff --git a/include/sys/atomic.h b/include/sys/atomic.h index 7de01e44058..9114f20fbf3 100644 --- a/include/sys/atomic.h +++ b/include/sys/atomic.h @@ -11,6 +11,7 @@ #include #include +#include #include @@ -20,6 +21,7 @@ extern "C" { typedef int atomic_t; typedef atomic_t atomic_val_t; +typedef void *atomic_ptr_t; /** * @defgroup atomic_apis Atomic Services APIs @@ -57,6 +59,35 @@ extern bool atomic_cas(atomic_t *target, atomic_val_t old_value, atomic_val_t new_value); #endif +/** + * @brief Atomic compare-and-set with pointer values + * + * This routine performs an atomic compare-and-set on @a target. If the current + * value of @a target equals @a old_value, @a target is set to @a new_value. + * If the current value of @a target does not equal @a old_value, @a target + * is left unchanged. + * + * @param target Address of atomic variable. + * @param old_value Original value to compare against. + * @param new_value New value to store. + * @return true if @a new_value is written, false otherwise. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline bool atomic_ptr_cas(atomic_ptr_t *target, void *old_value, + void *new_value) +{ + return __atomic_compare_exchange_n(target, &old_value, new_value, + 0, __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST); +} +#elif defined(CONFIG_ATOMIC_OPERATIONS_C) +__syscall bool atomic_ptr_cas(atomic_ptr_t *target, void *old_value, + void *new_value); +#else +extern bool atomic_ptr_cas(atomic_ptr_t *target, void *old_value, + void *new_value); +#endif + /** * * @brief Atomic addition. @@ -158,6 +189,25 @@ static inline atomic_val_t atomic_get(const atomic_t *target) extern atomic_val_t atomic_get(const atomic_t *target); #endif +/** + * + * @brief Atomic get a pointer value + * + * This routine performs an atomic read on @a target. + * + * @param target Address of pointer variable. + * + * @return Value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline void *atomic_ptr_get(const atomic_ptr_t *target) +{ + return __atomic_load_n(target, __ATOMIC_SEQ_CST); +} +#else +extern void *atomic_ptr_get(const atomic_ptr_t *target); +#endif + /** * * @brief Atomic get-and-set. @@ -185,6 +235,29 @@ __syscall atomic_val_t atomic_set(atomic_t *target, atomic_val_t value); extern atomic_val_t atomic_set(atomic_t *target, atomic_val_t value); #endif +/** + * + * @brief Atomic get-and-set for pointer values + * + * This routine atomically sets @a target to @a value and returns + * the previous value of @a target. + * + * @param target Address of atomic variable. + * @param value Value to write to @a target. + * + * @return Previous value of @a target. + */ +#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN +static inline void *atomic_ptr_set(atomic_ptr_t *target, void *value) +{ + return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST); +} +#elif defined(CONFIG_ATOMIC_OPERATIONS_C) +__syscall void *atomic_ptr_set(atomic_ptr_t *target, void *value); +#else +extern void *atomic_ptr_set(atomic_ptr_t *target, void *value); +#endif + /** * * @brief Atomic clear. @@ -205,6 +278,27 @@ static inline atomic_val_t atomic_clear(atomic_t *target) extern atomic_val_t atomic_clear(atomic_t *target); #endif +/** + * + * @brief Atomic clear of a pointer value + * + * This routine atomically sets @a target to zero and returns its previous + * value. (Hence, it is equivalent to atomic_set(target, 0).) + * + * @param target Address of atomic variable. + * + * @return Previous value of @a target. + */ +#if defined(CONFIG_ATOMIC_OPERATIONS_BUILTIN) || \ + defined (CONFIG_ATOMIC_OPERATIONS_C) +static inline void *atomic_ptr_clear(atomic_ptr_t *target) +{ + return atomic_ptr_set(target, NULL); +} +#else +extern void *atomic_ptr_clear(atomic_ptr_t *target); +#endif + /** * * @brief Atomic bitwise inclusive OR. diff --git a/kernel/atomic_c.c b/kernel/atomic_c.c index 56797e9f546..ee3c7889bbf 100644 --- a/kernel/atomic_c.c +++ b/kernel/atomic_c.c @@ -106,6 +106,35 @@ bool z_vrfy_atomic_cas(atomic_t *target, atomic_val_t old_value, #include #endif /* CONFIG_USERSPACE */ +bool z_impl_atomic_ptr_cas(atomic_ptr_t *target, void *old_value, + void *new_value) +{ + k_spinlock_key_t key; + int ret = false; + + key = k_spin_lock(&lock); + + if (*target == old_value) { + *target = new_value; + ret = true; + } + + k_spin_unlock(&lock, key); + + return ret; +} + +#ifdef CONFIG_USERSPACE +static inline bool z_vrfy_atomic_ptr_cas(atomic_ptr_t *target, void *old_value, + void *new_value) +{ + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(target, sizeof(atomic_ptr_t))); + + return z_impl_atomic_ptr_cas(target, old_value, new_value); +} +#include +#endif /* CONFIG_USERSPACE */ + /** * * @brief Atomic addition primitive @@ -183,6 +212,11 @@ atomic_val_t atomic_get(const atomic_t *target) return *target; } +void *atomic_ptr_get(const atomic_ptr_t *target) +{ + return *target; +} + /** * * @brief Atomic get-and-set primitive @@ -212,6 +246,31 @@ atomic_val_t z_impl_atomic_set(atomic_t *target, atomic_val_t value) ATOMIC_SYSCALL_HANDLER_TARGET_VALUE(atomic_set); +void *z_impl_atomic_ptr_set(atomic_ptr_t *target, void *value) +{ + k_spinlock_key_t key; + void *ret; + + key = k_spin_lock(&lock); + + ret = *target; + *target = value; + + k_spin_unlock(&lock, key); + + return ret; +} + +#ifdef CONFIG_USERSPACE +static inline void *z_vrfy_atomic_ptr_set(atomic_ptr_t *target, void *value) +{ + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(target, sizeof(atomic_ptr_t))); + + return z_impl_atomic_ptr_set(target, value); +} +#include +#endif /* CONFIG_USERSPACE */ + /** * * @brief Atomic bitwise inclusive OR primitive diff --git a/tests/kernel/common/src/atomic.c b/tests/kernel/common/src/atomic.c index abba01f682e..df5bf5610a7 100644 --- a/tests/kernel/common/src/atomic.c +++ b/tests/kernel/common/src/atomic.c @@ -26,8 +26,10 @@ void test_atomic(void) int i; atomic_t target, orig; + atomic_ptr_t ptr_target; atomic_val_t value; atomic_val_t oldvalue; + void *ptr_value, *old_ptr_value; target = 4; value = 5; @@ -39,6 +41,17 @@ void test_atomic(void) zassert_true(atomic_cas(&target, oldvalue, value), "atomic_cas"); zassert_true((target == value), "atomic_cas"); + /* atomic_ptr_cas() */ + ptr_target = (atomic_ptr_t)4; + ptr_value = (void *)5; + old_ptr_value = (void *)6; + zassert_false(atomic_ptr_cas(&ptr_target, old_ptr_value, ptr_value), + "atomic_ptr_cas"); + ptr_target = (void *)6; + zassert_true(atomic_ptr_cas(&ptr_target, old_ptr_value, ptr_value), + "atomic_ptr_cas"); + zassert_true((ptr_target == ptr_value), "atomic_ptr_cas"); + /* atomic_add() */ target = 1; value = 2; @@ -65,17 +78,35 @@ void test_atomic(void) target = 50; zassert_true((atomic_get(&target) == 50), "atomic_get"); + /* atomic_ptr_get() */ + ptr_target = (atomic_ptr_t)50; + zassert_true((atomic_ptr_get(&ptr_target) == (void *)50), + "atomic_ptr_get"); + /* atomic_set() */ target = 42; value = 77; zassert_true((atomic_set(&target, value) == 42), "atomic_set"); zassert_true((target == value), "atomic_set"); + /* atomic_ptr_set() */ + ptr_target = (atomic_ptr_t)42; + ptr_value = (void *)77; + zassert_true((atomic_ptr_set(&ptr_target, ptr_value) == (void *)42), + "atomic_ptr_set"); + zassert_true((ptr_target == ptr_value), "atomic_ptr_set"); + /* atomic_clear() */ target = 100; zassert_true((atomic_clear(&target) == 100), "atomic_clear"); zassert_true((target == 0), "atomic_clear"); + /* atomic_ptr_clear() */ + ptr_target = (atomic_ptr_t)100; + zassert_true((atomic_ptr_clear(&ptr_target) == (void *)100), + "atomic_ptr_clear"); + zassert_true((ptr_target == NULL), "atomic_ptr_clear"); + /* atomic_or() */ target = 0xFF00; value = 0x0F0F;