~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to arch/arm/include/asm/spinlock.h

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifndef __ASM_SPINLOCK_H
 
2
#define __ASM_SPINLOCK_H
 
3
 
 
4
#if __LINUX_ARM_ARCH__ < 6
 
5
#error SMP not supported on pre-ARMv6 CPUs
 
6
#endif
 
7
 
 
8
#include <asm/processor.h>
 
9
 
 
10
/*
 
11
 * sev and wfe are ARMv6K extensions.  Uniprocessor ARMv6 may not have the K
 
12
 * extensions, so when running on UP, we have to patch these instructions away.
 
13
 */
 
14
#define ALT_SMP(smp, up)                                        \
 
15
        "9998:  " smp "\n"                                      \
 
16
        "       .pushsection \".alt.smp.init\", \"a\"\n"        \
 
17
        "       .long   9998b\n"                                \
 
18
        "       " up "\n"                                       \
 
19
        "       .popsection\n"
 
20
 
 
21
#ifdef CONFIG_THUMB2_KERNEL
 
22
#define SEV             ALT_SMP("sev.w", "nop.w")
 
23
/*
 
24
 * For Thumb-2, special care is needed to ensure that the conditional WFE
 
25
 * instruction really does assemble to exactly 4 bytes (as required by
 
26
 * the SMP_ON_UP fixup code).   By itself "wfene" might cause the
 
27
 * assembler to insert a extra (16-bit) IT instruction, depending on the
 
28
 * presence or absence of neighbouring conditional instructions.
 
29
 *
 
30
 * To avoid this unpredictableness, an approprite IT is inserted explicitly:
 
31
 * the assembler won't change IT instructions which are explicitly present
 
32
 * in the input.
 
33
 */
 
34
#define WFE(cond)       ALT_SMP(                \
 
35
        "it " cond "\n\t"                       \
 
36
        "wfe" cond ".n",                        \
 
37
                                                \
 
38
        "nop.w"                                 \
 
39
)
 
40
#else
 
41
#define SEV             ALT_SMP("sev", "nop")
 
42
#define WFE(cond)       ALT_SMP("wfe" cond, "nop")
 
43
#endif
 
44
 
 
45
static inline void dsb_sev(void)
 
46
{
 
47
#if __LINUX_ARM_ARCH__ >= 7
 
48
        __asm__ __volatile__ (
 
49
                "dsb\n"
 
50
                SEV
 
51
        );
 
52
#else
 
53
        __asm__ __volatile__ (
 
54
                "mcr p15, 0, %0, c7, c10, 4\n"
 
55
                SEV
 
56
                : : "r" (0)
 
57
        );
 
58
#endif
 
59
}
 
60
 
 
61
/*
 
62
 * ARMv6 Spin-locking.
 
63
 *
 
64
 * We exclusively read the old value.  If it is zero, we may have
 
65
 * won the lock, so we try exclusively storing it.  A memory barrier
 
66
 * is required after we get a lock, and before we release it, because
 
67
 * V6 CPUs are assumed to have weakly ordered memory.
 
68
 *
 
69
 * Unlocked value: 0
 
70
 * Locked value: 1
 
71
 */
 
72
 
 
73
#define arch_spin_is_locked(x)          ((x)->lock != 0)
 
74
#define arch_spin_unlock_wait(lock) \
 
75
        do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0)
 
76
 
 
77
#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
 
78
 
 
79
static inline void arch_spin_lock(arch_spinlock_t *lock)
 
80
{
 
81
        unsigned long tmp;
 
82
 
 
83
        __asm__ __volatile__(
 
84
"1:     ldrex   %0, [%1]\n"
 
85
"       teq     %0, #0\n"
 
86
        WFE("ne")
 
87
"       strexeq %0, %2, [%1]\n"
 
88
"       teqeq   %0, #0\n"
 
89
"       bne     1b"
 
90
        : "=&r" (tmp)
 
91
        : "r" (&lock->lock), "r" (1)
 
92
        : "cc");
 
93
 
 
94
        smp_mb();
 
95
}
 
96
 
 
97
static inline int arch_spin_trylock(arch_spinlock_t *lock)
 
98
{
 
99
        unsigned long tmp;
 
100
 
 
101
        __asm__ __volatile__(
 
102
"       ldrex   %0, [%1]\n"
 
103
"       teq     %0, #0\n"
 
104
"       strexeq %0, %2, [%1]"
 
105
        : "=&r" (tmp)
 
106
        : "r" (&lock->lock), "r" (1)
 
107
        : "cc");
 
108
 
 
109
        if (tmp == 0) {
 
110
                smp_mb();
 
111
                return 1;
 
112
        } else {
 
113
                return 0;
 
114
        }
 
115
}
 
116
 
 
117
static inline void arch_spin_unlock(arch_spinlock_t *lock)
 
118
{
 
119
        smp_mb();
 
120
 
 
121
        __asm__ __volatile__(
 
122
"       str     %1, [%0]\n"
 
123
        :
 
124
        : "r" (&lock->lock), "r" (0)
 
125
        : "cc");
 
126
 
 
127
        dsb_sev();
 
128
}
 
