Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * atomic32.c: 32-bit atomic_t implementation | |
3 | * | |
4 | * Copyright (C) 2004 Keith M Wesolowski | |
6197fe4d | 5 | * Copyright (C) 2007 Kyle McMartin |
1da177e4 LT |
6 | * |
7 | * Based on asm-parisc/atomic.h Copyright (C) 2000 Philipp Rumpf | |
8 | */ | |
9 | ||
60063497 | 10 | #include <linux/atomic.h> |
1da177e4 LT |
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 | ||
18 | spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] = { | |
24774fbd | 19 | [0 ... (ATOMIC_HASH_SIZE-1)] = __SPIN_LOCK_UNLOCKED(__atomic_hash) |
1da177e4 LT |
20 | }; |
21 | ||
22 | #else /* SMP */ | |
23 | ||
a9f6a0dd | 24 | static DEFINE_SPINLOCK(dummy); |
1da177e4 LT |
25 | #define ATOMIC_HASH_SIZE 1 |
26 | #define ATOMIC_HASH(a) (&dummy) | |
27 | ||
28 | #endif /* SMP */ | |
29 | ||
3a1adb23 PZ |
30 | #define ATOMIC_FETCH_OP(op, c_op) \ |
31 | int atomic_fetch_##op(int i, atomic_t *v) \ | |
4f3316c2 PZ |
32 | { \ |
33 | int ret; \ | |
34 | unsigned long flags; \ | |
35 | spin_lock_irqsave(ATOMIC_HASH(v), flags); \ | |
36 | \ | |
3a1adb23 PZ |
37 | ret = v->counter; \ |
38 | v->counter c_op i; \ | |
4f3316c2 PZ |
39 | \ |
40 | spin_unlock_irqrestore(ATOMIC_HASH(v), flags); \ | |
41 | return ret; \ | |
42 | } \ | |
3a1adb23 | 43 | EXPORT_SYMBOL(atomic_fetch_##op); |
4f3316c2 | 44 | |
3a1adb23 PZ |
45 | #define ATOMIC_OP_RETURN(op, c_op) \ |
46 | int atomic_##op##_return(int i, atomic_t *v) \ | |
304a0d69 | 47 | { \ |
3a1adb23 | 48 | int ret; \ |
304a0d69 PZ |
49 | unsigned long flags; \ |
50 | spin_lock_irqsave(ATOMIC_HASH(v), flags); \ | |
51 | \ | |
3a1adb23 | 52 | ret = (v->counter c_op i); \ |
304a0d69 PZ |
53 | \ |
54 | spin_unlock_irqrestore(ATOMIC_HASH(v), flags); \ | |
3a1adb23 | 55 | return ret; \ |
304a0d69 | 56 | } \ |
3a1adb23 | 57 | EXPORT_SYMBOL(atomic_##op##_return); |
304a0d69 PZ |
58 | |
59 | ATOMIC_OP_RETURN(add, +=) | |
4f3316c2 | 60 | |
3a1adb23 PZ |
61 | ATOMIC_FETCH_OP(add, +=) |
62 | ATOMIC_FETCH_OP(and, &=) | |
63 | ATOMIC_FETCH_OP(or, |=) | |
64 | ATOMIC_FETCH_OP(xor, ^=) | |
65 | ||
66 | #undef ATOMIC_FETCH_OP | |
304a0d69 | 67 | #undef ATOMIC_OP_RETURN |
1da177e4 | 68 | |
1a17fdc4 AL |
69 | int atomic_xchg(atomic_t *v, int new) |
70 | { | |
71 | int ret; | |
72 | unsigned long flags; | |
73 | ||
74 | spin_lock_irqsave(ATOMIC_HASH(v), flags); | |
75 | ret = v->counter; | |
76 | v->counter = new; | |
77 | spin_unlock_irqrestore(ATOMIC_HASH(v), flags); | |
78 | return ret; | |
79 | } | |
80 | EXPORT_SYMBOL(atomic_xchg); | |
81 | ||
4a6dae6d | 82 | int atomic_cmpxchg(atomic_t *v, int old, int new) |
1da177e4 | 83 | { |
4a6dae6d | 84 | int ret; |
1da177e4 | 85 | unsigned long flags; |
1da177e4 | 86 | |
4a6dae6d NP |
87 | spin_lock_irqsave(ATOMIC_HASH(v), flags); |
88 | ret = v->counter; | |
89 | if (likely(ret == old)) | |
90 | v->counter = new; | |
1da177e4 LT |
91 | |
92 | spin_unlock_irqrestore(ATOMIC_HASH(v), flags); | |
4a6dae6d | 93 | return ret; |
1da177e4 | 94 | } |
74e61dee | 95 | EXPORT_SYMBOL(atomic_cmpxchg); |
1da177e4 | 96 | |
678624e4 | 97 | int __atomic_add_unless(atomic_t *v, int a, int u) |
8426e1f6 NP |
98 | { |
99 | int ret; | |
100 | unsigned long flags; | |
101 | ||
102 | spin_lock_irqsave(ATOMIC_HASH(v), flags); | |
103 | ret = v->counter; | |
104 | if (ret != u) | |
105 | v->counter += a; | |
106 | spin_unlock_irqrestore(ATOMIC_HASH(v), flags); | |
a61b5829 | 107 | return ret; |
8426e1f6 | 108 | } |
678624e4 | 109 | EXPORT_SYMBOL(__atomic_add_unless); |
8426e1f6 | 110 | |
8426e1f6 | 111 | /* Atomic operations are already serializing */ |
4a6dae6d NP |
112 | void atomic_set(atomic_t *v, int i) |
113 | { | |
114 | unsigned long flags; | |
1da177e4 | 115 | |
4a6dae6d NP |
116 | spin_lock_irqsave(ATOMIC_HASH(v), flags); |
117 | v->counter = i; | |
118 | spin_unlock_irqrestore(ATOMIC_HASH(v), flags); | |
119 | } | |
120 | EXPORT_SYMBOL(atomic_set); | |
8a8b836b DM |
121 | |
122 | unsigned long ___set_bit(unsigned long *addr, unsigned long mask) | |
123 | { | |
124 | unsigned long old, flags; | |
125 | ||
126 | spin_lock_irqsave(ATOMIC_HASH(addr), flags); | |
127 | old = *addr; | |
128 | *addr = old | mask; | |
129 | spin_unlock_irqrestore(ATOMIC_HASH(addr), flags); | |
130 | ||
131 | return old & mask; | |
132 | } | |
133 | EXPORT_SYMBOL(___set_bit); | |
134 | ||
135 | unsigned long ___clear_bit(unsigned long *addr, unsigned long mask) | |
136 | { | |
137 | unsigned long old, flags; | |
138 | ||
139 | spin_lock_irqsave(ATOMIC_HASH(addr), flags); | |
140 | old = *addr; | |
141 | *addr = old & ~mask; | |
142 | spin_unlock_irqrestore(ATOMIC_HASH(addr), flags); | |
143 | ||
144 | return old & mask; | |
145 | } | |
146 | EXPORT_SYMBOL(___clear_bit); | |
147 | ||
148 | unsigned long ___change_bit(unsigned long *addr, unsigned long mask) | |
149 | { | |
150 | unsigned long old, flags; | |
151 | ||
152 | spin_lock_irqsave(ATOMIC_HASH(addr), flags); | |
153 | old = *addr; | |
154 | *addr = old ^ mask; | |
155 | spin_unlock_irqrestore(ATOMIC_HASH(addr), flags); | |
156 | ||
157 | return old & mask; | |
158 | } | |
159 | EXPORT_SYMBOL(___change_bit); | |
6197fe4d KM |
160 | |
161 | unsigned long __cmpxchg_u32(volatile u32 *ptr, u32 old, u32 new) | |
162 | { | |
163 | unsigned long flags; | |
164 | u32 prev; | |
165 | ||
1fb8812b | 166 | spin_lock_irqsave(ATOMIC_HASH(ptr), flags); |
6197fe4d KM |
167 | if ((prev = *ptr) == old) |
168 | *ptr = new; | |
1fb8812b | 169 | spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags); |
6197fe4d KM |
170 | |
171 | return (unsigned long)prev; | |
172 | } | |
173 | EXPORT_SYMBOL(__cmpxchg_u32); | |
1a17fdc4 AL |
174 | |
175 | unsigned long __xchg_u32(volatile u32 *ptr, u32 new) | |
176 | { | |
177 | unsigned long flags; | |
178 | u32 prev; | |
179 | ||
180 | spin_lock_irqsave(ATOMIC_HASH(ptr), flags); | |
181 | prev = *ptr; | |
182 | *ptr = new; | |
183 | spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags); | |
184 | ||
185 | return (unsigned long)prev; | |
186 | } | |
187 | EXPORT_SYMBOL(__xchg_u32); |