atomic.h 6.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
#ifndef __ARCH_I386_ATOMIC__
#define __ARCH_I386_ATOMIC__

#include <linux/compiler.h>
#include <asm/processor.h>
J
Jeff Dike 已提交
6
#include <asm/cmpxchg.h>
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16 17

/*
 * Atomic operations that C can't guarantee us.  Useful for
 * resource counting etc..
 */

/*
 * Make sure gcc doesn't try to be clever and move things around
 * on us. We need to use _exactly_ the address the user gave us,
 * not some alias that contains the same information.
 */
18
typedef struct { int counter; } atomic_t;
L
Linus Torvalds 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

#define ATOMIC_INIT(i)	{ (i) }

/**
 * atomic_read - read atomic variable
 * @v: pointer of type atomic_t
 * 
 * Atomically reads the value of @v.
 */ 
#define atomic_read(v)		((v)->counter)

/**
 * atomic_set - set atomic variable
 * @v: pointer of type atomic_t
 * @i: required value
 * 
 * Atomically sets the value of @v to @i.
 */ 
#define atomic_set(v,i)		(((v)->counter) = (i))

/**
 * atomic_add - add integer to atomic variable
 * @i: integer value to add
 * @v: pointer of type atomic_t
 * 
 * Atomically adds @i to @v.
 */
static __inline__ void atomic_add(int i, atomic_t *v)
{
	__asm__ __volatile__(
G
Gerd Hoffmann 已提交
49
		LOCK_PREFIX "addl %1,%0"
50 51
		:"+m" (v->counter)
		:"ir" (i));
L
Linus Torvalds 已提交
52 53 54
}

/**
55
 * atomic_sub - subtract integer from atomic variable
L
Linus Torvalds 已提交
56 57 58 59 60 61 62 63
 * @i: integer value to subtract
 * @v: pointer of type atomic_t
 * 
 * Atomically subtracts @i from @v.
 */
static __inline__ void atomic_sub(int i, atomic_t *v)
{
	__asm__ __volatile__(
G
Gerd Hoffmann 已提交
64
		LOCK_PREFIX "subl %1,%0"
65 66
		:"+m" (v->counter)
		:"ir" (i));
L
Linus Torvalds 已提交
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
}

/**
 * atomic_sub_and_test - subtract value from variable and test result
 * @i: integer value to subtract
 * @v: pointer of type atomic_t
 * 
 * Atomically subtracts @i from @v and returns
 * true if the result is zero, or false for all
 * other cases.
 */
static __inline__ int atomic_sub_and_test(int i, atomic_t *v)
{
	unsigned char c;

	__asm__ __volatile__(
G
Gerd Hoffmann 已提交
83
		LOCK_PREFIX "subl %2,%0; sete %1"
84 85
		:"+m" (v->counter), "=qm" (c)
		:"ir" (i) : "memory");
L
Linus Torvalds 已提交
86 87 88 89 90 91 92 93 94 95 96 97
	return c;
}

/**
 * atomic_inc - increment atomic variable
 * @v: pointer of type atomic_t
 * 
 * Atomically increments @v by 1.
 */ 
static __inline__ void atomic_inc(atomic_t *v)
{
	__asm__ __volatile__(
G
Gerd Hoffmann 已提交
98
		LOCK_PREFIX "incl %0"
99
		:"+m" (v->counter));
L
Linus Torvalds 已提交
100 101 102 103 104 105 106 107 108 109 110
}

/**
 * atomic_dec - decrement atomic variable
 * @v: pointer of type atomic_t
 * 
 * Atomically decrements @v by 1.
 */ 
static __inline__ void atomic_dec(atomic_t *v)
{
	__asm__ __volatile__(
G
Gerd Hoffmann 已提交
111
		LOCK_PREFIX "decl %0"
112
		:"+m" (v->counter));
L
Linus Torvalds 已提交
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
}

/**
 * atomic_dec_and_test - decrement and test
 * @v: pointer of type atomic_t
 * 
 * Atomically decrements @v by 1 and
 * returns true if the result is 0, or false for all other
 * cases.
 */ 
static __inline__ int atomic_dec_and_test(atomic_t *v)
{
	unsigned char c;

	__asm__ __volatile__(
G
Gerd Hoffmann 已提交
128
		LOCK_PREFIX "decl %0; sete %1"
129 130
		:"+m" (v->counter), "=qm" (c)
		: : "memory");
L
Linus Torvalds 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
	return c != 0;
}

/**
 * atomic_inc_and_test - increment and test 
 * @v: pointer of type atomic_t
 * 
 * Atomically increments @v by 1
 * and returns true if the result is zero, or false for all
 * other cases.
 */ 
static __inline__ int atomic_inc_and_test(atomic_t *v)
{
	unsigned char c;

	__asm__ __volatile__(
G
Gerd Hoffmann 已提交
147
		LOCK_PREFIX "incl %0; sete %1"
148 149
		:"+m" (v->counter), "=qm" (c)
		: : "memory");
L
Linus Torvalds 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
	return c != 0;
}

