/* $NetBSD: lock.h,v 1.27 2013/01/22 22:09:44 christos Exp $ */ /*- * Copyright (c) 2000, 2006 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe and Andrew Doran. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Machine-dependent spin lock operations. */ #ifndef _X86_LOCK_H_ #define _X86_LOCK_H_ #include static __inline int __SIMPLELOCK_LOCKED_P(__cpu_simple_lock_t *__ptr) { return *__ptr == __SIMPLELOCK_LOCKED; } static __inline int __SIMPLELOCK_UNLOCKED_P(__cpu_simple_lock_t *__ptr) { return *__ptr == __SIMPLELOCK_UNLOCKED; } static __inline void __cpu_simple_lock_set(__cpu_simple_lock_t *__ptr) { *__ptr = __SIMPLELOCK_LOCKED; } static __inline void __cpu_simple_lock_clear(__cpu_simple_lock_t *__ptr) { *__ptr = __SIMPLELOCK_UNLOCKED; } #ifdef _HARDKERNEL # include # define SPINLOCK_SPIN_HOOK /* nothing */ # ifdef SPINLOCK_BACKOFF_HOOK # undef SPINLOCK_BACKOFF_HOOK # endif # define SPINLOCK_BACKOFF_HOOK x86_pause() # define SPINLOCK_INLINE #else /* !_HARDKERNEL */ # define SPINLOCK_BODY # define SPINLOCK_INLINE static __inline __unused #endif /* _HARDKERNEL */ SPINLOCK_INLINE void __cpu_simple_lock_init(__cpu_simple_lock_t *); SPINLOCK_INLINE void __cpu_simple_lock(__cpu_simple_lock_t *); SPINLOCK_INLINE int __cpu_simple_lock_try(__cpu_simple_lock_t *); SPINLOCK_INLINE void __cpu_simple_unlock(__cpu_simple_lock_t *); #ifdef SPINLOCK_BODY SPINLOCK_INLINE void __cpu_simple_lock_init(__cpu_simple_lock_t *lockp) { *lockp = __SIMPLELOCK_UNLOCKED; __insn_barrier(); } SPINLOCK_INLINE int __cpu_simple_lock_try(__cpu_simple_lock_t *lockp) { uint8_t val; val = __SIMPLELOCK_LOCKED; __asm volatile ("xchgb %0,(%2)" : "=qQ" (val) :"0" (val), "r" (lockp)); __insn_barrier(); return val == __SIMPLELOCK_UNLOCKED; } SPINLOCK_INLINE void __cpu_simple_lock(__cpu_simple_lock_t *lockp) { while (!__cpu_simple_lock_try(lockp)) /* nothing */; __insn_barrier(); } /* * Note on x86 memory ordering * * When releasing a lock we must ensure that no stores or loads from within * the critical section are re-ordered by the CPU to occur outside of it: * they must have completed and be visible to other processors once the lock * has been released. * * NetBSD usually runs with the kernel mapped (via MTRR) in a WB (write * back) memory region. In that case, memory ordering on x86 platforms * looks like this: * * i386 All loads/stores occur in instruction sequence. * * i486 All loads/stores occur in instruction sequence. In * Pentium exceptional circumstances, loads can be re-ordered around * stores, but for the purposes of releasing a lock it does * not matter. Stores may not be immediately visible to other * processors as they can be buffered. However, since the * stores are buffered in order the lock release will always be * the last operation in the critical section that becomes * visible to other CPUs. * * Pentium Pro The "Intel 64 and IA-32 Architectures Software Developer's * onwards Manual" volume 3A (order number 248966) says that (1) "Reads * can be carried out speculatively and in any order" and (2) * "Reads can pass buffered stores, but the processor is * self-consistent.". This would be a problem for the below, * and would mandate a locked instruction cycle or load fence * before releasing the simple lock. * * The "Intel Pentium 4 Processor Optimization" guide (order * number 253668-022US) says: "Loads can be moved before stores * that occurred earlier in the program if they are not * predicted to load from the same linear address.". This is * not a problem since the only loads that can be re-ordered * take place once the lock has been released via a store. * * The above two documents seem to contradict each other, * however with the exception of early steppings of the Pentium * Pro, the second document is closer to the truth: a store * will always act as a load fence for all loads that precede * the store in instruction order. * * Again, note that stores can be buffered and will not always * become immediately visible to other CPUs: they are however * buffered in order. * * AMD64 Stores occur in order and are buffered. Loads can be * reordered, however stores act as load fences, meaning that * loads can not be reordered around stores. */ SPINLOCK_INLINE void __cpu_simple_unlock(__cpu_simple_lock_t *lockp) { __insn_barrier(); *lockp = __SIMPLELOCK_UNLOCKED; } #endif /* SPINLOCK_BODY */ #endif /* _X86_LOCK_H_ */