linux-atomic.c revision 1.1.1.1
1/* Linux-specific atomic operations for PA Linux. 2 Copyright (C) 2008-2013 Free Software Foundation, Inc. 3 Based on code contributed by CodeSourcery for ARM EABI Linux. 4 Modifications for PA Linux by Helge Deller <deller@gmx.de> 5 6This file is part of GCC. 7 8GCC is free software; you can redistribute it and/or modify it under 9the terms of the GNU General Public License as published by the Free 10Software Foundation; either version 3, or (at your option) any later 11version. 12 13GCC is distributed in the hope that it will be useful, but WITHOUT ANY 14WARRANTY; without even the implied warranty of MERCHANTABILITY or 15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16for more details. 17 18Under Section 7 of GPL version 3, you are granted additional 19permissions described in the GCC Runtime Library Exception, version 203.1, as published by the Free Software Foundation. 21 22You should have received a copy of the GNU General Public License and 23a copy of the GCC Runtime Library Exception along with this program; 24see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 25<http://www.gnu.org/licenses/>. */ 26 27#define EFAULT 14 28#define EBUSY 16 29#define ENOSYS 251 30 31/* All PA-RISC implementations supported by linux have strongly 32 ordered loads and stores. Only cache flushes and purges can be 33 delayed. The data cache implementations are all globally 34 coherent. Thus, there is no need to synchonize memory accesses. 35 36 GCC automatically issues a asm memory barrier when it encounters 37 a __sync_synchronize builtin. Thus, we do not need to define this 38 builtin. 39 40 We implement byte, short and int versions of each atomic operation 41 using the kernel helper defined below. There is no support for 42 64-bit operations yet. */ 43 44/* A privileged instruction to crash a userspace program with SIGILL. */ 45#define ABORT_INSTRUCTION asm ("iitlbp %r0,(%sr0, %r0)") 46 47/* Determine kernel LWS function call (0=32-bit, 1=64-bit userspace). */ 48#define LWS_CAS (sizeof(unsigned long) == 4 ? 0 : 1) 49 50/* Kernel helper for compare-and-exchange a 32-bit value. */ 51static inline long 52__kernel_cmpxchg (int oldval, int newval, int *mem) 53{ 54 register unsigned long lws_mem asm("r26") = (unsigned long) (mem); 55 register long lws_ret asm("r28"); 56 register long lws_errno asm("r21"); 57 register int lws_old asm("r25") = oldval; 58 register int lws_new asm("r24") = newval; 59 asm volatile ( "ble 0xb0(%%sr2, %%r0) \n\t" 60 "ldi %5, %%r20 \n\t" 61 : "=r" (lws_ret), "=r" (lws_errno), "=r" (lws_mem), 62 "=r" (lws_old), "=r" (lws_new) 63 : "i" (LWS_CAS), "2" (lws_mem), "3" (lws_old), "4" (lws_new) 64 : "r1", "r20", "r22", "r23", "r29", "r31", "memory" 65 ); 66 if (__builtin_expect (lws_errno == -EFAULT || lws_errno == -ENOSYS, 0)) 67 ABORT_INSTRUCTION; 68 69 /* If the kernel LWS call succeeded (lws_errno == 0), lws_ret contains 70 the old value from memory. If this value is equal to OLDVAL, the 71 new value was written to memory. If not, return -EBUSY. */ 72 if (!lws_errno && lws_ret != oldval) 73 lws_errno = -EBUSY; 74 75 return lws_errno; 76} 77 78#define HIDDEN __attribute__ ((visibility ("hidden"))) 79 80/* Big endian masks */ 81#define INVERT_MASK_1 24 82#define INVERT_MASK_2 16 83 84#define MASK_1 0xffu 85#define MASK_2 0xffffu 86 87#define FETCH_AND_OP_WORD(OP, PFX_OP, INF_OP) \ 88 int HIDDEN \ 89 __sync_fetch_and_##OP##_4 (int *ptr, int val) \ 90 { \ 91 int failure, tmp; \ 92 \ 93 do { \ 94 tmp = *ptr; \ 95 failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr); \ 96 } while (failure != 0); \ 97 \ 98 return tmp; \ 99 } 100 101FETCH_AND_OP_WORD (add, , +) 102FETCH_AND_OP_WORD (sub, , -) 103FETCH_AND_OP_WORD (or, , |) 104FETCH_AND_OP_WORD (and, , &) 105FETCH_AND_OP_WORD (xor, , ^) 106FETCH_AND_OP_WORD (nand, ~, &) 107 108#define NAME_oldval(OP, WIDTH) __sync_fetch_and_##OP##_##WIDTH 109#define NAME_newval(OP, WIDTH) __sync_##OP##_and_fetch_##WIDTH 110 111/* Implement both __sync_<op>_and_fetch and __sync_fetch_and_<op> for 112 subword-sized quantities. */ 113 114#define SUBWORD_SYNC_OP(OP, PFX_OP, INF_OP, TYPE, WIDTH, RETURN) \ 115 TYPE HIDDEN \ 116 NAME##_##RETURN (OP, WIDTH) (TYPE *ptr, TYPE val) \ 117 { \ 118 int *wordptr = (int *) ((unsigned long) ptr & ~3); \ 119 unsigned int mask, shift, oldval, newval; \ 120 int failure; \ 121 \ 122 shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \ 123 mask = MASK_##WIDTH << shift; \ 124 \ 125 do { \ 126 oldval = *wordptr; \ 127 newval = ((PFX_OP (((oldval & mask) >> shift) \ 128 INF_OP (unsigned int) val)) << shift) & mask; \ 129 newval |= oldval & ~mask; \ 130 failure = __kernel_cmpxchg (oldval, newval, wordptr); \ 131 } while (failure != 0); \ 132 \ 133 return (RETURN & mask) >> shift; \ 134 } 135 136SUBWORD_SYNC_OP (add, , +, unsigned short, 2, oldval) 137SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, oldval) 138SUBWORD_SYNC_OP (or, , |, unsigned short, 2, oldval) 139SUBWORD_SYNC_OP (and, , &, unsigned short, 2, oldval) 140SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, oldval) 141SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, oldval) 142 143SUBWORD_SYNC_OP (add, , +, unsigned char, 1, oldval) 144SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, oldval) 145SUBWORD_SYNC_OP (or, , |, unsigned char, 1, oldval) 146SUBWORD_SYNC_OP (and, , &, unsigned char, 1, oldval) 147SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, oldval) 148SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, oldval) 149 150#define OP_AND_FETCH_WORD(OP, PFX_OP, INF_OP) \ 151 int HIDDEN \ 152 __sync_##OP##_and_fetch_4 (int *ptr, int val) \ 153 { \ 154 int tmp, failure; \ 155 \ 156 do { \ 157 tmp = *ptr; \ 158 failure = __kernel_cmpxchg (tmp, PFX_OP (tmp INF_OP val), ptr); \ 159 } while (failure != 0); \ 160 \ 161 return PFX_OP (tmp INF_OP val); \ 162 } 163 164OP_AND_FETCH_WORD (add, , +) 165OP_AND_FETCH_WORD (sub, , -) 166OP_AND_FETCH_WORD (or, , |) 167OP_AND_FETCH_WORD (and, , &) 168OP_AND_FETCH_WORD (xor, , ^) 169OP_AND_FETCH_WORD (nand, ~, &) 170 171SUBWORD_SYNC_OP (add, , +, unsigned short, 2, newval) 172SUBWORD_SYNC_OP (sub, , -, unsigned short, 2, newval) 173SUBWORD_SYNC_OP (or, , |, unsigned short, 2, newval) 174SUBWORD_SYNC_OP (and, , &, unsigned short, 2, newval) 175SUBWORD_SYNC_OP (xor, , ^, unsigned short, 2, newval) 176SUBWORD_SYNC_OP (nand, ~, &, unsigned short, 2, newval) 177 178SUBWORD_SYNC_OP (add, , +, unsigned char, 1, newval) 179SUBWORD_SYNC_OP (sub, , -, unsigned char, 1, newval) 180SUBWORD_SYNC_OP (or, , |, unsigned char, 1, newval) 181SUBWORD_SYNC_OP (and, , &, unsigned char, 1, newval) 182SUBWORD_SYNC_OP (xor, , ^, unsigned char, 1, newval) 183SUBWORD_SYNC_OP (nand, ~, &, unsigned char, 1, newval) 184 185int HIDDEN 186__sync_val_compare_and_swap_4 (int *ptr, int oldval, int newval) 187{ 188 int actual_oldval, fail; 189 190 while (1) 191 { 192 actual_oldval = *ptr; 193 194 if (__builtin_expect (oldval != actual_oldval, 0)) 195 return actual_oldval; 196 197 fail = __kernel_cmpxchg (actual_oldval, newval, ptr); 198 199 if (__builtin_expect (!fail, 1)) 200 return actual_oldval; 201 } 202} 203 204#define SUBWORD_VAL_CAS(TYPE, WIDTH) \ 205 TYPE HIDDEN \ 206 __sync_val_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \ 207 TYPE newval) \ 208 { \ 209 int *wordptr = (int *)((unsigned long) ptr & ~3), fail; \ 210 unsigned int mask, shift, actual_oldval, actual_newval; \ 211 \ 212 shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \ 213 mask = MASK_##WIDTH << shift; \ 214 \ 215 while (1) \ 216 { \ 217 actual_oldval = *wordptr; \ 218 \ 219 if (__builtin_expect (((actual_oldval & mask) >> shift) \ 220 != (unsigned int) oldval, 0)) \ 221 return (actual_oldval & mask) >> shift; \ 222 \ 223 actual_newval = (actual_oldval & ~mask) \ 224 | (((unsigned int) newval << shift) & mask); \ 225 \ 226 fail = __kernel_cmpxchg (actual_oldval, actual_newval, \ 227 wordptr); \ 228 \ 229 if (__builtin_expect (!fail, 1)) \ 230 return (actual_oldval & mask) >> shift; \ 231 } \ 232 } 233 234SUBWORD_VAL_CAS (unsigned short, 2) 235SUBWORD_VAL_CAS (unsigned char, 1) 236 237typedef unsigned char bool; 238 239bool HIDDEN 240__sync_bool_compare_and_swap_4 (int *ptr, int oldval, int newval) 241{ 242 int failure = __kernel_cmpxchg (oldval, newval, ptr); 243 return (failure == 0); 244} 245 246#define SUBWORD_BOOL_CAS(TYPE, WIDTH) \ 247 bool HIDDEN \ 248 __sync_bool_compare_and_swap_##WIDTH (TYPE *ptr, TYPE oldval, \ 249 TYPE newval) \ 250 { \ 251 TYPE actual_oldval \ 252 = __sync_val_compare_and_swap_##WIDTH (ptr, oldval, newval); \ 253 return (oldval == actual_oldval); \ 254 } 255 256SUBWORD_BOOL_CAS (unsigned short, 2) 257SUBWORD_BOOL_CAS (unsigned char, 1) 258 259int HIDDEN 260__sync_lock_test_and_set_4 (int *ptr, int val) 261{ 262 int failure, oldval; 263 264 do { 265 oldval = *ptr; 266 failure = __kernel_cmpxchg (oldval, val, ptr); 267 } while (failure != 0); 268 269 return oldval; 270} 271 272#define SUBWORD_TEST_AND_SET(TYPE, WIDTH) \ 273 TYPE HIDDEN \ 274 __sync_lock_test_and_set_##WIDTH (TYPE *ptr, TYPE val) \ 275 { \ 276 int failure; \ 277 unsigned int oldval, newval, shift, mask; \ 278 int *wordptr = (int *) ((unsigned long) ptr & ~3); \ 279 \ 280 shift = (((unsigned long) ptr & 3) << 3) ^ INVERT_MASK_##WIDTH; \ 281 mask = MASK_##WIDTH << shift; \ 282 \ 283 do { \ 284 oldval = *wordptr; \ 285 newval = (oldval & ~mask) \ 286 | (((unsigned int) val << shift) & mask); \ 287 failure = __kernel_cmpxchg (oldval, newval, wordptr); \ 288 } while (failure != 0); \ 289 \ 290 return (oldval & mask) >> shift; \ 291 } 292 293SUBWORD_TEST_AND_SET (unsigned short, 2) 294SUBWORD_TEST_AND_SET (unsigned char, 1) 295 296#define SYNC_LOCK_RELEASE(TYPE, WIDTH) \ 297 void HIDDEN \ 298 __sync_lock_release_##WIDTH (TYPE *ptr) \ 299 { \ 300 *ptr = 0; \ 301 } 302 303SYNC_LOCK_RELEASE (int, 4) 304SYNC_LOCK_RELEASE (short, 2) 305SYNC_LOCK_RELEASE (char, 1) 306