12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
209d4e0edSPaul Mackerras /*
309d4e0edSPaul Mackerras * Generic implementation of 64-bit atomics using spinlocks,
409d4e0edSPaul Mackerras * useful on processors that don't have 64-bit atomic instructions.
509d4e0edSPaul Mackerras *
609d4e0edSPaul Mackerras * Copyright © 2009 Paul Mackerras, IBM Corp. <[email protected]>
709d4e0edSPaul Mackerras */
809d4e0edSPaul Mackerras #include <linux/types.h>
909d4e0edSPaul Mackerras #include <linux/cache.h>
1009d4e0edSPaul Mackerras #include <linux/spinlock.h>
1109d4e0edSPaul Mackerras #include <linux/init.h>
128bc3bcc9SPaul Gortmaker #include <linux/export.h>
1360063497SArun Sharma #include <linux/atomic.h>
1409d4e0edSPaul Mackerras
1509d4e0edSPaul Mackerras /*
1609d4e0edSPaul Mackerras * We use a hashed array of spinlocks to provide exclusive access
1709d4e0edSPaul Mackerras * to each atomic64_t variable. Since this is expected to used on
1809d4e0edSPaul Mackerras * systems with small numbers of CPUs (<= 4 or so), we use a
1909d4e0edSPaul Mackerras * relatively small array of 16 spinlocks to avoid wasting too much
2009d4e0edSPaul Mackerras * memory on the spinlock array.
2109d4e0edSPaul Mackerras */
2209d4e0edSPaul Mackerras #define NR_LOCKS 16
2309d4e0edSPaul Mackerras
2409d4e0edSPaul Mackerras /*
2509d4e0edSPaul Mackerras * Ensure each lock is in a separate cacheline.
2609d4e0edSPaul Mackerras */
2709d4e0edSPaul Mackerras static union {
28*6c8ad3abSSteven Rostedt arch_spinlock_t lock;
2909d4e0edSPaul Mackerras char pad[L1_CACHE_BYTES];
30fcc16882SStephen Boyd } atomic64_lock[NR_LOCKS] __cacheline_aligned_in_smp = {
31fcc16882SStephen Boyd [0 ... (NR_LOCKS - 1)] = {
32*6c8ad3abSSteven Rostedt .lock = __ARCH_SPIN_LOCK_UNLOCKED,
33fcc16882SStephen Boyd },
34fcc16882SStephen Boyd };
3509d4e0edSPaul Mackerras
lock_addr(const atomic64_t * v)36*6c8ad3abSSteven Rostedt static inline arch_spinlock_t *lock_addr(const atomic64_t *v)
3709d4e0edSPaul Mackerras {
3809d4e0edSPaul Mackerras unsigned long addr = (unsigned long) v;
3909d4e0edSPaul Mackerras
4009d4e0edSPaul Mackerras addr >>= L1_CACHE_SHIFT;
4109d4e0edSPaul Mackerras addr ^= (addr >> 8) ^ (addr >> 16);
4209d4e0edSPaul Mackerras return &atomic64_lock[addr & (NR_LOCKS - 1)].lock;
4309d4e0edSPaul Mackerras }
4409d4e0edSPaul Mackerras
generic_atomic64_read(const atomic64_t * v)451bdadf46SMark Rutland s64 generic_atomic64_read(const atomic64_t *v)
4609d4e0edSPaul Mackerras {
4709d4e0edSPaul Mackerras unsigned long flags;
48*6c8ad3abSSteven Rostedt arch_spinlock_t *lock = lock_addr(v);
499255813dSMark Rutland s64 val;
5009d4e0edSPaul Mackerras
51*6c8ad3abSSteven Rostedt local_irq_save(flags);
52*6c8ad3abSSteven Rostedt arch_spin_lock(lock);
5309d4e0edSPaul Mackerras val = v->counter;
54*6c8ad3abSSteven Rostedt arch_spin_unlock(lock);
55*6c8ad3abSSteven Rostedt local_irq_restore(flags);
5609d4e0edSPaul Mackerras return val;
5709d4e0edSPaul Mackerras }
581bdadf46SMark Rutland EXPORT_SYMBOL(generic_atomic64_read);
5909d4e0edSPaul Mackerras
generic_atomic64_set(atomic64_t * v,s64 i)601bdadf46SMark Rutland void generic_atomic64_set(atomic64_t *v, s64 i)
6109d4e0edSPaul Mackerras {
6209d4e0edSPaul Mackerras unsigned long flags;
63*6c8ad3abSSteven Rostedt arch_spinlock_t *lock = lock_addr(v);
6409d4e0edSPaul Mackerras
65*6c8ad3abSSteven Rostedt local_irq_save(flags);
66*6c8ad3abSSteven Rostedt arch_spin_lock(lock);
6709d4e0edSPaul Mackerras v->counter = i;
68*6c8ad3abSSteven Rostedt arch_spin_unlock(lock);
69*6c8ad3abSSteven Rostedt local_irq_restore(flags);
7009d4e0edSPaul Mackerras }
711bdadf46SMark Rutland EXPORT_SYMBOL(generic_atomic64_set);
7209d4e0edSPaul Mackerras
73560cb12aSPeter Zijlstra #define ATOMIC64_OP(op, c_op) \
741bdadf46SMark Rutland void generic_atomic64_##op(s64 a, atomic64_t *v) \
75560cb12aSPeter Zijlstra { \
76560cb12aSPeter Zijlstra unsigned long flags; \
77*6c8ad3abSSteven Rostedt arch_spinlock_t *lock = lock_addr(v); \
78560cb12aSPeter Zijlstra \
79*6c8ad3abSSteven Rostedt local_irq_save(flags); \
80*6c8ad3abSSteven Rostedt arch_spin_lock(lock); \
81560cb12aSPeter Zijlstra v->counter c_op a; \
82*6c8ad3abSSteven Rostedt arch_spin_unlock(lock); \
83*6c8ad3abSSteven Rostedt local_irq_restore(flags); \
84560cb12aSPeter Zijlstra } \
851bdadf46SMark Rutland EXPORT_SYMBOL(generic_atomic64_##op);
8609d4e0edSPaul Mackerras
87560cb12aSPeter Zijlstra #define ATOMIC64_OP_RETURN(op, c_op) \
881bdadf46SMark Rutland s64 generic_atomic64_##op##_return(s64 a, atomic64_t *v) \
89560cb12aSPeter Zijlstra { \
90560cb12aSPeter Zijlstra unsigned long flags; \
91*6c8ad3abSSteven Rostedt arch_spinlock_t *lock = lock_addr(v); \
929255813dSMark Rutland s64 val; \
93560cb12aSPeter Zijlstra \
94*6c8ad3abSSteven Rostedt local_irq_save(flags); \
95*6c8ad3abSSteven Rostedt arch_spin_lock(lock); \
96560cb12aSPeter Zijlstra val = (v->counter c_op a); \
97*6c8ad3abSSteven Rostedt arch_spin_unlock(lock); \
98*6c8ad3abSSteven Rostedt local_irq_restore(flags); \
99560cb12aSPeter Zijlstra return val; \
100560cb12aSPeter Zijlstra } \
1011bdadf46SMark Rutland EXPORT_SYMBOL(generic_atomic64_##op##_return);
10209d4e0edSPaul Mackerras
10328aa2bdaSPeter Zijlstra #define ATOMIC64_FETCH_OP(op, c_op) \
1041bdadf46SMark Rutland s64 generic_atomic64_fetch_##op(s64 a, atomic64_t *v) \
10528aa2bdaSPeter Zijlstra { \
10628aa2bdaSPeter Zijlstra unsigned long flags; \
107*6c8ad3abSSteven Rostedt arch_spinlock_t *lock = lock_addr(v); \
1089255813dSMark Rutland s64 val; \
10928aa2bdaSPeter Zijlstra \
110*6c8ad3abSSteven Rostedt local_irq_save(flags); \
111*6c8ad3abSSteven Rostedt arch_spin_lock(lock); \
11228aa2bdaSPeter Zijlstra val = v->counter; \
11328aa2bdaSPeter Zijlstra v->counter c_op a; \
114*6c8ad3abSSteven Rostedt arch_spin_unlock(lock); \
115*6c8ad3abSSteven Rostedt local_irq_restore(flags); \
11628aa2bdaSPeter Zijlstra return val; \
11728aa2bdaSPeter Zijlstra } \
1181bdadf46SMark Rutland EXPORT_SYMBOL(generic_atomic64_fetch_##op);
11928aa2bdaSPeter Zijlstra
120560cb12aSPeter Zijlstra #define ATOMIC64_OPS(op, c_op) \
121560cb12aSPeter Zijlstra ATOMIC64_OP(op, c_op) \
12228aa2bdaSPeter Zijlstra ATOMIC64_OP_RETURN(op, c_op) \
12328aa2bdaSPeter Zijlstra ATOMIC64_FETCH_OP(op, c_op)
12409d4e0edSPaul Mackerras
125560cb12aSPeter Zijlstra ATOMIC64_OPS(add, +=)
126560cb12aSPeter Zijlstra ATOMIC64_OPS(sub, -=)
12709d4e0edSPaul Mackerras
128560cb12aSPeter Zijlstra #undef ATOMIC64_OPS
12928aa2bdaSPeter Zijlstra #define ATOMIC64_OPS(op, c_op) \
13028aa2bdaSPeter Zijlstra ATOMIC64_OP(op, c_op) \
13128aa2bdaSPeter Zijlstra ATOMIC64_FETCH_OP(op, c_op)
13228aa2bdaSPeter Zijlstra
13328aa2bdaSPeter Zijlstra ATOMIC64_OPS(and, &=)
13428aa2bdaSPeter Zijlstra ATOMIC64_OPS(or, |=)
13528aa2bdaSPeter Zijlstra ATOMIC64_OPS(xor, ^=)
13628aa2bdaSPeter Zijlstra
13728aa2bdaSPeter Zijlstra #undef ATOMIC64_OPS
13828aa2bdaSPeter Zijlstra #undef ATOMIC64_FETCH_OP
139560cb12aSPeter Zijlstra #undef ATOMIC64_OP
14009d4e0edSPaul Mackerras
generic_atomic64_dec_if_positive(atomic64_t * v)1411bdadf46SMark Rutland s64 generic_atomic64_dec_if_positive(atomic64_t *v)
14209d4e0edSPaul Mackerras {
14309d4e0edSPaul Mackerras unsigned long flags;
144*6c8ad3abSSteven Rostedt arch_spinlock_t *lock = lock_addr(v);
1459255813dSMark Rutland s64 val;
14609d4e0edSPaul Mackerras
147*6c8ad3abSSteven Rostedt local_irq_save(flags);
148*6c8ad3abSSteven Rostedt arch_spin_lock(lock);
14909d4e0edSPaul Mackerras val = v->counter - 1;
15009d4e0edSPaul Mackerras if (val >= 0)
15109d4e0edSPaul Mackerras v->counter = val;
152*6c8ad3abSSteven Rostedt arch_spin_unlock(lock);
153*6c8ad3abSSteven Rostedt local_irq_restore(flags);
15409d4e0edSPaul Mackerras return val;
15509d4e0edSPaul Mackerras }
1561bdadf46SMark Rutland EXPORT_SYMBOL(generic_atomic64_dec_if_positive);
15709d4e0edSPaul Mackerras
generic_atomic64_cmpxchg(atomic64_t * v,s64 o,s64 n)1581bdadf46SMark Rutland s64 generic_atomic64_cmpxchg(atomic64_t *v, s64 o, s64 n)
15909d4e0edSPaul Mackerras {
16009d4e0edSPaul Mackerras unsigned long flags;
161*6c8ad3abSSteven Rostedt arch_spinlock_t *lock = lock_addr(v);
1629255813dSMark Rutland s64 val;
16309d4e0edSPaul Mackerras
164*6c8ad3abSSteven Rostedt local_irq_save(flags);
165*6c8ad3abSSteven Rostedt arch_spin_lock(lock);
16609d4e0edSPaul Mackerras val = v->counter;
16709d4e0edSPaul Mackerras if (val == o)
16809d4e0edSPaul Mackerras v->counter = n;
169*6c8ad3abSSteven Rostedt arch_spin_unlock(lock);
170*6c8ad3abSSteven Rostedt local_irq_restore(flags);
17109d4e0edSPaul Mackerras return val;
17209d4e0edSPaul Mackerras }
1731bdadf46SMark Rutland EXPORT_SYMBOL(generic_atomic64_cmpxchg);
17409d4e0edSPaul Mackerras
generic_atomic64_xchg(atomic64_t * v,s64 new)1751bdadf46SMark Rutland s64 generic_atomic64_xchg(atomic64_t *v, s64 new)
17609d4e0edSPaul Mackerras {
17709d4e0edSPaul Mackerras unsigned long flags;
178*6c8ad3abSSteven Rostedt arch_spinlock_t *lock = lock_addr(v);
1799255813dSMark Rutland s64 val;
18009d4e0edSPaul Mackerras
181*6c8ad3abSSteven Rostedt local_irq_save(flags);
182*6c8ad3abSSteven Rostedt arch_spin_lock(lock);
18309d4e0edSPaul Mackerras val = v->counter;
18409d4e0edSPaul Mackerras v->counter = new;
185*6c8ad3abSSteven Rostedt arch_spin_unlock(lock);
186*6c8ad3abSSteven Rostedt local_irq_restore(flags);
18709d4e0edSPaul Mackerras return val;
18809d4e0edSPaul Mackerras }
1891bdadf46SMark Rutland EXPORT_SYMBOL(generic_atomic64_xchg);
19009d4e0edSPaul Mackerras
generic_atomic64_fetch_add_unless(atomic64_t * v,s64 a,s64 u)1911bdadf46SMark Rutland s64 generic_atomic64_fetch_add_unless(atomic64_t *v, s64 a, s64 u)
19209d4e0edSPaul Mackerras {
19309d4e0edSPaul Mackerras unsigned long flags;
194*6c8ad3abSSteven Rostedt arch_spinlock_t *lock = lock_addr(v);
1959255813dSMark Rutland s64 val;
19609d4e0edSPaul Mackerras
197*6c8ad3abSSteven Rostedt local_irq_save(flags);
198*6c8ad3abSSteven Rostedt arch_spin_lock(lock);
19900b808abSMark Rutland val = v->counter;
20000b808abSMark Rutland if (val != u)
20109d4e0edSPaul Mackerras v->counter += a;
202*6c8ad3abSSteven Rostedt arch_spin_unlock(lock);
203*6c8ad3abSSteven Rostedt local_irq_restore(flags);
20400b808abSMark Rutland
20500b808abSMark Rutland return val;
20609d4e0edSPaul Mackerras }
2071bdadf46SMark Rutland EXPORT_SYMBOL(generic_atomic64_fetch_add_unless);
208