Add 8 and 16 bit versions of atomic_cmpset and atomic_fcmpset for arm.

This adds 8 and 16 bit versions of the cmpset and fcmpset functions. Macros
are used to generate all the flavors from the same set of instructions; the
macro expansion handles the couple minor differences between each size
variation (generating ldrexb/ldrexh/ldrex for 8/16/32, etc).

In addition to handling new sizes, the instruction sequences used for cmpset
and fcmpset are rewritten to be a bit shorter/faster, and the new sequence
will not return false when *dst==*old but the store-exclusive fails because
of concurrent writers. Instead, it just loops like ldrex/strex sequences
normally do until it gets a non-conflicted store. The manpage allows LL/SC
architectures to bogusly return false, but there's no reason to actually do
so, at least on arm.

Reviewed by:	cognet
This commit is contained in:
Ian Lepore 2019-10-01 19:39:00 +00:00
parent 9f45d455d7
commit 044477e294
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=352938
2 changed files with 389 additions and 154 deletions

View File

@ -112,6 +112,43 @@ atomic_clear_64(volatile uint64_t *address, uint64_t clearmask)
__with_interrupts_disabled(*address &= ~clearmask);
}
static __inline int
atomic_fcmpset_8(volatile uint8_t *p, volatile uint8_t *cmpval, volatile uint8_t newval)
{
int ret;
__with_interrupts_disabled(
{
ret = *p;
if (*p == *cmpval) {
*p = newval;
ret = 1;
} else {
*cmpval = *p;
ret = 0;
}
});
return (ret);
}
static __inline int
atomic_fcmpset_16(volatile uint16_t *p, volatile uint16_t *cmpval, volatile uint16_t newval)
{
int ret;
__with_interrupts_disabled(
{
ret = *p;
if (*p == *cmpval) {
*p = newval;
ret = 1;
} else {
*cmpval = *p;
ret = 0;
}
});
return (ret);
}
static __inline int
atomic_fcmpset_32(volatile u_int32_t *p, volatile u_int32_t *cmpval, volatile u_int32_t newval)
{
@ -149,6 +186,40 @@ atomic_fcmpset_64(volatile u_int64_t *p, volatile u_int64_t *cmpval, volatile u_
return (ret);
}
static __inline int
atomic_cmpset_8(volatile uint8_t *p, volatile uint8_t cmpval, volatile uint8_t newval)
{
int ret;
__with_interrupts_disabled(
{
if (*p == cmpval) {
*p = newval;
ret = 1;
} else {
ret = 0;
}
});
return (ret);
}
static __inline int
atomic_cmpset_16(volatile uint16_t *p, volatile uint16_t cmpval, volatile uint16_t newval)
{
int ret;
__with_interrupts_disabled(
{
if (*p == cmpval) {
*p = newval;
ret = 1;
} else {
ret = 0;
}
});
return (ret);
}
static __inline int
atomic_cmpset_32(volatile u_int32_t *p, volatile u_int32_t cmpval, volatile u_int32_t newval)
{
@ -450,6 +521,10 @@ atomic_swap_32(volatile u_int32_t *p, u_int32_t v)
#define atomic_fcmpset_rel_32 atomic_fcmpset_32
#define atomic_fcmpset_acq_32 atomic_fcmpset_32
#ifdef _KERNEL
#define atomic_fcmpset_rel_8 atomic_fcmpset_8
#define atomic_fcmpset_acq_8 atomic_fcmpset_8
#define atomic_fcmpset_rel_16 atomic_fcmpset_16
#define atomic_fcmpset_acq_16 atomic_fcmpset_16
#define atomic_fcmpset_rel_64 atomic_fcmpset_64
#define atomic_fcmpset_acq_64 atomic_fcmpset_64
#endif
@ -458,6 +533,10 @@ atomic_swap_32(volatile u_int32_t *p, u_int32_t v)
#define atomic_cmpset_rel_32 atomic_cmpset_32
#define atomic_cmpset_acq_32 atomic_cmpset_32
#ifdef _KERNEL
#define atomic_cmpset_rel_8 atomic_cmpset_8
#define atomic_cmpset_acq_8 atomic_cmpset_8
#define atomic_cmpset_rel_16 atomic_cmpset_16
#define atomic_cmpset_acq_16 atomic_cmpset_16
#define atomic_cmpset_rel_64 atomic_cmpset_64
#define atomic_cmpset_acq_64 atomic_cmpset_64
#endif

View File

@ -190,224 +190,380 @@ ATOMIC_ACQ_REL(clear, 32)
ATOMIC_ACQ_REL(clear, 64)
ATOMIC_ACQ_REL_LONG(clear)
static __inline int
atomic_fcmpset_32(volatile uint32_t *p, uint32_t *cmpval, uint32_t newval)
{
uint32_t tmp;
uint32_t _cmpval = *cmpval;
int ret;
#define ATOMIC_FCMPSET_CODE(RET, TYPE, SUF) \
{ \
TYPE tmp; \
\
__asm __volatile( \
"1: ldrex" SUF " %[tmp], [%[ptr]] \n" \
" ldr %[ret], [%[oldv]] \n" \
" teq %[tmp], %[ret] \n" \
" ittee ne \n" \
" str" SUF "ne %[tmp], [%[oldv]] \n" \
" movne %[ret], #0 \n" \
" strex" SUF "eq %[ret], %[newv], [%[ptr]] \n" \
" eorseq %[ret], #1 \n" \
" beq 1b \n" \
: [ret] "=&r" (RET), \
[tmp] "=&r" (tmp) \
: [ptr] "r" (_ptr), \
[oldv] "r" (_old), \
[newv] "r" (_new) \
: "cc", "memory"); \
}
__asm __volatile(
" mov %0, #1 \n"
" ldrex %1, [%2] \n"
" cmp %1, %3 \n"
" it eq \n"
" strexeq %0, %4, [%2] \n"
: "=&r" (ret), "=&r" (tmp), "+r" (p), "+r" (_cmpval), "+r" (newval)
: : "cc", "memory");
*cmpval = tmp;
return (!ret);
}
#define ATOMIC_FCMPSET_CODE64(RET) \
{ \
uint64_t cmp, tmp; \
\
__asm __volatile( \
"1: ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" \
" ldrd %Q[cmp], %R[cmp], [%[oldv]] \n" \
" teq %Q[tmp], %Q[cmp] \n" \
" it eq \n" \
" teqeq %R[tmp], %R[cmp] \n" \
" ittee ne \n" \
" movne %[ret], #0 \n" \
" strdne %[cmp], [%[oldv]] \n" \
" strexdeq %[ret], %Q[newv], %R[newv], [%[ptr]] \n" \
" eorseq %[ret], #1 \n" \
" beq 1b \n" \
: [ret] "=&r" (RET), \
[cmp] "=&r" (cmp), \
[tmp] "=&r" (tmp) \
: [ptr] "r" (_ptr), \
[oldv] "r" (_old), \
[newv] "r" (_new) \
: "cc", "memory"); \
}
static __inline int
atomic_fcmpset_64(volatile uint64_t *p, uint64_t *cmpval, uint64_t newval)
{
uint64_t tmp;
uint64_t _cmpval = *cmpval;
int ret;
__asm __volatile(
"1: mov %[ret], #1 \n"
" ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
" teq %Q[tmp], %Q[_cmpval] \n"
" ite eq \n"
" teqeq %R[tmp], %R[_cmpval] \n"
" bne 2f \n"
" strexd %[ret], %Q[newval], %R[newval], [%[ptr]]\n"
"2: \n"
: [ret] "=&r" (ret),
[tmp] "=&r" (tmp)
: [ptr] "r" (p),
[_cmpval] "r" (_cmpval),
[newval] "r" (newval)
: "cc", "memory");
*cmpval = tmp;
return (!ret);
}
static __inline int
atomic_fcmpset_long(volatile u_long *p, u_long *cmpval, u_long newval)
{
return (atomic_fcmpset_32((volatile uint32_t *)p,
(uint32_t *)cmpval, newval));
}
static __inline int
atomic_fcmpset_acq_64(volatile uint64_t *p, uint64_t *cmpval, uint64_t newval)
atomic_fcmpset_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
{
int ret;
ret = atomic_fcmpset_64(p, cmpval, newval);
ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
return (ret);
}
static __inline int
atomic_fcmpset_acq_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
{
int ret;
ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
dmb();
return (ret);
}
static __inline int
atomic_fcmpset_acq_long(volatile u_long *p, u_long *cmpval, u_long newval)
atomic_fcmpset_rel_8(volatile uint8_t *_ptr, uint8_t *_old, uint8_t _new)
{
int ret;
ret = atomic_fcmpset_long(p, cmpval, newval);
dmb();
ATOMIC_FCMPSET_CODE(ret, uint8_t, "b");
return (ret);
}
static __inline int
atomic_fcmpset_acq_32(volatile uint32_t *p, uint32_t *cmpval, uint32_t newval)
{
int ret;
ret = atomic_fcmpset_32(p, cmpval, newval);
dmb();
return (ret);
}
static __inline int
atomic_fcmpset_rel_32(volatile uint32_t *p, uint32_t *cmpval, uint32_t newval)
{
dmb();
return (atomic_fcmpset_32(p, cmpval, newval));
}
static __inline int
atomic_fcmpset_rel_64(volatile uint64_t *p, uint64_t *cmpval, uint64_t newval)
{
dmb();
return (atomic_fcmpset_64(p, cmpval, newval));
}
static __inline int
atomic_fcmpset_rel_long(volatile u_long *p, u_long *cmpval, u_long newval)
{
dmb();
return (atomic_fcmpset_long(p, cmpval, newval));
}
static __inline int
atomic_cmpset_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval)
atomic_fcmpset_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
{
int ret;
__asm __volatile(
"1: ldrex %0, [%1] \n"
" cmp %0, %2 \n"
" itt ne \n"
" movne %0, #0 \n"
" bne 2f \n"
" strex %0, %3, [%1] \n"
" cmp %0, #0 \n"
" ite eq \n"
" moveq %0, #1 \n"
" bne 1b \n"
"2:"
: "=&r" (ret), "+r" (p), "+r" (cmpval), "+r" (newval)
: : "cc", "memory");
ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
return (ret);
}
static __inline int
atomic_cmpset_64(volatile uint64_t *p, uint64_t cmpval, uint64_t newval)
{
uint64_t tmp;
uint32_t ret;
__asm __volatile(
"1: \n"
" ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n"
" teq %Q[tmp], %Q[cmpval] \n"
" itee eq \n"
" teqeq %R[tmp], %R[cmpval] \n"
" movne %[ret], #0 \n"
" bne 2f \n"
" strexd %[ret], %Q[newval], %R[newval], [%[ptr]]\n"
" teq %[ret], #0 \n"
" it ne \n"
" bne 1b \n"
" mov %[ret], #1 \n"
"2: \n"
: [ret] "=&r" (ret),
[tmp] "=&r" (tmp)
: [ptr] "r" (p),
[cmpval] "r" (cmpval),
[newval] "r" (newval)
: "cc", "memory");
return (ret);
}
static __inline int
atomic_cmpset_long(volatile u_long *p, u_long cmpval, u_long newval)
{
return (atomic_cmpset_32((volatile uint32_t *)p, cmpval, newval));
}
static __inline int
atomic_cmpset_acq_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval)
atomic_fcmpset_acq_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
{
int ret;
ret = atomic_cmpset_32(p, cmpval, newval);
ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
dmb();
return (ret);
}
static __inline int
atomic_cmpset_acq_64(volatile uint64_t *p, uint64_t cmpval, uint64_t newval)
atomic_fcmpset_rel_16(volatile uint16_t *_ptr, uint16_t *_old, uint16_t _new)
{
int ret;
ret = atomic_cmpset_64(p, cmpval, newval);
dmb();
ATOMIC_FCMPSET_CODE(ret, uint16_t, "h");
return (ret);
}
static __inline int
atomic_cmpset_acq_long(volatile u_long *p, u_long cmpval, u_long newval)
atomic_fcmpset_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
{
int ret;
ret = atomic_cmpset_long(p, cmpval, newval);
ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
return (ret);
}
static __inline int
atomic_fcmpset_acq_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
{
int ret;
ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
dmb();
return (ret);
}
static __inline int
atomic_cmpset_rel_32(volatile uint32_t *p, uint32_t cmpval, uint32_t newval)
atomic_fcmpset_rel_32(volatile uint32_t *_ptr, uint32_t *_old, uint32_t _new)
{
int ret;
dmb();
return (atomic_cmpset_32(p, cmpval, newval));
ATOMIC_FCMPSET_CODE(ret, uint32_t, "");
return (ret);
}
static __inline int
atomic_cmpset_rel_64(volatile uint64_t *p, uint64_t cmpval, uint64_t newval)
atomic_fcmpset_long(volatile long *_ptr, long *_old, long _new)
{
int ret;
dmb();
return (atomic_cmpset_64(p, cmpval, newval));
ATOMIC_FCMPSET_CODE(ret, long, "");
return (ret);
}
static __inline int
atomic_cmpset_rel_long(volatile u_long *p, u_long cmpval, u_long newval)
atomic_fcmpset_acq_long(volatile long *_ptr, long *_old, long _new)
{
int ret;
ATOMIC_FCMPSET_CODE(ret, long, "");
dmb();
return (ret);
}
static __inline int
atomic_fcmpset_rel_long(volatile long *_ptr, long *_old, long _new)
{
int ret;
dmb();
return (atomic_cmpset_long(p, cmpval, newval));
ATOMIC_FCMPSET_CODE(ret, long, "");
return (ret);
}
static __inline int
atomic_fcmpset_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
{
int ret;
ATOMIC_FCMPSET_CODE64(ret);
return (ret);
}
static __inline int
atomic_fcmpset_acq_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
{
int ret;
ATOMIC_FCMPSET_CODE64(ret);
dmb();
return (ret);
}
static __inline int
atomic_fcmpset_rel_64(volatile uint64_t *_ptr, uint64_t *_old, uint64_t _new)
{
int ret;
dmb();
ATOMIC_FCMPSET_CODE64(ret);
return (ret);
}
#define ATOMIC_CMPSET_CODE(RET, SUF) \
{ \
__asm __volatile( \
"1: ldrex" SUF " %[ret], [%[ptr]] \n" \
" teq %[ret], %[oldv] \n" \
" itee ne \n" \
" movne %[ret], #0 \n" \
" strex" SUF "eq %[ret], %[newv], [%[ptr]] \n" \
" eorseq %[ret], #1 \n" \
" beq 1b \n" \
: [ret] "=&r" (RET) \
: [ptr] "r" (_ptr), \
[oldv] "r" (_old), \
[newv] "r" (_new) \
: "cc", "memory"); \
}
#define ATOMIC_CMPSET_CODE64(RET) \
{ \
uint64_t tmp; \
\
__asm __volatile( \
"1: ldrexd %Q[tmp], %R[tmp], [%[ptr]] \n" \
" teq %Q[tmp], %Q[oldv] \n" \
" it eq \n" \
" teqeq %R[tmp], %R[oldv] \n" \
" itee ne \n" \
" movne %[ret], #0 \n" \
" strexdeq %[ret], %Q[newv], %R[newv], [%[ptr]] \n" \
" eorseq %[ret], #1 \n" \
" beq 1b \n" \
: [ret] "=&r" (RET), \
[tmp] "=&r" (tmp) \
: [ptr] "r" (_ptr), \
[oldv] "r" (_old), \
[newv] "r" (_new) \
: "cc", "memory"); \
}
static __inline int
atomic_cmpset_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
{
int ret;
ATOMIC_CMPSET_CODE(ret, "b");
return (ret);
}
static __inline int
atomic_cmpset_acq_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
{
int ret;
ATOMIC_CMPSET_CODE(ret, "b");
dmb();
return (ret);
}
static __inline int
atomic_cmpset_rel_8(volatile uint8_t *_ptr, uint8_t _old, uint8_t _new)
{
int ret;
dmb();
ATOMIC_CMPSET_CODE(ret, "b");
return (ret);
}
static __inline int
atomic_cmpset_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
{
int ret;
ATOMIC_CMPSET_CODE(ret, "h");
return (ret);
}
static __inline int
atomic_cmpset_acq_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
{
int ret;
ATOMIC_CMPSET_CODE(ret, "h");
dmb();
return (ret);
}
static __inline int
atomic_cmpset_rel_16(volatile uint16_t *_ptr, uint16_t _old, uint16_t _new)
{
int ret;
dmb();
ATOMIC_CMPSET_CODE(ret, "h");
return (ret);
}
static __inline int
atomic_cmpset_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
{
int ret;
ATOMIC_CMPSET_CODE(ret, "");
return (ret);
}
static __inline int
atomic_cmpset_acq_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
{
int ret;
ATOMIC_CMPSET_CODE(ret, "");
dmb();
return (ret);
}
static __inline int
atomic_cmpset_rel_32(volatile uint32_t *_ptr, uint32_t _old, uint32_t _new)
{
int ret;
dmb();
ATOMIC_CMPSET_CODE(ret, "");
return (ret);
}
static __inline int
atomic_cmpset_long(volatile long *_ptr, long _old, long _new)
{
int ret;
ATOMIC_CMPSET_CODE(ret, "");
return (ret);
}
static __inline int
atomic_cmpset_acq_long(volatile long *_ptr, long _old, long _new)
{
int ret;
ATOMIC_CMPSET_CODE(ret, "");
dmb();
return (ret);
}
static __inline int
atomic_cmpset_rel_long(volatile long *_ptr, long _old, long _new)
{
int ret;
dmb();
ATOMIC_CMPSET_CODE(ret, "");
return (ret);
}
static __inline int
atomic_cmpset_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
{
int ret;
ATOMIC_CMPSET_CODE64(ret);
return (ret);
}
static __inline int
atomic_cmpset_acq_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
{
int ret;
ATOMIC_CMPSET_CODE64(ret);
dmb();
return (ret);
}
static __inline int
atomic_cmpset_rel_64(volatile uint64_t *_ptr, uint64_t _old, uint64_t _new)
{
int ret;
dmb();
ATOMIC_CMPSET_CODE64(ret);
return (ret);
}
static __inline uint32_t