/* * S390 version * Copyright IBM Corp. 1999 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) * * Derived from "include/asm-i386/spinlock.h" */ #ifndef __ASM_SPINLOCK_H #define __ASM_SPINLOCK_H #include <linux/smp.h> #define SPINLOCK_LOCKVAL (S390_lowcore.spinlock_lockval) extern int spin_retry; static inline int _raw_compare_and_swap(unsigned int *lock, unsigned int old, unsigned int new) { return __sync_bool_compare_and_swap(lock, old, new); } /* * Simple spin lock operations. There are two variants, one clears IRQ's * on the local processor, one does not. * * We make no fairness assumptions. They have a cost. * * (the type definitions are in asm/spinlock_types.h) */ void arch_lock_relax(unsigned int cpu); void arch_spin_lock_wait(arch_spinlock_t *); int arch_spin_trylock_retry(arch_spinlock_t *); void arch_spin_lock_wait_flags(arch_spinlock_t *, unsigned long flags); static inline void arch_spin_relax(arch_spinlock_t *lock) { arch_lock_relax(lock->lock); } static inline u32 arch_spin_lockval(int cpu) { return ~cpu; } static inline int arch_spin_value_unlocked(arch_spinlock_t lock) { return lock.lock == 0; } static inline int arch_spin_is_locked(arch_spinlock_t *lp) { return ACCESS_ONCE(lp->lock) != 0; } static inline int arch_spin_trylock_once(arch_spinlock_t *lp) { barrier(); return likely(arch_spin_value_unlocked(*lp) && _raw_compare_and_swap(&lp->lock, 0, SPINLOCK_LOCKVAL)); } static inline void arch_spin_lock(arch_spinlock_t *lp) { if (!arch_spin_trylock_once(lp)) arch_spin_lock_wait(lp); } static inline void arch_spin_lock_flags(arch_spinlock_t *lp, unsigned long flags) { if (!arch_spin_trylock_once(lp)) arch_spin_lock_wait_flags(lp, flags); } static inline int arch_spin_trylock(arch_spinlock_t *lp) { if (!arch_spin_trylock_once(lp)) return arch_spin_trylock_retry(lp); return 1; } static inline void arch_spin_unlock(arch_spinlock_t *lp) { typecheck(unsigned int, lp->lock); asm volatile( __ASM_BARRIER "st %1,%0\n" : "+Q" (lp->lock) : "d" (0) : "cc", "memory"); } static inline void arch_spin_unlock_wait(arch_spinlock_t *lock) { while (arch_spin_is_locked(lock)) arch_spin_relax(lock); } /* * Read-write spinlocks, allowing multiple readers * but only one writer. * * NOTE! it is quite common to have readers in interrupts * but no interrupt writers. For those circumstances we * can "mix" irq-safe locks - any writer needs to get a * irq-safe write-lock, but readers can get non-irqsafe * read-locks. */ /** * read_can_lock - would read_trylock() succeed? * @lock: the rwlock in question. */ #define arch_read_can_lock(x) ((int)(x)->lock >= 0) /** * write_can_lock - would write_trylock() succeed? * @lock: the rwlock in question. */ #define arch_write_can_lock(x) ((x)->lock == 0) extern int _raw_read_trylock_retry(arch_rwlock_t *lp); extern int _raw_write_trylock_retry(arch_rwlock_t *lp); #define arch_read_lock_flags(lock, flags) arch_read_lock(lock) #define arch_write_lock_flags(lock, flags) arch_write_lock(lock) static inline int arch_read_trylock_once(arch_rwlock_t *rw) { unsigned int old = ACCESS_ONCE(rw->lock); return likely((int) old >= 0 && _raw_compare_and_swap(&rw->lock, old, old + 1)); } static inline int arch_write_trylock_once(arch_rwlock_t *rw) { unsigned int old = ACCESS_ONCE(rw->lock); return likely(old == 0 && _raw_compare_and_swap(&rw->lock, 0, 0x80000000)); } #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES #define __RAW_OP_OR "lao" #define __RAW_OP_AND "lan" #define __RAW_OP_ADD "laa" #define __RAW_LOCK(ptr, op_val, op_string) \ ({ \ unsigned int old_val; \ \ typecheck(unsigned int *, ptr); \ asm volatile( \ op_string " %0,%2,%1\n" \ "bcr 14,0\n" \ : "=d" (old_val), "+Q" (*ptr) \ : "d" (op_val) \ : "cc", "memory"); \ old_val; \ }) #define __RAW_UNLOCK(ptr, op_val, op_string) \ ({ \ unsigned int old_val; \ \ typecheck(unsigned int *, ptr); \ asm volatile( \ "bcr 14,0\n" \ op_string " %0,%2,%1\n" \ : "=d" (old_val), "+Q" (*ptr) \ : "d" (op_val) \ : "cc", "memory"); \ old_val; \ }) extern void _raw_read_lock_wait(arch_rwlock_t *lp); extern void _raw_write_lock_wait(arch_rwlock_t *lp, unsigned int prev); static inline void arch_read_lock(arch_rwlock_t *rw) { unsigned int old; old = __RAW_LOCK(&rw->lock, 1, __RAW_OP_ADD); if ((int) old < 0) _raw_read_lock_wait(rw); } static inline void arch_read_unlock(arch_rwlock_t *rw) { __RAW_UNLOCK(&rw->lock, -1, __RAW_OP_ADD); } static inline void arch_write_lock(arch_rwlock_t *rw) { unsigned int old; old = __RAW_LOCK(&rw->lock, 0x80000000, __RAW_OP_OR); if (old != 0) _raw_write_lock_wait(rw, old); rw->owner = SPINLOCK_LOCKVAL; } static inline void arch_write_unlock(arch_rwlock_t *rw) { rw->owner = 0; __RAW_UNLOCK(&rw->lock, 0x7fffffff, __RAW_OP_AND); } #else /* CONFIG_HAVE_MARCH_Z196_FEATURES */ extern void _raw_read_lock_wait(arch_rwlock_t *lp); extern void _raw_write_lock_wait(arch_rwlock_t *lp); static inline void arch_read_lock(arch_rwlock_t *rw) { if (!arch_read_trylock_once(rw)) _raw_read_lock_wait(rw); } static inline void arch_read_unlock(arch_rwlock_t *rw) { unsigned int old; do { old = ACCESS_ONCE(rw->lock); } while (!_raw_compare_and_swap(&rw->lock, old, old - 1)); } static inline void arch_write_lock(arch_rwlock_t *rw) { if (!arch_write_trylock_once(rw)) _raw_write_lock_wait(rw); rw->owner = SPINLOCK_LOCKVAL; } static inline void arch_write_unlock(arch_rwlock_t *rw) { typecheck(unsigned int, rw->lock); rw->owner = 0; asm volatile( __ASM_BARRIER "st %1,%0\n" : "+Q" (rw->lock) : "d" (0) : "cc", "memory"); } #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */ static inline int arch_read_trylock(arch_rwlock_t *rw) { if (!arch_read_trylock_once(rw)) return _raw_read_trylock_retry(rw); return 1; } static inline int arch_write_trylock(arch_rwlock_t *rw) { if (!arch_write_trylock_once(rw) && !_raw_write_trylock_retry(rw)) return 0; rw->owner = SPINLOCK_LOCKVAL; return 1; } static inline void arch_read_relax(arch_rwlock_t *rw) { arch_lock_relax(rw->owner); } static inline void arch_write_relax(arch_rwlock_t *rw) { arch_lock_relax(rw->owner); } #endif /* __ASM_SPINLOCK_H */