atomic.h 5.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#ifndef __ARCH_I386_ATOMIC__
#define __ARCH_I386_ATOMIC__

#include <linux/compiler.h>
#include <asm/processor.h>

/*
 * 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.
 */
17
typedef struct { int counter; } atomic_t;
L
Linus Torvalds 已提交
18 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

#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 已提交
48
		LOCK_PREFIX "addl %1,%0"
49 50
		:"+m" (v->counter)
		:"ir" (i));
L
Linus Torvalds 已提交
51 52 53 54 55 56 57 58 59 60 61 62
}

/**
 * atomic_sub - subtract the atomic variable
 * @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 已提交
63
		LOCK_PREFIX "subl %1,%0"
64 65
		:"+m" (v->counter)
		:"ir" (i));
L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
}

/**
 * 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 已提交
82
		LOCK_PREFIX "subl %2,%0; sete %1"
83 84
		:"+m" (v->counter), "=qm" (c)
		:"ir" (i) : "memory");
L
Linus Torvalds 已提交
85 86 87 88 89 90 91 92 93 94 95 96
	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 已提交
97
		LOCK_PREFIX "incl %0"
98
		:"+m" (v->counter));
L
Linus Torvalds 已提交
99 100 101 102 103 104 105 106 107 108 109
}

/**
 * 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 已提交
110
		LOCK_PREFIX "decl %0"
111
		:"+m" (v->counter));
L
Linus Torvalds 已提交
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
}

/**
 * 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 已提交
127
		LOCK_PREFIX "decl %0; sete %1"
128 129
		:"+m" (v->counter), "=qm" (c)
		: : "memory");
L
Linus Torvalds 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
	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 已提交
146
		LOCK_PREFIX "incl %0; sete %1"
147 148
		:"+m" (v->counter), "=qm" (c)
		: : "memory");
L
Linus Torvalds 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
	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 已提交
166
		LOCK_PREFIX "addl %2,%0; sets %1"
167 168
		:"+m" (v->counter), "=qm" (c)
		:"ir" (i) : "memory");
L
Linus Torvalds 已提交
169 170 171 172 173 174 175 176 177 178 179 180 181 182
	return c;
}

/**
 * atomic_add_return - add and return
 * @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
183
	unsigned long flags;
L
Linus Torvalds 已提交
184 185 186 187 188 189
	if(unlikely(boot_cpu_data.x86==3))
		goto no_xadd;
#endif
	/* Modern 486+ processor */
	__i = i;
	__asm__ __volatile__(
G
Gerd Hoffmann 已提交
190
		LOCK_PREFIX "xaddl %0, %1;"
L
Linus Torvalds 已提交
191 192 193 194 195 196
		:"=r"(i)
		:"m"(v->counter), "0"(i));
	return i + __i;

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

static __inline__ int atomic_sub_return(int i, atomic_t *v)
{
	return atomic_add_return(-i,v);
}

N
Nick Piggin 已提交
210
#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new))
211
#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
N
Nick Piggin 已提交
212

N
Nick Piggin 已提交
213 214 215 216 217 218 219 220 221 222 223 224 225
/**
 * atomic_add_unless - add unless the number is a given value
 * @v: pointer of type atomic_t
 * @a: the amount to add to v...
 * @u: ...unless v is equal to u.
 *
 * Atomically adds @a to @v, so long as it was not @u.
 * Returns non-zero if @v was not @u, and zero otherwise.
 */
#define atomic_add_unless(v, a, u)				\
({								\
	int c, old;						\
	c = atomic_read(v);					\
226 227 228 229 230 231
	for (;;) {						\
		if (unlikely(c == (u)))				\
			break;					\
		old = atomic_cmpxchg((v), c, c + (a));		\
		if (likely(old == c))				\
			break;					\
N
Nick Piggin 已提交
232
		c = old;					\
233
	}							\
N
Nick Piggin 已提交
234 235 236 237
	c != (u);						\
})
#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)

L
Linus Torvalds 已提交
238 239 240 241 242
#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 已提交
243
__asm__ __volatile__(LOCK_PREFIX "andl %0,%1" \
L
Linus Torvalds 已提交
244 245 246
: : "r" (~(mask)),"m" (*addr) : "memory")

#define atomic_set_mask(mask, addr) \
G
Gerd Hoffmann 已提交
247
__asm__ __volatile__(LOCK_PREFIX "orl %0,%1" \
L
Linus Torvalds 已提交
248 249 250 251 252 253 254 255
: : "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()

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