xref: /linux/arch/s390/include/asm/spinlock.h (revision e0bf6c5ca2d3281f231c5f0c9bf145e9513644de)
1 /*
2  *  S390 version
3  *    Copyright IBM Corp. 1999
4  *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
5  *
6  *  Derived from "include/asm-i386/spinlock.h"
7  */
8 
9 #ifndef __ASM_SPINLOCK_H
10 #define __ASM_SPINLOCK_H
11 
12 #include <linux/smp.h>
13 
14 #define SPINLOCK_LOCKVAL (S390_lowcore.spinlock_lockval)
15 
16 extern int spin_retry;
17 
18 static inline int
19 _raw_compare_and_swap(unsigned int *lock, unsigned int old, unsigned int new)
20 {
21 	return __sync_bool_compare_and_swap(lock, old, new);
22 }
23 
24 /*
25  * Simple spin lock operations.  There are two variants, one clears IRQ's
26  * on the local processor, one does not.
27  *
28  * We make no fairness assumptions. They have a cost.
29  *
30  * (the type definitions are in asm/spinlock_types.h)
31  */
32 
33 void arch_lock_relax(unsigned int cpu);
34 
35 void arch_spin_lock_wait(arch_spinlock_t *);
36 int arch_spin_trylock_retry(arch_spinlock_t *);
37 void arch_spin_lock_wait_flags(arch_spinlock_t *, unsigned long flags);
38 
39 static inline void arch_spin_relax(arch_spinlock_t *lock)
40 {
41 	arch_lock_relax(lock->lock);
42 }
43 
44 static inline u32 arch_spin_lockval(int cpu)
45 {
46 	return ~cpu;
47 }
48 
49 static inline int arch_spin_value_unlocked(arch_spinlock_t lock)
50 {
51 	return lock.lock == 0;
52 }
53 
54 static inline int arch_spin_is_locked(arch_spinlock_t *lp)
55 {
56 	return ACCESS_ONCE(lp->lock) != 0;
57 }
58 
59 static inline int arch_spin_trylock_once(arch_spinlock_t *lp)
60 {
61 	barrier();
62 	return likely(arch_spin_value_unlocked(*lp) &&
63 		      _raw_compare_and_swap(&lp->lock, 0, SPINLOCK_LOCKVAL));
64 }
65 
66 static inline void arch_spin_lock(arch_spinlock_t *lp)
67 {
68 	if (!arch_spin_trylock_once(lp))
69 		arch_spin_lock_wait(lp);
70 }
71 
72 static inline void arch_spin_lock_flags(arch_spinlock_t *lp,
73 					unsigned long flags)
74 {
75 	if (!arch_spin_trylock_once(lp))
76 		arch_spin_lock_wait_flags(lp, flags);
77 }
78 
79 static inline int arch_spin_trylock(arch_spinlock_t *lp)
80 {
81 	if (!arch_spin_trylock_once(lp))
82 		return arch_spin_trylock_retry(lp);
83 	return 1;
84 }
85 
86 static inline void arch_spin_unlock(arch_spinlock_t *lp)
87 {
88 	typecheck(unsigned int, lp->lock);
89 	asm volatile(
90 		__ASM_BARRIER
91 		"st	%1,%0\n"
92 		: "+Q" (lp->lock)
93 		: "d" (0)
94 		: "cc", "memory");
95 }
96 
97 static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
98 {
99 	while (arch_spin_is_locked(lock))
100 		arch_spin_relax(lock);
101 }
102 
103 /*
104  * Read-write spinlocks, allowing multiple readers
105  * but only one writer.
106  *
107  * NOTE! it is quite common to have readers in interrupts
108  * but no interrupt writers. For those circumstances we
109  * can "mix" irq-safe locks - any writer needs to get a
110  * irq-safe write-lock, but readers can get non-irqsafe
111  * read-locks.
112  */
113 
114 /**
115  * read_can_lock - would read_trylock() succeed?
116  * @lock: the rwlock in question.
117  */
118 #define arch_read_can_lock(x) ((int)(x)->lock >= 0)
119 
120 /**
121  * write_can_lock - would write_trylock() succeed?
122  * @lock: the rwlock in question.
123  */
124 #define arch_write_can_lock(x) ((x)->lock == 0)
125 
126 extern int _raw_read_trylock_retry(arch_rwlock_t *lp);
127 extern int _raw_write_trylock_retry(arch_rwlock_t *lp);
128 
129 #define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
130 #define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
131 
132 static inline int arch_read_trylock_once(arch_rwlock_t *rw)
133 {
134 	unsigned int old = ACCESS_ONCE(rw->lock);
135 	return likely((int) old >= 0 &&
136 		      _raw_compare_and_swap(&rw->lock, old, old + 1));
137 }
138 
139 static inline int arch_write_trylock_once(arch_rwlock_t *rw)
140 {
141 	unsigned int old = ACCESS_ONCE(rw->lock);
142 	return likely(old == 0 &&
143 		      _raw_compare_and_swap(&rw->lock, 0, 0x80000000));
144 }
145 
146 #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
147 
148 #define __RAW_OP_OR	"lao"
149 #define __RAW_OP_AND	"lan"
150 #define __RAW_OP_ADD	"laa"
151 
152 #define __RAW_LOCK(ptr, op_val, op_string)		\
153 ({							\
154 	unsigned int old_val;				\
155 							\
156 	typecheck(unsigned int *, ptr);			\
157 	asm volatile(					\
158 		op_string "	%0,%2,%1\n"		\
159 		"bcr	14,0\n"				\
160 		: "=d" (old_val), "+Q" (*ptr)		\
161 		: "d" (op_val)				\
162 		: "cc", "memory");			\
163 	old_val;					\
164 })
165 
166 #define __RAW_UNLOCK(ptr, op_val, op_string)		\
167 ({							\
168 	unsigned int old_val;				\
169 							\
170 	typecheck(unsigned int *, ptr);			\
171 	asm volatile(					\
172 		"bcr	14,0\n"				\
173 		op_string "	%0,%2,%1\n"		\
174 		: "=d" (old_val), "+Q" (*ptr)		\
175 		: "d" (op_val)				\
176 		: "cc", "memory");			\
177 	old_val;					\
178 })
179 
180 extern void _raw_read_lock_wait(arch_rwlock_t *lp);
181 extern void _raw_write_lock_wait(arch_rwlock_t *lp, unsigned int prev);
182 
183 static inline void arch_read_lock(arch_rwlock_t *rw)
184 {
185 	unsigned int old;
186 
187 	old = __RAW_LOCK(&rw->lock, 1, __RAW_OP_ADD);
188 	if ((int) old < 0)
189 		_raw_read_lock_wait(rw);
190 }
191 
192 static inline void arch_read_unlock(arch_rwlock_t *rw)
193 {
194 	__RAW_UNLOCK(&rw->lock, -1, __RAW_OP_ADD);
195 }
196 
197 static inline void arch_write_lock(arch_rwlock_t *rw)
198 {
199 	unsigned int old;
200 
201 	old = __RAW_LOCK(&rw->lock, 0x80000000, __RAW_OP_OR);
202 	if (old != 0)
203 		_raw_write_lock_wait(rw, old);
204 	rw->owner = SPINLOCK_LOCKVAL;
205 }
206 
207 static inline void arch_write_unlock(arch_rwlock_t *rw)
208 {
209 	rw->owner = 0;
210 	__RAW_UNLOCK(&rw->lock, 0x7fffffff, __RAW_OP_AND);
211 }
212 
213 #else /* CONFIG_HAVE_MARCH_Z196_FEATURES */
214 
215 extern void _raw_read_lock_wait(arch_rwlock_t *lp);
216 extern void _raw_write_lock_wait(arch_rwlock_t *lp);
217 
218 static inline void arch_read_lock(arch_rwlock_t *rw)
219 {
220 	if (!arch_read_trylock_once(rw))
221 		_raw_read_lock_wait(rw);
222 }
223 
224 static inline void arch_read_unlock(arch_rwlock_t *rw)
225 {
226 	unsigned int old;
227 
228 	do {
229 		old = ACCESS_ONCE(rw->lock);
230 	} while (!_raw_compare_and_swap(&rw->lock, old, old - 1));
231 }
232 
233 static inline void arch_write_lock(arch_rwlock_t *rw)
234 {
235 	if (!arch_write_trylock_once(rw))
236 		_raw_write_lock_wait(rw);
237 	rw->owner = SPINLOCK_LOCKVAL;
238 }
239 
240 static inline void arch_write_unlock(arch_rwlock_t *rw)
241 {
242 	typecheck(unsigned int, rw->lock);
243 
244 	rw->owner = 0;
245 	asm volatile(
246 		__ASM_BARRIER
247 		"st	%1,%0\n"
248 		: "+Q" (rw->lock)
249 		: "d" (0)
250 		: "cc", "memory");
251 }
252 
253 #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */
254 
255 static inline int arch_read_trylock(arch_rwlock_t *rw)
256 {
257 	if (!arch_read_trylock_once(rw))
258 		return _raw_read_trylock_retry(rw);
259 	return 1;
260 }
261 
262 static inline int arch_write_trylock(arch_rwlock_t *rw)
263 {
264 	if (!arch_write_trylock_once(rw) && !_raw_write_trylock_retry(rw))
265 		return 0;
266 	rw->owner = SPINLOCK_LOCKVAL;
267 	return 1;
268 }
269 
270 static inline void arch_read_relax(arch_rwlock_t *rw)
271 {
272 	arch_lock_relax(rw->owner);
273 }
274 
275 static inline void arch_write_relax(arch_rwlock_t *rw)
276 {
277 	arch_lock_relax(rw->owner);
278 }
279 
280 #endif /* __ASM_SPINLOCK_H */
281