xref: /linux/arch/s390/include/asm/atomic_ops.h (revision c434e25b62f8efcfbb6bf1f7ce55960206c1137e)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Low level function for atomic operations
4  *
5  * Copyright IBM Corp. 1999, 2016
6  */
7 
8 #ifndef __ARCH_S390_ATOMIC_OPS__
9 #define __ARCH_S390_ATOMIC_OPS__
10 
11 #include <linux/limits.h>
12 
13 static __always_inline int __atomic_read(const atomic_t *v)
14 {
15 	int c;
16 
17 	asm volatile(
18 		"	l	%[c],%[counter]\n"
19 		: [c] "=d" (c) : [counter] "R" (v->counter));
20 	return c;
21 }
22 
23 static __always_inline void __atomic_set(atomic_t *v, int i)
24 {
25 	if (__builtin_constant_p(i) && i >= S16_MIN && i <= S16_MAX) {
26 		asm volatile(
27 			"	mvhi	%[counter], %[i]\n"
28 			: [counter] "=Q" (v->counter) : [i] "K" (i));
29 	} else {
30 		asm volatile(
31 			"	st	%[i],%[counter]\n"
32 			: [counter] "=R" (v->counter) : [i] "d" (i));
33 	}
34 }
35 
36 static __always_inline s64 __atomic64_read(const atomic64_t *v)
37 {
38 	s64 c;
39 
40 	asm volatile(
41 		"	lg	%[c],%[counter]\n"
42 		: [c] "=d" (c) : [counter] "RT" (v->counter));
43 	return c;
44 }
45 
46 static __always_inline void __atomic64_set(atomic64_t *v, s64 i)
47 {
48 	if (__builtin_constant_p(i) && i >= S16_MIN && i <= S16_MAX) {
49 		asm volatile(
50 			"	mvghi	%[counter], %[i]\n"
51 			: [counter] "=Q" (v->counter) : [i] "K" (i));
52 	} else {
53 		asm volatile(
54 			"	stg	%[i],%[counter]\n"
55 			: [counter] "=RT" (v->counter) : [i] "d" (i));
56 	}
57 }
58 
59 #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
60 
61 #define __ATOMIC_OP(op_name, op_type, op_string, op_barrier)		\
62 static __always_inline op_type op_name(op_type val, op_type *ptr)	\
63 {									\
64 	op_type old;							\
65 									\
66 	asm volatile(							\
67 		op_string "	%[old],%[val],%[ptr]\n"			\
68 		op_barrier						\
69 		: [old] "=d" (old), [ptr] "+QS" (*ptr)			\
70 		: [val] "d" (val) : "cc", "memory");			\
71 	return old;							\
72 }									\
73 
74 #define __ATOMIC_OPS(op_name, op_type, op_string)			\
75 	__ATOMIC_OP(op_name, op_type, op_string, "\n")			\
76 	__ATOMIC_OP(op_name##_barrier, op_type, op_string, "bcr 14,0\n")
77 
78 __ATOMIC_OPS(__atomic_add, int, "laa")
79 __ATOMIC_OPS(__atomic_and, int, "lan")
80 __ATOMIC_OPS(__atomic_or,  int, "lao")
81 __ATOMIC_OPS(__atomic_xor, int, "lax")
82 
83 __ATOMIC_OPS(__atomic64_add, long, "laag")
84 __ATOMIC_OPS(__atomic64_and, long, "lang")
85 __ATOMIC_OPS(__atomic64_or,  long, "laog")
86 __ATOMIC_OPS(__atomic64_xor, long, "laxg")
87 
88 #undef __ATOMIC_OPS
89 #undef __ATOMIC_OP
90 
91 #define __ATOMIC_CONST_OP(op_name, op_type, op_string, op_barrier)	\
92 static __always_inline void op_name(op_type val, op_type *ptr)		\
93 {									\
94 	asm volatile(							\
95 		op_string "	%[ptr],%[val]\n"			\
96 		op_barrier						\
97 		: [ptr] "+QS" (*ptr) : [val] "i" (val) : "cc", "memory");\
98 }
99 
100 #define __ATOMIC_CONST_OPS(op_name, op_type, op_string)			\
101 	__ATOMIC_CONST_OP(op_name, op_type, op_string, "\n")		\
102 	__ATOMIC_CONST_OP(op_name##_barrier, op_type, op_string, "bcr 14,0\n")
103 
104 __ATOMIC_CONST_OPS(__atomic_add_const, int, "asi")
105 __ATOMIC_CONST_OPS(__atomic64_add_const, long, "agsi")
106 
107 #undef __ATOMIC_CONST_OPS
108 #undef __ATOMIC_CONST_OP
109 
110 #else /* CONFIG_HAVE_MARCH_Z196_FEATURES */
111 
112 #define __ATOMIC_OP(op_name, op_string)					\
113 static __always_inline int op_name(int val, int *ptr)			\
114 {									\
115 	int old, new;							\
116 									\
117 	asm volatile(							\
118 		"0:	lr	%[new],%[old]\n"			\
119 		op_string "	%[new],%[val]\n"			\
120 		"	cs	%[old],%[new],%[ptr]\n"			\
121 		"	jl	0b"					\
122 		: [old] "=d" (old), [new] "=&d" (new), [ptr] "+Q" (*ptr)\
123 		: [val] "d" (val), "0" (*ptr) : "cc", "memory");	\
124 	return old;							\
125 }
126 
127 #define __ATOMIC_OPS(op_name, op_string)				\
128 	__ATOMIC_OP(op_name, op_string)					\
129 	__ATOMIC_OP(op_name##_barrier, op_string)
130 
131 __ATOMIC_OPS(__atomic_add, "ar")
132 __ATOMIC_OPS(__atomic_and, "nr")
133 __ATOMIC_OPS(__atomic_or,  "or")
134 __ATOMIC_OPS(__atomic_xor, "xr")
135 
136 #undef __ATOMIC_OPS
137 
138 #define __ATOMIC64_OP(op_name, op_string)				\
139 static __always_inline long op_name(long val, long *ptr)		\
140 {									\
141 	long old, new;							\
142 									\
143 	asm volatile(							\
144 		"0:	lgr	%[new],%[old]\n"			\
145 		op_string "	%[new],%[val]\n"			\
146 		"	csg	%[old],%[new],%[ptr]\n"			\
147 		"	jl	0b"					\
148 		: [old] "=d" (old), [new] "=&d" (new), [ptr] "+QS" (*ptr)\
149 		: [val] "d" (val), "0" (*ptr) : "cc", "memory");	\
150 	return old;							\
151 }
152 
153 #define __ATOMIC64_OPS(op_name, op_string)				\
154 	__ATOMIC64_OP(op_name, op_string)				\
155 	__ATOMIC64_OP(op_name##_barrier, op_string)
156 
157 __ATOMIC64_OPS(__atomic64_add, "agr")
158 __ATOMIC64_OPS(__atomic64_and, "ngr")
159 __ATOMIC64_OPS(__atomic64_or,  "ogr")
160 __ATOMIC64_OPS(__atomic64_xor, "xgr")
161 
162 #undef __ATOMIC64_OPS
163 
164 #define __atomic_add_const(val, ptr)		__atomic_add(val, ptr)
165 #define __atomic_add_const_barrier(val, ptr)	__atomic_add(val, ptr)
166 #define __atomic64_add_const(val, ptr)		__atomic64_add(val, ptr)
167 #define __atomic64_add_const_barrier(val, ptr)	__atomic64_add(val, ptr)
168 
169 #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */
170 
171 static __always_inline int __atomic_cmpxchg(int *ptr, int old, int new)
172 {
173 	asm volatile(
174 		"	cs	%[old],%[new],%[ptr]"
175 		: [old] "+d" (old), [ptr] "+Q" (*ptr)
176 		: [new] "d" (new)
177 		: "cc", "memory");
178 	return old;
179 }
180 
181 static __always_inline long __atomic64_cmpxchg(long *ptr, long old, long new)
182 {
183 	asm volatile(
184 		"	csg	%[old],%[new],%[ptr]"
185 		: [old] "+d" (old), [ptr] "+QS" (*ptr)
186 		: [new] "d" (new)
187 		: "cc", "memory");
188 	return old;
189 }
190 
191 #ifdef __GCC_ASM_FLAG_OUTPUTS__
192 
193 static __always_inline bool __atomic_cmpxchg_bool(int *ptr, int old, int new)
194 {
195 	int cc;
196 
197 	asm volatile(
198 		"	cs	%[old],%[new],%[ptr]"
199 		: [old] "+d" (old), [ptr] "+Q" (*ptr), "=@cc" (cc)
200 		: [new] "d" (new)
201 		: "memory");
202 	return cc == 0;
203 }
204 
205 static __always_inline bool __atomic64_cmpxchg_bool(long *ptr, long old, long new)
206 {
207 	int cc;
208 
209 	asm volatile(
210 		"	csg	%[old],%[new],%[ptr]"
211 		: [old] "+d" (old), [ptr] "+QS" (*ptr), "=@cc" (cc)
212 		: [new] "d" (new)
213 		: "memory");
214 	return cc == 0;
215 }
216 
217 #else /* __GCC_ASM_FLAG_OUTPUTS__ */
218 
219 static __always_inline bool __atomic_cmpxchg_bool(int *ptr, int old, int new)
220 {
221 	int old_expected = old;
222 
223 	asm volatile(
224 		"	cs	%[old],%[new],%[ptr]"
225 		: [old] "+d" (old), [ptr] "+Q" (*ptr)
226 		: [new] "d" (new)
227 		: "cc", "memory");
228 	return old == old_expected;
229 }
230 
231 static __always_inline bool __atomic64_cmpxchg_bool(long *ptr, long old, long new)
232 {
233 	long old_expected = old;
234 
235 	asm volatile(
236 		"	csg	%[old],%[new],%[ptr]"
237 		: [old] "+d" (old), [ptr] "+QS" (*ptr)
238 		: [new] "d" (new)
239 		: "cc", "memory");
240 	return old == old_expected;
241 }
242 
243 #endif /* __GCC_ASM_FLAG_OUTPUTS__ */
244 
245 #endif /* __ARCH_S390_ATOMIC_OPS__  */
246