| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2011 RedHat, Inc. |
| * All Rights Reserved. |
| */ |
| #ifndef __ATOMIC_H__ |
| #define __ATOMIC_H__ |
| |
| /* |
| * Atomics are provided by liburcu. |
| * |
| * API and guidelines for which operations provide memory barriers is here: |
| * |
| * https://github.com/urcu/userspace-rcu/blob/master/doc/uatomic-api.md |
| * |
| * Unlike the kernel, the same interface supports 32 and 64 bit atomic integers. |
| */ |
| #include <urcu/uatomic.h> |
| #include "spinlock.h" |
| |
| typedef int32_t atomic_t; |
| typedef int64_t atomic64_t; |
| |
| #define atomic_read(a) uatomic_read(a) |
| #define atomic_set(a, v) uatomic_set(a, v) |
| #define atomic_add(v, a) uatomic_add(a, v) |
| #define atomic_sub(v, a) uatomic_sub(a, v) |
| #define atomic_inc(a) uatomic_inc(a) |
| #define atomic_dec(a) uatomic_dec(a) |
| #define atomic_inc_return(a) uatomic_add_return(a, 1) |
| #define atomic_dec_return(a) uatomic_sub_return(a, 1) |
| #define atomic_dec_and_test(a) (atomic_dec_return(a) == 0) |
| #define cmpxchg(a, o, n) uatomic_cmpxchg(a, o, n); |
| |
| static inline bool atomic_add_unless(atomic_t *a, int v, int u) |
| { |
| int r = atomic_read(a); |
| int n, o; |
| |
| do { |
| o = r; |
| if (o == u) |
| break; |
| n = o + v; |
| r = uatomic_cmpxchg(a, o, n); |
| } while (r != o); |
| |
| return o != u; |
| } |
| |
| static inline bool atomic_dec_and_lock(atomic_t *a, spinlock_t *lock) |
| { |
| if (atomic_add_unless(a, -1, 1)) |
| return 0; |
| |
| spin_lock(lock); |
| if (atomic_dec_and_test(a)) |
| return 1; |
| spin_unlock(lock); |
| return 0; |
| } |
| |
| #ifdef HAVE_LIBURCU_ATOMIC64 |
| /* |
| * On most (64-bit) platforms, liburcu can handle 64-bit atomic counter |
| * updates, so we preferentially use that. |
| */ |
| #define atomic64_read(a) uatomic_read(a) |
| #define atomic64_set(a, v) uatomic_set(a, v) |
| #define atomic64_add(v, a) uatomic_add(a, v) |
| #define atomic64_sub(v, a) uatomic_sub(a, v) |
| #define atomic64_inc(a) uatomic_inc(a) |
| #define atomic64_dec(a) uatomic_dec(a) |
| #else |
| /* |
| * If we don't detect support for that, emulate it with a lock. Currently |
| * there are only three atomic64_t counters in userspace and none of them are |
| * performance critical, so we serialize them all with a single mutex since |
| * the kernel atomic64_t API doesn't have an _init call. |
| */ |
| extern pthread_mutex_t atomic64_lock; |
| |
| static inline int64_t |
| atomic64_read(atomic64_t *a) |
| { |
| int64_t ret; |
| |
| pthread_mutex_lock(&atomic64_lock); |
| ret = *a; |
| pthread_mutex_unlock(&atomic64_lock); |
| return ret; |
| } |
| |
| static inline void |
| atomic64_add(int64_t v, atomic64_t *a) |
| { |
| pthread_mutex_lock(&atomic64_lock); |
| (*a) += v; |
| pthread_mutex_unlock(&atomic64_lock); |
| } |
| |
| static inline void |
| atomic64_set(atomic64_t *a, int64_t v) |
| { |
| pthread_mutex_lock(&atomic64_lock); |
| (*a) = v; |
| pthread_mutex_unlock(&atomic64_lock); |
| } |
| |
| #define atomic64_inc(a) atomic64_add(1, (a)) |
| #define atomic64_dec(a) atomic64_add(-1, (a)) |
| #define atomic64_sub(v, a) atomic64_add(-(v), (a)) |
| |
| #endif /* HAVE_URCU_ATOMIC64 */ |
| |
| #endif /* __ATOMIC_H__ */ |