linux/arch/arm/include/asm/atomic.h
<<
>>
Prefs
   1/*
   2 *  arch/arm/include/asm/atomic.h
   3 *
   4 *  Copyright (C) 1996 Russell King.
   5 *  Copyright (C) 2002 Deep Blue Solutions Ltd.
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11#ifndef __ASM_ARM_ATOMIC_H
  12#define __ASM_ARM_ATOMIC_H
  13
  14#include <linux/compiler.h>
  15#include <linux/prefetch.h>
  16#include <linux/types.h>
  17#include <linux/irqflags.h>
  18#include <asm/barrier.h>
  19#include <asm/cmpxchg.h>
  20
  21#define ATOMIC_INIT(i)  { (i) }
  22
  23#ifdef __KERNEL__
  24
  25/*
  26 * On ARM, ordinary assignment (str instruction) doesn't clear the local
  27 * strex/ldrex monitor on some implementations. The reason we can use it for
  28 * atomic_set() is the clrex or dummy strex done on every exception return.
  29 */
  30#define atomic_read(v)  ACCESS_ONCE((v)->counter)
  31#define atomic_set(v,i) (((v)->counter) = (i))
  32
  33#if __LINUX_ARM_ARCH__ >= 6
  34
  35/*
  36 * ARMv6 UP and SMP safe atomic ops.  We use load exclusive and
  37 * store exclusive to ensure that these are atomic.  We may loop
  38 * to ensure that the update happens.
  39 */
  40
  41#define ATOMIC_OP(op, c_op, asm_op)                                     \
  42static inline void atomic_##op(int i, atomic_t *v)                      \
  43{                                                                       \
  44        unsigned long tmp;                                              \
  45        int result;                                                     \
  46                                                                        \
  47        prefetchw(&v->counter);                                         \
  48        __asm__ __volatile__("@ atomic_" #op "\n"                       \
  49"1:     ldrex   %0, [%3]\n"                                             \
  50"       " #asm_op "     %0, %0, %4\n"                                   \
  51"       strex   %1, %0, [%3]\n"                                         \
  52"       teq     %1, #0\n"                                               \
  53"       bne     1b"                                                     \
  54        : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)               \
  55        : "r" (&v->counter), "Ir" (i)                                   \
  56        : "cc");                                                        \
  57}                                                                       \
  58
  59#define ATOMIC_OP_RETURN(op, c_op, asm_op)                              \
  60static inline int atomic_##op##_return(int i, atomic_t *v)              \
  61{                                                                       \
  62        unsigned long tmp;                                              \
  63        int result;                                                     \
  64                                                                        \
  65        smp_mb();                                                       \
  66        prefetchw(&v->counter);                                         \
  67                                                                        \
  68        __asm__ __volatile__("@ atomic_" #op "_return\n"                \
  69"1:     ldrex   %0, [%3]\n"                                             \
  70"       " #asm_op "     %0, %0, %4\n"                                   \
  71"       strex   %1, %0, [%3]\n"                                         \
  72"       teq     %1, #0\n"                                               \
  73"       bne     1b"                                                     \
  74        : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)               \
  75        : "r" (&v->counter), "Ir" (i)                                   \
  76        : "cc");                                                        \
  77                                                                        \
  78        smp_mb();                                                       \
  79                                                                        \
  80        return result;                                                  \
  81}
  82
  83static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)
  84{
  85        int oldval;
  86        unsigned long res;
  87
  88        smp_mb();
  89        prefetchw(&ptr->counter);
  90
  91        do {
  92                __asm__ __volatile__("@ atomic_cmpxchg\n"
  93                "ldrex  %1, [%3]\n"
  94                "mov    %0, #0\n"
  95                "teq    %1, %4\n"
  96                "strexeq %0, %5, [%3]\n"
  97                    : "=&r" (res), "=&r" (oldval), "+Qo" (ptr->counter)
  98                    : "r" (&ptr->counter), "Ir" (old), "r" (new)
  99                    : "cc");
 100        } while (res);
 101
 102        smp_mb();
 103
 104        return oldval;
 105}
 106
 107static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 108{
 109        int oldval, newval;
 110        unsigned long tmp;
 111
 112        smp_mb();
 113        prefetchw(&v->counter);
 114
 115        __asm__ __volatile__ ("@ atomic_add_unless\n"
 116"1:     ldrex   %0, [%4]\n"
 117"       teq     %0, %5\n"
 118"       beq     2f\n"
 119"       add     %1, %0, %6\n"
 120"       strex   %2, %1, [%4]\n"
 121"       teq     %2, #0\n"
 122"       bne     1b\n"
 123"2:"
 124        : "=&r" (oldval), "=&r" (newval), "=&r" (tmp), "+Qo" (v->counter)
 125        : "r" (&v->counter), "r" (u), "r" (a)
 126        : "cc");
 127
 128        if (oldval != u)
 129                smp_mb();
 130
 131        return oldval;
 132}
 133
 134#else /* ARM_ARCH_6 */
 135
 136#ifdef CONFIG_SMP
 137#error SMP not supported on pre-ARMv6 CPUs
 138#endif
 139
 140#define ATOMIC_OP(op, c_op, asm_op)                                     \
 141static inline void atomic_##op(int i, atomic_t *v)                      \
 142{                                                                       \
 143        unsigned long flags;                                            \
 144                                                                        \
 145        raw_local_irq_save(flags);                                      \
 146        v->counter c_op i;                                              \
 147        raw_local_irq_restore(flags);                                   \
 148}                                                                       \
 149
 150#define ATOMIC_OP_RETURN(op, c_op, asm_op)                              \
 151static inline int atomic_##op##_return(int i, atomic_t *v)              \
 152{                                                                       \
 153        unsigned long flags;                                            \
 154        int val;                                                        \
 155                                                                        \
 156        raw_local_irq_save(flags);                                      \
 157        v->counter c_op i;                                              \
 158        val = v->counter;                                               \
 159        raw_local_irq_restore(flags);                                   \
 160                                                                        \
 161        return val;                                                     \
 162}
 163
 164static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
 165{
 166        int ret;
 167        unsigned long flags;
 168
 169        raw_local_irq_save(flags);
 170        ret = v->counter;
 171        if (likely(ret == old))
 172                v->counter = new;
 173        raw_local_irq_restore(flags);
 174
 175        return ret;
 176}
 177
 178static inline int __atomic_add_unless(atomic_t *v, int a, int u)
 179{
 180        int c, old;
 181
 182        c = atomic_read(v);
 183        while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)
 184                c = old;
 185        return c;
 186}
 187
 188#endif /* __LINUX_ARM_ARCH__ */
 189
 190#define ATOMIC_OPS(op, c_op, asm_op)                                    \
 191        ATOMIC_OP(op, c_op, asm_op)                                     \
 192        ATOMIC_OP_RETURN(op, c_op, asm_op)
 193
 194ATOMIC_OPS(add, +=, add)
 195ATOMIC_OPS(sub, -=, sub)
 196
 197#undef ATOMIC_OPS
 198#undef ATOMIC_OP_RETURN
 199#undef ATOMIC_OP
 200
 201#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 202
 203#define atomic_inc(v)           atomic_add(1, v)
 204#define atomic_dec(v)           atomic_sub(1, v)
 205
 206#define atomic_inc_and_test(v)  (atomic_add_return(1, v) == 0)
 207#define atomic_dec_and_test(v)  (atomic_sub_return(1, v) == 0)
 208#define atomic_inc_return(v)    (atomic_add_return(1, v))
 209#define atomic_dec_return(v)    (atomic_sub_return(1, v))
 210#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
 211
 212#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
 213
 214#ifndef CONFIG_GENERIC_ATOMIC64
 215typedef struct {
 216        long long counter;
 217} atomic64_t;
 218
 219#define ATOMIC64_INIT(i) { (i) }
 220
 221#ifdef CONFIG_ARM_LPAE
 222static inline long long atomic64_read(const atomic64_t *v)
 223{
 224        long long result;
 225
 226        __asm__ __volatile__("@ atomic64_read\n"
 227"       ldrd    %0, %H0, [%1]"
 228        : "=&r" (result)
 229        : "r" (&v->counter), "Qo" (v->counter)
 230        );
 231
 232        return result;
 233}
 234
 235static inline void atomic64_set(atomic64_t *v, long long i)
 236{
 237        __asm__ __volatile__("@ atomic64_set\n"
 238"       strd    %2, %H2, [%1]"
 239        : "=Qo" (v->counter)
 240        : "r" (&v->counter), "r" (i)
 241        );
 242}
 243#else
 244static inline long long atomic64_read(const atomic64_t *v)
 245{
 246        long long result;
 247
 248        __asm__ __volatile__("@ atomic64_read\n"
 249"       ldrexd  %0, %H0, [%1]"
 250        : "=&r" (result)
 251        : "r" (&v->counter), "Qo" (v->counter)
 252        );
 253
 254        return result;
 255}
 256
 257static inline void atomic64_set(atomic64_t *v, long long i)
 258{
 259        long long tmp;
 260
 261        prefetchw(&v->counter);
 262        __asm__ __volatile__("@ atomic64_set\n"
 263"1:     ldrexd  %0, %H0, [%2]\n"
 264"       strexd  %0, %3, %H3, [%2]\n"
 265"       teq     %0, #0\n"
 266"       bne     1b"
 267        : "=&r" (tmp), "=Qo" (v->counter)
 268        : "r" (&v->counter), "r" (i)
 269        : "cc");
 270}
 271#endif
 272
 273#define ATOMIC64_OP(op, op1, op2)                                       \
 274static inline void atomic64_##op(long long i, atomic64_t *v)            \
 275{                                                                       \
 276        long long result;                                               \
 277        unsigned long tmp;                                              \
 278                                                                        \
 279        prefetchw(&v->counter);                                         \
 280        __asm__ __volatile__("@ atomic64_" #op "\n"                     \
 281"1:     ldrexd  %0, %H0, [%3]\n"                                        \
 282"       " #op1 " %Q0, %Q0, %Q4\n"                                       \
 283"       " #op2 " %R0, %R0, %R4\n"                                       \
 284"       strexd  %1, %0, %H0, [%3]\n"                                    \
 285"       teq     %1, #0\n"                                               \
 286"       bne     1b"                                                     \
 287        : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)               \
 288        : "r" (&v->counter), "r" (i)                                    \
 289        : "cc");                                                        \
 290}                                                                       \
 291
 292#define ATOMIC64_OP_RETURN(op, op1, op2)                                \
 293static inline long long atomic64_##op##_return(long long i, atomic64_t *v) \
 294{                                                                       \
 295        long long result;                                               \
 296        unsigned long tmp;                                              \
 297                                                                        \
 298        smp_mb();                                                       \
 299        prefetchw(&v->counter);                                         \
 300                                                                        \
 301        __asm__ __volatile__("@ atomic64_" #op "_return\n"              \
 302"1:     ldrexd  %0, %H0, [%3]\n"                                        \
 303"       " #op1 " %Q0, %Q0, %Q4\n"                                       \
 304"       " #op2 " %R0, %R0, %R4\n"                                       \
 305"       strexd  %1, %0, %H0, [%3]\n"                                    \
 306"       teq     %1, #0\n"                                               \
 307"       bne     1b"                                                     \
 308        : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)               \
 309        : "r" (&v->counter), "r" (i)                                    \
 310        : "cc");                                                        \
 311                                                                        \
 312        smp_mb();                                                       \
 313                                                                        \
 314        return result;                                                  \
 315}
 316
 317#define ATOMIC64_OPS(op, op1, op2)                                      \
 318        ATOMIC64_OP(op, op1, op2)                                       \
 319        ATOMIC64_OP_RETURN(op, op1, op2)
 320
 321ATOMIC64_OPS(add, adds, adc)
 322ATOMIC64_OPS(sub, subs, sbc)
 323
 324#undef ATOMIC64_OPS
 325#undef ATOMIC64_OP_RETURN
 326#undef ATOMIC64_OP
 327
 328static inline long long atomic64_cmpxchg(atomic64_t *ptr, long long old,
 329                                        long long new)
 330{
 331        long long oldval;
 332        unsigned long res;
 333
 334        smp_mb();
 335        prefetchw(&ptr->counter);
 336
 337        do {
 338                __asm__ __volatile__("@ atomic64_cmpxchg\n"
 339                "ldrexd         %1, %H1, [%3]\n"
 340                "mov            %0, #0\n"
 341                "teq            %1, %4\n"
 342                "teqeq          %H1, %H4\n"
 343                "strexdeq       %0, %5, %H5, [%3]"
 344                : "=&r" (res), "=&r" (oldval), "+Qo" (ptr->counter)
 345                : "r" (&ptr->counter), "r" (old), "r" (new)
 346                : "cc");
 347        } while (res);
 348
 349        smp_mb();
 350
 351        return oldval;
 352}
 353
 354static inline long long atomic64_xchg(atomic64_t *ptr, long long new)
 355{
 356        long long result;
 357        unsigned long tmp;
 358
 359        smp_mb();
 360        prefetchw(&ptr->counter);
 361
 362        __asm__ __volatile__("@ atomic64_xchg\n"
 363"1:     ldrexd  %0, %H0, [%3]\n"
 364"       strexd  %1, %4, %H4, [%3]\n"
 365"       teq     %1, #0\n"
 366"       bne     1b"
 367        : "=&r" (result), "=&r" (tmp), "+Qo" (ptr->counter)
 368        : "r" (&ptr->counter), "r" (new)
 369        : "cc");
 370
 371        smp_mb();
 372
 373        return result;
 374}
 375
 376static inline long long atomic64_dec_if_positive(atomic64_t *v)
 377{
 378        long long result;
 379        unsigned long tmp;
 380
 381        smp_mb();
 382        prefetchw(&v->counter);
 383
 384        __asm__ __volatile__("@ atomic64_dec_if_positive\n"
 385"1:     ldrexd  %0, %H0, [%3]\n"
 386"       subs    %Q0, %Q0, #1\n"
 387"       sbc     %R0, %R0, #0\n"
 388"       teq     %R0, #0\n"
 389"       bmi     2f\n"
 390"       strexd  %1, %0, %H0, [%3]\n"
 391"       teq     %1, #0\n"
 392"       bne     1b\n"
 393"2:"
 394        : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
 395        : "r" (&v->counter)
 396        : "cc");
 397
 398        smp_mb();
 399
 400        return result;
 401}
 402
 403static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u)
 404{
 405        long long val;
 406        unsigned long tmp;
 407        int ret = 1;
 408
 409        smp_mb();
 410        prefetchw(&v->counter);
 411
 412        __asm__ __volatile__("@ atomic64_add_unless\n"
 413"1:     ldrexd  %0, %H0, [%4]\n"
 414"       teq     %0, %5\n"
 415"       teqeq   %H0, %H5\n"
 416"       moveq   %1, #0\n"
 417"       beq     2f\n"
 418"       adds    %Q0, %Q0, %Q6\n"
 419"       adc     %R0, %R0, %R6\n"
 420"       strexd  %2, %0, %H0, [%4]\n"
 421"       teq     %2, #0\n"
 422"       bne     1b\n"
 423"2:"
 424        : "=&r" (val), "+r" (ret), "=&r" (tmp), "+Qo" (v->counter)
 425        : "r" (&v->counter), "r" (u), "r" (a)
 426        : "cc");
 427
 428        if (ret)
 429                smp_mb();
 430
 431        return ret;
 432}
 433
 434#define atomic64_add_negative(a, v)     (atomic64_add_return((a), (v)) < 0)
 435#define atomic64_inc(v)                 atomic64_add(1LL, (v))
 436#define atomic64_inc_return(v)          atomic64_add_return(1LL, (v))
 437#define atomic64_inc_and_test(v)        (atomic64_inc_return(v) == 0)
 438#define atomic64_sub_and_test(a, v)     (atomic64_sub_return((a), (v)) == 0)
 439#define atomic64_dec(v)                 atomic64_sub(1LL, (v))
 440#define atomic64_dec_return(v)          atomic64_sub_return(1LL, (v))
 441#define atomic64_dec_and_test(v)        (atomic64_dec_return((v)) == 0)
 442#define atomic64_inc_not_zero(v)        atomic64_add_unless((v), 1LL, 0LL)
 443
 444#endif /* !CONFIG_GENERIC_ATOMIC64 */
 445#endif
 446#endif
 447