/**
 * atomic_add_negative - add and test if negative
 * @v: pointer of type atomic_t
 * @i: integer value to add
 * 
 * Atomically adds @i to @v and returns true
 * if the result is negative, or false when
 * result is greater than or equal to zero.
 */ 
static __inline__ int atomic_add_negative(int i, atomic_t *v)
{
	unsigned char c;

	__asm__ __volatile__(
G
Gerd Hoffmann 已提交
167
		LOCK_PREFIX "addl %2,%0; sets %1"
168 169
		:"+m" (v->counter), "=qm" (c)
		:"ir" (i) : "memory");
L
Linus Torvalds 已提交
170 171 172 173
	return c;
}

/**
174
 * atomic_add_return - add integer and return
L
Linus Torvalds 已提交
175 176 177 178 179 180 181 182 183
 * @v: pointer of type atomic_t
 * @i: integer value to add
 *
 * Atomically adds @i to @v and returns @i + @v
 */
static __inline__ int atomic_add_return(int i, atomic_t *v)
{
	int __i;
#ifdef CONFIG_M386
184
	unsigned long flags;
185
	if(unlikely(boot_cpu_data.x86 <= 3))
L
Linus Torvalds 已提交
186 187 188 189 190
		goto no_xadd;
#endif
	/* Modern 486+ processor */
	__i = i;
	__asm__ __volatile__(
191 192 193
		LOCK_PREFIX "xaddl %0, %1"
		:"+r" (i), "+m" (v->counter)
		: : "memory");
L
Linus Torvalds 已提交
194 195 196 197
	return i + __i;

#ifdef CONFIG_M386
no_xadd: /* Legacy 386 processor */
198
	local_irq_save(flags);
L
Linus Torvalds 已提交
199 200
	__i = atomic_read(v);
	atomic_set(v, i + __i);
201
	local_irq_restore(flags);
L
Linus Torvalds 已提交
202 203 204 205
	return i + __i;
#endif
}

206 207 208 209 210 211 212
/**
 * atomic_sub_return - subtract integer and return
 * @v: pointer of type atomic_t
 * @i: integer value to subtract
 *
 * Atomically subtracts @i from @v and returns @v - @i
 */
L
Linus Torvalds 已提交
213 214 215 216 217
static __inline__ int atomic_sub_return(int i, atomic_t *v)
{
	return atomic_add_return(-i,v);
}

218 219
#define atomic_cmpxchg(v, old, new) (cmpxchg(&((v)->counter), (old), (new)))
#define atomic_xchg(v, new) (xchg(&((v)->counter), (new)))
N
Nick Piggin 已提交
220

N
Nick Piggin 已提交
221
/**
222
 * atomic_add_unless - add unless the number is already a given value
N
Nick Piggin 已提交
223 224 225 226
 * @v: pointer of type atomic_t
 * @a: the amount to add to v...
 * @u: ...unless v is equal to u.
 *
227
 * Atomically adds @a to @v, so long as @v was not already @u.
N
Nick Piggin 已提交
228 229
 * Returns non-zero if @v was not @u, and zero otherwise.
 */
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
static __inline__ int atomic_add_unless(atomic_t *v, int a, int u)
{
	int c, old;
	c = atomic_read(v);
	for (;;) {
		if (unlikely(c == (u)))
			break;
		old = atomic_cmpxchg((v), c, c + (a));
		if (likely(old == c))
			break;
		c = old;
	}
	return c != (u);
}

N
Nick Piggin 已提交
245 246
#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)

L
Linus Torvalds 已提交
247 248 249 250 251
#define atomic_inc_return(v)  (atomic_add_return(1,v))
#define atomic_dec_return(v)  (atomic_sub_return(1,v))

/* These are x86-specific, used by some header files */
#define atomic_clear_mask(mask, addr) \
G
Gerd Hoffmann 已提交
252
__asm__ __volatile__(LOCK_PREFIX "andl %0,%1" \
L
Linus Torvalds 已提交
253 254 255
: : "r" (~(mask)),"m" (*addr) : "memory")

#define atomic_set_mask(mask, addr) \
G
Gerd Hoffmann 已提交
256
__asm__ __volatile__(LOCK_PREFIX "orl %0,%1" \
L
Linus Torvalds 已提交
257 258 259 260 261 262 263 264
: : "r" (mask),"m" (*(addr)) : "memory")

/* Atomic operations are already serializing on x86 */
#define smp_mb__before_atomic_dec()	barrier()
#define smp_mb__after_atomic_dec()	barrier()
#define smp_mb__before_atomic_inc()	barrier()
#define smp_mb__after_atomic_inc()	barrier()

265
#include <asm-generic/atomic.h>
L
Linus Torvalds 已提交
266
#endif