/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright ( C ) 2004 , 2007 - 2010 , 2011 - 2012 Synopsys , Inc . ( www . synopsys . com )
*/
#ifndef __ASM_SPINLOCK_H
#define __ASM_SPINLOCK_H
#include <asm /spinlock_types.h>
#include <asm /processor.h>
#include <asm /barrier.h>
#define arch_spin_is_locked(x) ((x)->slock != __ARCH_SPIN_LOCK_UNLOCKED__)
#ifdef CONFIG_ARC_HAS_LLSC
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned int val;
__asm__ __volatile__(
"1: llock %[val], [%[slock]] \n"
" breq %[val], %[LOCKED], 1b \n" /* spin while LOCKED */
" scond %[LOCKED], [%[slock]] \n" /* acquire */
" bnz 1b \n"
" \n"
: [val] "=&r" (val)
: [slock] "r" (&(lock->slock)),
[LOCKED] "r" (__ARCH_SPIN_LOCK_LOCKED__)
: "memory" , "cc" );
/*
* ACQUIRE barrier to ensure load / store after taking the lock
* don ' t " bleed - up " out of the critical section ( leak - in is allowed )
* http : //www.spinics.net/lists/kernel/msg2010409.html
*
* ARCv2 only has load - load , store - store and all - all barrier
* thus need the full all - all barrier
*/
smp_mb();
}
/* 1 - lock taken successfully */
static inline int arch_spin_trylock(arch_spinlock_t *lock)
{
unsigned int val, got_it = 0 ;
__asm__ __volatile__(
"1: llock %[val], [%[slock]] \n"
" breq %[val], %[LOCKED], 4f \n" /* already LOCKED, just bail */
" scond %[LOCKED], [%[slock]] \n" /* acquire */
" bnz 1b \n"
" mov %[got_it], 1 \n"
"4: \n"
" \n"
: [val] "=&r" (val),
[got_it] "+&r" (got_it)
: [slock] "r" (&(lock->slock)),
[LOCKED] "r" (__ARCH_SPIN_LOCK_LOCKED__)
: "memory" , "cc" );
smp_mb();
return got_it;
}
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
smp_mb();
WRITE_ONCE(lock->slock, __ARCH_SPIN_LOCK_UNLOCKED__);
}
/*
* Read - write spinlocks , allowing multiple readers but only one writer .
* Unfair locking as Writers could be starved indefinitely by Reader ( s )
*/
static inline void arch_read_lock(arch_rwlock_t *rw)
{
unsigned int val;
/*
* zero means writer holds the lock exclusively , deny Reader .
* Otherwise grant lock to first / subseq reader
*
* if ( rw - > counter > 0 ) {
* rw - > counter - - ;
* ret = 1 ;
* }
*/
__asm__ __volatile__(
"1: llock %[val], [%[rwlock]] \n"
" brls %[val], %[WR_LOCKED], 1b\n" /* <= 0: spin while write locked */
" sub %[val], %[val], 1 \n" /* reader lock */
" scond %[val], [%[rwlock]] \n"
" bnz 1b \n"
" \n"
: [val] "=&r" (val)
: [rwlock] "r" (&(rw->counter)),
[WR_LOCKED] "ir" (0 )
: "memory" , "cc" );
smp_mb();
}
/* 1 - lock taken successfully */
static inline int arch_read_trylock(arch_rwlock_t *rw)
{
unsigned int val, got_it = 0 ;
__asm__ __volatile__(
"1: llock %[val], [%[rwlock]] \n"
" brls %[val], %[WR_LOCKED], 4f\n" /* <= 0: already write locked, bail */
" sub %[val], %[val], 1 \n" /* counter-- */
" scond %[val], [%[rwlock]] \n"
" bnz 1b \n" /* retry if collided with someone */
" mov %[got_it], 1 \n"
" \n"
"4: ; --- done --- \n"
: [val] "=&r" (val),
[got_it] "+&r" (got_it)
: [rwlock] "r" (&(rw->counter)),
[WR_LOCKED] "ir" (0 )
: "memory" , "cc" );
smp_mb();
return got_it;
}
static inline void arch_write_lock(arch_rwlock_t *rw)
{
unsigned int val;
/*
* If reader ( s ) hold lock ( lock < _ _ ARCH_RW_LOCK_UNLOCKED__ ) ,
* deny writer . Otherwise if unlocked grant to writer
* Hence the claim that Linux rwlocks are unfair to writers .
* ( can be starved for an indefinite time by readers ) .
*
* if ( rw - > counter = = _ _ ARCH_RW_LOCK_UNLOCKED__ ) {
* rw - > counter = 0 ;
* ret = 1 ;
* }
*/
__asm__ __volatile__(
"1: llock %[val], [%[rwlock]] \n"
" brne %[val], %[UNLOCKED], 1b \n" /* while !UNLOCKED spin */
" mov %[val], %[WR_LOCKED] \n"
" scond %[val], [%[rwlock]] \n"
" bnz 1b \n"
" \n"
: [val] "=&r" (val)
: [rwlock] "r" (&(rw->counter)),
[UNLOCKED] "ir" (__ARCH_RW_LOCK_UNLOCKED__),
[WR_LOCKED] "ir" (0 )
: "memory" , "cc" );
smp_mb();
}
/* 1 - lock taken successfully */
static inline int arch_write_trylock(arch_rwlock_t *rw)
{
unsigned int val, got_it = 0 ;
__asm__ __volatile__(
"1: llock %[val], [%[rwlock]] \n"
" brne %[val], %[UNLOCKED], 4f \n" /* !UNLOCKED, bail */
" mov %[val], %[WR_LOCKED] \n"
" scond %[val], [%[rwlock]] \n"
" bnz 1b \n" /* retry if collided with someone */
" mov %[got_it], 1 \n"
" \n"
"4: ; --- done --- \n"
: [val] "=&r" (val),
[got_it] "+&r" (got_it)
: [rwlock] "r" (&(rw->counter)),
[UNLOCKED] "ir" (__ARCH_RW_LOCK_UNLOCKED__),
[WR_LOCKED] "ir" (0 )
: "memory" , "cc" );
smp_mb();
return got_it;
}
static inline void arch_read_unlock(arch_rwlock_t *rw)
{
unsigned int val;
smp_mb();
/*
* rw - > counter + + ;
*/
__asm__ __volatile__(
"1: llock %[val], [%[rwlock]] \n"
" add %[val], %[val], 1 \n"
" scond %[val], [%[rwlock]] \n"
" bnz 1b \n"
" \n"
: [val] "=&r" (val)
: [rwlock] "r" (&(rw->counter))
: "memory" , "cc" );
}
static inline void arch_write_unlock(arch_rwlock_t *rw)
{
smp_mb();
WRITE_ONCE(rw->counter, __ARCH_RW_LOCK_UNLOCKED__);
}
#else /* !CONFIG_ARC_HAS_LLSC */
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned int val = __ARCH_SPIN_LOCK_LOCKED__;
/*
* Per lkmm , smp_mb ( ) is only required after _ lock ( and before_unlock )
* for ACQ and REL semantics respectively . However EX based spinlocks
* need the extra smp_mb to workaround a hardware quirk .
*/
smp_mb();
__asm__ __volatile__(
"1: ex %0, [%1] \n"
" breq %0, %2, 1b \n"
: "+&r" (val)
: "r" (&(lock->slock)), "ir" (__ARCH_SPIN_LOCK_LOCKED__)
: "memory" );
smp_mb();
}
/* 1 - lock taken successfully */
static inline int arch_spin_trylock(arch_spinlock_t *lock)
{
unsigned int val = __ARCH_SPIN_LOCK_LOCKED__;
smp_mb();
__asm__ __volatile__(
"1: ex %0, [%1] \n"
: "+r" (val)
: "r" (&(lock->slock))
: "memory" );
smp_mb();
return (val == __ARCH_SPIN_LOCK_UNLOCKED__);
}
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
unsigned int val = __ARCH_SPIN_LOCK_UNLOCKED__;
/*
* RELEASE barrier : given the instructions avail on ARCv2 , full barrier
* is the only option
*/
smp_mb();
/*
* EX is not really required here , a simple STore of 0 suffices .
* However this causes tasklist livelocks in SystemC based SMP virtual
* platforms where the systemc core scheduler uses EX as a cue for
* moving to next core . Do a git log of this file for details
*/
__asm__ __volatile__(
" ex %0, [%1] \n"
: "+r" (val)
: "r" (&(lock->slock))
: "memory" );
/*
* see pairing version / comment in arch_spin_lock above
*/
smp_mb();
}
/*
* Read - write spinlocks , allowing multiple readers but only one writer .
* Unfair locking as Writers could be starved indefinitely by Reader ( s )
*
* The spinlock itself is contained in @ counter and access to it is
* serialized with @ lock_mutex .
*/
/* 1 - lock taken successfully */
static inline int arch_read_trylock(arch_rwlock_t *rw)
{
int ret = 0 ;
unsigned long flags;
local_irq_save(flags);
arch_spin_lock(&(rw->lock_mutex));
/*
* zero means writer holds the lock exclusively , deny Reader .
* Otherwise grant lock to first / subseq reader
*/
if (rw->counter > 0 ) {
rw->counter--;
ret = 1 ;
}
arch_spin_unlock(&(rw->lock_mutex));
local_irq_restore(flags);
return ret;
}
/* 1 - lock taken successfully */
static inline int arch_write_trylock(arch_rwlock_t *rw)
{
int ret = 0 ;
unsigned long flags;
local_irq_save(flags);
arch_spin_lock(&(rw->lock_mutex));
/*
* If reader ( s ) hold lock ( lock < _ _ ARCH_RW_LOCK_UNLOCKED__ ) ,
* deny writer . Otherwise if unlocked grant to writer
* Hence the claim that Linux rwlocks are unfair to writers .
* ( can be starved for an indefinite time by readers ) .
*/
if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) {
rw->counter = 0 ;
ret = 1 ;
}
arch_spin_unlock(&(rw->lock_mutex));
local_irq_restore(flags);
return ret;
}
static inline void arch_read_lock(arch_rwlock_t *rw)
{
while (!arch_read_trylock(rw))
cpu_relax();
}
static inline void arch_write_lock(arch_rwlock_t *rw)
{
while (!arch_write_trylock(rw))
cpu_relax();
}
static inline void arch_read_unlock(arch_rwlock_t *rw)
{
unsigned long flags;
local_irq_save(flags);
arch_spin_lock(&(rw->lock_mutex));
rw->counter++;
arch_spin_unlock(&(rw->lock_mutex));
local_irq_restore(flags);
}
static inline void arch_write_unlock(arch_rwlock_t *rw)
{
unsigned long flags;
local_irq_save(flags);
arch_spin_lock(&(rw->lock_mutex));
rw->counter = __ARCH_RW_LOCK_UNLOCKED__;
arch_spin_unlock(&(rw->lock_mutex));
local_irq_restore(flags);
}
#endif
#endif /* __ASM_SPINLOCK_H */
Messung V0.5 in Prozent C=89 H=94 G=91
¤ Dauer der Verarbeitung: 0.5 Sekunden
¤
*© Formatika GbR, Deutschland