This patch implements the _read, _set, and _cmpxchg operations on the atomic_t
type in user-space for ARM, using ldrex/strex on ARM v6, and the Linux kernel
helper kuser_cmpxchg on ARM pre-v6 (so, without syscalls) without SMP. Only the
SMP case for pre-v6 ARMs still use syscalls, but this case should not be so
frequent anyway.

A new macro XNARCH_HAVE_US_ATOMIC_CMPXCHG is defined both in kernel-space and
user-space, so that the kernel-space can rely on the user-space having
atomic_cmpxchg defined.

The plan is to implement these atomic operations for other platforms, defining
the XNARCH_HAVE_US_ATOMIC_CMPXCHG macro when this is done. When all platforms
define XNARCH_HAVE_US_ATOMIC_CMPXCGH, we will be able to remove all the
#ifdefs.

A question arise about memory barriers. I assumed that atomic_cmpxchg implied a
barrier, but I am not to sure about that. If that is not the case, we will have
to implement barriers in user-space as well, and call the in the apropriate
places in mutexes implementation.

---
 atomic.h |  169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 166 insertions(+), 3 deletions(-)

Index: include/asm-arm/atomic.h
===================================================================
--- include/asm-arm/atomic.h    (revision 3718)
+++ include/asm-arm/atomic.h    (working copy)
@@ -23,7 +23,6 @@
 #ifndef _XENO_ASM_ARM_ATOMIC_H
 #define _XENO_ASM_ARM_ATOMIC_H
 
-
 #ifdef __KERNEL__
 
 #include <linux/bitops.h>
@@ -35,6 +34,8 @@
 #define xnarch_memory_barrier()        smp_mb()
 
 #if __LINUX_ARM_ARCH__ >= 6
+#define XNARCH_HAVE_US_ATOMIC_CMPXCHG
+
 static inline void atomic_set_mask(unsigned long mask, unsigned long *addr)
 {
     unsigned long tmp, tmp2;
@@ -58,6 +59,11 @@ static inline void atomic_set_mask(unsig
     *addr |= mask;
     local_irq_restore_hw(flags);
 }
+
+#ifndef CONFIG_SMP
+#define XNARCH_HAVE_US_ATOMIC_CMPXCHG
+#endif /* CONFIG_SMP */
+
 #endif /* ARM_ARCH_6 */
 
 #define xnarch_atomic_set(pcounter,i)          atomic_set(pcounter,i)
@@ -75,9 +81,14 @@ typedef atomic_t atomic_counter_t;
 
 #include <asm/xenomai/features.h>
 #include <asm/xenomai/syscall.h>
+#include <nucleus/compiler.h>
 
 typedef struct { volatile int counter; } atomic_counter_t;
 
+typedef atomic_counter_t atomic_t;
+
+#define atomic_read(v) ((v)->counter)
+
 /*
  * This function doesn't exist, so you'll get a linker error
  * if something tries to do an invalid xchg().
@@ -129,6 +140,40 @@ __xchg(volatile void *ptr, unsigned long
  * Atomic operations lifted from linux/include/asm-arm/atomic.h 
  */
 #if CONFIG_XENO_ARM_ARCH >= 6
+#define XNARCH_HAVE_US_ATOMIC_CMPXCHG
+
+static __inline__ void atomic_set(atomic_t *v, int i)
+{
+       unsigned long tmp;
+
+       __asm__ __volatile__("@ atomic_set\n"
+"1:    ldrex   %0, [%1]\n"
+"      strex   %0, %2, [%1]\n"
+"      teq     %0, #0\n"
+"      bne     1b"
+       : "=&r" (tmp)
+       : "r" (&v->counter), "r" (i)
+       : "cc");
+}
+
+static __inline__ int atomic_cmpxchg(atomic_t *ptr, int old, int new)
+{
+       unsigned long oldval, res;
+
+       do {
+               __asm__ __volatile__("@ atomic_cmpxchg\n"
+               "ldrex  %1, [%2]\n"
+               "mov    %0, #0\n"
+               "teq    %1, %3\n"
+               "strexeq %0, %4, [%2]\n"
+                   : "=&r" (res), "=&r" (oldval)
+                   : "r" (&ptr->counter), "Ir" (old), "r" (new)
+                   : "cc");
+       } while (res);
+
+       return oldval;
+}
+
 static __inline__ int atomic_add_return(int i, atomic_counter_t *v)
 {
     unsigned long tmp;
@@ -194,7 +239,7 @@ static __inline__ void atomic_clear_mask
     : "r" (addr), "Ir" (mask)
     : "cc");
 }
