blob: 54dca376c96f9ece13ef1da196aa525a63dd2651 [file] [log] [blame]
/* Copyright (c) 2009-2011 The Regents of the University of California
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*
* x86 atomics and locking functions. */
#pragma once
#include <ros/common.h>
#include <arch/membar.h>
#include <arch/x86.h>
#include <arch/arch.h>
static inline void atomic_andb(volatile uint8_t *number, uint8_t mask);
static inline void atomic_orb(volatile uint8_t *number, uint8_t mask);
/* Inlined functions declared above */
static inline void atomic_init(atomic_t *number, long val)
{
asm volatile("mov %1,%0" : "=m"(*number) : "r"(val));
}
static inline long atomic_read(atomic_t *number)
{
long val;
asm volatile("mov %1,%0" : "=r"(val) : "m"(*number));
return val;
}
static inline void atomic_set(atomic_t *number, long val)
{
asm volatile("mov %1,%0" : "=m"(*number) : "r"(val));
}
static inline void atomic_add(atomic_t *number, long val)
{
__sync_fetch_and_add(number, val);
}
static inline void atomic_inc(atomic_t *number)
{
__sync_fetch_and_add(number, 1);
}
static inline void atomic_dec(atomic_t *number)
{
__sync_fetch_and_sub(number, 1);
}
static inline long atomic_fetch_and_add(atomic_t *number, long val)
{
return (long)__sync_fetch_and_add(number, val);
}
static inline void atomic_and(atomic_t *number, long mask)
{
__sync_fetch_and_and(number, mask);
}
static inline void atomic_or(atomic_t *number, long mask)
{
__sync_fetch_and_or(number, mask);
}
static inline long atomic_swap(atomic_t *addr, long val)
{
/* This poorly named function does an xchg */
return (long)__sync_lock_test_and_set(addr, val);
}
static inline void *atomic_swap_ptr(void **addr, void *val)
{
return (void*)__sync_lock_test_and_set(addr, val);
}
static inline bool atomic_cas(atomic_t *addr, long exp_val, long new_val)
{
return __sync_bool_compare_and_swap(addr, exp_val, new_val);
}
static inline bool atomic_cas_ptr(void **addr, void *exp_val, void *new_val)
{
return __sync_bool_compare_and_swap(addr, exp_val, new_val);
}
static inline bool atomic_cas_u32(uint32_t *addr, uint32_t exp_val,
uint32_t new_val)
{
return __sync_bool_compare_and_swap(addr, exp_val, new_val);
}
/* Adds val to number, so long as number was not zero. Returns TRUE if the
* operation succeeded (added, not zero), returns FALSE if number is zero. */
static inline bool atomic_add_not_zero(atomic_t *number, long val)
{
long old_num, new_num;
do {
old_num = atomic_read(number);
if (!old_num)
return FALSE;
new_num = old_num + val;
} while (!atomic_cas(number, old_num, new_num));
return TRUE;
}
/* Subtracts val from number, returning True if the new value is 0. */
static inline bool atomic_sub_and_test(atomic_t *number, long val)
{
bool b;
asm volatile("lock sub %2,%1; setz %0" : "=q"(b), "=m"(*number)
: "r"(val), "m"(*number)
: "cc" );
return b;
}
static inline void atomic_andb(volatile uint8_t *number, uint8_t mask)
{
__sync_fetch_and_and(number, mask);
}
static inline void atomic_orb(volatile uint8_t *number, uint8_t mask)
{
__sync_fetch_and_or(number, mask);
}
static inline bool spin_locked(spinlock_t *lock)
{
// the lock status is the lowest byte of the lock
return lock->rlock & 0xff;
}
static inline void __spin_lock_raw(volatile uint32_t *rlock)
{
uint8_t dicks = 0;
asm volatile("1: "
" cmpb $0, %0; "
" je 2f; "
" pause; "
" jmp 1b; "
"2: "
" movb $1, %1; "
" xchgb %1, %0; "
" cmpb $0, %1; "
" jne 1b; "
: : "m"(*rlock), "r"(dicks) : "cc");
cmb(); /* need cmb(), the CPU mb() was handled by the xchg */
}
static inline void __spin_lock(spinlock_t *lock)
{
__spin_lock_raw(&lock->rlock);
}
static inline bool __spin_trylock(spinlock_t *lock)
{
/* since this is an or, we're not going to clobber the top bytes (if
* that matters) */
return !__sync_fetch_and_or(&lock->rlock, 1);
}
static inline void __spin_unlock(spinlock_t *lock)
{
/* Need to prevent the compiler from reordering older stores. */
wmb();
rwmb(); /* x86 makes both of these a cmb() */
lock->rlock = 0;
}
static inline void __spinlock_init(spinlock_t *lock)
{
lock->rlock = 0;
}