129
 
 
130
/*
 
131
 * RWLOCKS
 
132
 *
 
133
 *
 
134
 * Write locks are easy - we just set bit 31.  When unlocking, we can
 
135
 * just write zero since the lock is exclusively held.
 
136
 */
 
137
 
 
138
static inline void arch_write_lock(arch_rwlock_t *rw)
 
139
{
 
140
        unsigned long tmp;
 
141
 
 
142
        __asm__ __volatile__(
 
143
"1:     ldrex   %0, [%1]\n"
 
144
"       teq     %0, #0\n"
 
145
        WFE("ne")
 
146
"       strexeq %0, %2, [%1]\n"
 
147
"       teq     %0, #0\n"
 
148
"       bne     1b"
 
149
        : "=&r" (tmp)
 
150
        : "r" (&rw->lock), "r" (0x80000000)
 
151
        : "cc");
 
152
 
 
153
        smp_mb();
 
154
}
 
155
 
 
156
static inline int arch_write_trylock(arch_rwlock_t *rw)
 
157
{
 
158
        unsigned long tmp;
 
159
 
 
160
        __asm__ __volatile__(
 
161
"1:     ldrex   %0, [%1]\n"
 
162
"       teq     %0, #0\n"
 
163
"       strexeq %0, %2, [%1]"
 
164
        : "=&r" (tmp)
 
165
        : "r" (&rw->lock), "r" (0x80000000)
 
166
        : "cc");
 
167
 
 
168
        if (tmp == 0) {
 
169
                smp_mb();
 
170
                return 1;
 
171
        } else {
 
172
                return 0;
 
173
        }
 
174
}
 
175
 
 
176
static inline void arch_write_unlock(arch_rwlock_t *rw)
 
177
{
 
178
        smp_mb();
 
179
 
 
180
        __asm__ __volatile__(
 
181
        "str    %1, [%0]\n"
 
182
        :
 
183
        : "r" (&rw->lock), "r" (0)
 
184
        : "cc");
 
185
 
 
186
        dsb_sev();
 
187
}
 
188
 
 
189
/* write_can_lock - would write_trylock() succeed? */
 
190
#define arch_write_can_lock(x)          ((x)->lock == 0)
 
191
 
 
192
/*
 
193
 * Read locks are a bit more hairy:
 
194
 *  - Exclusively load the lock value.
 
195
 *  - Increment it.
 
196
 *  - Store new lock value if positive, and we still own this location.
 
197
 *    If the value is negative, we've already failed.
 
198
 *  - If we failed to store the value, we want a negative result.
 
199
 *  - If we failed, try again.
 
200
 * Unlocking is similarly hairy.  We may have multiple read locks
 
201
 * currently active.  However, we know we won't have any write
 
202
 * locks.
 
203
 */
 
204
static inline void arch_read_lock(arch_rwlock_t *rw)
 
205
{
 
206
        unsigned long tmp, tmp2;
 
207
 
 
208
        __asm__ __volatile__(
 
209
"1:     ldrex   %0, [%2]\n"
 
210
"       adds    %0, %0, #1\n"
 
211
"       strexpl %1, %0, [%2]\n"
 
212
        WFE("mi")
 
213
"       rsbpls  %0, %1, #0\n"
 
214
"       bmi     1b"
 
215
        : "=&r" (tmp), "=&r" (tmp2)
 
216
        : "r" (&rw->lock)
 
217
        : "cc");
 
218
 
 
219
        smp_mb();
 
220
}
 
221
 
 
222
static inline void arch_read_unlock(arch_rwlock_t *rw)
 
223
{
 
224
        unsigned long tmp, tmp2;
 
225
 
 
226
        smp_mb();
 
227
 
 
228
        __asm__ __volatile__(
 
229
"1:     ldrex   %0, [%2]\n"
 
230
"       sub     %0, %0, #1\n"
 
231
"       strex   %1, %0, [%2]\n"
 
232
"       teq     %1, #0\n"
 
233
"       bne     1b"
 
234
        : "=&r" (tmp), "=&r" (tmp2)
 
235
        : "r" (&rw->lock)
 
236
        : "cc");
 
237
 
 
238
        if (tmp == 0)
 
239
                dsb_sev();
 
240
}
 
241
 
 
242
static inline int arch_read_trylock(arch_rwlock_t *rw)
 
243
{
 
244
        unsigned long tmp, tmp2 = 1;
 
245
 
 
246
        __asm__ __volatile__(
 
247
"1:     ldrex   %0, [%2]\n"
 
248
"       adds    %0, %0, #1\n"
 
249
"       strexpl %1, %0, [%2]\n"
 
250
        : "=&r" (tmp), "+r" (tmp2)
 
251
        : "r" (&rw->lock)
 
252
        : "cc");
 
253
 
 
254
        smp_mb();
 
255
        return tmp2 == 0;
 
256
}
 
257
 
 
258
/* read_can_lock - would read_trylock() succeed? */
 
259
#define arch_read_can_lock(x)           ((x)->lock < 0x80000000)
 
260
 
 
261
#define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
 
262
#define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
 
263
 
 
264
#define arch_spin_relax(lock)   cpu_relax()
 
265
#define arch_read_relax(lock)   cpu_relax()
 
266
#define arch_write_relax(lock)  cpu_relax()
 
267
 
 
268
#endif /* __ASM_SPINLOCK_H */