162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef _ASM_X86_LOCAL_H
362306a36Sopenharmony_ci#define _ASM_X86_LOCAL_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/percpu.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/atomic.h>
862306a36Sopenharmony_ci#include <asm/asm.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_citypedef struct {
1162306a36Sopenharmony_ci	atomic_long_t a;
1262306a36Sopenharmony_ci} local_t;
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define LOCAL_INIT(i)	{ ATOMIC_LONG_INIT(i) }
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define local_read(l)	atomic_long_read(&(l)->a)
1762306a36Sopenharmony_ci#define local_set(l, i)	atomic_long_set(&(l)->a, (i))
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic inline void local_inc(local_t *l)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	asm volatile(_ASM_INC "%0"
2262306a36Sopenharmony_ci		     : "+m" (l->a.counter));
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic inline void local_dec(local_t *l)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	asm volatile(_ASM_DEC "%0"
2862306a36Sopenharmony_ci		     : "+m" (l->a.counter));
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic inline void local_add(long i, local_t *l)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	asm volatile(_ASM_ADD "%1,%0"
3462306a36Sopenharmony_ci		     : "+m" (l->a.counter)
3562306a36Sopenharmony_ci		     : "ir" (i));
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic inline void local_sub(long i, local_t *l)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	asm volatile(_ASM_SUB "%1,%0"
4162306a36Sopenharmony_ci		     : "+m" (l->a.counter)
4262306a36Sopenharmony_ci		     : "ir" (i));
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/**
4662306a36Sopenharmony_ci * local_sub_and_test - subtract value from variable and test result
4762306a36Sopenharmony_ci * @i: integer value to subtract
4862306a36Sopenharmony_ci * @l: pointer to type local_t
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * Atomically subtracts @i from @l and returns
5162306a36Sopenharmony_ci * true if the result is zero, or false for all
5262306a36Sopenharmony_ci * other cases.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic inline bool local_sub_and_test(long i, local_t *l)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	return GEN_BINARY_RMWcc(_ASM_SUB, l->a.counter, e, "er", i);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/**
6062306a36Sopenharmony_ci * local_dec_and_test - decrement and test
6162306a36Sopenharmony_ci * @l: pointer to type local_t
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * Atomically decrements @l by 1 and
6462306a36Sopenharmony_ci * returns true if the result is 0, or false for all other
6562306a36Sopenharmony_ci * cases.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic inline bool local_dec_and_test(local_t *l)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	return GEN_UNARY_RMWcc(_ASM_DEC, l->a.counter, e);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/**
7362306a36Sopenharmony_ci * local_inc_and_test - increment and test
7462306a36Sopenharmony_ci * @l: pointer to type local_t
7562306a36Sopenharmony_ci *
7662306a36Sopenharmony_ci * Atomically increments @l by 1
7762306a36Sopenharmony_ci * and returns true if the result is zero, or false for all
7862306a36Sopenharmony_ci * other cases.
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_cistatic inline bool local_inc_and_test(local_t *l)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	return GEN_UNARY_RMWcc(_ASM_INC, l->a.counter, e);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/**
8662306a36Sopenharmony_ci * local_add_negative - add and test if negative
8762306a36Sopenharmony_ci * @i: integer value to add
8862306a36Sopenharmony_ci * @l: pointer to type local_t
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * Atomically adds @i to @l and returns true
9162306a36Sopenharmony_ci * if the result is negative, or false when
9262306a36Sopenharmony_ci * result is greater than or equal to zero.
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_cistatic inline bool local_add_negative(long i, local_t *l)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	return GEN_BINARY_RMWcc(_ASM_ADD, l->a.counter, s, "er", i);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/**
10062306a36Sopenharmony_ci * local_add_return - add and return
10162306a36Sopenharmony_ci * @i: integer value to add
10262306a36Sopenharmony_ci * @l: pointer to type local_t
10362306a36Sopenharmony_ci *
10462306a36Sopenharmony_ci * Atomically adds @i to @l and returns @i + @l
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_cistatic inline long local_add_return(long i, local_t *l)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	long __i = i;
10962306a36Sopenharmony_ci	asm volatile(_ASM_XADD "%0, %1;"
11062306a36Sopenharmony_ci		     : "+r" (i), "+m" (l->a.counter)
11162306a36Sopenharmony_ci		     : : "memory");
11262306a36Sopenharmony_ci	return i + __i;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic inline long local_sub_return(long i, local_t *l)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	return local_add_return(-i, l);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#define local_inc_return(l)  (local_add_return(1, l))
12162306a36Sopenharmony_ci#define local_dec_return(l)  (local_sub_return(1, l))
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic inline long local_cmpxchg(local_t *l, long old, long new)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	return cmpxchg_local(&l->a.counter, old, new);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic inline bool local_try_cmpxchg(local_t *l, long *old, long new)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	return try_cmpxchg_local(&l->a.counter,
13162306a36Sopenharmony_ci				 (typeof(l->a.counter) *) old, new);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/* Always has a lock prefix */
13562306a36Sopenharmony_ci#define local_xchg(l, n) (xchg(&((l)->a.counter), (n)))
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/**
13862306a36Sopenharmony_ci * local_add_unless - add unless the number is a given value
13962306a36Sopenharmony_ci * @l: pointer of type local_t
14062306a36Sopenharmony_ci * @a: the amount to add to l...
14162306a36Sopenharmony_ci * @u: ...unless l is equal to u.
14262306a36Sopenharmony_ci *
14362306a36Sopenharmony_ci * Atomically adds @a to @l, so long as it was not @u.
14462306a36Sopenharmony_ci * Returns non-zero if @l was not @u, and zero otherwise.
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_ci#define local_add_unless(l, a, u)				\
14762306a36Sopenharmony_ci({								\
14862306a36Sopenharmony_ci	long c, old;						\
14962306a36Sopenharmony_ci	c = local_read((l));					\
15062306a36Sopenharmony_ci	for (;;) {						\
15162306a36Sopenharmony_ci		if (unlikely(c == (u)))				\
15262306a36Sopenharmony_ci			break;					\
15362306a36Sopenharmony_ci		old = local_cmpxchg((l), c, c + (a));		\
15462306a36Sopenharmony_ci		if (likely(old == c))				\
15562306a36Sopenharmony_ci			break;					\
15662306a36Sopenharmony_ci		c = old;					\
15762306a36Sopenharmony_ci	}							\
15862306a36Sopenharmony_ci	c != (u);						\
15962306a36Sopenharmony_ci})
16062306a36Sopenharmony_ci#define local_inc_not_zero(l) local_add_unless((l), 1, 0)
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/* On x86_32, these are no better than the atomic variants.
16362306a36Sopenharmony_ci * On x86-64 these are better than the atomic variants on SMP kernels
16462306a36Sopenharmony_ci * because they dont use a lock prefix.
16562306a36Sopenharmony_ci */
16662306a36Sopenharmony_ci#define __local_inc(l)		local_inc(l)
16762306a36Sopenharmony_ci#define __local_dec(l)		local_dec(l)
16862306a36Sopenharmony_ci#define __local_add(i, l)	local_add((i), (l))
16962306a36Sopenharmony_ci#define __local_sub(i, l)	local_sub((i), (l))
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci#endif /* _ASM_X86_LOCAL_H */
172