-#else /* ARM_ARCH_6 */
+#elif CONFIG_SMP
 static __inline__ int atomic_add_return(int i, atomic_counter_t *v)
 {
     int ret;
@@ -224,7 +269,122 @@ static inline void atomic_clear_mask(uns
     XENOMAI_SYSCALL3(__xn_sys_arch,
                      XENOMAI_SYSARCH_ATOMIC_CLEAR_MASK, mask, addr);
 }
-#endif /* ARM_ARCH_6 */
+#else /* ARM_ARCH <= 5 && !CONFIG_SMP */
+#define XNARCH_HAVE_US_ATOMIC_CMPXCHG
+
+static __inline__ void atomic_set(atomic_counter_t *ptr, int val)
+{
+       ptr->counter = val;
+}
+
+static __inline__ int atomic_cmpxchg(atomic_counter_t *ptr, int old, int new)
+{
+        register int asm_old asm("r0") = old;
+        register int asm_new asm("r1") = new;
+        register int *asm_ptr asm("r2") = (int *) &ptr->counter;
+        register int asm_lr asm("lr");
+       register int asm_tmp asm("r3");
+
+       do {
+               asm volatile ( \
+                       "mov %1, #0xffff0fff\n\t"       \
+                       "mov lr, pc\n\t"                 \
+                       "add pc, %1, #(0xffff0fc0 - 0xffff0fff)\n\t"    \
+                       : "+r"(asm_old), "=&r"(asm_tmp), "=r"(asm_lr)   \
+                       : "r"(asm_new), "r"(asm_ptr) \
+                       : "ip", "cc", "memory");
+               if (likely(!asm_old))
+                       return old;
+       } while ((asm_old = *asm_ptr) == old);
+        return asm_old;
+}
+
+static __inline__ int atomic_add_return(int i, atomic_counter_t *v)
+{
+       register int asm_old asm("r0");
+       register int asm_new asm("r1");
+       register int *asm_ptr asm("r2") = (int *) &v->counter;
+        register int asm_lr asm("lr");
+       register int asm_tmp asm("r3");
+
+       asm volatile ( \
+               "1: @ atomic_add\n\t" \
+               "ldr    %0, [%4]\n\t" \
+               "mov    %1, #0xffff0fff\n\t" \
+               "add    lr, pc, #4\n\t" \
+               "add    %3, %0, %5\n\t"\
+               "add    pc, %1, #(0xffff0fc0 - 0xffff0fff)\n\t" \
+               "bcc    1b" \
+               : "=&r" (asm_old), "=&r"(asm_tmp), "=r"(asm_lr), "=r"(asm_new) \
+               : "r" (asm_ptr), "rIL"(i) \
+               : "ip", "cc", "memory");
+       return asm_new;
+}
+
+static __inline__ int atomic_sub_return(int i, atomic_counter_t *v)
+{
+       register int asm_old asm("r0");
+       register int asm_new asm("r1");
+       register int *asm_ptr asm("r2") = (int *) &v->counter;
+        register int asm_lr asm("lr");
+       register int asm_tmp asm("r3");
+
+       asm volatile ( \
+               "1: @ atomic_sub\n\t" \
+               "ldr    %0, [%4]\n\t" \
+               "mov    %1, #0xffff0fff\n\t" \
+               "add    lr, pc, #4\n\t" \
+               "sub    %3, %0, %5\n\t"\
+               "add    pc, %1, #(0xffff0fc0 - 0xffff0fff)\n\t" \
+               "bcc    1b" \
+               : "=&r" (asm_old), "=&r"(asm_tmp), "=r"(asm_lr), "=r"(asm_new) \
+               : "r" (asm_ptr), "rIL"(i) \
+               : "ip", "cc", "memory");
+       return asm_new;
+}
+
+static __inline__ void atomic_set_mask(long mask, atomic_counter_t *v)
+{
+       register int asm_old asm("r0");
+       register int asm_new asm("r1");
+       register int *asm_ptr asm("r2") = (int *) &v->counter;
+        register int asm_lr asm("lr");
+       register int asm_tmp asm("r3");
+
+       asm volatile ( \
+               "1: @ atomic_set_mask\n\t" \
+               "ldr    %0, [%4]\n\t" \
+               "mov    %1, #0xffff0fff\n\t" \
+               "add    lr, pc, #4\n\t" \
+               "orr    %3, %0, %5\n\t"\
+               "add    pc, %1, #(0xffff0fc0 - 0xffff0fff)\n\t" \
+               "bcc    1b" \
+               : "=&r" (asm_old), "=&r"(asm_tmp), "=r"(asm_lr), "=r"(asm_new) \
+               : "r" (asm_ptr), "rIL"(mask) \
+               : "ip", "cc", "memory");
+}
+
+static __inline__ void atomic_clear_mask(long mask, atomic_counter_t *v)
+{
+       register int asm_old asm("r0");
+       register int asm_new asm("r1");
+       register int *asm_ptr asm("r2") = (int *) &v->counter;
+        register int asm_lr asm("lr");
+       register int asm_tmp asm("r3");
+
+       asm volatile ( \
+               "1: @ atomic_clear_mask\n\t" \
+               "ldr    %0, [%4]\n\t" \
+               "mov    %1, #0xffff0fff\n\t" \
+               "add    lr, pc, #4\n\t" \
+               "bic    %3, %0, %5\n\t" \
+               "add    pc, %1, #(0xffff0fc0 - 0xffff0fff)\n\t" \
+               "bcc    1b" \
+               : "=&r" (asm_old), "=&r"(asm_tmp), "=r"(asm_lr), "=r"(asm_new) \
+               : "r" (asm_ptr), "rIL"(mask) \
+               : "ip", "cc", "memory");
+}
+#endif /* ARM_ARCH <= 5 && !CONFIG_SMP */
 
 #define xnarch_memory_barrier()                 __asm__ __volatile__("": : 
:"memory")
 
@@ -241,6 +401,9 @@ static inline void atomic_clear_mask(uns
 
 typedef unsigned long atomic_flags_t;
 
+/* Add support for atomic_long_t and atomic_ptr_t */
+#include <asm-generic/xenomai/atomic.h>
+
 #endif /* !_XENO_ASM_ARM_ATOMIC_H */
 
 // vim: ts=4 et sw=4 sts=4

-- 


                                            Gilles.

_______________________________________________
Xenomai-core mailing list
Xenomai-core@gna.org
https://mail.gna.org/listinfo/xenomai-core

Reply via email to