199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 299a2dd95SBruce Richardson * Copyright(c) 2015 Cavium, Inc 399a2dd95SBruce Richardson * Copyright(c) 2020 Arm Limited 499a2dd95SBruce Richardson */ 599a2dd95SBruce Richardson 699a2dd95SBruce Richardson #ifndef _RTE_ATOMIC_ARM64_H_ 799a2dd95SBruce Richardson #define _RTE_ATOMIC_ARM64_H_ 899a2dd95SBruce Richardson 999a2dd95SBruce Richardson #ifndef RTE_FORCE_INTRINSICS 1099a2dd95SBruce Richardson # error Platform must be built with RTE_FORCE_INTRINSICS 1199a2dd95SBruce Richardson #endif 1299a2dd95SBruce Richardson 1399a2dd95SBruce Richardson #include "generic/rte_atomic.h" 1499a2dd95SBruce Richardson #include <rte_branch_prediction.h> 1599a2dd95SBruce Richardson #include <rte_debug.h> 1699a2dd95SBruce Richardson 17*719834a6SMattias Rönnblom #ifdef __cplusplus 18*719834a6SMattias Rönnblom extern "C" { 19*719834a6SMattias Rönnblom #endif 20*719834a6SMattias Rönnblom 2199a2dd95SBruce Richardson #define rte_mb() asm volatile("dmb osh" : : : "memory") 2299a2dd95SBruce Richardson 2399a2dd95SBruce Richardson #define rte_wmb() asm volatile("dmb oshst" : : : "memory") 2499a2dd95SBruce Richardson 2599a2dd95SBruce Richardson #define rte_rmb() asm volatile("dmb oshld" : : : "memory") 2699a2dd95SBruce Richardson 2799a2dd95SBruce Richardson #define rte_smp_mb() asm volatile("dmb ish" : : : "memory") 2899a2dd95SBruce Richardson 2999a2dd95SBruce Richardson #define rte_smp_wmb() asm volatile("dmb ishst" : : : "memory") 3099a2dd95SBruce Richardson 3199a2dd95SBruce Richardson #define rte_smp_rmb() asm volatile("dmb ishld" : : : "memory") 3299a2dd95SBruce Richardson 3399a2dd95SBruce Richardson #define rte_io_mb() rte_mb() 3499a2dd95SBruce Richardson 3599a2dd95SBruce Richardson #define rte_io_wmb() rte_wmb() 3699a2dd95SBruce Richardson 3799a2dd95SBruce Richardson #define rte_io_rmb() rte_rmb() 3899a2dd95SBruce Richardson 3999a2dd95SBruce Richardson static __rte_always_inline void 401ec6a845STyler Retzlaff rte_atomic_thread_fence(rte_memory_order memorder) 4199a2dd95SBruce Richardson { 421ec6a845STyler Retzlaff __rte_atomic_thread_fence(memorder); 4399a2dd95SBruce Richardson } 4499a2dd95SBruce Richardson 4599a2dd95SBruce Richardson /*------------------------ 128 bit atomic operations -------------------------*/ 4699a2dd95SBruce Richardson 4799a2dd95SBruce Richardson #if defined(__ARM_FEATURE_ATOMICS) || defined(RTE_ARM_FEATURE_ATOMICS) 4899a2dd95SBruce Richardson 49264ff3f2SPavan Nikhilesh #if defined(RTE_CC_CLANG) 5099a2dd95SBruce Richardson #define __ATOMIC128_CAS_OP(cas_op_name, op_string) \ 5199a2dd95SBruce Richardson static __rte_noinline void \ 5299a2dd95SBruce Richardson cas_op_name(rte_int128_t *dst, rte_int128_t *old, rte_int128_t updated) \ 5399a2dd95SBruce Richardson { \ 5499a2dd95SBruce Richardson /* caspX instructions register pair must start from even-numbered 5599a2dd95SBruce Richardson * register at operand 1. 5699a2dd95SBruce Richardson * So, specify registers for local variables here. 5799a2dd95SBruce Richardson */ \ 5899a2dd95SBruce Richardson register uint64_t x0 __asm("x0") = (uint64_t)old->val[0]; \ 5999a2dd95SBruce Richardson register uint64_t x1 __asm("x1") = (uint64_t)old->val[1]; \ 6099a2dd95SBruce Richardson register uint64_t x2 __asm("x2") = (uint64_t)updated.val[0]; \ 6199a2dd95SBruce Richardson register uint64_t x3 __asm("x3") = (uint64_t)updated.val[1]; \ 6299a2dd95SBruce Richardson asm volatile( \ 63264ff3f2SPavan Nikhilesh ".arch armv8-a+lse\n" \ 6499a2dd95SBruce Richardson op_string " %[old0], %[old1], %[upd0], %[upd1], [%[dst]]" \ 6599a2dd95SBruce Richardson : [old0] "+r" (x0), \ 6699a2dd95SBruce Richardson [old1] "+r" (x1) \ 6799a2dd95SBruce Richardson : [upd0] "r" (x2), \ 6899a2dd95SBruce Richardson [upd1] "r" (x3), \ 6999a2dd95SBruce Richardson [dst] "r" (dst) \ 7099a2dd95SBruce Richardson : "memory"); \ 7199a2dd95SBruce Richardson old->val[0] = x0; \ 7299a2dd95SBruce Richardson old->val[1] = x1; \ 7399a2dd95SBruce Richardson } 74264ff3f2SPavan Nikhilesh #else 75264ff3f2SPavan Nikhilesh #define __ATOMIC128_CAS_OP(cas_op_name, op_string) \ 76264ff3f2SPavan Nikhilesh static __rte_always_inline void \ 77264ff3f2SPavan Nikhilesh cas_op_name(rte_int128_t *dst, rte_int128_t *old, rte_int128_t updated) \ 78264ff3f2SPavan Nikhilesh { \ 79264ff3f2SPavan Nikhilesh asm volatile( \ 80264ff3f2SPavan Nikhilesh op_string " %[old], %H[old], %[upd], %H[upd], [%[dst]]" \ 81264ff3f2SPavan Nikhilesh : [old] "+r"(old->int128) \ 82264ff3f2SPavan Nikhilesh : [upd] "r"(updated.int128), [dst] "r"(dst) \ 83264ff3f2SPavan Nikhilesh : "memory"); \ 84264ff3f2SPavan Nikhilesh } 85264ff3f2SPavan Nikhilesh #endif 8699a2dd95SBruce Richardson 8799a2dd95SBruce Richardson __ATOMIC128_CAS_OP(__cas_128_relaxed, "casp") 8899a2dd95SBruce Richardson __ATOMIC128_CAS_OP(__cas_128_acquire, "caspa") 8999a2dd95SBruce Richardson __ATOMIC128_CAS_OP(__cas_128_release, "caspl") 9099a2dd95SBruce Richardson __ATOMIC128_CAS_OP(__cas_128_acq_rel, "caspal") 9199a2dd95SBruce Richardson 9299a2dd95SBruce Richardson #undef __ATOMIC128_CAS_OP 9399a2dd95SBruce Richardson 9499a2dd95SBruce Richardson #endif 9599a2dd95SBruce Richardson 9699a2dd95SBruce Richardson static inline int 9799a2dd95SBruce Richardson rte_atomic128_cmp_exchange(rte_int128_t *dst, rte_int128_t *exp, 9899a2dd95SBruce Richardson const rte_int128_t *src, unsigned int weak, int success, 9999a2dd95SBruce Richardson int failure) 10099a2dd95SBruce Richardson { 10199a2dd95SBruce Richardson /* Always do strong CAS */ 10299a2dd95SBruce Richardson RTE_SET_USED(weak); 10399a2dd95SBruce Richardson /* Ignore memory ordering for failure, memory order for 10499a2dd95SBruce Richardson * success must be stronger or equal 10599a2dd95SBruce Richardson */ 10699a2dd95SBruce Richardson RTE_SET_USED(failure); 10799a2dd95SBruce Richardson /* Find invalid memory order */ 1081ec6a845STyler Retzlaff RTE_ASSERT(success == rte_memory_order_relaxed || 1091ec6a845STyler Retzlaff success == rte_memory_order_acquire || 1101ec6a845STyler Retzlaff success == rte_memory_order_release || 1111ec6a845STyler Retzlaff success == rte_memory_order_acq_rel || 1121ec6a845STyler Retzlaff success == rte_memory_order_seq_cst); 11399a2dd95SBruce Richardson 11499a2dd95SBruce Richardson rte_int128_t expected = *exp; 11599a2dd95SBruce Richardson rte_int128_t desired = *src; 11699a2dd95SBruce Richardson rte_int128_t old; 11799a2dd95SBruce Richardson 11899a2dd95SBruce Richardson #if defined(__ARM_FEATURE_ATOMICS) || defined(RTE_ARM_FEATURE_ATOMICS) 1191ec6a845STyler Retzlaff if (success == rte_memory_order_relaxed) 12099a2dd95SBruce Richardson __cas_128_relaxed(dst, exp, desired); 1211ec6a845STyler Retzlaff else if (success == rte_memory_order_acquire) 12299a2dd95SBruce Richardson __cas_128_acquire(dst, exp, desired); 1231ec6a845STyler Retzlaff else if (success == rte_memory_order_release) 12499a2dd95SBruce Richardson __cas_128_release(dst, exp, desired); 12599a2dd95SBruce Richardson else 12699a2dd95SBruce Richardson __cas_128_acq_rel(dst, exp, desired); 12799a2dd95SBruce Richardson old = *exp; 12899a2dd95SBruce Richardson #else 1291ec6a845STyler Retzlaff #define __HAS_ACQ(mo) ((mo) != rte_memory_order_relaxed && (mo) != rte_memory_order_release) 1301ec6a845STyler Retzlaff #define __HAS_RLS(mo) ((mo) == rte_memory_order_release || (mo) == rte_memory_order_acq_rel || \ 1311ec6a845STyler Retzlaff (mo) == rte_memory_order_seq_cst) 13299a2dd95SBruce Richardson 1331ec6a845STyler Retzlaff int ldx_mo = __HAS_ACQ(success) ? rte_memory_order_acquire : rte_memory_order_relaxed; 1341ec6a845STyler Retzlaff int stx_mo = __HAS_RLS(success) ? rte_memory_order_release : rte_memory_order_relaxed; 13599a2dd95SBruce Richardson 13699a2dd95SBruce Richardson #undef __HAS_ACQ 13799a2dd95SBruce Richardson #undef __HAS_RLS 13899a2dd95SBruce Richardson 13999a2dd95SBruce Richardson uint32_t ret = 1; 14099a2dd95SBruce Richardson 14199a2dd95SBruce Richardson /* ldx128 can not guarantee atomic, 14299a2dd95SBruce Richardson * Must write back src or old to verify atomicity of ldx128; 14399a2dd95SBruce Richardson */ 14499a2dd95SBruce Richardson do { 14599a2dd95SBruce Richardson 14699a2dd95SBruce Richardson #define __LOAD_128(op_string, src, dst) { \ 14799a2dd95SBruce Richardson asm volatile( \ 14899a2dd95SBruce Richardson op_string " %0, %1, %2" \ 14999a2dd95SBruce Richardson : "=&r" (dst.val[0]), \ 15099a2dd95SBruce Richardson "=&r" (dst.val[1]) \ 15199a2dd95SBruce Richardson : "Q" (src->val[0]) \ 15299a2dd95SBruce Richardson : "memory"); } 15399a2dd95SBruce Richardson 1541ec6a845STyler Retzlaff if (ldx_mo == rte_memory_order_relaxed) 15599a2dd95SBruce Richardson __LOAD_128("ldxp", dst, old) 15699a2dd95SBruce Richardson else 15799a2dd95SBruce Richardson __LOAD_128("ldaxp", dst, old) 15899a2dd95SBruce Richardson 15999a2dd95SBruce Richardson #undef __LOAD_128 16099a2dd95SBruce Richardson 16199a2dd95SBruce Richardson #define __STORE_128(op_string, dst, src, ret) { \ 16299a2dd95SBruce Richardson asm volatile( \ 16399a2dd95SBruce Richardson op_string " %w0, %1, %2, %3" \ 16499a2dd95SBruce Richardson : "=&r" (ret) \ 16599a2dd95SBruce Richardson : "r" (src.val[0]), \ 16699a2dd95SBruce Richardson "r" (src.val[1]), \ 16799a2dd95SBruce Richardson "Q" (dst->val[0]) \ 16899a2dd95SBruce Richardson : "memory"); } 16999a2dd95SBruce Richardson 17099a2dd95SBruce Richardson if (likely(old.int128 == expected.int128)) { 1711ec6a845STyler Retzlaff if (stx_mo == rte_memory_order_relaxed) 17299a2dd95SBruce Richardson __STORE_128("stxp", dst, desired, ret) 17399a2dd95SBruce Richardson else 17499a2dd95SBruce Richardson __STORE_128("stlxp", dst, desired, ret) 17599a2dd95SBruce Richardson } else { 17699a2dd95SBruce Richardson /* In the failure case (since 'weak' is ignored and only 17799a2dd95SBruce Richardson * weak == 0 is implemented), expected should contain 17899a2dd95SBruce Richardson * the atomically read value of dst. This means, 'old' 17999a2dd95SBruce Richardson * needs to be stored back to ensure it was read 18099a2dd95SBruce Richardson * atomically. 18199a2dd95SBruce Richardson */ 1821ec6a845STyler Retzlaff if (stx_mo == rte_memory_order_relaxed) 18399a2dd95SBruce Richardson __STORE_128("stxp", dst, old, ret) 18499a2dd95SBruce Richardson else 18599a2dd95SBruce Richardson __STORE_128("stlxp", dst, old, ret) 18699a2dd95SBruce Richardson } 18799a2dd95SBruce Richardson 18899a2dd95SBruce Richardson #undef __STORE_128 18999a2dd95SBruce Richardson 19099a2dd95SBruce Richardson } while (unlikely(ret)); 19199a2dd95SBruce Richardson 19299a2dd95SBruce Richardson /* Unconditionally updating the value of exp removes an 'if' statement. 19399a2dd95SBruce Richardson * The value of exp should already be in register if not in the cache. 19499a2dd95SBruce Richardson */ 19599a2dd95SBruce Richardson *exp = old; 19699a2dd95SBruce Richardson #endif 19799a2dd95SBruce Richardson 19899a2dd95SBruce Richardson return (old.int128 == expected.int128); 19999a2dd95SBruce Richardson } 20099a2dd95SBruce Richardson 20199a2dd95SBruce Richardson #ifdef __cplusplus 20299a2dd95SBruce Richardson } 20399a2dd95SBruce Richardson #endif 20499a2dd95SBruce Richardson 20599a2dd95SBruce Richardson #endif /* _RTE_ATOMIC_ARM64_H_ */ 206