| Commit | Line | Data |
|---|---|---|
| fc3f9779 | 1 | /*- |
| 46d4e165 | 2 | * Copyright (c) 1998 Doug Rabson |
| fc3f9779 SS |
3 | * All rights reserved. |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * | |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 24 | * SUCH DAMAGE. | |
| 25 | * | |
| 46d4e165 JG |
26 | * $FreeBSD: src/sys/i386/include/atomic.h,v 1.9.2.1 2000/07/07 00:38:47 obrien Exp $ |
| 27 | * $DragonFly: src/sys/cpu/i386/include/atomic.h,v 1.25 2008/06/26 23:06:50 dillon Exp $ | |
| fc3f9779 SS |
28 | */ |
| 29 | #ifndef _CPU_ATOMIC_H_ | |
| 30 | #define _CPU_ATOMIC_H_ | |
| 31 | ||
| d7f50089 YY |
32 | #ifndef _SYS_TYPES_H_ |
| 33 | #include <sys/types.h> | |
| 34 | #endif | |
| 35 | ||
| fc3f9779 | 36 | /* |
| 46d4e165 JG |
37 | * Various simple arithmetic on memory which is atomic in the presence |
| 38 | * of interrupts and multiple processors. | |
| fc3f9779 | 39 | * |
| 46d4e165 JG |
40 | * atomic_set_char(P, V) (*(u_char*)(P) |= (V)) |
| 41 | * atomic_clear_char(P, V) (*(u_char*)(P) &= ~(V)) | |
| 42 | * atomic_add_char(P, V) (*(u_char*)(P) += (V)) | |
| 43 | * atomic_subtract_char(P, V) (*(u_char*)(P) -= (V)) | |
| fc3f9779 | 44 | * |
| 46d4e165 JG |
45 | * atomic_set_short(P, V) (*(u_short*)(P) |= (V)) |
| 46 | * atomic_clear_short(P, V) (*(u_short*)(P) &= ~(V)) | |
| 47 | * atomic_add_short(P, V) (*(u_short*)(P) += (V)) | |
| 48 | * atomic_subtract_short(P, V) (*(u_short*)(P) -= (V)) | |
| fc3f9779 | 49 | * |
| 46d4e165 JG |
50 | * atomic_set_int(P, V) (*(u_int*)(P) |= (V)) |
| 51 | * atomic_clear_int(P, V) (*(u_int*)(P) &= ~(V)) | |
| 52 | * atomic_add_int(P, V) (*(u_int*)(P) += (V)) | |
| 53 | * atomic_subtract_int(P, V) (*(u_int*)(P) -= (V)) | |
| fc3f9779 | 54 | * |
| 46d4e165 JG |
55 | * atomic_set_long(P, V) (*(u_long*)(P) |= (V)) |
| 56 | * atomic_clear_long(P, V) (*(u_long*)(P) &= ~(V)) | |
| 57 | * atomic_add_long(P, V) (*(u_long*)(P) += (V)) | |
| 58 | * atomic_subtract_long(P, V) (*(u_long*)(P) -= (V)) | |
| 59 | * atomic_readandclear_long(P) (return (*(u_long*)(P)); *(u_long*)(P) = 0;) | |
| fc3f9779 SS |
60 | */ |
| 61 | ||
| 62 | /* | |
| 63 | * The above functions are expanded inline in the statically-linked | |
| 64 | * kernel. Lock prefixes are generated if an SMP kernel is being | |
| 46d4e165 | 65 | * built, or if user code is using these functions. |
| fc3f9779 SS |
66 | * |
| 67 | * Kernel modules call real functions which are built into the kernel. | |
| 68 | * This allows kernel modules to be portable between UP and SMP systems. | |
| 69 | */ | |
| 70 | #if defined(KLD_MODULE) | |
| 46d4e165 JG |
71 | #define ATOMIC_ASM(NAME, TYPE, OP, V) \ |
| 72 | extern void atomic_##NAME##_##TYPE(volatile u_##TYPE *p, u_##TYPE v); \ | |
| 73 | extern void atomic_##NAME##_##TYPE##_nonlocked(volatile u_##TYPE *p, u_##TYPE v); | |
| fc3f9779 | 74 | #else /* !KLD_MODULE */ |
| fc3f9779 | 75 | #if defined(SMP) || !defined(_KERNEL) |
| 46d4e165 | 76 | #define MPLOCKED "lock ; " |
| fc3f9779 | 77 | #else |
| 46d4e165 | 78 | #define MPLOCKED |
| fc3f9779 SS |
79 | #endif |
| 80 | ||
| 81 | /* | |
| 82 | * The assembly is volatilized to demark potential before-and-after side | |
| 46d4e165 JG |
83 | * effects if an interrupt or SMP collision were to occur. The primary |
| 84 | * atomic instructions are MP safe, the nonlocked instructions are | |
| 85 | * local-interrupt-safe (so we don't depend on C 'X |= Y' generating an | |
| 86 | * atomic instruction). | |
| 87 | * | |
| 88 | * +m - memory is read and written (=m - memory is only written) | |
| 89 | * iq - integer constant or %ax/%bx/%cx/%dx (ir = int constant or any reg) | |
| 90 | * (Note: byte instructions only work on %ax,%bx,%cx, or %dx). iq | |
| 91 | * is good enough for our needs so don't get fancy. | |
| fc3f9779 | 92 | */ |
| 46d4e165 JG |
93 | |
| 94 | /* egcs 1.1.2+ version */ | |
| 95 | #define ATOMIC_ASM(NAME, TYPE, OP, V) \ | |
| fc3f9779 SS |
96 | static __inline void \ |
| 97 | atomic_##NAME##_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\ | |
| 98 | { \ | |
| 99 | __asm __volatile(MPLOCKED OP \ | |
| 100 | : "+m" (*p) \ | |
| 46d4e165 | 101 | : "iq" (V)); \ |
| fc3f9779 | 102 | } \ |
| 46d4e165 | 103 | static __inline void \ |
| d7f50089 | 104 | atomic_##NAME##_##TYPE##_nonlocked(volatile u_##TYPE *p, u_##TYPE v)\ |
| 46d4e165 JG |
105 | { \ |
| 106 | __asm __volatile(OP \ | |
| 107 | : "+m" (*p) \ | |
| 108 | : "iq" (V)); \ | |
| 109 | } | |
| 110 | ||
| 111 | #endif /* KLD_MODULE */ | |
| 112 | ||
| 113 | /* egcs 1.1.2+ version */ | |
| 114 | ATOMIC_ASM(set, char, "orb %b1,%0", v) | |
| 115 | ATOMIC_ASM(clear, char, "andb %b1,%0", ~v) | |
| 116 | ATOMIC_ASM(add, char, "addb %b1,%0", v) | |
| 117 | ATOMIC_ASM(subtract, char, "subb %b1,%0", v) | |
| 118 | ||
| 119 | ATOMIC_ASM(set, short, "orw %w1,%0", v) | |
| 120 | ATOMIC_ASM(clear, short, "andw %w1,%0", ~v) | |
| 121 | ATOMIC_ASM(add, short, "addw %w1,%0", v) | |
| 122 | ATOMIC_ASM(subtract, short, "subw %w1,%0", v) | |
| 123 | ||
| 124 | ATOMIC_ASM(set, int, "orl %1,%0", v) | |
| 125 | ATOMIC_ASM(clear, int, "andl %1,%0", ~v) | |
| 126 | ATOMIC_ASM(add, int, "addl %1,%0", v) | |
| 127 | ATOMIC_ASM(subtract, int, "subl %1,%0", v) | |
| 128 | ||
| 129 | ATOMIC_ASM(set, long, "orq %1,%0", v) | |
| 130 | ATOMIC_ASM(clear, long, "andq %1,%0", ~v) | |
| 131 | ATOMIC_ASM(add, long, "addq %1,%0", v) | |
| 132 | ATOMIC_ASM(subtract, long, "subq %1,%0", v) | |
| 133 | ||
| 134 | #if defined(KLD_MODULE) | |
| 135 | u_long atomic_readandclear_long(volatile u_long *addr); | |
| 136 | #else /* !KLD_MODULE */ | |
| 137 | static __inline u_long | |
| 138 | atomic_readandclear_long(volatile u_long *addr) | |
| 139 | { | |
| 140 | u_long res; | |
| 141 | ||
| 142 | res = 0; | |
| 143 | __asm __volatile( | |
| 144 | " xchgq %1,%0 ; " | |
| 145 | "# atomic_readandclear_long" | |
| 146 | : "+r" (res), /* 0 */ | |
| 147 | "=m" (*addr) /* 1 */ | |
| 148 | : "m" (*addr)); | |
| 149 | ||
| 150 | return (res); | |
| 151 | } | |
| 152 | #endif /* KLD_MODULE */ | |
| 153 | ||
| 154 | /* | |
| 155 | * atomic_poll_acquire_int(P) Returns non-zero on success, 0 if the lock | |
| 156 | * has already been acquired. | |
| 157 | * atomic_poll_release_int(P) | |
| 158 | * | |
| 159 | * These support the NDIS driver and are also used for IPIQ interlocks | |
| 160 | * between cpus. Both the acquisition and release must be | |
| 161 | * cache-synchronizing instructions. | |
| 162 | */ | |
| 163 | ||
| 164 | #if defined(KLD_MODULE) | |
| 165 | ||
| 166 | extern int atomic_swap_int(volatile int *addr, int value); | |
| 167 | extern int atomic_poll_acquire_int(volatile u_int *p); | |
| 168 | extern void atomic_poll_release_int(volatile u_int *p); | |
| 169 | ||
| 170 | #else | |
| 171 | ||
| 172 | static __inline int | |
| 173 | atomic_swap_int(volatile int *addr, int value) | |
| 174 | { | |
| 175 | __asm __volatile("xchgl %0, %1" : | |
| 176 | "=r" (value), "=m" (*addr) : "0" (value) : "memory"); | |
| 177 | return (value); | |
| d7f50089 | 178 | } |
| fc3f9779 | 179 | |
| 46d4e165 JG |
180 | static __inline |
| 181 | int | |
| 182 | atomic_poll_acquire_int(volatile u_int *p) | |
| 183 | { | |
| 184 | u_int data; | |
| fc3f9779 | 185 | |
| 46d4e165 JG |
186 | __asm __volatile(MPLOCKED "btsl $0,%0; setnc %%al; andl $255,%%eax" : "+m" (*p), "=a" (data)); |
| 187 | return(data); | |
| 188 | } | |
| fc3f9779 | 189 | |
| 46d4e165 JG |
190 | static __inline |
| 191 | void | |
| 192 | atomic_poll_release_int(volatile u_int *p) | |
| 193 | { | |
| 194 | __asm __volatile(MPLOCKED "btrl $0,%0" : "+m" (*p)); | |
| 195 | } | |
| 196 | ||
| 197 | #endif | |
| fc3f9779 SS |
198 | |
| 199 | /* | |
| 200 | * These functions operate on a 32 bit interrupt interlock which is defined | |
| 201 | * as follows: | |
| 202 | * | |
| 203 | * bit 0-30 interrupt handler disabled bits (counter) | |
| 204 | * bit 31 interrupt handler currently running bit (1 = run) | |
| 205 | * | |
| 206 | * atomic_intr_cond_test(P) Determine if the interlock is in an | |
| 207 | * acquired state. Returns 0 if it not | |
| 208 | * acquired, non-zero if it is. | |
| 209 | * | |
| 210 | * atomic_intr_cond_try(P) | |
| 211 | * Increment the request counter and attempt to | |
| 212 | * set bit 31 to acquire the interlock. If | |
| 213 | * we are unable to set bit 31 the request | |
| 214 | * counter is decremented and we return -1, | |
| 215 | * otherwise we return 0. | |
| 216 | * | |
| 217 | * atomic_intr_cond_enter(P, func, arg) | |
| 218 | * Increment the request counter and attempt to | |
| 219 | * set bit 31 to acquire the interlock. If | |
| 220 | * we are unable to set bit 31 func(arg) is | |
| 221 | * called in a loop until we are able to set | |
| 222 | * bit 31. | |
| 223 | * | |
| 224 | * atomic_intr_cond_exit(P, func, arg) | |
| 225 | * Decrement the request counter and clear bit | |
| 226 | * 31. If the request counter is still non-zero | |
| 227 | * call func(arg) once. | |
| 228 | * | |
| 229 | * atomic_intr_handler_disable(P) | |
| 230 | * Set bit 30, indicating that the interrupt | |
| 231 | * handler has been disabled. Must be called | |
| 232 | * after the hardware is disabled. | |
| 233 | * | |
| 234 | * Returns bit 31 indicating whether a serialized | |
| 235 | * accessor is active (typically the interrupt | |
| 236 | * handler is running). 0 == not active, | |
| 237 | * non-zero == active. | |
| 238 | * | |
| 239 | * atomic_intr_handler_enable(P) | |
| 240 | * Clear bit 30, indicating that the interrupt | |
| 241 | * handler has been enabled. Must be called | |
| 242 | * before the hardware is actually enabled. | |
| 243 | * | |
| 244 | * atomic_intr_handler_is_enabled(P) | |
| 245 | * Returns bit 30, 0 indicates that the handler | |
| 246 | * is enabled, non-zero indicates that it is | |
| 247 | * disabled. The request counter portion of | |
| 248 | * the field is ignored. | |
| 249 | */ | |
| 250 | ||
| fc3f9779 SS |
251 | #if defined(KLD_MODULE) |
| 252 | ||
| 46d4e165 JG |
253 | void atomic_intr_init(__atomic_intr_t *p); |
| 254 | int atomic_intr_handler_disable(__atomic_intr_t *p); | |
| 255 | void atomic_intr_handler_enable(__atomic_intr_t *p); | |
| 256 | int atomic_intr_handler_is_enabled(__atomic_intr_t *p); | |
| 257 | int atomic_intr_cond_test(__atomic_intr_t *p); | |
| 258 | int atomic_intr_cond_try(__atomic_intr_t *p); | |
| 259 | void atomic_intr_cond_enter(__atomic_intr_t *p, void (*func)(void *), void *arg); | |
| 260 | void atomic_intr_cond_exit(__atomic_intr_t *p, void (*func)(void *), void *arg); | |
| fc3f9779 | 261 | |
| 46d4e165 | 262 | #else |
| fc3f9779 SS |
263 | |
| 264 | static __inline | |
| 265 | void | |
| 46d4e165 | 266 | atomic_intr_init(__atomic_intr_t *p) |
| fc3f9779 SS |
267 | { |
| 268 | *p = 0; | |
| 269 | } | |
| 270 | ||
| 271 | static __inline | |
| 272 | int | |
| 46d4e165 | 273 | atomic_intr_handler_disable(__atomic_intr_t *p) |
| fc3f9779 SS |
274 | { |
| 275 | int data; | |
| 276 | ||
| 277 | __asm __volatile(MPLOCKED "orl $0x40000000,%1; movl %1,%%eax; " \ | |
| 278 | "andl $0x80000000,%%eax" \ | |
| 279 | : "=a"(data) , "+m"(*p)); | |
| 280 | return(data); | |
| 281 | } | |
| 282 | ||
| 283 | static __inline | |
| 284 | void | |
| 46d4e165 | 285 | atomic_intr_handler_enable(__atomic_intr_t *p) |
| fc3f9779 SS |
286 | { |
| 287 | __asm __volatile(MPLOCKED "andl $0xBFFFFFFF,%0" : "+m" (*p)); | |
| 288 | } | |
| 289 | ||
| 290 | static __inline | |
| 291 | int | |
| 46d4e165 | 292 | atomic_intr_handler_is_enabled(__atomic_intr_t *p) |
| fc3f9779 SS |
293 | { |
| 294 | int data; | |
| 295 | ||
| 296 | __asm __volatile("movl %1,%%eax; andl $0x40000000,%%eax" \ | |
| 297 | : "=a"(data) : "m"(*p)); | |
| 298 | return(data); | |
| 299 | } | |
| 300 | ||
| 301 | static __inline | |
| 302 | void | |
| 46d4e165 | 303 | atomic_intr_cond_enter(__atomic_intr_t *p, void (*func)(void *), void *arg) |
| fc3f9779 SS |
304 | { |
| 305 | __asm __volatile(MPLOCKED "incl %0; " \ | |
| 306 | "1: ;" \ | |
| 307 | MPLOCKED "btsl $31,%0; jnc 2f; " \ | |
| d7f50089 | 308 | "movq %2,%%rdi; call *%1; " \ |
| fc3f9779 SS |
309 | "jmp 1b; " \ |
| 310 | "2: ;" \ | |
| 311 | : "+m" (*p) \ | |
| 312 | : "r"(func), "m"(arg) \ | |
| 0855a2af JG |
313 | : "ax", "cx", "dx", "rsi", "rdi", "r8", "r9", "r10", "r11"); |
| 314 | /* YYY the function call may clobber even more registers? */ | |
| fc3f9779 SS |
315 | } |
| 316 | ||
| 317 | /* | |
| 318 | * Attempt to enter the interrupt condition variable. Returns zero on | |
| 319 | * success, 1 on failure. | |
| 320 | */ | |
| 321 | static __inline | |
| 322 | int | |
| 46d4e165 | 323 | atomic_intr_cond_try(__atomic_intr_t *p) |
| fc3f9779 SS |
324 | { |
| 325 | int ret; | |
| 326 | ||
| 3c9ed2a4 MD |
327 | __asm __volatile(MPLOCKED "incl %0; " \ |
| 328 | "1: ;" \ | |
| 329 | "subl %%eax,%%eax; " \ | |
| 330 | MPLOCKED "btsl $31,%0; jnc 2f; " \ | |
| 331 | MPLOCKED "decl %0; " \ | |
| 332 | "movl $1,%%eax;" \ | |
| 333 | "2: ;" | |
| 334 | : "+m" (*p), "=a"(ret) | |
| c4fb66bd A |
335 | #ifdef __clang__ |
| 336 | : : "ax", "cx", "dx"); | |
| 337 | #else | |
| 46d4e165 | 338 | : : "cx", "dx"); |
| c4fb66bd | 339 | #endif |
| fc3f9779 SS |
340 | return (ret); |
| 341 | } | |
| 342 | ||
| 343 | ||
| 344 | static __inline | |
| 345 | int | |
| 46d4e165 | 346 | atomic_intr_cond_test(__atomic_intr_t *p) |
| fc3f9779 SS |
347 | { |
| 348 | return((int)(*p & 0x80000000)); | |
| 349 | } | |
| 350 | ||
| 351 | static __inline | |
| 352 | void | |
| 46d4e165 | 353 | atomic_intr_cond_exit(__atomic_intr_t *p, void (*func)(void *), void *arg) |
| fc3f9779 SS |
354 | { |
| 355 | __asm __volatile(MPLOCKED "decl %0; " \ | |
| 356 | MPLOCKED "btrl $31,%0; " \ | |
| 357 | "testl $0x3FFFFFFF,%0; jz 1f; " \ | |
| d7f50089 | 358 | "movq %2,%%rdi; call *%1; " \ |
| fc3f9779 SS |
359 | "1: ;" \ |
| 360 | : "+m" (*p) \ | |
| 361 | : "r"(func), "m"(arg) \ | |
| 46d4e165 JG |
362 | : "ax", "cx", "dx", "rsi", "rdi", "r8", "r9", "r10", "r11"); |
| 363 | /* YYY the function call may clobber even more registers? */ | |
| fc3f9779 SS |
364 | } |
| 365 | ||
| 366 | #endif | |
| 367 | ||
| 368 | /* | |
| 46d4e165 | 369 | * Atomic compare and set |
| fc3f9779 | 370 | * |
| 46d4e165 | 371 | * if (*_dst == _old) *_dst = _new (all 32 bit words) |
| fc3f9779 SS |
372 | * |
| 373 | * Returns 0 on failure, non-zero on success | |
| 374 | */ | |
| 46d4e165 JG |
375 | #if defined(KLD_MODULE) |
| 376 | ||
| 377 | extern int atomic_cmpset_int(volatile u_int *_dst, u_int _old, u_int _new); | |
| 378 | extern int atomic_cmpset_long(volatile u_long *dst, u_long exp, u_long src); | |
| 379 | extern u_int atomic_fetchadd_int(volatile u_int *p, u_int v); | |
| fc3f9779 | 380 | |
| 46d4e165 | 381 | #else |
| fc3f9779 SS |
382 | |
| 383 | static __inline int | |
| 46d4e165 | 384 | atomic_cmpset_int(volatile u_int *_dst, u_int _old, u_int _new) |
| fc3f9779 | 385 | { |
| 46d4e165 JG |
386 | int res = _old; |
| 387 | ||
| 388 | __asm __volatile(MPLOCKED "cmpxchgl %2,%1; " \ | |
| 389 | "setz %%al; " \ | |
| 390 | "movzbl %%al,%0; " \ | |
| 391 | : "+a" (res), "=m" (*_dst) \ | |
| 392 | : "r" (_new), "m" (*_dst) \ | |
| 393 | : "memory"); | |
| 394 | return res; | |
| fc3f9779 SS |
395 | } |
| 396 | ||
| 397 | static __inline int | |
| 398 | atomic_cmpset_long(volatile u_long *dst, u_long exp, u_long src) | |
| 399 | { | |
| 46d4e165 JG |
400 | return (atomic_cmpset_int((volatile u_int *)dst, (u_int)exp, |
| 401 | (u_int)src)); | |
| fc3f9779 | 402 | } |
| fc3f9779 | 403 | |
| 46d4e165 JG |
404 | /* |
| 405 | * Atomically add the value of v to the integer pointed to by p and return | |
| 406 | * the previous value of *p. | |
| 407 | */ | |
| 408 | static __inline u_int | |
| 409 | atomic_fetchadd_int(volatile u_int *p, u_int v) | |
| 410 | { | |
| 411 | __asm __volatile(MPLOCKED "xaddl %0,%1; " \ | |
| 412 | : "+r" (v), "=m" (*p) \ | |
| 413 | : "m" (*p) \ | |
| 414 | : "memory"); | |
| 415 | return (v); | |
| 416 | } | |
| d7f50089 | 417 | |
| 46d4e165 | 418 | #endif /* KLD_MODULE */ |
| fc3f9779 | 419 | |
| e774ca6d MD |
420 | #if defined(KLD_MODULE) |
| 421 | ||
| 422 | #define ATOMIC_STORE_LOAD(TYPE, LOP, SOP) \ | |
| 423 | extern u_##TYPE atomic_load_acq_##TYPE(volatile u_##TYPE *p); \ | |
| 424 | extern void atomic_store_rel_##TYPE(volatile u_##TYPE *p, u_##TYPE v); | |
| 425 | ||
| 426 | #else /* !KLD_MODULE */ | |
| 427 | ||
| 428 | #if defined(_KERNEL) && !defined(SMP) | |
| 429 | /* | |
| 430 | * We assume that a = b will do atomic loads and stores. However, on a | |
| 431 | * PentiumPro or higher, reads may pass writes, so for that case we have | |
| 432 | * to use a serializing instruction (i.e. with LOCK) to do the load in | |
| 433 | * SMP kernels. For UP kernels, however, the cache of the single processor | |
| 434 | * is always consistent, so we don't need any memory barriers. | |
| 435 | */ | |
| 436 | #define ATOMIC_STORE_LOAD(TYPE, LOP, SOP) \ | |
| 437 | static __inline u_##TYPE \ | |
| 438 | atomic_load_acq_##TYPE(volatile u_##TYPE *p) \ | |
| 439 | { \ | |
| 440 | return (*p); \ | |
| 441 | } \ | |
| 442 | \ | |
| 443 | static __inline void \ | |
| 444 | atomic_store_rel_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\ | |
| 445 | { \ | |
| 446 | *p = v; \ | |
| 447 | } \ | |
| 448 | struct __hack | |
| 449 | ||
| 450 | #else /* !(_KERNEL && !SMP) */ | |
| 451 | ||
| 452 | #define ATOMIC_STORE_LOAD(TYPE, LOP, SOP) \ | |
| 453 | static __inline u_##TYPE \ | |
| 454 | atomic_load_acq_##TYPE(volatile u_##TYPE *p) \ | |
| 455 | { \ | |
| 456 | u_##TYPE res; \ | |
| 457 | \ | |
| 458 | __asm __volatile(MPLOCKED LOP \ | |
| 459 | : "=a" (res), /* 0 */ \ | |
| 460 | "=m" (*p) /* 1 */ \ | |
| 461 | : "m" (*p) /* 2 */ \ | |
| 462 | : "memory"); \ | |
| 463 | \ | |
| 464 | return (res); \ | |
| 465 | } \ | |
| 466 | \ | |
| 467 | /* \ | |
| 468 | * The XCHG instruction asserts LOCK automagically. \ | |
| 469 | */ \ | |
| 470 | static __inline void \ | |
| 471 | atomic_store_rel_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\ | |
| 472 | { \ | |
| 473 | __asm __volatile(SOP \ | |
| 474 | : "=m" (*p), /* 0 */ \ | |
| 475 | "+r" (v) /* 1 */ \ | |
| 476 | : "m" (*p)); /* 2 */ \ | |
| 477 | } \ | |
| 478 | struct __hack | |
| 479 | ||
| 480 | #endif /* _KERNEL && !SMP */ | |
| 481 | ||
| 482 | #endif /* !KLD_MODULE */ | |
| 483 | ||
| 484 | ATOMIC_STORE_LOAD(char, "cmpxchgb %b0,%1", "xchgb %b1,%0"); | |
| 485 | ATOMIC_STORE_LOAD(short,"cmpxchgw %w0,%1", "xchgw %w1,%0"); | |
| 486 | ATOMIC_STORE_LOAD(int, "cmpxchgl %0,%1", "xchgl %1,%0"); | |
| 487 | ATOMIC_STORE_LOAD(long, "cmpxchgq %0,%1", "xchgq %1,%0"); | |
| 488 | ||
| 46d4e165 | 489 | /* Acquire and release variants are identical to the normal ones. */ |
| fc3f9779 SS |
490 | #define atomic_set_acq_char atomic_set_char |
| 491 | #define atomic_set_rel_char atomic_set_char | |
| 492 | #define atomic_clear_acq_char atomic_clear_char | |
| 493 | #define atomic_clear_rel_char atomic_clear_char | |
| 494 | #define atomic_add_acq_char atomic_add_char | |
| 495 | #define atomic_add_rel_char atomic_add_char | |
| 496 | #define atomic_subtract_acq_char atomic_subtract_char | |
| 497 | #define atomic_subtract_rel_char atomic_subtract_char | |
| 498 | ||
| 499 | #define atomic_set_acq_short atomic_set_short | |
| 500 | #define atomic_set_rel_short atomic_set_short | |
| 501 | #define atomic_clear_acq_short atomic_clear_short | |
| 502 | #define atomic_clear_rel_short atomic_clear_short | |
| 503 | #define atomic_add_acq_short atomic_add_short | |
| 504 | #define atomic_add_rel_short atomic_add_short | |
| 505 | #define atomic_subtract_acq_short atomic_subtract_short | |
| 506 | #define atomic_subtract_rel_short atomic_subtract_short | |
| 507 | ||
| 508 | #define atomic_set_acq_int atomic_set_int | |
| 509 | #define atomic_set_rel_int atomic_set_int | |
| 510 | #define atomic_clear_acq_int atomic_clear_int | |
| 511 | #define atomic_clear_rel_int atomic_clear_int | |
| 512 | #define atomic_add_acq_int atomic_add_int | |
| 513 | #define atomic_add_rel_int atomic_add_int | |
| 514 | #define atomic_subtract_acq_int atomic_subtract_int | |
| 515 | #define atomic_subtract_rel_int atomic_subtract_int | |
| c8fe38ae MD |
516 | #define atomic_cmpset_acq_int atomic_cmpset_int |
| 517 | #define atomic_cmpset_rel_int atomic_cmpset_int | |
| fc3f9779 SS |
518 | |
| 519 | #define atomic_set_acq_long atomic_set_long | |
| 520 | #define atomic_set_rel_long atomic_set_long | |
| 521 | #define atomic_clear_acq_long atomic_clear_long | |
| 522 | #define atomic_clear_rel_long atomic_clear_long | |
| 523 | #define atomic_add_acq_long atomic_add_long | |
| 524 | #define atomic_add_rel_long atomic_add_long | |
| 525 | #define atomic_subtract_acq_long atomic_subtract_long | |
| 526 | #define atomic_subtract_rel_long atomic_subtract_long | |
| 46d4e165 JG |
527 | #define atomic_cmpset_acq_long atomic_cmpset_long |
| 528 | #define atomic_cmpset_rel_long atomic_cmpset_long | |
| fc3f9779 | 529 | |
| 46d4e165 | 530 | /* Operations on 8-bit bytes. */ |
| fc3f9779 SS |
531 | #define atomic_set_8 atomic_set_char |
| 532 | #define atomic_set_acq_8 atomic_set_acq_char | |
| 533 | #define atomic_set_rel_8 atomic_set_rel_char | |
| 534 | #define atomic_clear_8 atomic_clear_char | |
| 535 | #define atomic_clear_acq_8 atomic_clear_acq_char | |
| 536 | #define atomic_clear_rel_8 atomic_clear_rel_char | |
| 537 | #define atomic_add_8 atomic_add_char | |
| 538 | #define atomic_add_acq_8 atomic_add_acq_char | |
| 539 | #define atomic_add_rel_8 atomic_add_rel_char | |
| 540 | #define atomic_subtract_8 atomic_subtract_char | |
| 541 | #define atomic_subtract_acq_8 atomic_subtract_acq_char | |
| 542 | #define atomic_subtract_rel_8 atomic_subtract_rel_char | |
| 543 | #define atomic_load_acq_8 atomic_load_acq_char | |
| 544 | #define atomic_store_rel_8 atomic_store_rel_char | |
| 545 | ||
| c8fe38ae | 546 | /* Operations on 16-bit words. */ |
| fc3f9779 SS |
547 | #define atomic_set_16 atomic_set_short |
| 548 | #define atomic_set_acq_16 atomic_set_acq_short | |
| 549 | #define atomic_set_rel_16 atomic_set_rel_short | |
| 550 | #define atomic_clear_16 atomic_clear_short | |
| 551 | #define atomic_clear_acq_16 atomic_clear_acq_short | |
| 552 | #define atomic_clear_rel_16 atomic_clear_rel_short | |
| 553 | #define atomic_add_16 atomic_add_short | |
| 554 | #define atomic_add_acq_16 atomic_add_acq_short | |
| 555 | #define atomic_add_rel_16 atomic_add_rel_short | |
| 556 | #define atomic_subtract_16 atomic_subtract_short | |
| 557 | #define atomic_subtract_acq_16 atomic_subtract_acq_short | |
| 558 | #define atomic_subtract_rel_16 atomic_subtract_rel_short | |
| 559 | #define atomic_load_acq_16 atomic_load_acq_short | |
| 560 | #define atomic_store_rel_16 atomic_store_rel_short | |
| 561 | ||
| c8fe38ae | 562 | /* Operations on 32-bit double words. */ |
| fc3f9779 SS |
563 | #define atomic_set_32 atomic_set_int |
| 564 | #define atomic_set_acq_32 atomic_set_acq_int | |
| 565 | #define atomic_set_rel_32 atomic_set_rel_int | |
| 566 | #define atomic_clear_32 atomic_clear_int | |
| 567 | #define atomic_clear_acq_32 atomic_clear_acq_int | |
| 568 | #define atomic_clear_rel_32 atomic_clear_rel_int | |
| 569 | #define atomic_add_32 atomic_add_int | |
| 570 | #define atomic_add_acq_32 atomic_add_acq_int | |
| 571 | #define atomic_add_rel_32 atomic_add_rel_int | |
| 572 | #define atomic_subtract_32 atomic_subtract_int | |
| 573 | #define atomic_subtract_acq_32 atomic_subtract_acq_int | |
| 574 | #define atomic_subtract_rel_32 atomic_subtract_rel_int | |
| 575 | #define atomic_load_acq_32 atomic_load_acq_int | |
| 576 | #define atomic_store_rel_32 atomic_store_rel_int | |
| c8fe38ae | 577 | #define atomic_cmpset_32 atomic_cmpset_int |
| fc3f9779 SS |
578 | #define atomic_cmpset_acq_32 atomic_cmpset_acq_int |
| 579 | #define atomic_cmpset_rel_32 atomic_cmpset_rel_int | |
| 580 | #define atomic_readandclear_32 atomic_readandclear_int | |
| c8fe38ae MD |
581 | #define atomic_fetchadd_32 atomic_fetchadd_int |
| 582 | ||
| 583 | /* Operations on pointers. */ | |
| 584 | #define atomic_set_ptr atomic_set_long | |
| 585 | #define atomic_set_acq_ptr atomic_set_acq_long | |
| 586 | #define atomic_set_rel_ptr atomic_set_rel_long | |
| 587 | #define atomic_clear_ptr atomic_clear_long | |
| 588 | #define atomic_clear_acq_ptr atomic_clear_acq_long | |
| 589 | #define atomic_clear_rel_ptr atomic_clear_rel_long | |
| 590 | #define atomic_add_ptr atomic_add_long | |
| 591 | #define atomic_add_acq_ptr atomic_add_acq_long | |
| 592 | #define atomic_add_rel_ptr atomic_add_rel_long | |
| 593 | #define atomic_subtract_ptr atomic_subtract_long | |
| 594 | #define atomic_subtract_acq_ptr atomic_subtract_acq_long | |
| 595 | #define atomic_subtract_rel_ptr atomic_subtract_rel_long | |
| 596 | #define atomic_load_acq_ptr atomic_load_acq_long | |
| 597 | #define atomic_store_rel_ptr atomic_store_rel_long | |
| 598 | #define atomic_cmpset_ptr atomic_cmpset_long | |
| 599 | #define atomic_cmpset_acq_ptr atomic_cmpset_acq_long | |
| 600 | #define atomic_cmpset_rel_ptr atomic_cmpset_rel_long | |
| 601 | #define atomic_readandclear_ptr atomic_readandclear_long | |
| fc3f9779 | 602 | |
| fc3f9779 | 603 | #endif /* ! _CPU_ATOMIC_H_ */ |