2012-03-05 19:49:30 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 ARM Ltd.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#ifndef __ASM_SPINLOCK_H
|
|
|
|
#define __ASM_SPINLOCK_H
|
|
|
|
|
2015-02-10 11:03:15 +08:00
|
|
|
#include <asm/lse.h>
|
2012-03-05 19:49:30 +08:00
|
|
|
#include <asm/spinlock_types.h>
|
|
|
|
#include <asm/processor.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Spinlock implementation.
|
|
|
|
*
|
|
|
|
* The memory barriers are implicit with the load-acquire and store-release
|
|
|
|
* instructions.
|
|
|
|
*/
|
2015-11-20 01:48:31 +08:00
|
|
|
static inline void arch_spin_unlock_wait(arch_spinlock_t *lock)
|
|
|
|
{
|
|
|
|
unsigned int tmp;
|
|
|
|
arch_spinlock_t lockval;
|
arm64: spinlock: Ensure forward-progress in spin_unlock_wait
Rather than wait until we observe the lock being free (which might never
happen), we can also return from spin_unlock_wait if we observe that the
lock is now held by somebody else, which implies that it was unlocked
but we just missed seeing it in that state.
Furthermore, in such a scenario there is no longer a need to write back
the value that we loaded, since we know that there has been a lock
hand-off, which is sufficient to publish any stores prior to the
unlock_wait because the ARm architecture ensures that a Store-Release
instruction is multi-copy atomic when observed by a Load-Acquire
instruction.
The litmus test is something like:
AArch64
{
0:X1=x; 0:X3=y;
1:X1=y;
2:X1=y; 2:X3=x;
}
P0 | P1 | P2 ;
MOV W0,#1 | MOV W0,#1 | LDAR W0,[X1] ;
STR W0,[X1] | STLR W0,[X1] | LDR W2,[X3] ;
DMB SY | | ;
LDR W2,[X3] | | ;
exists
(0:X2=0 /\ 2:X0=1 /\ 2:X2=0)
where P0 is doing spin_unlock_wait, P1 is doing spin_unlock and P2 is
doing spin_lock.
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-06-03 01:40:07 +08:00
|
|
|
u32 owner;
|
2012-03-05 19:49:30 +08:00
|
|
|
|
2016-06-02 22:27:04 +08:00
|
|
|
/*
|
|
|
|
* Ensure prior spin_lock operations to other locks have completed
|
|
|
|
* on this CPU before we test whether "lock" is locked.
|
|
|
|
*/
|
|
|
|
smp_mb();
|
arm64: spinlock: Ensure forward-progress in spin_unlock_wait
Rather than wait until we observe the lock being free (which might never
happen), we can also return from spin_unlock_wait if we observe that the
lock is now held by somebody else, which implies that it was unlocked
but we just missed seeing it in that state.
Furthermore, in such a scenario there is no longer a need to write back
the value that we loaded, since we know that there has been a lock
hand-off, which is sufficient to publish any stores prior to the
unlock_wait because the ARm architecture ensures that a Store-Release
instruction is multi-copy atomic when observed by a Load-Acquire
instruction.
The litmus test is something like:
AArch64
{
0:X1=x; 0:X3=y;
1:X1=y;
2:X1=y; 2:X3=x;
}
P0 | P1 | P2 ;
MOV W0,#1 | MOV W0,#1 | LDAR W0,[X1] ;
STR W0,[X1] | STLR W0,[X1] | LDR W2,[X3] ;
DMB SY | | ;
LDR W2,[X3] | | ;
exists
(0:X2=0 /\ 2:X0=1 /\ 2:X2=0)
where P0 is doing spin_unlock_wait, P1 is doing spin_unlock and P2 is
doing spin_lock.
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-06-03 01:40:07 +08:00
|
|
|
owner = READ_ONCE(lock->owner) << 16;
|
2016-06-02 22:27:04 +08:00
|
|
|
|
2015-11-20 01:48:31 +08:00
|
|
|
asm volatile(
|
|
|
|
" sevl\n"
|
|
|
|
"1: wfe\n"
|
|
|
|
"2: ldaxr %w0, %2\n"
|
arm64: spinlock: Ensure forward-progress in spin_unlock_wait
Rather than wait until we observe the lock being free (which might never
happen), we can also return from spin_unlock_wait if we observe that the
lock is now held by somebody else, which implies that it was unlocked
but we just missed seeing it in that state.
Furthermore, in such a scenario there is no longer a need to write back
the value that we loaded, since we know that there has been a lock
hand-off, which is sufficient to publish any stores prior to the
unlock_wait because the ARm architecture ensures that a Store-Release
instruction is multi-copy atomic when observed by a Load-Acquire
instruction.
The litmus test is something like:
AArch64
{
0:X1=x; 0:X3=y;
1:X1=y;
2:X1=y; 2:X3=x;
}
P0 | P1 | P2 ;
MOV W0,#1 | MOV W0,#1 | LDAR W0,[X1] ;
STR W0,[X1] | STLR W0,[X1] | LDR W2,[X3] ;
DMB SY | | ;
LDR W2,[X3] | | ;
exists
(0:X2=0 /\ 2:X0=1 /\ 2:X2=0)
where P0 is doing spin_unlock_wait, P1 is doing spin_unlock and P2 is
doing spin_lock.
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-06-03 01:40:07 +08:00
|
|
|
/* Is the lock free? */
|
2015-11-20 01:48:31 +08:00
|
|
|
" eor %w1, %w0, %w0, ror #16\n"
|
arm64: spinlock: Ensure forward-progress in spin_unlock_wait
Rather than wait until we observe the lock being free (which might never
happen), we can also return from spin_unlock_wait if we observe that the
lock is now held by somebody else, which implies that it was unlocked
but we just missed seeing it in that state.
Furthermore, in such a scenario there is no longer a need to write back
the value that we loaded, since we know that there has been a lock
hand-off, which is sufficient to publish any stores prior to the
unlock_wait because the ARm architecture ensures that a Store-Release
instruction is multi-copy atomic when observed by a Load-Acquire
instruction.
The litmus test is something like:
AArch64
{
0:X1=x; 0:X3=y;
1:X1=y;
2:X1=y; 2:X3=x;
}
P0 | P1 | P2 ;
MOV W0,#1 | MOV W0,#1 | LDAR W0,[X1] ;
STR W0,[X1] | STLR W0,[X1] | LDR W2,[X3] ;
DMB SY | | ;
LDR W2,[X3] | | ;
exists
(0:X2=0 /\ 2:X0=1 /\ 2:X2=0)
where P0 is doing spin_unlock_wait, P1 is doing spin_unlock and P2 is
doing spin_lock.
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-06-03 01:40:07 +08:00
|
|
|
" cbz %w1, 3f\n"
|
|
|
|
/* Lock taken -- has there been a subsequent unlock->lock transition? */
|
|
|
|
" eor %w1, %w3, %w0, lsl #16\n"
|
|
|
|
" cbz %w1, 1b\n"
|
|
|
|
/*
|
|
|
|
* The owner has been updated, so there was an unlock->lock
|
|
|
|
* transition that we missed. That means we can rely on the
|
|
|
|
* store-release of the unlock operation paired with the
|
|
|
|
* load-acquire of the lock operation to publish any of our
|
|
|
|
* previous stores to the new lock owner and therefore don't
|
|
|
|
* need to bother with the writeback below.
|
|
|
|
*/
|
|
|
|
" b 4f\n"
|
|
|
|
"3:\n"
|
|
|
|
/*
|
|
|
|
* Serialise against any concurrent lockers by writing back the
|
|
|
|
* unlocked lock value
|
|
|
|
*/
|
2015-11-20 01:48:31 +08:00
|
|
|
ARM64_LSE_ATOMIC_INSN(
|
|
|
|
/* LL/SC */
|
|
|
|
" stxr %w1, %w0, %2\n"
|
|
|
|
" nop\n"
|
2016-06-08 22:10:57 +08:00
|
|
|
" nop\n",
|
|
|
|
/* LSE atomics */
|
|
|
|
" mov %w1, %w0\n"
|
|
|
|
" cas %w0, %w0, %2\n"
|
|
|
|
" eor %w1, %w1, %w0\n")
|
arm64: spinlock: Ensure forward-progress in spin_unlock_wait
Rather than wait until we observe the lock being free (which might never
happen), we can also return from spin_unlock_wait if we observe that the
lock is now held by somebody else, which implies that it was unlocked
but we just missed seeing it in that state.
Furthermore, in such a scenario there is no longer a need to write back
the value that we loaded, since we know that there has been a lock
hand-off, which is sufficient to publish any stores prior to the
unlock_wait because the ARm architecture ensures that a Store-Release
instruction is multi-copy atomic when observed by a Load-Acquire
instruction.
The litmus test is something like:
AArch64
{
0:X1=x; 0:X3=y;
1:X1=y;
2:X1=y; 2:X3=x;
}
P0 | P1 | P2 ;
MOV W0,#1 | MOV W0,#1 | LDAR W0,[X1] ;
STR W0,[X1] | STLR W0,[X1] | LDR W2,[X3] ;
DMB SY | | ;
LDR W2,[X3] | | ;
exists
(0:X2=0 /\ 2:X0=1 /\ 2:X2=0)
where P0 is doing spin_unlock_wait, P1 is doing spin_unlock and P2 is
doing spin_lock.
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-06-03 01:40:07 +08:00
|
|
|
/* Somebody else wrote to the lock, GOTO 10 and reload the value */
|
2016-06-08 22:10:57 +08:00
|
|
|
" cbnz %w1, 2b\n"
|
arm64: spinlock: Ensure forward-progress in spin_unlock_wait
Rather than wait until we observe the lock being free (which might never
happen), we can also return from spin_unlock_wait if we observe that the
lock is now held by somebody else, which implies that it was unlocked
but we just missed seeing it in that state.
Furthermore, in such a scenario there is no longer a need to write back
the value that we loaded, since we know that there has been a lock
hand-off, which is sufficient to publish any stores prior to the
unlock_wait because the ARm architecture ensures that a Store-Release
instruction is multi-copy atomic when observed by a Load-Acquire
instruction.
The litmus test is something like:
AArch64
{
0:X1=x; 0:X3=y;
1:X1=y;
2:X1=y; 2:X3=x;
}
P0 | P1 | P2 ;
MOV W0,#1 | MOV W0,#1 | LDAR W0,[X1] ;
STR W0,[X1] | STLR W0,[X1] | LDR W2,[X3] ;
DMB SY | | ;
LDR W2,[X3] | | ;
exists
(0:X2=0 /\ 2:X0=1 /\ 2:X2=0)
where P0 is doing spin_unlock_wait, P1 is doing spin_unlock and P2 is
doing spin_lock.
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-06-03 01:40:07 +08:00
|
|
|
"4:"
|
2015-11-20 01:48:31 +08:00
|
|
|
: "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
|
arm64: spinlock: Ensure forward-progress in spin_unlock_wait
Rather than wait until we observe the lock being free (which might never
happen), we can also return from spin_unlock_wait if we observe that the
lock is now held by somebody else, which implies that it was unlocked
but we just missed seeing it in that state.
Furthermore, in such a scenario there is no longer a need to write back
the value that we loaded, since we know that there has been a lock
hand-off, which is sufficient to publish any stores prior to the
unlock_wait because the ARm architecture ensures that a Store-Release
instruction is multi-copy atomic when observed by a Load-Acquire
instruction.
The litmus test is something like:
AArch64
{
0:X1=x; 0:X3=y;
1:X1=y;
2:X1=y; 2:X3=x;
}
P0 | P1 | P2 ;
MOV W0,#1 | MOV W0,#1 | LDAR W0,[X1] ;
STR W0,[X1] | STLR W0,[X1] | LDR W2,[X3] ;
DMB SY | | ;
LDR W2,[X3] | | ;
exists
(0:X2=0 /\ 2:X0=1 /\ 2:X2=0)
where P0 is doing spin_unlock_wait, P1 is doing spin_unlock and P2 is
doing spin_lock.
Signed-off-by: Will Deacon <will.deacon@arm.com>
2016-06-03 01:40:07 +08:00
|
|
|
: "r" (owner)
|
2015-11-20 01:48:31 +08:00
|
|
|
: "memory");
|
|
|
|
}
|
2012-03-05 19:49:30 +08:00
|
|
|
|
|
|
|
#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
|
|
|
|
|
|
|
|
static inline void arch_spin_lock(arch_spinlock_t *lock)
|
|
|
|
{
|
|
|
|
unsigned int tmp;
|
2013-10-09 22:54:26 +08:00
|
|
|
arch_spinlock_t lockval, newval;
|
2012-03-05 19:49:30 +08:00
|
|
|
|
|
|
|
asm volatile(
|
2013-10-09 22:54:26 +08:00
|
|
|
/* Atomically increment the next ticket. */
|
2015-02-10 11:03:15 +08:00
|
|
|
ARM64_LSE_ATOMIC_INSN(
|
|
|
|
/* LL/SC */
|
2013-10-09 22:54:26 +08:00
|
|
|
" prfm pstl1strm, %3\n"
|
|
|
|
"1: ldaxr %w0, %3\n"
|
|
|
|
" add %w1, %w0, %w5\n"
|
|
|
|
" stxr %w2, %w1, %3\n"
|
2015-02-10 11:03:15 +08:00
|
|
|
" cbnz %w2, 1b\n",
|
|
|
|
/* LSE atomics */
|
|
|
|
" mov %w2, %w5\n"
|
|
|
|
" ldadda %w2, %w0, %3\n"
|
|
|
|
" nop\n"
|
|
|
|
" nop\n"
|
|
|
|
" nop\n"
|
|
|
|
)
|
|
|
|
|
2013-10-09 22:54:26 +08:00
|
|
|
/* Did we get the lock? */
|
|
|
|
" eor %w1, %w0, %w0, ror #16\n"
|
|
|
|
" cbz %w1, 3f\n"
|
|
|
|
/*
|
|
|
|
* No: spin on the owner. Send a local event to avoid missing an
|
|
|
|
* unlock before the exclusive load.
|
|
|
|
*/
|
|
|
|
" sevl\n"
|
|
|
|
"2: wfe\n"
|
|
|
|
" ldaxrh %w2, %4\n"
|
|
|
|
" eor %w1, %w2, %w0, lsr #16\n"
|
|
|
|
" cbnz %w1, 2b\n"
|
|
|
|
/* We got the lock. Critical section starts here. */
|
|
|
|
"3:"
|
|
|
|
: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock)
|
|
|
|
: "Q" (lock->owner), "I" (1 << TICKET_SHIFT)
|
|
|
|
: "memory");
|
2012-03-05 19:49:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int arch_spin_trylock(arch_spinlock_t *lock)
|
|
|
|
{
|
|
|
|
unsigned int tmp;
|
2013-10-09 22:54:26 +08:00
|
|
|
arch_spinlock_t lockval;
|
2012-03-05 19:49:30 +08:00
|
|
|
|
2015-02-10 11:03:15 +08:00
|
|
|
asm volatile(ARM64_LSE_ATOMIC_INSN(
|
|
|
|
/* LL/SC */
|
|
|
|
" prfm pstl1strm, %2\n"
|
|
|
|
"1: ldaxr %w0, %2\n"
|
|
|
|
" eor %w1, %w0, %w0, ror #16\n"
|
|
|
|
" cbnz %w1, 2f\n"
|
|
|
|
" add %w0, %w0, %3\n"
|
|
|
|
" stxr %w1, %w0, %2\n"
|
|
|
|
" cbnz %w1, 1b\n"
|
|
|
|
"2:",
|
|
|
|
/* LSE atomics */
|
|
|
|
" ldr %w0, %2\n"
|
|
|
|
" eor %w1, %w0, %w0, ror #16\n"
|
|
|
|
" cbnz %w1, 1f\n"
|
|
|
|
" add %w1, %w0, %3\n"
|
|
|
|
" casa %w0, %w1, %2\n"
|
|
|
|
" and %w1, %w1, #0xffff\n"
|
|
|
|
" eor %w1, %w1, %w0, lsr #16\n"
|
|
|
|
"1:")
|
2013-10-09 22:54:26 +08:00
|
|
|
: "=&r" (lockval), "=&r" (tmp), "+Q" (*lock)
|
|
|
|
: "I" (1 << TICKET_SHIFT)
|
|
|
|
: "memory");
|
2012-03-05 19:49:30 +08:00
|
|
|
|
|
|
|
return !tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void arch_spin_unlock(arch_spinlock_t *lock)
|
|
|
|
{
|
2015-02-10 11:03:15 +08:00
|
|
|
unsigned long tmp;
|
|
|
|
|
|
|
|
asm volatile(ARM64_LSE_ATOMIC_INSN(
|
|
|
|
/* LL/SC */
|
2015-07-28 21:48:00 +08:00
|
|
|
" ldrh %w1, %0\n"
|
2015-02-10 11:03:15 +08:00
|
|
|
" add %w1, %w1, #1\n"
|
|
|
|
" stlrh %w1, %0",
|
|
|
|
/* LSE atomics */
|
|
|
|
" mov %w1, #1\n"
|
|
|
|
" nop\n"
|
|
|
|
" staddlh %w1, %0")
|
|
|
|
: "=Q" (lock->owner), "=&r" (tmp)
|
|
|
|
:
|
2013-10-09 22:54:26 +08:00
|
|
|
: "memory");
|
|
|
|
}
|
|
|
|
|
2013-10-09 22:54:27 +08:00
|
|
|
static inline int arch_spin_value_unlocked(arch_spinlock_t lock)
|
|
|
|
{
|
|
|
|
return lock.owner == lock.next;
|
|
|
|
}
|
|
|
|
|
2013-10-09 22:54:26 +08:00
|
|
|
static inline int arch_spin_is_locked(arch_spinlock_t *lock)
|
|
|
|
{
|
2016-06-02 22:27:04 +08:00
|
|
|
smp_mb(); /* See arch_spin_unlock_wait */
|
2014-11-24 17:53:11 +08:00
|
|
|
return !arch_spin_value_unlocked(READ_ONCE(*lock));
|
2013-10-09 22:54:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int arch_spin_is_contended(arch_spinlock_t *lock)
|
|
|
|
{
|
2014-11-24 17:53:11 +08:00
|
|
|
arch_spinlock_t lockval = READ_ONCE(*lock);
|
2013-10-09 22:54:26 +08:00
|
|
|
return (lockval.next - lockval.owner) > 1;
|
2012-03-05 19:49:30 +08:00
|
|
|
}
|
2013-10-09 22:54:26 +08:00
|
|
|
#define arch_spin_is_contended arch_spin_is_contended
|
2012-03-05 19:49:30 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Write lock implementation.
|
|
|
|
*
|
|
|
|
* Write locks set bit 31. Unlocking, is done by writing 0 since the lock is
|
|
|
|
* exclusively held.
|
|
|
|
*
|
|
|
|
* The memory barriers are implicit with the load-acquire and store-release
|
|
|
|
* instructions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static inline void arch_write_lock(arch_rwlock_t *rw)
|
|
|
|
{
|
|
|
|
unsigned int tmp;
|
|
|
|
|
2015-02-10 11:03:15 +08:00
|
|
|
asm volatile(ARM64_LSE_ATOMIC_INSN(
|
|
|
|
/* LL/SC */
|
2012-03-05 19:49:30 +08:00
|
|
|
" sevl\n"
|
|
|
|
"1: wfe\n"
|
2013-02-04 20:12:33 +08:00
|
|
|
"2: ldaxr %w0, %1\n"
|
2012-03-05 19:49:30 +08:00
|
|
|
" cbnz %w0, 1b\n"
|
2013-02-04 20:12:33 +08:00
|
|
|
" stxr %w0, %w2, %1\n"
|
2012-03-05 19:49:30 +08:00
|
|
|
" cbnz %w0, 2b\n"
|
2015-02-10 11:03:15 +08:00
|
|
|
" nop",
|
|
|
|
/* LSE atomics */
|
|
|
|
"1: mov %w0, wzr\n"
|
|
|
|
"2: casa %w0, %w2, %1\n"
|
|
|
|
" cbz %w0, 3f\n"
|
|
|
|
" ldxr %w0, %1\n"
|
|
|
|
" cbz %w0, 2b\n"
|
|
|
|
" wfe\n"
|
|
|
|
" b 1b\n"
|
|
|
|
"3:")
|
2013-02-04 20:12:33 +08:00
|
|
|
: "=&r" (tmp), "+Q" (rw->lock)
|
|
|
|
: "r" (0x80000000)
|
2014-02-04 20:29:13 +08:00
|
|
|
: "memory");
|
2012-03-05 19:49:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int arch_write_trylock(arch_rwlock_t *rw)
|
|
|
|
{
|
|
|
|
unsigned int tmp;
|
|
|
|
|
2015-02-10 11:03:15 +08:00
|
|
|
asm volatile(ARM64_LSE_ATOMIC_INSN(
|
|
|
|
/* LL/SC */
|
2015-07-23 01:25:52 +08:00
|
|
|
"1: ldaxr %w0, %1\n"
|
|
|
|
" cbnz %w0, 2f\n"
|
2013-02-04 20:12:33 +08:00
|
|
|
" stxr %w0, %w2, %1\n"
|
2015-07-23 01:25:52 +08:00
|
|
|
" cbnz %w0, 1b\n"
|
2015-02-10 11:03:15 +08:00
|
|
|
"2:",
|
|
|
|
/* LSE atomics */
|
|
|
|
" mov %w0, wzr\n"
|
|
|
|
" casa %w0, %w2, %1\n"
|
|
|
|
" nop\n"
|
|
|
|
" nop")
|
2013-02-04 20:12:33 +08:00
|
|
|
: "=&r" (tmp), "+Q" (rw->lock)
|
|
|
|
: "r" (0x80000000)
|
2014-02-04 20:29:13 +08:00
|
|
|
: "memory");
|
2012-03-05 19:49:30 +08:00
|
|
|
|
|
|
|
return !tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void arch_write_unlock(arch_rwlock_t *rw)
|
|
|
|
{
|
2015-02-10 11:03:15 +08:00
|
|
|
asm volatile(ARM64_LSE_ATOMIC_INSN(
|
|
|
|
" stlr wzr, %0",
|
|
|
|
" swpl wzr, wzr, %0")
|
|
|
|
: "=Q" (rw->lock) :: "memory");
|
2012-03-05 19:49:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* write_can_lock - would write_trylock() succeed? */
|
|
|
|
#define arch_write_can_lock(x) ((x)->lock == 0)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read lock implementation.
|
|
|
|
*
|
|
|
|
* It exclusively loads the lock value, increments it and stores the new value
|
|
|
|
* back if positive and the CPU still exclusively owns the location. If the
|
|
|
|
* value is negative, the lock is already held.
|
|
|
|
*
|
|
|
|
* During unlocking there may be multiple active read locks but no write lock.
|
|
|
|
*
|
|
|
|
* The memory barriers are implicit with the load-acquire and store-release
|
|
|
|
* instructions.
|
2015-02-10 11:03:15 +08:00
|
|
|
*
|
|
|
|
* Note that in UNDEFINED cases, such as unlocking a lock twice, the LL/SC
|
|
|
|
* and LSE implementations may exhibit different behaviour (although this
|
|
|
|
* will have no effect on lockdep).
|
2012-03-05 19:49:30 +08:00
|
|
|
*/
|
|
|
|
static inline void arch_read_lock(arch_rwlock_t *rw)
|
|
|
|
{
|
|
|
|
unsigned int tmp, tmp2;
|
|
|
|
|
|
|
|
asm volatile(
|
|
|
|
" sevl\n"
|
2015-02-10 11:03:15 +08:00
|
|
|
ARM64_LSE_ATOMIC_INSN(
|
|
|
|
/* LL/SC */
|
2012-03-05 19:49:30 +08:00
|
|
|
"1: wfe\n"
|
2013-02-04 20:12:33 +08:00
|
|
|
"2: ldaxr %w0, %2\n"
|
2012-03-05 19:49:30 +08:00
|
|
|
" add %w0, %w0, #1\n"
|
|
|
|
" tbnz %w0, #31, 1b\n"
|
2013-02-04 20:12:33 +08:00
|
|
|
" stxr %w1, %w0, %2\n"
|
2015-02-10 11:03:15 +08:00
|
|
|
" nop\n"
|
|
|
|
" cbnz %w1, 2b",
|
|
|
|
/* LSE atomics */
|
|
|
|
"1: wfe\n"
|
|
|
|
"2: ldxr %w0, %2\n"
|
|
|
|
" adds %w1, %w0, #1\n"
|
|
|
|
" tbnz %w1, #31, 1b\n"
|
|
|
|
" casa %w0, %w1, %2\n"
|
|
|
|
" sbc %w0, %w1, %w0\n"
|
|
|
|
" cbnz %w0, 2b")
|
2013-02-04 20:12:33 +08:00
|
|
|
: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
|
|
|
|
:
|
2015-02-10 11:03:15 +08:00
|
|
|
: "cc", "memory");
|
2012-03-05 19:49:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void arch_read_unlock(arch_rwlock_t *rw)
|
|
|
|
{
|
|
|
|
unsigned int tmp, tmp2;
|
|
|
|
|
2015-02-10 11:03:15 +08:00
|
|
|
asm volatile(ARM64_LSE_ATOMIC_INSN(
|
|
|
|
/* LL/SC */
|
2013-02-04 20:12:33 +08:00
|
|
|
"1: ldxr %w0, %2\n"
|
2012-03-05 19:49:30 +08:00
|
|
|
" sub %w0, %w0, #1\n"
|
2013-02-04 20:12:33 +08:00
|
|
|
" stlxr %w1, %w0, %2\n"
|
2015-02-10 11:03:15 +08:00
|
|
|
" cbnz %w1, 1b",
|
|
|
|
/* LSE atomics */
|
|
|
|
" movn %w0, #0\n"
|
|
|
|
" nop\n"
|
|
|
|
" nop\n"
|
|
|
|
" staddl %w0, %2")
|
2013-02-04 20:12:33 +08:00
|
|
|
: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
|
|
|
|
:
|
2014-02-04 20:29:13 +08:00
|
|
|
: "memory");
|
2012-03-05 19:49:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline int arch_read_trylock(arch_rwlock_t *rw)
|
|
|
|
{
|
2015-02-10 11:03:15 +08:00
|
|
|
unsigned int tmp, tmp2;
|
2012-03-05 19:49:30 +08:00
|
|
|
|
2015-02-10 11:03:15 +08:00
|
|
|
asm volatile(ARM64_LSE_ATOMIC_INSN(
|
|
|
|
/* LL/SC */
|
|
|
|
" mov %w1, #1\n"
|
2015-07-23 01:25:52 +08:00
|
|
|
"1: ldaxr %w0, %2\n"
|
2012-03-05 19:49:30 +08:00
|
|
|
" add %w0, %w0, #1\n"
|
2015-07-23 01:25:52 +08:00
|
|
|
" tbnz %w0, #31, 2f\n"
|
2013-02-04 20:12:33 +08:00
|
|
|
" stxr %w1, %w0, %2\n"
|
2015-07-23 01:25:52 +08:00
|
|
|
" cbnz %w1, 1b\n"
|
2015-02-10 11:03:15 +08:00
|
|
|
"2:",
|
|
|
|
/* LSE atomics */
|
|
|
|
" ldr %w0, %2\n"
|
|
|
|
" adds %w1, %w0, #1\n"
|
|
|
|
" tbnz %w1, #31, 1f\n"
|
|
|
|
" casa %w0, %w1, %2\n"
|
|
|
|
" sbc %w1, %w1, %w0\n"
|
|
|
|
" nop\n"
|
|
|
|
"1:")
|
|
|
|
: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)
|
2013-02-04 20:12:33 +08:00
|
|
|
:
|
2015-02-10 11:03:15 +08:00
|
|
|
: "cc", "memory");
|
2012-03-05 19:49:30 +08:00
|
|
|
|
|
|
|
return !tmp2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read_can_lock - would read_trylock() succeed? */
|
|
|
|
#define arch_read_can_lock(x) ((x)->lock < 0x80000000)
|
|
|
|
|
|
|
|
#define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
|
|
|
|
#define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
|
|
|
|
|
|
|
|
#define arch_spin_relax(lock) cpu_relax()
|
|
|
|
#define arch_read_relax(lock) cpu_relax()
|
|
|
|
#define arch_write_relax(lock) cpu_relax()
|
|
|
|
|
|
|
|
#endif /* __ASM_SPINLOCK_H */
|