1/* 2 * atomic32.c: 32-bit atomic_t implementation 3 * 4 * Copyright (C) 2004 Keith M Wesolowski 5 * Copyright (C) 2007 Kyle McMartin 6 * 7 * Based on asm-parisc/atomic.h Copyright (C) 2000 Philipp Rumpf 8 */ 9 10#include <linux/atomic.h> 11#include <linux/spinlock.h> 12#include <linux/module.h> 13 14#ifdef CONFIG_SMP 15#define ATOMIC_HASH_SIZE 4 16#define ATOMIC_HASH(a) (&__atomic_hash[(((unsigned long)a)>>8) & (ATOMIC_HASH_SIZE-1)]) 17 18spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] = { 19 [0 ... (ATOMIC_HASH_SIZE-1)] = __SPIN_LOCK_UNLOCKED(__atomic_hash) 20}; 21 22#else /* SMP */ 23 24static DEFINE_SPINLOCK(dummy); 25#define ATOMIC_HASH_SIZE 1 26#define ATOMIC_HASH(a) (&dummy) 27 28#endif /* SMP */ 29 30#define ATOMIC_OP(op, cop) \ 31int atomic_##op##_return(int i, atomic_t *v) \ 32{ \ 33 int ret; \ 34 unsigned long flags; \ 35 spin_lock_irqsave(ATOMIC_HASH(v), flags); \ 36 \ 37 ret = (v->counter cop i); \ 38 \ 39 spin_unlock_irqrestore(ATOMIC_HASH(v), flags); \ 40 return ret; \ 41} \ 42EXPORT_SYMBOL(atomic_##op##_return); 43 44ATOMIC_OP(add, +=) 45 46#undef ATOMIC_OP 47 48int atomic_xchg(atomic_t *v, int new) 49{ 50 int ret; 51 unsigned long flags; 52 53 spin_lock_irqsave(ATOMIC_HASH(v), flags); 54 ret = v->counter; 55 v->counter = new; 56 spin_unlock_irqrestore(ATOMIC_HASH(v), flags); 57 return ret; 58} 59EXPORT_SYMBOL(atomic_xchg); 60 61int atomic_cmpxchg(atomic_t *v, int old, int new) 62{ 63 int ret; 64 unsigned long flags; 65 66 spin_lock_irqsave(ATOMIC_HASH(v), flags); 67 ret = v->counter; 68 if (likely(ret == old)) 69 v->counter = new; 70 71 spin_unlock_irqrestore(ATOMIC_HASH(v), flags); 72 return ret; 73} 74EXPORT_SYMBOL(atomic_cmpxchg); 75 76int __atomic_add_unless(atomic_t *v, int a, int u) 77{ 78 int ret; 79 unsigned long flags; 80 81 spin_lock_irqsave(ATOMIC_HASH(v), flags); 82 ret = v->counter; 83 if (ret != u) 84 v->counter += a; 85 spin_unlock_irqrestore(ATOMIC_HASH(v), flags); 86 return ret; 87} 88EXPORT_SYMBOL(__atomic_add_unless); 89 90/* Atomic operations are already serializing */ 91void atomic_set(atomic_t *v, int i) 92{ 93 unsigned long flags; 94 95 spin_lock_irqsave(ATOMIC_HASH(v), flags); 96 v->counter = i; 97 spin_unlock_irqrestore(ATOMIC_HASH(v), flags); 98} 99EXPORT_SYMBOL(atomic_set); 100 101unsigned long ___set_bit(unsigned long *addr, unsigned long mask) 102{ 103 unsigned long old, flags; 104 105 spin_lock_irqsave(ATOMIC_HASH(addr), flags); 106 old = *addr; 107 *addr = old | mask; 108 spin_unlock_irqrestore(ATOMIC_HASH(addr), flags); 109 110 return old & mask; 111} 112EXPORT_SYMBOL(___set_bit); 113 114unsigned long ___clear_bit(unsigned long *addr, unsigned long mask) 115{ 116 unsigned long old, flags; 117 118 spin_lock_irqsave(ATOMIC_HASH(addr), flags); 119 old = *addr; 120 *addr = old & ~mask; 121 spin_unlock_irqrestore(ATOMIC_HASH(addr), flags); 122 123 return old & mask; 124} 125EXPORT_SYMBOL(___clear_bit); 126 127unsigned long ___change_bit(unsigned long *addr, unsigned long mask) 128{ 129 unsigned long old, flags; 130 131 spin_lock_irqsave(ATOMIC_HASH(addr), flags); 132 old = *addr; 133 *addr = old ^ mask; 134 spin_unlock_irqrestore(ATOMIC_HASH(addr), flags); 135 136 return old & mask; 137} 138EXPORT_SYMBOL(___change_bit); 139 140unsigned long __cmpxchg_u32(volatile u32 *ptr, u32 old, u32 new) 141{ 142 unsigned long flags; 143 u32 prev; 144 145 spin_lock_irqsave(ATOMIC_HASH(ptr), flags); 146 if ((prev = *ptr) == old) 147 *ptr = new; 148 spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags); 149 150 return (unsigned long)prev; 151} 152EXPORT_SYMBOL(__cmpxchg_u32); 153 154unsigned long __xchg_u32(volatile u32 *ptr, u32 new) 155{ 156 unsigned long flags; 157 u32 prev; 158 159 spin_lock_irqsave(ATOMIC_HASH(ptr), flags); 160 prev = *ptr; 161 *ptr = new; 162 spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags); 163 164 return (unsigned long)prev; 165} 166EXPORT_SYMBOL(__xchg_u32); 167