kernel: add APIs for atomic os on pointers
The existing APIs are insufficient on 64-bit systems as atomic_t is 32-bits wide. Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
This commit is contained in:
parent
60e0019751
commit
9f0acd44a4
4 changed files with 196 additions and 0 deletions
|
@ -22,8 +22,11 @@
|
||||||
*/
|
*/
|
||||||
.global atomic_clear
|
.global atomic_clear
|
||||||
.type atomic_clear,@function
|
.type atomic_clear,@function
|
||||||
|
.global atomic_ptr_clear
|
||||||
|
.type atomic_ptr_clear,@function
|
||||||
.align 4
|
.align 4
|
||||||
atomic_clear:
|
atomic_clear:
|
||||||
|
atomic_ptr_clear:
|
||||||
ENTRY(48)
|
ENTRY(48)
|
||||||
movi a4, 0
|
movi a4, 0
|
||||||
.L_LoopClear:
|
.L_LoopClear:
|
||||||
|
@ -54,8 +57,11 @@ atomic_clear:
|
||||||
*/
|
*/
|
||||||
.global atomic_set
|
.global atomic_set
|
||||||
.type atomic_set,@function
|
.type atomic_set,@function
|
||||||
|
.global atomic_ptr_set
|
||||||
|
.type atomic_ptr_set,@function
|
||||||
.align 4
|
.align 4
|
||||||
atomic_set:
|
atomic_set:
|
||||||
|
atomic_ptr_set:
|
||||||
ENTRY(48)
|
ENTRY(48)
|
||||||
.L_LoopSet:
|
.L_LoopSet:
|
||||||
l32ai a4, a2, 0
|
l32ai a4, a2, 0
|
||||||
|
@ -81,8 +87,11 @@ atomic_set:
|
||||||
*/
|
*/
|
||||||
.global atomic_get
|
.global atomic_get
|
||||||
.type atomic_get,@function
|
.type atomic_get,@function
|
||||||
|
.global atomic_ptr_get
|
||||||
|
.type atomic_ptr_get,@function
|
||||||
.align 4
|
.align 4
|
||||||
atomic_get:
|
atomic_get:
|
||||||
|
atomic_ptr_get:
|
||||||
ENTRY(48)
|
ENTRY(48)
|
||||||
l32ai a2, a2, 0
|
l32ai a2, a2, 0
|
||||||
RET(48)
|
RET(48)
|
||||||
|
@ -388,8 +397,11 @@ atomic_xor:
|
||||||
*/
|
*/
|
||||||
.global atomic_cas
|
.global atomic_cas
|
||||||
.type atomic_cas,@function
|
.type atomic_cas,@function
|
||||||
|
.global atomic_ptr_cas
|
||||||
|
.type atomic_ptr_cas,@function
|
||||||
.align 4
|
.align 4
|
||||||
atomic_cas:
|
atomic_cas:
|
||||||
|
atomic_ptr_cas:
|
||||||
ENTRY(48)
|
ENTRY(48)
|
||||||
l32ai a5, a2, 0
|
l32ai a5, a2, 0
|
||||||
beq a5, a3, 2f
|
beq a5, a3, 2f
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <toolchain.h>
|
#include <toolchain.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
#include <zephyr/types.h>
|
#include <zephyr/types.h>
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ extern "C" {
|
||||||
|
|
||||||
typedef int atomic_t;
|
typedef int atomic_t;
|
||||||
typedef atomic_t atomic_val_t;
|
typedef atomic_t atomic_val_t;
|
||||||
|
typedef void *atomic_ptr_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @defgroup atomic_apis Atomic Services APIs
|
* @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);
|
atomic_val_t new_value);
|
||||||
#endif
|
#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.
|
* @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);
|
extern atomic_val_t atomic_get(const atomic_t *target);
|
||||||
#endif
|
#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.
|
* @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);
|
extern atomic_val_t atomic_set(atomic_t *target, atomic_val_t value);
|
||||||
#endif
|
#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.
|
* @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);
|
extern atomic_val_t atomic_clear(atomic_t *target);
|
||||||
#endif
|
#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.
|
* @brief Atomic bitwise inclusive OR.
|
||||||
|
|
|
@ -106,6 +106,35 @@ bool z_vrfy_atomic_cas(atomic_t *target, atomic_val_t old_value,
|
||||||
#include <syscalls/atomic_cas_mrsh.c>
|
#include <syscalls/atomic_cas_mrsh.c>
|
||||||
#endif /* CONFIG_USERSPACE */
|
#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 <syscalls/atomic_ptr_cas_mrsh.c>
|
||||||
|
#endif /* CONFIG_USERSPACE */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @brief Atomic addition primitive
|
* @brief Atomic addition primitive
|
||||||
|
@ -183,6 +212,11 @@ atomic_val_t atomic_get(const atomic_t *target)
|
||||||
return *target;
|
return *target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *atomic_ptr_get(const atomic_ptr_t *target)
|
||||||
|
{
|
||||||
|
return *target;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @brief Atomic get-and-set primitive
|
* @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);
|
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 <syscalls/atomic_ptr_set_mrsh.c>
|
||||||
|
#endif /* CONFIG_USERSPACE */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @brief Atomic bitwise inclusive OR primitive
|
* @brief Atomic bitwise inclusive OR primitive
|
||||||
|
|
|
@ -26,8 +26,10 @@ void test_atomic(void)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
atomic_t target, orig;
|
atomic_t target, orig;
|
||||||
|
atomic_ptr_t ptr_target;
|
||||||
atomic_val_t value;
|
atomic_val_t value;
|
||||||
atomic_val_t oldvalue;
|
atomic_val_t oldvalue;
|
||||||
|
void *ptr_value, *old_ptr_value;
|
||||||
|
|
||||||
target = 4;
|
target = 4;
|
||||||
value = 5;
|
value = 5;
|
||||||
|
@ -39,6 +41,17 @@ void test_atomic(void)
|
||||||
zassert_true(atomic_cas(&target, oldvalue, value), "atomic_cas");
|
zassert_true(atomic_cas(&target, oldvalue, value), "atomic_cas");
|
||||||
zassert_true((target == 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() */
|
/* atomic_add() */
|
||||||
target = 1;
|
target = 1;
|
||||||
value = 2;
|
value = 2;
|
||||||
|
@ -65,17 +78,35 @@ void test_atomic(void)
|
||||||
target = 50;
|
target = 50;
|
||||||
zassert_true((atomic_get(&target) == 50), "atomic_get");
|
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() */
|
/* atomic_set() */
|
||||||
target = 42;
|
target = 42;
|
||||||
value = 77;
|
value = 77;
|
||||||
zassert_true((atomic_set(&target, value) == 42), "atomic_set");
|
zassert_true((atomic_set(&target, value) == 42), "atomic_set");
|
||||||
zassert_true((target == value), "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() */
|
/* atomic_clear() */
|
||||||
target = 100;
|
target = 100;
|
||||||
zassert_true((atomic_clear(&target) == 100), "atomic_clear");
|
zassert_true((atomic_clear(&target) == 100), "atomic_clear");
|
||||||
zassert_true((target == 0), "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() */
|
/* atomic_or() */
|
||||||
target = 0xFF00;
|
target = 0xFF00;
|
||||||
value = 0x0F0F;
|
value = 0x0F0F;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue