OSDN Git Service

Merge tag 'arc-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc
[uclinux-h8/linux.git] / arch / arc / include / asm / cmpxchg.h
index 03cd689..44fd531 100644 (file)
@@ -10,6 +10,8 @@
 #define __ASM_ARC_CMPXCHG_H
 
 #include <linux/types.h>
+
+#include <asm/barrier.h>
 #include <asm/smp.h>
 
 #ifdef CONFIG_ARC_HAS_LLSC
@@ -19,16 +21,25 @@ __cmpxchg(volatile void *ptr, unsigned long expected, unsigned long new)
 {
        unsigned long prev;
 
+       /*
+        * Explicit full memory barrier needed before/after as
+        * LLOCK/SCOND thmeselves don't provide any such semantics
+        */
+       smp_mb();
+
        __asm__ __volatile__(
        "1:     llock   %0, [%1]        \n"
        "       brne    %0, %2, 2f      \n"
        "       scond   %3, [%1]        \n"
        "       bnz     1b              \n"
        "2:                             \n"
-       : "=&r"(prev)
-       : "r"(ptr), "ir"(expected),
-         "r"(new) /* can't be "ir". scond can't take limm for "b" */
-       : "cc");
+       : "=&r"(prev)   /* Early clobber, to prevent reg reuse */
+       : "r"(ptr),     /* Not "m": llock only supports reg direct addr mode */
+         "ir"(expected),
+         "r"(new)      /* can't be "ir". scond can't take LIMM for "b" */
+       : "cc", "memory"); /* so that gcc knows memory is being written here */
+
+       smp_mb();
 
        return prev;
 }
@@ -42,6 +53,9 @@ __cmpxchg(volatile void *ptr, unsigned long expected, unsigned long new)
        int prev;
        volatile unsigned long *p = ptr;
 
+       /*
+        * spin lock/unlock provide the needed smp_mb() before/after
+        */
        atomic_ops_lock(flags);
        prev = *p;
        if (prev == expected)
@@ -77,12 +91,16 @@ static inline unsigned long __xchg(unsigned long val, volatile void *ptr,
 
        switch (size) {
        case 4:
+               smp_mb();
+
                __asm__ __volatile__(
                "       ex  %0, [%1]    \n"
                : "+r"(val)
                : "r"(ptr)
                : "memory");
 
+               smp_mb();
+
                return val;
        }
        return __xchg_bad_pointer();