local.h 4.96 KB
Newer Older
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
1 2
#ifndef _ARCH_MIPS_LOCAL_H
#define _ARCH_MIPS_LOCAL_H
Linus Torvalds's avatar
Linus Torvalds committed
3 4

#include <linux/percpu.h>
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
5
#include <linux/bitops.h>
Arun Sharma's avatar
Arun Sharma committed
6
#include <linux/atomic.h>
7
#include <asm/cmpxchg.h>
8
#include <asm/compiler.h>
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
9
#include <asm/war.h>
Linus Torvalds's avatar
Linus Torvalds committed
10

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
11 12 13 14
typedef struct
{
	atomic_long_t a;
} local_t;
Linus Torvalds's avatar
Linus Torvalds committed
15

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
16
#define LOCAL_INIT(i)	{ ATOMIC_LONG_INIT(i) }
Linus Torvalds's avatar
Linus Torvalds committed
17

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
18
#define local_read(l)	atomic_long_read(&(l)->a)
Ralf Baechle's avatar
Ralf Baechle committed
19
#define local_set(l, i) atomic_long_set(&(l)->a, (i))
Linus Torvalds's avatar
Linus Torvalds committed
20

Ralf Baechle's avatar
Ralf Baechle committed
21 22
#define local_add(i, l) atomic_long_add((i), (&(l)->a))
#define local_sub(i, l) atomic_long_sub((i), (&(l)->a))
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
23 24
#define local_inc(l)	atomic_long_inc(&(l)->a)
#define local_dec(l)	atomic_long_dec(&(l)->a)
Linus Torvalds's avatar
Linus Torvalds committed
25

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
26 27 28 29 30 31 32
/*
 * Same as above, but return the result value
 */
static __inline__ long local_add_return(long i, local_t * l)
{
	unsigned long result;

33
	if (kernel_uses_llsc && R10000_LLSC_WAR) {
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
34 35 36
		unsigned long temp;

		__asm__ __volatile__(
37
		"	.set	arch=r4000				\n"
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
38 39 40 41 42 43 44 45 46
		"1:"	__LL	"%1, %2		# local_add_return	\n"
		"	addu	%0, %1, %3				\n"
			__SC	"%0, %2					\n"
		"	beqzl	%0, 1b					\n"
		"	addu	%0, %1, %3				\n"
		"	.set	mips0					\n"
		: "=&r" (result), "=&r" (temp), "=m" (l->a.counter)
		: "Ir" (i), "m" (l->a.counter)
		: "memory");
47
	} else if (kernel_uses_llsc) {
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
48 49 50
		unsigned long temp;

		__asm__ __volatile__(
51
		"	.set	"MIPS_ISA_ARCH_LEVEL"			\n"
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
52 53 54 55 56 57 58 59 60 61 62
		"1:"	__LL	"%1, %2		# local_add_return	\n"
		"	addu	%0, %1, %3				\n"
			__SC	"%0, %2					\n"
		"	beqz	%0, 1b					\n"
		"	addu	%0, %1, %3				\n"
		"	.set	mips0					\n"
		: "=&r" (result), "=&r" (temp), "=m" (l->a.counter)
		: "Ir" (i), "m" (l->a.counter)
		: "memory");
	} else {
		unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
63

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
64 65 66 67 68 69
		local_irq_save(flags);
		result = l->a.counter;
		result += i;
		l->a.counter = result;
		local_irq_restore(flags);
	}
Linus Torvalds's avatar
Linus Torvalds committed
70

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
71 72
	return result;
}
Linus Torvalds's avatar
Linus Torvalds committed
73

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
74 75 76
static __inline__ long local_sub_return(long i, local_t * l)
{
	unsigned long result;
Linus Torvalds's avatar
Linus Torvalds committed
77

78
	if (kernel_uses_llsc && R10000_LLSC_WAR) {
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
79
		unsigned long temp;
Linus Torvalds's avatar
Linus Torvalds committed
80

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
81
		__asm__ __volatile__(
82
		"	.set	arch=r4000				\n"
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
83 84 85 86 87 88 89 90 91
		"1:"	__LL	"%1, %2		# local_sub_return	\n"
		"	subu	%0, %1, %3				\n"
			__SC	"%0, %2					\n"
		"	beqzl	%0, 1b					\n"
		"	subu	%0, %1, %3				\n"
		"	.set	mips0					\n"
		: "=&r" (result), "=&r" (temp), "=m" (l->a.counter)
		: "Ir" (i), "m" (l->a.counter)
		: "memory");
92
	} else if (kernel_uses_llsc) {
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
93
		unsigned long temp;
Linus Torvalds's avatar
Linus Torvalds committed
94

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
95
		__asm__ __volatile__(
96
		"	.set	"MIPS_ISA_ARCH_LEVEL"			\n"
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
		"1:"	__LL	"%1, %2		# local_sub_return	\n"
		"	subu	%0, %1, %3				\n"
			__SC	"%0, %2					\n"
		"	beqz	%0, 1b					\n"
		"	subu	%0, %1, %3				\n"
		"	.set	mips0					\n"
		: "=&r" (result), "=&r" (temp), "=m" (l->a.counter)
		: "Ir" (i), "m" (l->a.counter)
		: "memory");
	} else {
		unsigned long flags;

		local_irq_save(flags);
		result = l->a.counter;
		result -= i;
		l->a.counter = result;
		local_irq_restore(flags);
	}

	return result;
}
Linus Torvalds's avatar
Linus Torvalds committed
118

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
119 120
#define local_cmpxchg(l, o, n) \
	((long)cmpxchg_local(&((l)->a.counter), (o), (n)))
