Home | History | Annotate | Line # | Download | only in atomic
      1 /*	$NetBSD: atomic_init_testset.c,v 1.19 2024/01/21 03:42:08 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2008 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 /*
     30  * libc glue for atomic operations where the hardware does not provide
     31  * compare-and-swap.  It's assumed that this will only be used on 32-bit
     32  * platforms.
     33  *
     34  * This should be compiled with '-fno-reorder-blocks -fomit-frame-pointer'
     35  * if using gcc.
     36  */
     37 
     38 #include <sys/cdefs.h>
     39 __RCSID("$NetBSD: atomic_init_testset.c,v 1.19 2024/01/21 03:42:08 thorpej Exp $");
     40 
     41 #include "extern.h"
     42 #include "atomic_op_namespace.h"
     43 
     44 #include <sys/types.h>
     45 #include <sys/atomic.h>
     46 #include <sys/lock.h>
     47 #include <sys/ras.h>
     48 #include <sys/sysctl.h>
     49 
     50 #include <string.h>
     51 
     52 #define	I2	__SIMPLELOCK_UNLOCKED, __SIMPLELOCK_UNLOCKED,
     53 #define	I16	I2 I2 I2 I2 I2 I2 I2 I2
     54 #define	I128	I16 I16 I16 I16 I16 I16 I16 I16
     55 
     56 static __cpu_simple_lock_t atomic_locks[128] = { I128 };
     57 /*
     58  * Pick a lock out of above array depending on the object address
     59  * passed. Most variables used atomically will not be in the same
     60  * cacheline - and if they are, using the same lock is fine.
     61  */
     62 #define HASH(PTR)	(((uintptr_t)(PTR) >> 3) & 127)
     63 
     64 #ifdef	__HAVE_ASM_ATOMIC_CAS_UP
     65 extern uint32_t _atomic_cas_up(volatile uint32_t *, uint32_t, uint32_t);
     66 #else
     67 static uint32_t _atomic_cas_up(volatile uint32_t *, uint32_t, uint32_t);
     68 #endif
     69 static uint32_t (*_atomic_cas_fn)(volatile uint32_t *, uint32_t, uint32_t) =
     70     _atomic_cas_up;
     71 RAS_DECL(_atomic_cas);
     72 
     73 #ifdef	__HAVE_ATOMIC_CAS_64_UP
     74 #ifdef	__HAVE_ASM_ATOMIC_CAS_64_UP
     75 extern uint64_t _atomic_cas_64_up(volatile uint64_t *, uint64_t, uint64_t);
     76 #else
     77 static uint64_t _atomic_cas_64_up(volatile uint64_t *, uint64_t, uint64_t);
     78 #endif
     79 static uint64_t (*_atomic_cas_64_fn)(volatile uint64_t *, uint64_t, uint64_t) =
     80     _atomic_cas_64_up;
     81 RAS_DECL(_atomic_cas_64);
     82 #endif
     83 
     84 #ifdef	__HAVE_ASM_ATOMIC_CAS_16_UP
     85 extern uint16_t _atomic_cas_16_up(volatile uint16_t *, uint16_t, uint16_t);
     86 #else
     87 static uint16_t _atomic_cas_16_up(volatile uint16_t *, uint16_t, uint16_t);
     88 #endif
     89 static uint16_t (*_atomic_cas_16_fn)(volatile uint16_t *, uint16_t, uint16_t) =
     90     _atomic_cas_16_up;
     91 RAS_DECL(_atomic_cas_16);
     92 
     93 #ifdef	__HAVE_ASM_ATOMIC_CAS_8_UP
     94 extern uint8_t _atomic_cas_8_up(volatile uint8_t *, uint8_t, uint8_t);
     95 #else
     96 static uint8_t _atomic_cas_8_up(volatile uint8_t *, uint8_t, uint8_t);
     97 #endif
     98 static uint8_t (*_atomic_cas_8_fn)(volatile uint8_t *, uint8_t, uint8_t) =
     99     _atomic_cas_8_up;
    100 RAS_DECL(_atomic_cas_8);
    101 
    102 #ifndef	__HAVE_ASM_ATOMIC_CAS_UP
    103 static uint32_t
    104 _atomic_cas_up(volatile uint32_t *ptr, uint32_t old, uint32_t new)
    105 {
    106 	uint32_t ret;
    107 
    108 	RAS_START(_atomic_cas);
    109 	ret = *ptr;
    110 	if (__predict_false(ret != old)) {
    111 		return ret;
    112 	}
    113 	*ptr = new;
    114 	RAS_END(_atomic_cas);
    115 
    116 	return ret;
    117 }
    118 #endif
    119 
    120 #if defined(__HAVE_ATOMIC_CAS_64_UP) && !defined(__HAVE_ASM_ATOMIC_CAS_64_UP)
    121 static uint64_t
    122 _atomic_cas_64_up(volatile uint64_t *ptr, uint64_t old, uint64_t new)
    123 {
    124 	uint64_t ret;
    125 
    126 	RAS_START(_atomic_cas_64);
    127 	ret = *ptr;
    128 	if (__predict_false(ret != old)) {
    129 		return ret;
    130 	}
    131 	*ptr = new;
    132 	RAS_END(_atomic_cas_64);
    133 
    134 	return ret;
    135 }
    136 #endif
    137 
    138 #ifndef	__HAVE_ASM_ATOMIC_CAS_16_UP
    139 static uint16_t
    140 _atomic_cas_16_up(volatile uint16_t *ptr, uint16_t old, uint16_t new)
    141 {
    142 	uint16_t ret;
    143 
    144 	RAS_START(_atomic_cas_16);
    145 	ret = *ptr;
    146 	if (__predict_false(ret != old)) {
    147 		return ret;
    148 	}
    149 	*ptr = new;
    150 	RAS_END(_atomic_cas_16);
    151 
    152 	return ret;
    153 }
    154 #endif
    155 
    156 #ifndef	__HAVE_ASM_ATOMIC_CAS_8_UP
    157 static uint8_t
    158 _atomic_cas_8_up(volatile uint8_t *ptr, uint8_t old, uint8_t new)
    159 {
    160 	uint8_t ret;
    161 
    162 	RAS_START(_atomic_cas_8);
    163 	ret = *ptr;
    164 	if (__predict_false(ret != old)) {
    165 		return ret;
    166 	}
    167 	*ptr = new;
    168 	RAS_END(_atomic_cas_8);
    169 
    170 	return ret;
    171 }
    172 #endif
    173 
    174 static uint32_t
    175 _atomic_cas_mp(volatile uint32_t *ptr, uint32_t old, uint32_t new)
    176 {
    177 	__cpu_simple_lock_t *lock;
    178 	uint32_t ret;
    179 
    180 	lock = &atomic_locks[HASH(ptr)];
    181 	__cpu_simple_lock(lock);
    182 	ret = *ptr;
    183 	if (__predict_true(ret == old)) {
    184 		*ptr = new;
    185 	}
    186 	__cpu_simple_unlock(lock);
    187 
    188 	return ret;
    189 }
    190 
    191 #ifdef	__HAVE_ATOMIC_CAS_64_UP
    192 static uint64_t
    193 _atomic_cas_64_mp(volatile uint64_t *ptr, uint64_t old, uint64_t new)
    194 {
    195 	__cpu_simple_lock_t *lock;
    196 	uint64_t ret;
    197 
    198 	lock = &atomic_locks[HASH(ptr)];
    199 	__cpu_simple_lock(lock);
    200 	ret = *ptr;
    201 	if (__predict_true(ret == old)) {
    202 		*ptr = new;
    203 	}
    204 	__cpu_simple_unlock(lock);
    205 
    206 	return ret;
    207 }
    208 #endif
    209 
    210 static uint16_t
    211 _atomic_cas_16_mp(volatile uint16_t *ptr, uint16_t old, uint16_t new)
    212 {
    213 	__cpu_simple_lock_t *lock;
    214 	uint16_t ret;
    215 
    216 	lock = &atomic_locks[HASH(ptr)];
    217 	__cpu_simple_lock(lock);
    218 	ret = *ptr;
    219 	if (__predict_true(ret == old)) {
    220 		*ptr = new;
    221 	}
    222 	__cpu_simple_unlock(lock);
    223 
    224 	return ret;
    225 }
    226 
    227 static uint8_t
    228 _atomic_cas_8_mp(volatile uint8_t *ptr, uint8_t old, uint8_t new)
    229 {
    230 	__cpu_simple_lock_t *lock;
    231 	uint8_t ret;
    232 
    233 	lock = &atomic_locks[HASH(ptr)];
    234 	__cpu_simple_lock(lock);
    235 	ret = *ptr;
    236 	if (__predict_true(ret == old)) {
    237 		*ptr = new;
    238 	}
    239 	__cpu_simple_unlock(lock);
    240 
    241 	return ret;
    242 }
    243 
    244 uint32_t
    245 _atomic_cas_32(volatile uint32_t *ptr, uint32_t old, uint32_t new)
    246 {
    247 
    248 	return (*_atomic_cas_fn)(ptr, old, new);
    249 }
    250 
    251 #ifdef	__HAVE_ATOMIC_CAS_64_UP
    252 uint64_t _atomic_cas_64(volatile uint64_t *, uint64_t, uint64_t);
    253 
    254 uint64_t
    255 _atomic_cas_64(volatile uint64_t *ptr, uint64_t old, uint64_t new)
    256 {
    257 
    258 	return (*_atomic_cas_64_fn)(ptr, old, new);
    259 }
    260 #endif
    261 
    262 uint16_t
    263 _atomic_cas_16(volatile uint16_t *ptr, uint16_t old, uint16_t new)
    264 {
    265 
    266 	return (*_atomic_cas_16_fn)(ptr, old, new);
    267 }
    268 
    269 uint8_t _atomic_cas_8(volatile uint8_t *, uint8_t, uint8_t);
    270 
    271 uint8_t
    272 _atomic_cas_8(volatile uint8_t *ptr, uint8_t old, uint8_t new)
    273 {
    274 
    275 	return (*_atomic_cas_8_fn)(ptr, old, new);
    276 }
    277 
    278 void __section(".text.startup") __attribute__ ((__visibility__("hidden")))
    279 __libc_atomic_init(void)
    280 {
    281 	int ncpu, mib[2];
    282 	size_t len;
    283 
    284 	_atomic_cas_fn = _atomic_cas_mp;
    285 #ifdef	__HAVE_ATOMIC_CAS_64_UP
    286 	_atomic_cas_64_fn = _atomic_cas_64_mp;
    287 #endif
    288 	_atomic_cas_16_fn = _atomic_cas_16_mp;
    289 	_atomic_cas_8_fn = _atomic_cas_8_mp;
    290 
    291 	mib[0] = CTL_HW;
    292 	mib[1] = HW_NCPU;
    293 	len = sizeof(ncpu);
    294 	if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1)
    295 		return;
    296 	if (ncpu > 1)
    297 		return;
    298 
    299 	if (rasctl(RAS_ADDR(_atomic_cas), RAS_SIZE(_atomic_cas),
    300 	    RAS_INSTALL) == 0) {
    301 		_atomic_cas_fn = _atomic_cas_up;
    302 	}
    303 
    304 
    305 #ifdef	__HAVE_ATOMIC_CAS_64_UP
    306 	if (rasctl(RAS_ADDR(_atomic_cas_64), RAS_SIZE(_atomic_cas_64),
    307 	    RAS_INSTALL) == 0) {
    308 		_atomic_cas_64_fn = _atomic_cas_64_up;
    309 	}
    310 #endif
    311 
    312 	if (rasctl(RAS_ADDR(_atomic_cas_16), RAS_SIZE(_atomic_cas_16),
    313 	    RAS_INSTALL) == 0) {
    314 		_atomic_cas_16_fn = _atomic_cas_16_up;
    315 	}
    316 
    317 	if (rasctl(RAS_ADDR(_atomic_cas_8), RAS_SIZE(_atomic_cas_8),
    318 	    RAS_INSTALL) == 0) {
    319 		_atomic_cas_8_fn = _atomic_cas_8_up;
    320 	}
    321 }
    322 
    323 #undef atomic_cas_32
    324 #undef atomic_cas_uint
    325 #undef atomic_cas_ulong
    326 #undef atomic_cas_ptr
    327 #undef atomic_cas_32_ni
    328 #undef atomic_cas_uint_ni
    329 #undef atomic_cas_ulong_ni
    330 #undef atomic_cas_ptr_ni
    331 
    332 atomic_op_alias(atomic_cas_32,_atomic_cas_32)
    333 atomic_op_alias(atomic_cas_uint,_atomic_cas_32)
    334 __strong_alias(_atomic_cas_uint,_atomic_cas_32)
    335 atomic_op_alias(atomic_cas_ulong,_atomic_cas_32)
    336 __strong_alias(_atomic_cas_ulong,_atomic_cas_32)
    337 atomic_op_alias(atomic_cas_ptr,_atomic_cas_32)
    338 __strong_alias(_atomic_cas_ptr,_atomic_cas_32)
    339 
    340 atomic_op_alias(atomic_cas_32_ni,_atomic_cas_32)
    341 __strong_alias(_atomic_cas_32_ni,_atomic_cas_32)
    342 atomic_op_alias(atomic_cas_uint_ni,_atomic_cas_32)
    343 __strong_alias(_atomic_cas_uint_ni,_atomic_cas_32)
    344 atomic_op_alias(atomic_cas_ulong_ni,_atomic_cas_32)
    345 __strong_alias(_atomic_cas_ulong_ni,_atomic_cas_32)
    346 atomic_op_alias(atomic_cas_ptr_ni,_atomic_cas_32)
    347 __strong_alias(_atomic_cas_ptr_ni,_atomic_cas_32)
    348 
    349 //atomic_op_alias(atomic_cas_16,_atomic_cas_16)
    350 //atomic_op_alias(atomic_cas_16_ni,_atomic_cas_16)
    351 //atomic_op_alias(atomic_cas_8,_atomic_cas_8)
    352 //atomic_op_alias(atomic_cas_8_ni,_atomic_cas_8)
    353 #ifdef	__HAVE_ATOMIC_CAS_64_UP
    354 atomic_op_alias(atomic_cas_64_ni,_atomic_cas_64)
    355 __strong_alias(_atomic_cas_64_ni,_atomic_cas_64)
    356 crt_alias(__sync_val_compare_and_swap_8,_atomic_cas_64)
    357 #endif
    358 crt_alias(__sync_val_compare_and_swap_4,_atomic_cas_32)
    359 crt_alias(__sync_val_compare_and_swap_2,_atomic_cas_16)
    360 crt_alias(__sync_val_compare_and_swap_1,_atomic_cas_8)
    361