/***********************license start*************** * Copyright (c) 2003-2010 Cavium Networks (support@cavium.com). All rights * reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of Cavium Networks nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * This Software, including technical data, may be subject to U.S. export control * laws, including the U.S. Export Administration Act and its associated * regulations, and may be subject to export or import regulations in other * countries. * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" * AND WITH ALL FAULTS AND CAVIUM NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE, * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK ARISING OUT OF USE OR * PERFORMANCE OF THE SOFTWARE LIES WITH YOU. ***********************license end**************************************/ /** * @file * * This file provides atomic operations * *
$Revision: 49448 $
* * */ #ifndef __CVMX_ATOMIC_H__ #define __CVMX_ATOMIC_H__ #ifdef __cplusplus extern "C" { #endif /** * Atomically adds a signed value to a 32 bit (aligned) memory location. * * This version does not perform 'sync' operations to enforce memory * operations. This should only be used when there are no memory operation * ordering constraints. (This should NOT be used for reference counting - * use the standard version instead.) * * @param ptr address in memory to add incr to * @param incr amount to increment memory location by (signed) */ static inline void cvmx_atomic_add32_nosync(int32_t *ptr, int32_t incr) { if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) { uint32_t tmp; __asm__ __volatile__( ".set noreorder \n" "1: ll %[tmp], %[val] \n" " addu %[tmp], %[inc] \n" " sc %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp) : [inc] "r" (incr) : "memory"); } else { __asm__ __volatile__( " saa %[inc], (%[base]) \n" : "+m" (*ptr) : [inc] "r" (incr), [base] "r" (ptr) : "memory"); } } /** * Atomically adds a signed value to a 32 bit (aligned) memory location. * * Memory access ordering is enforced before/after the atomic operation, * so no additional 'sync' instructions are required. * * * @param ptr address in memory to add incr to * @param incr amount to increment memory location by (signed) */ static inline void cvmx_atomic_add32(int32_t *ptr, int32_t incr) { CVMX_SYNCWS; cvmx_atomic_add32_nosync(ptr, incr); CVMX_SYNCWS; } /** * Atomically sets a 32 bit (aligned) memory location to a value * * @param ptr address of memory to set * @param value value to set memory location to. */ static inline void cvmx_atomic_set32(int32_t *ptr, int32_t value) { CVMX_SYNCWS; *ptr = value; CVMX_SYNCWS; } /** * Returns the current value of a 32 bit (aligned) memory * location. * * @param ptr Address of memory to get * @return Value of the memory */ static inline int32_t cvmx_atomic_get32(int32_t *ptr) { return *(volatile int32_t *)ptr; } /** * Atomically adds a signed value to a 64 bit (aligned) memory location. * * This version does not perform 'sync' operations to enforce memory * operations. This should only be used when there are no memory operation * ordering constraints. (This should NOT be used for reference counting - * use the standard version instead.) * * @param ptr address in memory to add incr to * @param incr amount to increment memory location by (signed) */ static inline void cvmx_atomic_add64_nosync(int64_t *ptr, int64_t incr) { if (OCTEON_IS_MODEL(OCTEON_CN3XXX)) { uint64_t tmp; __asm__ __volatile__( ".set noreorder \n" "1: lld %[tmp], %[val] \n" " daddu %[tmp], %[inc] \n" " scd %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp) : [inc] "r" (incr) : "memory"); } else { __asm__ __volatile__( " saad %[inc], (%[base]) \n" : "+m" (*ptr) : [inc] "r" (incr), [base] "r" (ptr) : "memory"); } } /** * Atomically adds a signed value to a 64 bit (aligned) memory location. * * Memory access ordering is enforced before/after the atomic operation, * so no additional 'sync' instructions are required. * * * @param ptr address in memory to add incr to * @param incr amount to increment memory location by (signed) */ static inline void cvmx_atomic_add64(int64_t *ptr, int64_t incr) { CVMX_SYNCWS; cvmx_atomic_add64_nosync(ptr, incr); CVMX_SYNCWS; } /** * Atomically sets a 64 bit (aligned) memory location to a value * * @param ptr address of memory to set * @param value value to set memory location to. */ static inline void cvmx_atomic_set64(int64_t *ptr, int64_t value) { CVMX_SYNCWS; *ptr = value; CVMX_SYNCWS; } /** * Returns the current value of a 64 bit (aligned) memory * location. * * @param ptr Address of memory to get * @return Value of the memory */ static inline int64_t cvmx_atomic_get64(int64_t *ptr) { return *(volatile int64_t *)ptr; } /** * Atomically compares the old value with the value at ptr, and if they match, * stores new_val to ptr. * If *ptr and old don't match, function returns failure immediately. * If *ptr and old match, function spins until *ptr updated to new atomically, or * until *ptr and old no longer match * * Does no memory synchronization. * * @return 1 on success (match and store) * 0 on no match */ static inline uint32_t cvmx_atomic_compare_and_store32_nosync(uint32_t *ptr, uint32_t old_val, uint32_t new_val) { uint32_t tmp, ret; __asm__ __volatile__( ".set noreorder \n" "1: ll %[tmp], %[val] \n" " li %[ret], 0 \n" " bne %[tmp], %[old], 2f \n" " move %[tmp], %[new_val] \n" " sc %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " li %[ret], 1 \n" "2: nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp), [ret] "=&r" (ret) : [old] "r" (old_val), [new_val] "r" (new_val) : "memory"); return(ret); } /** * Atomically compares the old value with the value at ptr, and if they match, * stores new_val to ptr. * If *ptr and old don't match, function returns failure immediately. * If *ptr and old match, function spins until *ptr updated to new atomically, or * until *ptr and old no longer match * * Does memory synchronization that is required to use this as a locking primitive. * * @return 1 on success (match and store) * 0 on no match */ static inline uint32_t cvmx_atomic_compare_and_store32(uint32_t *ptr, uint32_t old_val, uint32_t new_val) { uint32_t ret; CVMX_SYNCWS; ret = cvmx_atomic_compare_and_store32_nosync(ptr, old_val, new_val); CVMX_SYNCWS; return ret; } /** * Atomically compares the old value with the value at ptr, and if they match, * stores new_val to ptr. * If *ptr and old don't match, function returns failure immediately. * If *ptr and old match, function spins until *ptr updated to new atomically, or * until *ptr and old no longer match * * Does no memory synchronization. * * @return 1 on success (match and store) * 0 on no match */ static inline uint64_t cvmx_atomic_compare_and_store64_nosync(uint64_t *ptr, uint64_t old_val, uint64_t new_val) { uint64_t tmp, ret; __asm__ __volatile__( ".set noreorder \n" "1: lld %[tmp], %[val] \n" " li %[ret], 0 \n" " bne %[tmp], %[old], 2f \n" " move %[tmp], %[new_val] \n" " scd %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " li %[ret], 1 \n" "2: nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp), [ret] "=&r" (ret) : [old] "r" (old_val), [new_val] "r" (new_val) : "memory"); return(ret); } /** * Atomically compares the old value with the value at ptr, and if they match, * stores new_val to ptr. * If *ptr and old don't match, function returns failure immediately. * If *ptr and old match, function spins until *ptr updated to new atomically, or * until *ptr and old no longer match * * Does memory synchronization that is required to use this as a locking primitive. * * @return 1 on success (match and store) * 0 on no match */ static inline uint64_t cvmx_atomic_compare_and_store64(uint64_t *ptr, uint64_t old_val, uint64_t new_val) { uint64_t ret; CVMX_SYNCWS; ret = cvmx_atomic_compare_and_store64_nosync(ptr, old_val, new_val); CVMX_SYNCWS; return ret; } /** * Atomically adds a signed value to a 64 bit (aligned) memory location, * and returns previous value. * * This version does not perform 'sync' operations to enforce memory * operations. This should only be used when there are no memory operation * ordering constraints. (This should NOT be used for reference counting - * use the standard version instead.) * * @param ptr address in memory to add incr to * @param incr amount to increment memory location by (signed) * * @return Value of memory location before increment */ static inline int64_t cvmx_atomic_fetch_and_add64_nosync(int64_t *ptr, int64_t incr) { uint64_t tmp, ret; #if !defined(__FreeBSD__) || !defined(_KERNEL) if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { CVMX_PUSH_OCTEON2; if (__builtin_constant_p(incr) && incr == 1) { __asm__ __volatile__( "laid %0,(%2)" : "=r" (ret), "+m" (ptr) : "r" (ptr) : "memory"); } else if (__builtin_constant_p(incr) && incr == -1) { __asm__ __volatile__( "ladd %0,(%2)" : "=r" (ret), "+m" (ptr) : "r" (ptr) : "memory"); } else { __asm__ __volatile__( "laad %0,(%2),%3" : "=r" (ret), "+m" (ptr) : "r" (ptr), "r" (incr) : "memory"); } CVMX_POP_OCTEON2; } else { #endif __asm__ __volatile__( ".set noreorder \n" "1: lld %[tmp], %[val] \n" " move %[ret], %[tmp] \n" " daddu %[tmp], %[inc] \n" " scd %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp), [ret] "=&r" (ret) : [inc] "r" (incr) : "memory"); #if !defined(__FreeBSD__) || !defined(_KERNEL) } #endif return (ret); } /** * Atomically adds a signed value to a 64 bit (aligned) memory location, * and returns previous value. * * Memory access ordering is enforced before/after the atomic operation, * so no additional 'sync' instructions are required. * * @param ptr address in memory to add incr to * @param incr amount to increment memory location by (signed) * * @return Value of memory location before increment */ static inline int64_t cvmx_atomic_fetch_and_add64(int64_t *ptr, int64_t incr) { uint64_t ret; CVMX_SYNCWS; ret = cvmx_atomic_fetch_and_add64_nosync(ptr, incr); CVMX_SYNCWS; return ret; } /** * Atomically adds a signed value to a 32 bit (aligned) memory location, * and returns previous value. * * This version does not perform 'sync' operations to enforce memory * operations. This should only be used when there are no memory operation * ordering constraints. (This should NOT be used for reference counting - * use the standard version instead.) * * @param ptr address in memory to add incr to * @param incr amount to increment memory location by (signed) * * @return Value of memory location before increment */ static inline int32_t cvmx_atomic_fetch_and_add32_nosync(int32_t *ptr, int32_t incr) { uint32_t tmp, ret; #if !defined(__FreeBSD__) || !defined(_KERNEL) if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { CVMX_PUSH_OCTEON2; if (__builtin_constant_p(incr) && incr == 1) { __asm__ __volatile__( "lai %0,(%2)" : "=r" (ret), "+m" (ptr) : "r" (ptr) : "memory"); } else if (__builtin_constant_p(incr) && incr == -1) { __asm__ __volatile__( "lad %0,(%2)" : "=r" (ret), "+m" (ptr) : "r" (ptr) : "memory"); } else { __asm__ __volatile__( "laa %0,(%2),%3" : "=r" (ret), "+m" (ptr) : "r" (ptr), "r" (incr) : "memory"); } CVMX_POP_OCTEON2; } else { #endif __asm__ __volatile__( ".set noreorder \n" "1: ll %[tmp], %[val] \n" " move %[ret], %[tmp] \n" " addu %[tmp], %[inc] \n" " sc %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp), [ret] "=&r" (ret) : [inc] "r" (incr) : "memory"); #if !defined(__FreeBSD__) || !defined(_KERNEL) } #endif return (ret); } /** * Atomically adds a signed value to a 32 bit (aligned) memory location, * and returns previous value. * * Memory access ordering is enforced before/after the atomic operation, * so no additional 'sync' instructions are required. * * @param ptr address in memory to add incr to * @param incr amount to increment memory location by (signed) * * @return Value of memory location before increment */ static inline int32_t cvmx_atomic_fetch_and_add32(int32_t *ptr, int32_t incr) { uint32_t ret; CVMX_SYNCWS; ret = cvmx_atomic_fetch_and_add32_nosync(ptr, incr); CVMX_SYNCWS; return ret; } /** * Atomically set bits in a 64 bit (aligned) memory location, * and returns previous value. * * This version does not perform 'sync' operations to enforce memory * operations. This should only be used when there are no memory operation * ordering constraints. * * @param ptr address in memory * @param mask mask of bits to set * * @return Value of memory location before setting bits */ static inline uint64_t cvmx_atomic_fetch_and_bset64_nosync(uint64_t *ptr, uint64_t mask) { uint64_t tmp, ret; __asm__ __volatile__( ".set noreorder \n" "1: lld %[tmp], %[val] \n" " move %[ret], %[tmp] \n" " or %[tmp], %[msk] \n" " scd %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp), [ret] "=&r" (ret) : [msk] "r" (mask) : "memory"); return (ret); } /** * Atomically set bits in a 32 bit (aligned) memory location, * and returns previous value. * * This version does not perform 'sync' operations to enforce memory * operations. This should only be used when there are no memory operation * ordering constraints. * * @param ptr address in memory * @param mask mask of bits to set * * @return Value of memory location before setting bits */ static inline uint32_t cvmx_atomic_fetch_and_bset32_nosync(uint32_t *ptr, uint32_t mask) { uint32_t tmp, ret; __asm__ __volatile__( ".set noreorder \n" "1: ll %[tmp], %[val] \n" " move %[ret], %[tmp] \n" " or %[tmp], %[msk] \n" " sc %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp), [ret] "=&r" (ret) : [msk] "r" (mask) : "memory"); return (ret); } /** * Atomically clear bits in a 64 bit (aligned) memory location, * and returns previous value. * * This version does not perform 'sync' operations to enforce memory * operations. This should only be used when there are no memory operation * ordering constraints. * * @param ptr address in memory * @param mask mask of bits to clear * * @return Value of memory location before clearing bits */ static inline uint64_t cvmx_atomic_fetch_and_bclr64_nosync(uint64_t *ptr, uint64_t mask) { uint64_t tmp, ret; __asm__ __volatile__( ".set noreorder \n" " nor %[msk], 0 \n" "1: lld %[tmp], %[val] \n" " move %[ret], %[tmp] \n" " and %[tmp], %[msk] \n" " scd %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp), [ret] "=&r" (ret), [msk] "+r" (mask) : : "memory"); return (ret); } /** * Atomically clear bits in a 32 bit (aligned) memory location, * and returns previous value. * * This version does not perform 'sync' operations to enforce memory * operations. This should only be used when there are no memory operation * ordering constraints. * * @param ptr address in memory * @param mask mask of bits to clear * * @return Value of memory location before clearing bits */ static inline uint32_t cvmx_atomic_fetch_and_bclr32_nosync(uint32_t *ptr, uint32_t mask) { uint32_t tmp, ret; __asm__ __volatile__( ".set noreorder \n" " nor %[msk], 0 \n" "1: ll %[tmp], %[val] \n" " move %[ret], %[tmp] \n" " and %[tmp], %[msk] \n" " sc %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp), [ret] "=&r" (ret), [msk] "+r" (mask) : : "memory"); return (ret); } /** * Atomically swaps value in 64 bit (aligned) memory location, * and returns previous value. * * This version does not perform 'sync' operations to enforce memory * operations. This should only be used when there are no memory operation * ordering constraints. * * @param ptr address in memory * @param new_val new value to write * * @return Value of memory location before swap operation */ static inline uint64_t cvmx_atomic_swap64_nosync(uint64_t *ptr, uint64_t new_val) { uint64_t tmp, ret; #if !defined(__FreeBSD__) || !defined(_KERNEL) if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { CVMX_PUSH_OCTEON2; if (__builtin_constant_p(new_val) && new_val == 0) { __asm__ __volatile__( "lacd %0,(%1)" : "=r" (ret) : "r" (ptr) : "memory"); } else if (__builtin_constant_p(new_val) && new_val == ~0ull) { __asm__ __volatile__( "lasd %0,(%1)" : "=r" (ret) : "r" (ptr) : "memory"); } else { __asm__ __volatile__( "lawd %0,(%1),%2" : "=r" (ret) : "r" (ptr), "r" (new_val) : "memory"); } CVMX_POP_OCTEON2; } else { #endif __asm__ __volatile__( ".set noreorder \n" "1: lld %[ret], %[val] \n" " move %[tmp], %[new_val] \n" " scd %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp), [ret] "=&r" (ret) : [new_val] "r" (new_val) : "memory"); #if !defined(__FreeBSD__) || !defined(_KERNEL) } #endif return (ret); } /** * Atomically swaps value in 32 bit (aligned) memory location, * and returns previous value. * * This version does not perform 'sync' operations to enforce memory * operations. This should only be used when there are no memory operation * ordering constraints. * * @param ptr address in memory * @param new_val new value to write * * @return Value of memory location before swap operation */ static inline uint32_t cvmx_atomic_swap32_nosync(uint32_t *ptr, uint32_t new_val) { uint32_t tmp, ret; #if !defined(__FreeBSD__) || !defined(_KERNEL) if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { CVMX_PUSH_OCTEON2; if (__builtin_constant_p(new_val) && new_val == 0) { __asm__ __volatile__( "lac %0,(%1)" : "=r" (ret) : "r" (ptr) : "memory"); } else if (__builtin_constant_p(new_val) && new_val == ~0u) { __asm__ __volatile__( "las %0,(%1)" : "=r" (ret) : "r" (ptr) : "memory"); } else { __asm__ __volatile__( "law %0,(%1),%2" : "=r" (ret) : "r" (ptr), "r" (new_val) : "memory"); } CVMX_POP_OCTEON2; } else { #endif __asm__ __volatile__( ".set noreorder \n" "1: ll %[ret], %[val] \n" " move %[tmp], %[new_val] \n" " sc %[tmp], %[val] \n" " beqz %[tmp], 1b \n" " nop \n" ".set reorder \n" : [val] "+m" (*ptr), [tmp] "=&r" (tmp), [ret] "=&r" (ret) : [new_val] "r" (new_val) : "memory"); #if !defined(__FreeBSD__) || !defined(_KERNEL) } #endif return (ret); } /** * This atomic operation is now named cvmx_atomic_compare_and_store32_nosync * and the (deprecated) macro is provided for backward compatibility. * @deprecated */ #define cvmx_atomic_compare_and_store_nosync32 cvmx_atomic_compare_and_store32_nosync /** * This atomic operation is now named cvmx_atomic_compare_and_store64_nosync * and the (deprecated) macro is provided for backward compatibility. * @deprecated */ #define cvmx_atomic_compare_and_store_nosync64 cvmx_atomic_compare_and_store64_nosync #ifdef __cplusplus } #endif #endif /* __CVMX_ATOMIC_H__ */