xref: /linux-6.15/lib/atomic64.c (revision 6c8ad3ab)
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