1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef _ASM_X86_ATOMIC_H
3#define _ASM_X86_ATOMIC_H
4
5#include <linux/compiler.h>
6#include <linux/types.h>
7#include <asm/alternative.h>
8#include <asm/cmpxchg.h>
9#include <asm/rmwcc.h>
10#include <asm/barrier.h>
11
12/*
13 * Atomic operations that C can't guarantee us.  Useful for
14 * resource counting etc..
15 */
16
17static __always_inline int arch_atomic_read(const atomic_t *v)
18{
19	/*
20	 * Note for KASAN: we deliberately don't use READ_ONCE_NOCHECK() here,
21	 * it's non-inlined function that increases binary size and stack usage.
22	 */
23	return __READ_ONCE((v)->counter);
24}
25
26static __always_inline void arch_atomic_set(atomic_t *v, int i)
27{
28	__WRITE_ONCE(v->counter, i);
29}
30
31static __always_inline void arch_atomic_add(int i, atomic_t *v)
32{
33	asm volatile(LOCK_PREFIX "addl %1,%0"
34		     : "+m" (v->counter)
35		     : "ir" (i) : "memory");
36}
37
38static __always_inline void arch_atomic_sub(int i, atomic_t *v)
39{
40	asm volatile(LOCK_PREFIX "subl %1,%0"
41		     : "+m" (v->counter)
42		     : "ir" (i) : "memory");
43}
44
45static __always_inline bool arch_atomic_sub_and_test(int i, atomic_t *v)
46{
47	return GEN_BINARY_RMWcc(LOCK_PREFIX "subl", v->counter, e, "er", i);
48}
49#define arch_atomic_sub_and_test arch_atomic_sub_and_test
50
51static __always_inline void arch_atomic_inc(atomic_t *v)
52{
53	asm volatile(LOCK_PREFIX "incl %0"
54		     : "+m" (v->counter) :: "memory");
55}
56#define arch_atomic_inc arch_atomic_inc
57
58static __always_inline void arch_atomic_dec(atomic_t *v)
59{
60	asm volatile(LOCK_PREFIX "decl %0"
61		     : "+m" (v->counter) :: "memory");
62}
63#define arch_atomic_dec arch_atomic_dec
64
65static __always_inline bool arch_atomic_dec_and_test(atomic_t *v)
66{
67	return GEN_UNARY_RMWcc(LOCK_PREFIX "decl", v->counter, e);
68}
69#define arch_atomic_dec_and_test arch_atomic_dec_and_test
70
71static __always_inline bool arch_atomic_inc_and_test(atomic_t *v)
72{
73	return GEN_UNARY_RMWcc(LOCK_PREFIX "incl", v->counter, e);
74}
75#define arch_atomic_inc_and_test arch_atomic_inc_and_test
76
77static __always_inline bool arch_atomic_add_negative(int i, atomic_t *v)
78{
79	return GEN_BINARY_RMWcc(LOCK_PREFIX "addl", v->counter, s, "er", i);
80}
81#define arch_atomic_add_negative arch_atomic_add_negative
82
83static __always_inline int arch_atomic_add_return(int i, atomic_t *v)
84{
85	return i + xadd(&v->counter, i);
86}
87#define arch_atomic_add_return arch_atomic_add_return
88
89static __always_inline int arch_atomic_sub_return(int i, atomic_t *v)
90{
91	return arch_atomic_add_return(-i, v);
92}
93#define arch_atomic_sub_return arch_atomic_sub_return
94
95static __always_inline int arch_atomic_fetch_add(int i, atomic_t *v)
96{
97	return xadd(&v->counter, i);
98}
99#define arch_atomic_fetch_add arch_atomic_fetch_add
100
101static __always_inline int arch_atomic_fetch_sub(int i, atomic_t *v)
102{
103	return xadd(&v->counter, -i);
104}
105#define arch_atomic_fetch_sub arch_atomic_fetch_sub
106
107static __always_inline int arch_atomic_cmpxchg(atomic_t *v, int old, int new)
108{
109	return arch_cmpxchg(&v->counter, old, new);
110}
111#define arch_atomic_cmpxchg arch_atomic_cmpxchg
112
113static __always_inline bool arch_atomic_try_cmpxchg(atomic_t *v, int *old, int new)
114{
115	return arch_try_cmpxchg(&v->counter, old, new);
116}
117#define arch_atomic_try_cmpxchg arch_atomic_try_cmpxchg
118
119static __always_inline int arch_atomic_xchg(atomic_t *v, int new)
120{
121	return arch_xchg(&v->counter, new);
122}
123#define arch_atomic_xchg arch_atomic_xchg
124
125static __always_inline void arch_atomic_and(int i, atomic_t *v)
126{
127	asm volatile(LOCK_PREFIX "andl %1,%0"
128			: "+m" (v->counter)
129			: "ir" (i)
130			: "memory");
131}
132
133static __always_inline int arch_atomic_fetch_and(int i, atomic_t *v)
134{
135	int val = arch_atomic_read(v);
136
137	do { } while (!arch_atomic_try_cmpxchg(v, &val, val & i));
138
139	return val;
140}
141#define arch_atomic_fetch_and arch_atomic_fetch_and
142
143static __always_inline void arch_atomic_or(int i, atomic_t *v)
144{
145	asm volatile(LOCK_PREFIX "orl %1,%0"
146			: "+m" (v->counter)
147			: "ir" (i)
148			: "memory");
149}
150
151static __always_inline int arch_atomic_fetch_or(int i, atomic_t *v)
152{
153	int val = arch_atomic_read(v);
154
155	do { } while (!arch_atomic_try_cmpxchg(v, &val, val | i));
156
157	return val;
158}
159#define arch_atomic_fetch_or arch_atomic_fetch_or
160
161static __always_inline void arch_atomic_xor(int i, atomic_t *v)
162{
163	asm volatile(LOCK_PREFIX "xorl %1,%0"
164			: "+m" (v->counter)
165			: "ir" (i)
166			: "memory");
167}
168
169static __always_inline int arch_atomic_fetch_xor(int i, atomic_t *v)
170{
171	int val = arch_atomic_read(v);
172
173	do { } while (!arch_atomic_try_cmpxchg(v, &val, val ^ i));
174
175	return val;
176}
177#define arch_atomic_fetch_xor arch_atomic_fetch_xor
178
179#ifdef CONFIG_X86_32
180# include <asm/atomic64_32.h>
181#else
182# include <asm/atomic64_64.h>
183#endif
184
185#endif /* _ASM_X86_ATOMIC_H */
186