linux/arch/parisc/include/asm/spinlock.h
John David Anglin 86d4d068df parisc: Revert "Release spinlocks using ordered store"
This reverts commit d27dfa13b9.

Unfortunately, this patch needs to be reverted.  We need the full sync
barrier and not the limited barrier provided by using an ordered store.
The sync ensures that all accesses and cache purge instructions that
follow the sync are performed after all such instructions prior the sync
instruction have completed executing.

The patch breaks the rwlock implementation in glibc.  This caused the
test-lock application in the libprelude testsuite to hang.  With the
change reverted, the test runs correctly and the libprelude package
builds successfully.

Signed-off-by: John David Anglin <dave.anglin@bell.net>
Signed-off-by: Helge Deller <deller@gmx.de>
2018-11-06 12:03:22 +01:00

170 lines
4.0 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_SPINLOCK_H
#define __ASM_SPINLOCK_H
#include <asm/barrier.h>
#include <asm/ldcw.h>
#include <asm/processor.h>
#include <asm/spinlock_types.h>
static inline int arch_spin_is_locked(arch_spinlock_t *x)
{
volatile unsigned int *a = __ldcw_align(x);
return *a == 0;
}
#define arch_spin_lock(lock) arch_spin_lock_flags(lock, 0)
static inline void arch_spin_lock_flags(arch_spinlock_t *x,
unsigned long flags)
{
volatile unsigned int *a;
a = __ldcw_align(x);
while (__ldcw(a) == 0)
while (*a == 0)
if (flags & PSW_SM_I) {
local_irq_enable();
cpu_relax();
local_irq_disable();
} else
cpu_relax();
}
#define arch_spin_lock_flags arch_spin_lock_flags
static inline void arch_spin_unlock(arch_spinlock_t *x)
{
volatile unsigned int *a;
a = __ldcw_align(x);
mb();
*a = 1;
}
static inline int arch_spin_trylock(arch_spinlock_t *x)
{
volatile unsigned int *a;
int ret;
a = __ldcw_align(x);
ret = __ldcw(a) != 0;
return ret;
}
/*
* Read-write spinlocks, allowing multiple readers but only one writer.
* Linux rwlocks are unfair to writers; they can be starved for an indefinite
* time by readers. With care, they can also be taken in interrupt context.
*
* In the PA-RISC implementation, we have a spinlock and a counter.
* Readers use the lock to serialise their access to the counter (which
* records how many readers currently hold the lock).
* Writers hold the spinlock, preventing any readers or other writers from
* grabbing the rwlock.
*/
/* Note that we have to ensure interrupts are disabled in case we're
* interrupted by some other code that wants to grab the same read lock */
static __inline__ void arch_read_lock(arch_rwlock_t *rw)
{
unsigned long flags;
local_irq_save(flags);
arch_spin_lock_flags(&rw->lock, flags);
rw->counter++;
arch_spin_unlock(&rw->lock);
local_irq_restore(flags);
}
/* Note that we have to ensure interrupts are disabled in case we're
* interrupted by some other code that wants to grab the same read lock */
static __inline__ void arch_read_unlock(arch_rwlock_t *rw)
{
unsigned long flags;
local_irq_save(flags);
arch_spin_lock_flags(&rw->lock, flags);
rw->counter--;
arch_spin_unlock(&rw->lock);
local_irq_restore(flags);
}
/* Note that we have to ensure interrupts are disabled in case we're
* interrupted by some other code that wants to grab the same read lock */
static __inline__ int arch_read_trylock(arch_rwlock_t *rw)
{
unsigned long flags;
retry:
local_irq_save(flags);
if (arch_spin_trylock(&rw->lock)) {
rw->counter++;
arch_spin_unlock(&rw->lock);
local_irq_restore(flags);
return 1;
}
local_irq_restore(flags);
/* If write-locked, we fail to acquire the lock */
if (rw->counter < 0)
return 0;
/* Wait until we have a realistic chance at the lock */
while (arch_spin_is_locked(&rw->lock) && rw->counter >= 0)
cpu_relax();
goto retry;
}
/* Note that we have to ensure interrupts are disabled in case we're
* interrupted by some other code that wants to read_trylock() this lock */
static __inline__ void arch_write_lock(arch_rwlock_t *rw)
{
unsigned long flags;
retry:
local_irq_save(flags);
arch_spin_lock_flags(&rw->lock, flags);
if (rw->counter != 0) {
arch_spin_unlock(&rw->lock);
local_irq_restore(flags);
while (rw->counter != 0)
cpu_relax();
goto retry;
}
rw->counter = -1; /* mark as write-locked */
mb();
local_irq_restore(flags);
}
static __inline__ void arch_write_unlock(arch_rwlock_t *rw)
{
rw->counter = 0;
arch_spin_unlock(&rw->lock);
}
/* Note that we have to ensure interrupts are disabled in case we're
* interrupted by some other code that wants to read_trylock() this lock */
static __inline__ int arch_write_trylock(arch_rwlock_t *rw)
{
unsigned long flags;
int result = 0;
local_irq_save(flags);
if (arch_spin_trylock(&rw->lock)) {
if (rw->counter == 0) {
rw->counter = -1;
result = 1;
} else {
/* Read-locked. Oh well. */
arch_spin_unlock(&rw->lock);
}
}
local_irq_restore(flags);
return result;
}
#endif /* __ASM_SPINLOCK_H */