162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic implementation of 64-bit atomics using spinlocks, 462306a36Sopenharmony_ci * useful on processors that don't have 64-bit atomic instructions. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/cache.h> 1062306a36Sopenharmony_ci#include <linux/spinlock.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <linux/atomic.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * We use a hashed array of spinlocks to provide exclusive access 1762306a36Sopenharmony_ci * to each atomic64_t variable. Since this is expected to used on 1862306a36Sopenharmony_ci * systems with small numbers of CPUs (<= 4 or so), we use a 1962306a36Sopenharmony_ci * relatively small array of 16 spinlocks to avoid wasting too much 2062306a36Sopenharmony_ci * memory on the spinlock array. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci#define NR_LOCKS 16 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Ensure each lock is in a separate cacheline. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_cistatic union { 2862306a36Sopenharmony_ci raw_spinlock_t lock; 2962306a36Sopenharmony_ci char pad[L1_CACHE_BYTES]; 3062306a36Sopenharmony_ci} atomic64_lock[NR_LOCKS] __cacheline_aligned_in_smp = { 3162306a36Sopenharmony_ci [0 ... (NR_LOCKS - 1)] = { 3262306a36Sopenharmony_ci .lock = __RAW_SPIN_LOCK_UNLOCKED(atomic64_lock.lock), 3362306a36Sopenharmony_ci }, 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic inline raw_spinlock_t *lock_addr(const atomic64_t *v) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci unsigned long addr = (unsigned long) v; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci addr >>= L1_CACHE_SHIFT; 4162306a36Sopenharmony_ci addr ^= (addr >> 8) ^ (addr >> 16); 4262306a36Sopenharmony_ci return &atomic64_lock[addr & (NR_LOCKS - 1)].lock; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cis64 generic_atomic64_read(const atomic64_t *v) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci unsigned long flags; 4862306a36Sopenharmony_ci raw_spinlock_t *lock = lock_addr(v); 4962306a36Sopenharmony_ci s64 val; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); 5262306a36Sopenharmony_ci val = v->counter; 5362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); 5462306a36Sopenharmony_ci return val; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ciEXPORT_SYMBOL(generic_atomic64_read); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_civoid generic_atomic64_set(atomic64_t *v, s64 i) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci unsigned long flags; 6162306a36Sopenharmony_ci raw_spinlock_t *lock = lock_addr(v); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); 6462306a36Sopenharmony_ci v->counter = i; 6562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ciEXPORT_SYMBOL(generic_atomic64_set); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define ATOMIC64_OP(op, c_op) \ 7062306a36Sopenharmony_civoid generic_atomic64_##op(s64 a, atomic64_t *v) \ 7162306a36Sopenharmony_ci{ \ 7262306a36Sopenharmony_ci unsigned long flags; \ 7362306a36Sopenharmony_ci raw_spinlock_t *lock = lock_addr(v); \ 7462306a36Sopenharmony_ci \ 7562306a36Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); \ 7662306a36Sopenharmony_ci v->counter c_op a; \ 7762306a36Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); \ 7862306a36Sopenharmony_ci} \ 7962306a36Sopenharmony_ciEXPORT_SYMBOL(generic_atomic64_##op); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define ATOMIC64_OP_RETURN(op, c_op) \ 8262306a36Sopenharmony_cis64 generic_atomic64_##op##_return(s64 a, atomic64_t *v) \ 8362306a36Sopenharmony_ci{ \ 8462306a36Sopenharmony_ci unsigned long flags; \ 8562306a36Sopenharmony_ci raw_spinlock_t *lock = lock_addr(v); \ 8662306a36Sopenharmony_ci s64 val; \ 8762306a36Sopenharmony_ci \ 8862306a36Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); \ 8962306a36Sopenharmony_ci val = (v->counter c_op a); \ 9062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); \ 9162306a36Sopenharmony_ci return val; \ 9262306a36Sopenharmony_ci} \ 9362306a36Sopenharmony_ciEXPORT_SYMBOL(generic_atomic64_##op##_return); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define ATOMIC64_FETCH_OP(op, c_op) \ 9662306a36Sopenharmony_cis64 generic_atomic64_fetch_##op(s64 a, atomic64_t *v) \ 9762306a36Sopenharmony_ci{ \ 9862306a36Sopenharmony_ci unsigned long flags; \ 9962306a36Sopenharmony_ci raw_spinlock_t *lock = lock_addr(v); \ 10062306a36Sopenharmony_ci s64 val; \ 10162306a36Sopenharmony_ci \ 10262306a36Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); \ 10362306a36Sopenharmony_ci val = v->counter; \ 10462306a36Sopenharmony_ci v->counter c_op a; \ 10562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); \ 10662306a36Sopenharmony_ci return val; \ 10762306a36Sopenharmony_ci} \ 10862306a36Sopenharmony_ciEXPORT_SYMBOL(generic_atomic64_fetch_##op); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define ATOMIC64_OPS(op, c_op) \ 11162306a36Sopenharmony_ci ATOMIC64_OP(op, c_op) \ 11262306a36Sopenharmony_ci ATOMIC64_OP_RETURN(op, c_op) \ 11362306a36Sopenharmony_ci ATOMIC64_FETCH_OP(op, c_op) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciATOMIC64_OPS(add, +=) 11662306a36Sopenharmony_ciATOMIC64_OPS(sub, -=) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#undef ATOMIC64_OPS 11962306a36Sopenharmony_ci#define ATOMIC64_OPS(op, c_op) \ 12062306a36Sopenharmony_ci ATOMIC64_OP(op, c_op) \ 12162306a36Sopenharmony_ci ATOMIC64_FETCH_OP(op, c_op) 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ciATOMIC64_OPS(and, &=) 12462306a36Sopenharmony_ciATOMIC64_OPS(or, |=) 12562306a36Sopenharmony_ciATOMIC64_OPS(xor, ^=) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#undef ATOMIC64_OPS 12862306a36Sopenharmony_ci#undef ATOMIC64_FETCH_OP 12962306a36Sopenharmony_ci#undef ATOMIC64_OP 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cis64 generic_atomic64_dec_if_positive(atomic64_t *v) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci unsigned long flags; 13462306a36Sopenharmony_ci raw_spinlock_t *lock = lock_addr(v); 13562306a36Sopenharmony_ci s64 val; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); 13862306a36Sopenharmony_ci val = v->counter - 1; 13962306a36Sopenharmony_ci if (val >= 0) 14062306a36Sopenharmony_ci v->counter = val; 14162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); 14262306a36Sopenharmony_ci return val; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ciEXPORT_SYMBOL(generic_atomic64_dec_if_positive); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cis64 generic_atomic64_cmpxchg(atomic64_t *v, s64 o, s64 n) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci unsigned long flags; 14962306a36Sopenharmony_ci raw_spinlock_t *lock = lock_addr(v); 15062306a36Sopenharmony_ci s64 val; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); 15362306a36Sopenharmony_ci val = v->counter; 15462306a36Sopenharmony_ci if (val == o) 15562306a36Sopenharmony_ci v->counter = n; 15662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); 15762306a36Sopenharmony_ci return val; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ciEXPORT_SYMBOL(generic_atomic64_cmpxchg); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cis64 generic_atomic64_xchg(atomic64_t *v, s64 new) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci unsigned long flags; 16462306a36Sopenharmony_ci raw_spinlock_t *lock = lock_addr(v); 16562306a36Sopenharmony_ci s64 val; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); 16862306a36Sopenharmony_ci val = v->counter; 16962306a36Sopenharmony_ci v->counter = new; 17062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); 17162306a36Sopenharmony_ci return val; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL(generic_atomic64_xchg); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cis64 generic_atomic64_fetch_add_unless(atomic64_t *v, s64 a, s64 u) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci unsigned long flags; 17862306a36Sopenharmony_ci raw_spinlock_t *lock = lock_addr(v); 17962306a36Sopenharmony_ci s64 val; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci raw_spin_lock_irqsave(lock, flags); 18262306a36Sopenharmony_ci val = v->counter; 18362306a36Sopenharmony_ci if (val != u) 18462306a36Sopenharmony_ci v->counter += a; 18562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(lock, flags); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return val; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ciEXPORT_SYMBOL(generic_atomic64_fetch_add_unless); 190