sparc32: Implement xchg and atomic_xchg using ATOMIC_HASH locks

[ Upstream commit 1a17fdc4f4 ]

Atomicity between xchg and cmpxchg cannot be guaranteed when xchg is
implemented with a swap and cmpxchg is implemented with locks.
Without this, e.g. mcs_spin_lock and mcs_spin_unlock are broken.

Signed-off-by: Andreas Larsson <andreas@gaisler.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Andreas Larsson 2014-11-05 15:52:08 +01:00 committed by Greg Kroah-Hartman
parent ca9811bf3f
commit 882b3ba4a8
3 changed files with 30 additions and 11 deletions

View File

@ -21,7 +21,7 @@
extern int __atomic_add_return(int, atomic_t *);
extern int atomic_cmpxchg(atomic_t *, int, int);
#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
extern int atomic_xchg(atomic_t *, int);
extern int __atomic_add_unless(atomic_t *, int, int);
extern void atomic_set(atomic_t *, int);

View File

@ -11,22 +11,14 @@
#ifndef __ARCH_SPARC_CMPXCHG__
#define __ARCH_SPARC_CMPXCHG__
static inline unsigned long xchg_u32(__volatile__ unsigned long *m, unsigned long val)
{
__asm__ __volatile__("swap [%2], %0"
: "=&r" (val)
: "0" (val), "r" (m)
: "memory");
return val;
}
extern unsigned long __xchg_u32(volatile u32 *m, u32 new);
extern void __xchg_called_with_bad_pointer(void);
static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr, int size)
{
switch (size) {
case 4:
return xchg_u32(ptr, x);
return __xchg_u32(ptr, x);
}
__xchg_called_with_bad_pointer();
return x;

View File

@ -40,6 +40,19 @@ int __atomic_add_return(int i, atomic_t *v)
}
EXPORT_SYMBOL(__atomic_add_return);
int atomic_xchg(atomic_t *v, int new)
{
int ret;
unsigned long flags;
spin_lock_irqsave(ATOMIC_HASH(v), flags);
ret = v->counter;
v->counter = new;
spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
return ret;
}
EXPORT_SYMBOL(atomic_xchg);
int atomic_cmpxchg(atomic_t *v, int old, int new)
{
int ret;
@ -132,3 +145,17 @@ unsigned long __cmpxchg_u32(volatile u32 *ptr, u32 old, u32 new)
return (unsigned long)prev;
}
EXPORT_SYMBOL(__cmpxchg_u32);
unsigned long __xchg_u32(volatile u32 *ptr, u32 new)
{
unsigned long flags;
u32 prev;
spin_lock_irqsave(ATOMIC_HASH(ptr), flags);
prev = *ptr;
*ptr = new;
spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags);
return (unsigned long)prev;
}
EXPORT_SYMBOL(__xchg_u32);