121
#define local_xchg(l, n) (atomic_long_xchg((&(l)->a), (n)))
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

/**
 * local_add_unless - add unless the number is a given value
 * @l: pointer of type local_t
 * @a: the amount to add to l...
 * @u: ...unless l is equal to u.
 *
 * Atomically adds @a to @l, so long as it was not @u.
 * Returns non-zero if @l was not @u, and zero otherwise.
 */
#define local_add_unless(l, a, u)				\
({								\
	long c, old;						\
	c = local_read(l);					\
	while (c != (u) && (old = local_cmpxchg((l), c, c + (a))) != c) \
		c = old;					\
	c != (u);						\
})
#define local_inc_not_zero(l) local_add_unless((l), 1, 0)

142 143
#define local_dec_return(l) local_sub_return(1, (l))
#define local_inc_return(l) local_add_return(1, (l))
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
144 145 146 147 148 149 150 151 152 153

/*
 * local_sub_and_test - subtract value from variable and test result
 * @i: integer value to subtract
 * @l: pointer of type local_t
 *
 * Atomically subtracts @i from @l and returns
 * true if the result is zero, or false for all
 * other cases.
 */
154
#define local_sub_and_test(i, l) (local_sub_return((i), (l)) == 0)
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

/*
 * local_inc_and_test - increment and test
 * @l: pointer of type local_t
 *
 * Atomically increments @l by 1
 * and returns true if the result is zero, or false for all
 * other cases.
 */
#define local_inc_and_test(l) (local_inc_return(l) == 0)

/*
 * local_dec_and_test - decrement by 1 and test
 * @l: pointer of type local_t
 *
 * Atomically decrements @l by 1 and
 * returns true if the result is 0, or false for all other
 * cases.
 */
#define local_dec_and_test(l) (local_sub_return(1, (l)) == 0)

/*
 * local_add_negative - add and test if negative
 * @l: pointer of type local_t
 * @i: integer value to add
 *
 * Atomically adds @i to @l and returns true
 * if the result is negative, or false when
 * result is greater than or equal to zero.
 */
185
#define local_add_negative(i, l) (local_add_return(i, (l)) < 0)
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
186 187

/* Use these for per-cpu local_t variables: on some archs they are
Linus Torvalds's avatar
Linus Torvalds committed
188 189 190 191
 * much more efficient than these naive implementations.  Note they take
 * a variable, not an address.
 */

Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
192 193
#define __local_inc(l)		((l)->a.counter++)
#define __local_dec(l)		((l)->a.counter++)
194 195
#define __local_add(i, l)	((l)->a.counter+=(i))
#define __local_sub(i, l)	((l)->a.counter-=(i))
Mathieu Desnoyers's avatar
Mathieu Desnoyers committed
196 197

#endif /* _ARCH_MIPS_LOCAL_H */