linux/arch/x86/lib/atomic64_32.c
Eric Dumazet aacf682fd8 x86: atomic64: Improve atomic64_read()
Linus noticed that the 32-bit version of atomic64_read() was
being overly complex with re-reading the value and doing a
retry loop over that.

Instead we can just rely on cmpxchg8b returning either the new
value or returning the current value.

We can use any 'old' value, which will be faster as it can be
loaded via immediates. Using some value that is not equal to
the real value in memory the instruction gets faster.

This also has the advantage that the CPU could avoid dirtying
the cacheline.

Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: David Howells <dhowells@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Arnd Bergmann <arnd@arndb.de>
LKML-Reference: <alpine.LFD.2.01.0907021653030.3210@localhost.localdomain>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
2009-07-03 13:26:40 +02:00

213 lines
4.3 KiB
C

#include <linux/compiler.h>
#include <linux/types.h>
#include <asm/processor.h>
#include <asm/cmpxchg.h>
#include <asm/atomic.h>
static inline u64 cmpxchg8b(u64 *ptr, u64 old, u64 new)
{
asm volatile(
LOCK_PREFIX "cmpxchg8b (%[ptr])\n"
: "=A" (old)
: [ptr] "D" (ptr),
"A" (old),
"b" (ll_low(new)),
"c" (ll_high(new))
: "memory");
return old;
}
u64 atomic64_cmpxchg(atomic64_t *ptr, u64 old_val, u64 new_val)
{
return cmpxchg8b(&ptr->counter, old_val, new_val);
}
/**
* atomic64_xchg - xchg atomic64 variable
* @ptr: pointer to type atomic64_t
* @new_val: value to assign
*
* Atomically xchgs the value of @ptr to @new_val and returns
* the old value.
*/
u64 atomic64_xchg(atomic64_t *ptr, u64 new_val)
{
u64 old_val;
do {
old_val = atomic_read(ptr);
} while (atomic64_cmpxchg(ptr, old_val, new_val) != old_val);
return old_val;
}
/**
* atomic64_set - set atomic64 variable
* @ptr: pointer to type atomic64_t
* @new_val: value to assign
*
* Atomically sets the value of @ptr to @new_val.
*/
void atomic64_set(atomic64_t *ptr, u64 new_val)
{
atomic64_xchg(ptr, new_val);
}
/**
* atomic64_read - read atomic64 variable
* @ptr: pointer to type atomic64_t
*
* Atomically reads the value of @ptr and returns it.
*/
u64 atomic64_read(atomic64_t *ptr)
{
u64 old = 1LL << 32;
return cmpxchg8b(&ptr->counter, old, old);
}
/**
* atomic64_add_return - add and return
* @delta: integer value to add
* @ptr: pointer to type atomic64_t
*
* Atomically adds @delta to @ptr and returns @delta + *@ptr
*/
u64 atomic64_add_return(u64 delta, atomic64_t *ptr)
{
u64 old_val, new_val;
do {
old_val = atomic_read(ptr);
new_val = old_val + delta;
} while (atomic64_cmpxchg(ptr, old_val, new_val) != old_val);
return new_val;
}
u64 atomic64_sub_return(u64 delta, atomic64_t *ptr)
{
return atomic64_add_return(-delta, ptr);
}
u64 atomic64_inc_return(atomic64_t *ptr)
{
return atomic64_add_return(1, ptr);
}
u64 atomic64_dec_return(atomic64_t *ptr)
{
return atomic64_sub_return(1, ptr);
}
/**
* atomic64_add - add integer to atomic64 variable
* @delta: integer value to add
* @ptr: pointer to type atomic64_t
*
* Atomically adds @delta to @ptr.
*/
void atomic64_add(u64 delta, atomic64_t *ptr)
{
atomic64_add_return(delta, ptr);
}
/**
* atomic64_sub - subtract the atomic64 variable
* @delta: integer value to subtract
* @ptr: pointer to type atomic64_t
*
* Atomically subtracts @delta from @ptr.
*/
void atomic64_sub(u64 delta, atomic64_t *ptr)
{
atomic64_add(-delta, ptr);
}
/**
* atomic64_sub_and_test - subtract value from variable and test result
* @delta: integer value to subtract
* @ptr: pointer to type atomic64_t
*
* Atomically subtracts @delta from @ptr and returns
* true if the result is zero, or false for all
* other cases.
*/
int atomic64_sub_and_test(u64 delta, atomic64_t *ptr)
{
u64 old_val = atomic64_sub_return(delta, ptr);
return old_val == 0;
}
/**
* atomic64_inc - increment atomic64 variable
* @ptr: pointer to type atomic64_t
*
* Atomically increments @ptr by 1.
*/
void atomic64_inc(atomic64_t *ptr)
{
atomic64_add(1, ptr);
}
/**
* atomic64_dec - decrement atomic64 variable
* @ptr: pointer to type atomic64_t
*
* Atomically decrements @ptr by 1.
*/
void atomic64_dec(atomic64_t *ptr)
{
atomic64_sub(1, ptr);
}
/**
* atomic64_dec_and_test - decrement and test
* @ptr: pointer to type atomic64_t
*
* Atomically decrements @ptr by 1 and
* returns true if the result is 0, or false for all other
* cases.
*/
int atomic64_dec_and_test(atomic64_t *ptr)
{
return atomic64_sub_and_test(1, ptr);
}
/**
* atomic64_inc_and_test - increment and test
* @ptr: pointer to type atomic64_t
*
* Atomically increments @ptr by 1
* and returns true if the result is zero, or false for all
* other cases.
*/
int atomic64_inc_and_test(atomic64_t *ptr)
{
return atomic64_sub_and_test(-1, ptr);
}
/**
* atomic64_add_negative - add and test if negative
* @delta: integer value to add
* @ptr: pointer to type atomic64_t
*
* Atomically adds @delta to @ptr and returns true
* if the result is negative, or false when
* result is greater than or equal to zero.
*/
int atomic64_add_negative(u64 delta, atomic64_t *ptr)
{
long long old_val = atomic64_add_return(delta, ptr);
return old_val < 0;
}