|  | /* 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; | 
|  | // XXX this this OK? | 
|  | 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; | 
|  | } |