18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci#ifndef __ASM_SPINLOCK_H 38c2ecf20Sopenharmony_ci#define __ASM_SPINLOCK_H 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <asm/barrier.h> 68c2ecf20Sopenharmony_ci#include <asm/ldcw.h> 78c2ecf20Sopenharmony_ci#include <asm/processor.h> 88c2ecf20Sopenharmony_ci#include <asm/spinlock_types.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistatic inline int arch_spin_is_locked(arch_spinlock_t *x) 118c2ecf20Sopenharmony_ci{ 128c2ecf20Sopenharmony_ci volatile unsigned int *a = __ldcw_align(x); 138c2ecf20Sopenharmony_ci return READ_ONCE(*a) == 0; 148c2ecf20Sopenharmony_ci} 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic inline void arch_spin_lock(arch_spinlock_t *x) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci volatile unsigned int *a; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci a = __ldcw_align(x); 218c2ecf20Sopenharmony_ci while (__ldcw(a) == 0) 228c2ecf20Sopenharmony_ci while (*a == 0) 238c2ecf20Sopenharmony_ci continue; 248c2ecf20Sopenharmony_ci} 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic inline void arch_spin_lock_flags(arch_spinlock_t *x, 278c2ecf20Sopenharmony_ci unsigned long flags) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci volatile unsigned int *a; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci a = __ldcw_align(x); 328c2ecf20Sopenharmony_ci while (__ldcw(a) == 0) 338c2ecf20Sopenharmony_ci while (*a == 0) 348c2ecf20Sopenharmony_ci if (flags & PSW_SM_I) { 358c2ecf20Sopenharmony_ci local_irq_enable(); 368c2ecf20Sopenharmony_ci local_irq_disable(); 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci#define arch_spin_lock_flags arch_spin_lock_flags 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic inline void arch_spin_unlock(arch_spinlock_t *x) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci volatile unsigned int *a; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci a = __ldcw_align(x); 468c2ecf20Sopenharmony_ci /* Release with ordered store. */ 478c2ecf20Sopenharmony_ci __asm__ __volatile__("stw,ma %0,0(%1)" : : "r"(1), "r"(a) : "memory"); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic inline int arch_spin_trylock(arch_spinlock_t *x) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci volatile unsigned int *a; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci a = __ldcw_align(x); 558c2ecf20Sopenharmony_ci return __ldcw(a) != 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Read-write spinlocks, allowing multiple readers but only one writer. 608c2ecf20Sopenharmony_ci * Unfair locking as Writers could be starved indefinitely by Reader(s) 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * The spinlock itself is contained in @counter and access to it is 638c2ecf20Sopenharmony_ci * serialized with @lock_mutex. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 1 - lock taken successfully */ 678c2ecf20Sopenharmony_cistatic inline int arch_read_trylock(arch_rwlock_t *rw) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci int ret = 0; 708c2ecf20Sopenharmony_ci unsigned long flags; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci local_irq_save(flags); 738c2ecf20Sopenharmony_ci arch_spin_lock(&(rw->lock_mutex)); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* 768c2ecf20Sopenharmony_ci * zero means writer holds the lock exclusively, deny Reader. 778c2ecf20Sopenharmony_ci * Otherwise grant lock to first/subseq reader 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci if (rw->counter > 0) { 808c2ecf20Sopenharmony_ci rw->counter--; 818c2ecf20Sopenharmony_ci ret = 1; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci arch_spin_unlock(&(rw->lock_mutex)); 858c2ecf20Sopenharmony_ci local_irq_restore(flags); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 1 - lock taken successfully */ 918c2ecf20Sopenharmony_cistatic inline int arch_write_trylock(arch_rwlock_t *rw) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int ret = 0; 948c2ecf20Sopenharmony_ci unsigned long flags; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci local_irq_save(flags); 978c2ecf20Sopenharmony_ci arch_spin_lock(&(rw->lock_mutex)); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__), 1018c2ecf20Sopenharmony_ci * deny writer. Otherwise if unlocked grant to writer 1028c2ecf20Sopenharmony_ci * Hence the claim that Linux rwlocks are unfair to writers. 1038c2ecf20Sopenharmony_ci * (can be starved for an indefinite time by readers). 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) { 1068c2ecf20Sopenharmony_ci rw->counter = 0; 1078c2ecf20Sopenharmony_ci ret = 1; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci arch_spin_unlock(&(rw->lock_mutex)); 1108c2ecf20Sopenharmony_ci local_irq_restore(flags); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic inline void arch_read_lock(arch_rwlock_t *rw) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci while (!arch_read_trylock(rw)) 1188c2ecf20Sopenharmony_ci cpu_relax(); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic inline void arch_write_lock(arch_rwlock_t *rw) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci while (!arch_write_trylock(rw)) 1248c2ecf20Sopenharmony_ci cpu_relax(); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic inline void arch_read_unlock(arch_rwlock_t *rw) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci unsigned long flags; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci local_irq_save(flags); 1328c2ecf20Sopenharmony_ci arch_spin_lock(&(rw->lock_mutex)); 1338c2ecf20Sopenharmony_ci rw->counter++; 1348c2ecf20Sopenharmony_ci arch_spin_unlock(&(rw->lock_mutex)); 1358c2ecf20Sopenharmony_ci local_irq_restore(flags); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic inline void arch_write_unlock(arch_rwlock_t *rw) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci unsigned long flags; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci local_irq_save(flags); 1438c2ecf20Sopenharmony_ci arch_spin_lock(&(rw->lock_mutex)); 1448c2ecf20Sopenharmony_ci rw->counter = __ARCH_RW_LOCK_UNLOCKED__; 1458c2ecf20Sopenharmony_ci arch_spin_unlock(&(rw->lock_mutex)); 1468c2ecf20Sopenharmony_ci local_irq_restore(flags); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#endif /* __ASM_SPINLOCK_H */ 150