1951f22d5SMartin Schwidefsky /* 2951f22d5SMartin Schwidefsky * Out of line spinlock code. 3951f22d5SMartin Schwidefsky * 4a53c8fabSHeiko Carstens * Copyright IBM Corp. 2004, 2006 5951f22d5SMartin Schwidefsky * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) 6951f22d5SMartin Schwidefsky */ 7951f22d5SMartin Schwidefsky 8951f22d5SMartin Schwidefsky #include <linux/types.h> 9d3217967SPaul Gortmaker #include <linux/export.h> 10951f22d5SMartin Schwidefsky #include <linux/spinlock.h> 11951f22d5SMartin Schwidefsky #include <linux/init.h> 128b646bd7SMartin Schwidefsky #include <linux/smp.h> 13951f22d5SMartin Schwidefsky #include <asm/io.h> 14951f22d5SMartin Schwidefsky 152c72a44eSMartin Schwidefsky int spin_retry = -1; 162c72a44eSMartin Schwidefsky 172c72a44eSMartin Schwidefsky static int __init spin_retry_init(void) 182c72a44eSMartin Schwidefsky { 192c72a44eSMartin Schwidefsky if (spin_retry < 0) 20b13de4b7SMartin Schwidefsky spin_retry = 1000; 212c72a44eSMartin Schwidefsky return 0; 222c72a44eSMartin Schwidefsky } 232c72a44eSMartin Schwidefsky early_initcall(spin_retry_init); 24951f22d5SMartin Schwidefsky 25951f22d5SMartin Schwidefsky /** 26951f22d5SMartin Schwidefsky * spin_retry= parameter 27951f22d5SMartin Schwidefsky */ 28951f22d5SMartin Schwidefsky static int __init spin_retry_setup(char *str) 29951f22d5SMartin Schwidefsky { 30951f22d5SMartin Schwidefsky spin_retry = simple_strtoul(str, &str, 0); 31951f22d5SMartin Schwidefsky return 1; 32951f22d5SMartin Schwidefsky } 33951f22d5SMartin Schwidefsky __setup("spin_retry=", spin_retry_setup); 34951f22d5SMartin Schwidefsky 350199c4e6SThomas Gleixner void arch_spin_lock_wait(arch_spinlock_t *lp) 36951f22d5SMartin Schwidefsky { 3702c503ffSMartin Schwidefsky int cpu = SPINLOCK_LOCKVAL; 3802c503ffSMartin Schwidefsky int owner, count, first_diag; 39951f22d5SMartin Schwidefsky 40db1c4515SMartin Schwidefsky first_diag = 1; 41951f22d5SMartin Schwidefsky while (1) { 42470ada6bSMartin Schwidefsky owner = ACCESS_ONCE(lp->lock); 43470ada6bSMartin Schwidefsky /* Try to get the lock if it is free. */ 44470ada6bSMartin Schwidefsky if (!owner) { 4502c503ffSMartin Schwidefsky if (__atomic_cmpxchg_bool(&lp->lock, 0, cpu)) 4659b69787SGerald Schaefer return; 4759b69787SGerald Schaefer continue; 4859b69787SGerald Schaefer } 49db1c4515SMartin Schwidefsky /* First iteration: check if the lock owner is running. */ 50760928c0SChristian Borntraeger if (first_diag && arch_vcpu_is_preempted(~owner)) { 518b646bd7SMartin Schwidefsky smp_yield_cpu(~owner); 52db1c4515SMartin Schwidefsky first_diag = 0; 53470ada6bSMartin Schwidefsky continue; 54470ada6bSMartin Schwidefsky } 55470ada6bSMartin Schwidefsky /* Loop for a while on the lock value. */ 56470ada6bSMartin Schwidefsky count = spin_retry; 57470ada6bSMartin Schwidefsky do { 58470ada6bSMartin Schwidefsky owner = ACCESS_ONCE(lp->lock); 59470ada6bSMartin Schwidefsky } while (owner && count-- > 0); 60470ada6bSMartin Schwidefsky if (!owner) 61470ada6bSMartin Schwidefsky continue; 62470ada6bSMartin Schwidefsky /* 63470ada6bSMartin Schwidefsky * For multiple layers of hypervisors, e.g. z/VM + LPAR 64db1c4515SMartin Schwidefsky * yield the CPU unconditionally. For LPAR rely on the 65db1c4515SMartin Schwidefsky * sense running status. 66470ada6bSMartin Schwidefsky */ 67760928c0SChristian Borntraeger if (!MACHINE_IS_LPAR || arch_vcpu_is_preempted(~owner)) { 68470ada6bSMartin Schwidefsky smp_yield_cpu(~owner); 69db1c4515SMartin Schwidefsky first_diag = 0; 70db1c4515SMartin Schwidefsky } 71951f22d5SMartin Schwidefsky } 72951f22d5SMartin Schwidefsky } 730199c4e6SThomas Gleixner EXPORT_SYMBOL(arch_spin_lock_wait); 74951f22d5SMartin Schwidefsky 750199c4e6SThomas Gleixner void arch_spin_lock_wait_flags(arch_spinlock_t *lp, unsigned long flags) 76894cdde2SHisashi Hifumi { 7702c503ffSMartin Schwidefsky int cpu = SPINLOCK_LOCKVAL; 7802c503ffSMartin Schwidefsky int owner, count, first_diag; 79894cdde2SHisashi Hifumi 80894cdde2SHisashi Hifumi local_irq_restore(flags); 81db1c4515SMartin Schwidefsky first_diag = 1; 82894cdde2SHisashi Hifumi while (1) { 83470ada6bSMartin Schwidefsky owner = ACCESS_ONCE(lp->lock); 84470ada6bSMartin Schwidefsky /* Try to get the lock if it is free. */ 85470ada6bSMartin Schwidefsky if (!owner) { 86894cdde2SHisashi Hifumi local_irq_disable(); 8702c503ffSMartin Schwidefsky if (__atomic_cmpxchg_bool(&lp->lock, 0, cpu)) 8859b69787SGerald Schaefer return; 8959b69787SGerald Schaefer local_irq_restore(flags); 9084976952SHeiko Carstens continue; 91470ada6bSMartin Schwidefsky } 92470ada6bSMartin Schwidefsky /* Check if the lock owner is running. */ 93760928c0SChristian Borntraeger if (first_diag && arch_vcpu_is_preempted(~owner)) { 94470ada6bSMartin Schwidefsky smp_yield_cpu(~owner); 95db1c4515SMartin Schwidefsky first_diag = 0; 9659b69787SGerald Schaefer continue; 9759b69787SGerald Schaefer } 98470ada6bSMartin Schwidefsky /* Loop for a while on the lock value. */ 99470ada6bSMartin Schwidefsky count = spin_retry; 100470ada6bSMartin Schwidefsky do { 101470ada6bSMartin Schwidefsky owner = ACCESS_ONCE(lp->lock); 102470ada6bSMartin Schwidefsky } while (owner && count-- > 0); 103470ada6bSMartin Schwidefsky if (!owner) 104470ada6bSMartin Schwidefsky continue; 105470ada6bSMartin Schwidefsky /* 106470ada6bSMartin Schwidefsky * For multiple layers of hypervisors, e.g. z/VM + LPAR 107db1c4515SMartin Schwidefsky * yield the CPU unconditionally. For LPAR rely on the 108db1c4515SMartin Schwidefsky * sense running status. 109470ada6bSMartin Schwidefsky */ 110760928c0SChristian Borntraeger if (!MACHINE_IS_LPAR || arch_vcpu_is_preempted(~owner)) { 1118b646bd7SMartin Schwidefsky smp_yield_cpu(~owner); 112db1c4515SMartin Schwidefsky first_diag = 0; 113db1c4515SMartin Schwidefsky } 114894cdde2SHisashi Hifumi } 115894cdde2SHisashi Hifumi } 1160199c4e6SThomas Gleixner EXPORT_SYMBOL(arch_spin_lock_wait_flags); 117894cdde2SHisashi Hifumi 1185b3f683eSPhilipp Hachtmann int arch_spin_trylock_retry(arch_spinlock_t *lp) 1195b3f683eSPhilipp Hachtmann { 12002c503ffSMartin Schwidefsky int cpu = SPINLOCK_LOCKVAL; 12102c503ffSMartin Schwidefsky int owner, count; 1225b3f683eSPhilipp Hachtmann 1232c72a44eSMartin Schwidefsky for (count = spin_retry; count > 0; count--) { 124187b5f41SChristian Borntraeger owner = READ_ONCE(lp->lock); 1252c72a44eSMartin Schwidefsky /* Try to get the lock if it is free. */ 1262c72a44eSMartin Schwidefsky if (!owner) { 12702c503ffSMartin Schwidefsky if (__atomic_cmpxchg_bool(&lp->lock, 0, cpu)) 1285b3f683eSPhilipp Hachtmann return 1; 129b13de4b7SMartin Schwidefsky } 1302c72a44eSMartin Schwidefsky } 1315b3f683eSPhilipp Hachtmann return 0; 1325b3f683eSPhilipp Hachtmann } 1335b3f683eSPhilipp Hachtmann EXPORT_SYMBOL(arch_spin_trylock_retry); 1345b3f683eSPhilipp Hachtmann 135fb3a6bbcSThomas Gleixner void _raw_read_lock_wait(arch_rwlock_t *rw) 136951f22d5SMartin Schwidefsky { 137951f22d5SMartin Schwidefsky int count = spin_retry; 13802c503ffSMartin Schwidefsky int owner, old; 139951f22d5SMartin Schwidefsky 140bbae71bfSMartin Schwidefsky #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES 141bbae71bfSMartin Schwidefsky __RAW_LOCK(&rw->lock, -1, __RAW_OP_ADD); 142bbae71bfSMartin Schwidefsky #endif 143d59b93daSMartin Schwidefsky owner = 0; 144951f22d5SMartin Schwidefsky while (1) { 145951f22d5SMartin Schwidefsky if (count-- <= 0) { 146760928c0SChristian Borntraeger if (owner && arch_vcpu_is_preempted(~owner)) 147d59b93daSMartin Schwidefsky smp_yield_cpu(~owner); 148951f22d5SMartin Schwidefsky count = spin_retry; 149951f22d5SMartin Schwidefsky } 150bae8f567SMartin Schwidefsky old = ACCESS_ONCE(rw->lock); 151d59b93daSMartin Schwidefsky owner = ACCESS_ONCE(rw->owner); 152b13de4b7SMartin Schwidefsky if (old < 0) 15396567161SChristian Ehrhardt continue; 15402c503ffSMartin Schwidefsky if (__atomic_cmpxchg_bool(&rw->lock, old, old + 1)) 155951f22d5SMartin Schwidefsky return; 156951f22d5SMartin Schwidefsky } 157951f22d5SMartin Schwidefsky } 158951f22d5SMartin Schwidefsky EXPORT_SYMBOL(_raw_read_lock_wait); 159951f22d5SMartin Schwidefsky 160fb3a6bbcSThomas Gleixner int _raw_read_trylock_retry(arch_rwlock_t *rw) 161951f22d5SMartin Schwidefsky { 162951f22d5SMartin Schwidefsky int count = spin_retry; 16302c503ffSMartin Schwidefsky int old; 164951f22d5SMartin Schwidefsky 165951f22d5SMartin Schwidefsky while (count-- > 0) { 166bae8f567SMartin Schwidefsky old = ACCESS_ONCE(rw->lock); 167b13de4b7SMartin Schwidefsky if (old < 0) 16896567161SChristian Ehrhardt continue; 16902c503ffSMartin Schwidefsky if (__atomic_cmpxchg_bool(&rw->lock, old, old + 1)) 170951f22d5SMartin Schwidefsky return 1; 171951f22d5SMartin Schwidefsky } 172951f22d5SMartin Schwidefsky return 0; 173951f22d5SMartin Schwidefsky } 174951f22d5SMartin Schwidefsky EXPORT_SYMBOL(_raw_read_trylock_retry); 175951f22d5SMartin Schwidefsky 176bbae71bfSMartin Schwidefsky #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES 177bbae71bfSMartin Schwidefsky 17802c503ffSMartin Schwidefsky void _raw_write_lock_wait(arch_rwlock_t *rw, int prev) 179bbae71bfSMartin Schwidefsky { 180bbae71bfSMartin Schwidefsky int count = spin_retry; 18102c503ffSMartin Schwidefsky int owner, old; 182bbae71bfSMartin Schwidefsky 183bbae71bfSMartin Schwidefsky owner = 0; 184bbae71bfSMartin Schwidefsky while (1) { 185bbae71bfSMartin Schwidefsky if (count-- <= 0) { 186760928c0SChristian Borntraeger if (owner && arch_vcpu_is_preempted(~owner)) 187bbae71bfSMartin Schwidefsky smp_yield_cpu(~owner); 188bbae71bfSMartin Schwidefsky count = spin_retry; 189bbae71bfSMartin Schwidefsky } 190bbae71bfSMartin Schwidefsky old = ACCESS_ONCE(rw->lock); 191bbae71bfSMartin Schwidefsky owner = ACCESS_ONCE(rw->owner); 192e0af21c5SChristian Borntraeger smp_mb(); 19302c503ffSMartin Schwidefsky if (old >= 0) { 194bbae71bfSMartin Schwidefsky prev = __RAW_LOCK(&rw->lock, 0x80000000, __RAW_OP_OR); 195bbae71bfSMartin Schwidefsky old = prev; 196bbae71bfSMartin Schwidefsky } 19702c503ffSMartin Schwidefsky if ((old & 0x7fffffff) == 0 && prev >= 0) 198bbae71bfSMartin Schwidefsky break; 199bbae71bfSMartin Schwidefsky } 200bbae71bfSMartin Schwidefsky } 201bbae71bfSMartin Schwidefsky EXPORT_SYMBOL(_raw_write_lock_wait); 202bbae71bfSMartin Schwidefsky 203bbae71bfSMartin Schwidefsky #else /* CONFIG_HAVE_MARCH_Z196_FEATURES */ 204bbae71bfSMartin Schwidefsky 205fb3a6bbcSThomas Gleixner void _raw_write_lock_wait(arch_rwlock_t *rw) 206951f22d5SMartin Schwidefsky { 207951f22d5SMartin Schwidefsky int count = spin_retry; 20802c503ffSMartin Schwidefsky int owner, old, prev; 209951f22d5SMartin Schwidefsky 21094232a43SMartin Schwidefsky prev = 0x80000000; 211d59b93daSMartin Schwidefsky owner = 0; 212951f22d5SMartin Schwidefsky while (1) { 213951f22d5SMartin Schwidefsky if (count-- <= 0) { 214760928c0SChristian Borntraeger if (owner && arch_vcpu_is_preempted(~owner)) 215d59b93daSMartin Schwidefsky smp_yield_cpu(~owner); 216951f22d5SMartin Schwidefsky count = spin_retry; 217951f22d5SMartin Schwidefsky } 218bae8f567SMartin Schwidefsky old = ACCESS_ONCE(rw->lock); 219d59b93daSMartin Schwidefsky owner = ACCESS_ONCE(rw->owner); 22002c503ffSMartin Schwidefsky if (old >= 0 && 22102c503ffSMartin Schwidefsky __atomic_cmpxchg_bool(&rw->lock, old, old | 0x80000000)) 22294232a43SMartin Schwidefsky prev = old; 22394232a43SMartin Schwidefsky else 224e0af21c5SChristian Borntraeger smp_mb(); 22502c503ffSMartin Schwidefsky if ((old & 0x7fffffff) == 0 && prev >= 0) 22694232a43SMartin Schwidefsky break; 227951f22d5SMartin Schwidefsky } 228951f22d5SMartin Schwidefsky } 229951f22d5SMartin Schwidefsky EXPORT_SYMBOL(_raw_write_lock_wait); 230951f22d5SMartin Schwidefsky 231bbae71bfSMartin Schwidefsky #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */ 232bbae71bfSMartin Schwidefsky 233fb3a6bbcSThomas Gleixner int _raw_write_trylock_retry(arch_rwlock_t *rw) 234951f22d5SMartin Schwidefsky { 235951f22d5SMartin Schwidefsky int count = spin_retry; 23602c503ffSMartin Schwidefsky int old; 237951f22d5SMartin Schwidefsky 238951f22d5SMartin Schwidefsky while (count-- > 0) { 239bae8f567SMartin Schwidefsky old = ACCESS_ONCE(rw->lock); 240b13de4b7SMartin Schwidefsky if (old) 24196567161SChristian Ehrhardt continue; 24202c503ffSMartin Schwidefsky if (__atomic_cmpxchg_bool(&rw->lock, 0, 0x80000000)) 243951f22d5SMartin Schwidefsky return 1; 244951f22d5SMartin Schwidefsky } 245951f22d5SMartin Schwidefsky return 0; 246951f22d5SMartin Schwidefsky } 247951f22d5SMartin Schwidefsky EXPORT_SYMBOL(_raw_write_trylock_retry); 248d59b93daSMartin Schwidefsky 24902c503ffSMartin Schwidefsky void arch_lock_relax(int cpu) 250d59b93daSMartin Schwidefsky { 251d59b93daSMartin Schwidefsky if (!cpu) 252d59b93daSMartin Schwidefsky return; 253760928c0SChristian Borntraeger if (MACHINE_IS_LPAR && !arch_vcpu_is_preempted(~cpu)) 254d59b93daSMartin Schwidefsky return; 255d59b93daSMartin Schwidefsky smp_yield_cpu(~cpu); 256d59b93daSMartin Schwidefsky } 257d59b93daSMartin Schwidefsky EXPORT_SYMBOL(arch_lock_relax); 258