spinlock.h 11.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
6
 * Copyright (C) 1999, 2000, 06 Ralf Baechle (ralf@linux-mips.org)
L
Linus Torvalds 已提交
7 8 9 10 11
 * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
 */
#ifndef _ASM_SPINLOCK_H
#define _ASM_SPINLOCK_H

12 13
#include <linux/compiler.h>

14
#include <asm/barrier.h>
L
Linus Torvalds 已提交
15 16 17 18
#include <asm/war.h>

/*
 * Your basic SMP spinlocks, allowing only a single CPU anywhere
19 20 21 22 23 24 25
 *
 * Simple spin lock operations.  There are two variants, one clears IRQ's
 * on the local processor, one does not.
 *
 * These are fair FIFO ticket locks
 *
 * (the type definitions are in asm/spinlock_types.h)
L
Linus Torvalds 已提交
26 27 28 29
 */


/*
30 31 32 33 34
 * Ticket locks are conceptually two parts, one indicating the current head of
 * the queue, and the other indicating the current tail. The lock is acquired
 * by atomically noting the tail and incrementing it by one (thus adding
 * ourself to the queue and noting our position), then waiting until the head
 * becomes equal to the the initial value of the tail.
L
Linus Torvalds 已提交
35 36
 */

37
static inline int arch_spin_is_locked(arch_spinlock_t *lock)
38
{
D
David Daney 已提交
39
	u32 counters = ACCESS_ONCE(lock->lock);
40

D
David Daney 已提交
41
	return ((counters >> 16) ^ counters) & 0xffff;
42 43
}

44 45 46
#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
#define arch_spin_unlock_wait(x) \
	while (arch_spin_is_locked(x)) { cpu_relax(); }
47

48
static inline int arch_spin_is_contended(arch_spinlock_t *lock)
49
{
D
David Daney 已提交
50
	u32 counters = ACCESS_ONCE(lock->lock);
51

D
David Daney 已提交
52
	return (((counters >> 16) - counters) & 0xffff) > 1;
53
}
54
#define arch_spin_is_contended	arch_spin_is_contended
55

56
static inline void arch_spin_lock(arch_spinlock_t *lock)
L
Linus Torvalds 已提交
57
{
58 59
	int my_ticket;
	int tmp;
D
David Daney 已提交
60
	int inc = 0x10000;
L
Linus Torvalds 已提交
61 62

	if (R10000_LLSC_WAR) {
63
		__asm__ __volatile__ (
64
		"	.set push		# arch_spin_lock	\n"
65 66 67
		"	.set noreorder					\n"
		"							\n"
		"1:	ll	%[ticket], %[ticket_ptr]		\n"
D
David Daney 已提交
68
		"	addu	%[my_ticket], %[ticket], %[inc]		\n"
69 70
		"	sc	%[my_ticket], %[ticket_ptr]		\n"
		"	beqzl	%[my_ticket], 1b			\n"
L
Linus Torvalds 已提交
71
		"	 nop						\n"
D
David Daney 已提交
72 73 74
		"	srl	%[my_ticket], %[ticket], 16		\n"
		"	andi	%[ticket], %[ticket], 0xffff		\n"
		"	andi	%[my_ticket], %[my_ticket], 0xffff	\n"
75 76 77 78
		"	bne	%[ticket], %[my_ticket], 4f		\n"
		"	 subu	%[ticket], %[my_ticket], %[ticket]	\n"
		"2:							\n"
		"	.subsection 2					\n"
D
David Daney 已提交
79
		"4:	andi	%[ticket], %[ticket], 0xffff		\n"
80
		"	sll	%[ticket], 5				\n"
81 82 83 84
		"							\n"
		"6:	bnez	%[ticket], 6b				\n"
		"	 subu	%[ticket], 1				\n"
		"							\n"
D
David Daney 已提交
85
		"	lhu	%[ticket], %[serving_now_ptr]		\n"
86 87
		"	beq	%[ticket], %[my_ticket], 2b		\n"
		"	 subu	%[ticket], %[my_ticket], %[ticket]	\n"
88
		"	b	4b					\n"
89 90 91 92
		"	 subu	%[ticket], %[ticket], 1			\n"
		"	.previous					\n"
		"	.set pop					\n"
		: [ticket_ptr] "+m" (lock->lock),
D
David Daney 已提交
93
		  [serving_now_ptr] "+m" (lock->h.serving_now),
94
		  [ticket] "=&r" (tmp),
D
David Daney 已提交
95 96
		  [my_ticket] "=&r" (my_ticket)
		: [inc] "r" (inc));
L
Linus Torvalds 已提交
97
	} else {
98
		__asm__ __volatile__ (
99
		"	.set push		# arch_spin_lock	\n"
100 101
		"	.set noreorder					\n"
		"							\n"
D
David Daney 已提交
102 103
		"1:	ll	%[ticket], %[ticket_ptr]		\n"
		"	addu	%[my_ticket], %[ticket], %[inc]		\n"
104
		"	sc	%[my_ticket], %[ticket_ptr]		\n"
D
David Daney 已提交
105 106 107 108
		"	beqz	%[my_ticket], 1b			\n"
		"	 srl	%[my_ticket], %[ticket], 16		\n"
		"	andi	%[ticket], %[ticket], 0xffff		\n"
		"	andi	%[my_ticket], %[my_ticket], 0xffff	\n"
109 110 111
		"	bne	%[ticket], %[my_ticket], 4f		\n"
		"	 subu	%[ticket], %[my_ticket], %[ticket]	\n"
		"2:							\n"
112
		"	.subsection 2					\n"
113
		"4:	andi	%[ticket], %[ticket], 0x1fff		\n"
114
		"	sll	%[ticket], 5				\n"
115 116 117 118
		"							\n"
		"6:	bnez	%[ticket], 6b				\n"
		"	 subu	%[ticket], 1				\n"
		"							\n"
D
David Daney 已提交
119
		"	lhu	%[ticket], %[serving_now_ptr]		\n"
120 121
		"	beq	%[ticket], %[my_ticket], 2b		\n"
		"	 subu	%[ticket], %[my_ticket], %[ticket]	\n"
122
		"	b	4b					\n"
123
		"	 subu	%[ticket], %[ticket], 1			\n"
124
		"	.previous					\n"
125 126
		"	.set pop					\n"
		: [ticket_ptr] "+m" (lock->lock),
D
David Daney 已提交
127
		  [serving_now_ptr] "+m" (lock->h.serving_now),
128
		  [ticket] "=&r" (tmp),
D
David Daney 已提交
129 130
		  [my_ticket] "=&r" (my_ticket)
		: [inc] "r" (inc));
L
Linus Torvalds 已提交
131
	}
132

133
	smp_llsc_mb();
L
Linus Torvalds 已提交
134 135
}

136
static inline void arch_spin_unlock(arch_spinlock_t *lock)
L
Linus Torvalds 已提交
137
{
D
David Daney 已提交
138 139 140 141
	unsigned int serving_now = lock->h.serving_now + 1;
	wmb();
	lock->h.serving_now = (u16)serving_now;
	nudge_writes();
L
Linus Torvalds 已提交
142 143
}

144
static inline unsigned int arch_spin_trylock(arch_spinlock_t *lock)
L
Linus Torvalds 已提交
145
{
146
	int tmp, tmp2, tmp3;
D
David Daney 已提交
147
	int inc = 0x10000;
L
Linus Torvalds 已提交
148 149

	if (R10000_LLSC_WAR) {
150
		__asm__ __volatile__ (
151
		"	.set push		# arch_spin_trylock	\n"
152 153 154
		"	.set noreorder					\n"
		"							\n"
		"1:	ll	%[ticket], %[ticket_ptr]		\n"
D
David Daney 已提交
155 156 157
		"	srl	%[my_ticket], %[ticket], 16		\n"
		"	andi	%[my_ticket], %[my_ticket], 0xffff	\n"
		"	andi	%[now_serving], %[ticket], 0xffff	\n"
158
		"	bne	%[my_ticket], %[now_serving], 3f	\n"
D
David Daney 已提交
159
		"	 addu	%[ticket], %[ticket], %[inc]		\n"
160 161 162 163 164 165 166 167 168 169 170 171
		"	sc	%[ticket], %[ticket_ptr]		\n"
		"	beqzl	%[ticket], 1b				\n"
		"	 li	%[ticket], 1				\n"
		"2:							\n"
		"	.subsection 2					\n"
		"3:	b	2b					\n"
		"	 li	%[ticket], 0				\n"
		"	.previous					\n"
		"	.set pop					\n"
		: [ticket_ptr] "+m" (lock->lock),
		  [ticket] "=&r" (tmp),
		  [my_ticket] "=&r" (tmp2),
D
David Daney 已提交
172 173
		  [now_serving] "=&r" (tmp3)
		: [inc] "r" (inc));
L
Linus Torvalds 已提交
174
	} else {
175
		__asm__ __volatile__ (
176
		"	.set push		# arch_spin_trylock	\n"
177 178
		"	.set noreorder					\n"
		"							\n"
D
David Daney 已提交
179 180 181 182
		"1:	ll	%[ticket], %[ticket_ptr]		\n"
		"	srl	%[my_ticket], %[ticket], 16		\n"
		"	andi	%[my_ticket], %[my_ticket], 0xffff	\n"
		"	andi	%[now_serving], %[ticket], 0xffff	\n"
183
		"	bne	%[my_ticket], %[now_serving], 3f	\n"
D
David Daney 已提交
184
		"	 addu	%[ticket], %[ticket], %[inc]		\n"
185
		"	sc	%[ticket], %[ticket_ptr]		\n"
D
David Daney 已提交
186
		"	beqz	%[ticket], 1b				\n"
187 188
		"	 li	%[ticket], 1				\n"
		"2:							\n"
189
		"	.subsection 2					\n"
190 191
		"3:	b	2b					\n"
		"	 li	%[ticket], 0				\n"
192
		"	.previous					\n"
193 194 195 196
		"	.set pop					\n"
		: [ticket_ptr] "+m" (lock->lock),
		  [ticket] "=&r" (tmp),
		  [my_ticket] "=&r" (tmp2),
D
David Daney 已提交
197 198
		  [now_serving] "=&r" (tmp3)
		: [inc] "r" (inc));
L
Linus Torvalds 已提交
199 200
	}

201
	smp_llsc_mb();
202

203
	return tmp;
L
Linus Torvalds 已提交
204 205 206 207 208 209 210 211 212 213 214
}

/*
 * Read-write spinlocks, allowing multiple readers but only one writer.
 *
 * NOTE! it is quite common to have readers in interrupts but no interrupt
 * writers. For those circumstances we can "mix" irq-safe locks - any writer
 * needs to get a irq-safe write-lock, but readers can get non-irqsafe
 * read-locks.
 */

215 216 217 218
/*
 * read_can_lock - would read_trylock() succeed?
 * @lock: the rwlock in question.
 */
219
#define arch_read_can_lock(rw)	((rw)->lock >= 0)
220 221 222 223 224

/*
 * write_can_lock - would write_trylock() succeed?
 * @lock: the rwlock in question.
 */
225
#define arch_write_can_lock(rw)	(!(rw)->lock)
226

227
static inline void arch_read_lock(arch_rwlock_t *rw)
L
Linus Torvalds 已提交
228 229 230 231 232
{
	unsigned int tmp;

	if (R10000_LLSC_WAR) {
		__asm__ __volatile__(
233
		"	.set	noreorder	# arch_read_lock	\n"
L
Linus Torvalds 已提交
234 235 236 237 238 239 240 241 242 243 244 245
		"1:	ll	%1, %2					\n"
		"	bltz	%1, 1b					\n"
		"	 addu	%1, 1					\n"
		"	sc	%1, %0					\n"
		"	beqzl	%1, 1b					\n"
		"	 nop						\n"
		"	.set	reorder					\n"
		: "=m" (rw->lock), "=&r" (tmp)
		: "m" (rw->lock)
		: "memory");
	} else {
		__asm__ __volatile__(
246
		"	.set	noreorder	# arch_read_lock	\n"
L
Linus Torvalds 已提交
247
		"1:	ll	%1, %2					\n"
248
		"	bltz	%1, 3f					\n"
L
Linus Torvalds 已提交
249
		"	 addu	%1, 1					\n"
250
		"2:	sc	%1, %0					\n"
L
Linus Torvalds 已提交
251
		"	beqz	%1, 1b					\n"
252
		"	 nop						\n"
253
		"	.subsection 2					\n"
254 255
		"3:	ll	%1, %2					\n"
		"	bltz	%1, 3b					\n"
256
		"	 addu	%1, 1					\n"
257
		"	b	2b					\n"
258 259
		"	 nop						\n"
		"	.previous					\n"
L
Linus Torvalds 已提交
260 261 262 263 264
		"	.set	reorder					\n"
		: "=m" (rw->lock), "=&r" (tmp)
		: "m" (rw->lock)
		: "memory");
	}
265

266
	smp_llsc_mb();
L
Linus Torvalds 已提交
267 268 269 270 271
}

/* Note the use of sub, not subu which will make the kernel die with an
   overflow exception if we ever try to unlock an rwlock that is already
   unlocked or is being held by a writer.  */
272
static inline void arch_read_unlock(arch_rwlock_t *rw)
L
Linus Torvalds 已提交
273 274 275
{
	unsigned int tmp;

276
	smp_mb__before_llsc();
277

L
Linus Torvalds 已提交
278 279
	if (R10000_LLSC_WAR) {
		__asm__ __volatile__(
280
		"1:	ll	%1, %2		# arch_read_unlock	\n"
L
Linus Torvalds 已提交
281 282 283 284 285 286 287 288
		"	sub	%1, 1					\n"
		"	sc	%1, %0					\n"
		"	beqzl	%1, 1b					\n"
		: "=m" (rw->lock), "=&r" (tmp)
		: "m" (rw->lock)
		: "memory");
	} else {
		__asm__ __volatile__(
289
		"	.set	noreorder	# arch_read_unlock	\n"
L
Linus Torvalds 已提交
290 291 292
		"1:	ll	%1, %2					\n"
		"	sub	%1, 1					\n"
		"	sc	%1, %0					\n"
293 294 295 296
		"	beqz	%1, 2f					\n"
		"	 nop						\n"
		"	.subsection 2					\n"
		"2:	b	1b					\n"
297
		"	 nop						\n"
298
		"	.previous					\n"
L
Linus Torvalds 已提交
299 300 301 302 303 304 305
		"	.set	reorder					\n"
		: "=m" (rw->lock), "=&r" (tmp)
		: "m" (rw->lock)
		: "memory");
	}
}

306
static inline void arch_write_lock(arch_rwlock_t *rw)
L
Linus Torvalds 已提交
307 308 309 310 311
{
	unsigned int tmp;

	if (R10000_LLSC_WAR) {
		__asm__ __volatile__(
312
		"	.set	noreorder	# arch_write_lock	\n"
L
Linus Torvalds 已提交
313 314 315 316 317
		"1:	ll	%1, %2					\n"
		"	bnez	%1, 1b					\n"
		"	 lui	%1, 0x8000				\n"
		"	sc	%1, %0					\n"
		"	beqzl	%1, 1b					\n"
318
		"	 nop						\n"
L
Linus Torvalds 已提交
319 320 321 322 323 324
		"	.set	reorder					\n"
		: "=m" (rw->lock), "=&r" (tmp)
		: "m" (rw->lock)
		: "memory");
	} else {
		__asm__ __volatile__(
325
		"	.set	noreorder	# arch_write_lock	\n"
L
Linus Torvalds 已提交
326
		"1:	ll	%1, %2					\n"
327
		"	bnez	%1, 3f					\n"
L
Linus Torvalds 已提交
328
		"	 lui	%1, 0x8000				\n"
329 330
		"2:	sc	%1, %0					\n"
		"	beqz	%1, 3f					\n"
331 332
		"	 nop						\n"
		"	.subsection 2					\n"
333 334
		"3:	ll	%1, %2					\n"
		"	bnez	%1, 3b					\n"
335
		"	 lui	%1, 0x8000				\n"
336
		"	b	2b					\n"
337
		"	 nop						\n"
338
		"	.previous					\n"
L
Linus Torvalds 已提交
339 340 341 342 343
		"	.set	reorder					\n"
		: "=m" (rw->lock), "=&r" (tmp)
		: "m" (rw->lock)
		: "memory");
	}
344

345
	smp_llsc_mb();
L
Linus Torvalds 已提交
346 347
}

348
static inline void arch_write_unlock(arch_rwlock_t *rw)
L
Linus Torvalds 已提交
349
{
350 351
	smp_mb();

L
Linus Torvalds 已提交
352
	__asm__ __volatile__(
353
	"				# arch_write_unlock	\n"
L
Linus Torvalds 已提交
354 355 356 357 358 359
	"	sw	$0, %0					\n"
	: "=m" (rw->lock)
	: "m" (rw->lock)
	: "memory");
}

360
static inline int arch_read_trylock(arch_rwlock_t *rw)
361 362 363 364 365 366
{
	unsigned int tmp;
	int ret;

	if (R10000_LLSC_WAR) {
		__asm__ __volatile__(
367
		"	.set	noreorder	# arch_read_trylock	\n"
368 369
		"	li	%2, 0					\n"
		"1:	ll	%1, %3					\n"
370
		"	bltz	%1, 2f					\n"
371 372 373
		"	 addu	%1, 1					\n"
		"	sc	%1, %0					\n"
		"	.set	reorder					\n"
374 375
		"	beqzl	%1, 1b					\n"
		"	 nop						\n"
376
		__WEAK_LLSC_MB
377 378 379 380 381 382 383
		"	li	%2, 1					\n"
		"2:							\n"
		: "=m" (rw->lock), "=&r" (tmp), "=&r" (ret)
		: "m" (rw->lock)
		: "memory");
	} else {
		__asm__ __volatile__(
384
		"	.set	noreorder	# arch_read_trylock	\n"
385 386
		"	li	%2, 0					\n"
		"1:	ll	%1, %3					\n"
387
		"	bltz	%1, 2f					\n"
388 389 390
		"	 addu	%1, 1					\n"
		"	sc	%1, %0					\n"
		"	beqz	%1, 1b					\n"
391
		"	 nop						\n"
392
		"	.set	reorder					\n"
393
		__WEAK_LLSC_MB
394 395 396 397 398 399 400 401 402
		"	li	%2, 1					\n"
		"2:							\n"
		: "=m" (rw->lock), "=&r" (tmp), "=&r" (ret)
		: "m" (rw->lock)
		: "memory");
	}

	return ret;
}
L
Linus Torvalds 已提交
403

404
static inline int arch_write_trylock(arch_rwlock_t *rw)
L
Linus Torvalds 已提交
405 406 407 408 409 410
{
	unsigned int tmp;
	int ret;

	if (R10000_LLSC_WAR) {
		__asm__ __volatile__(
411
		"	.set	noreorder	# arch_write_trylock	\n"
L
Linus Torvalds 已提交
412 413 414 415 416 417
		"	li	%2, 0					\n"
		"1:	ll	%1, %3					\n"
		"	bnez	%1, 2f					\n"
		"	 lui	%1, 0x8000				\n"
		"	sc	%1, %0					\n"
		"	beqzl	%1, 1b					\n"
418
		"	 nop						\n"
419
		__WEAK_LLSC_MB
L
Linus Torvalds 已提交
420 421 422 423 424 425 426 427
		"	li	%2, 1					\n"
		"	.set	reorder					\n"
		"2:							\n"
		: "=m" (rw->lock), "=&r" (tmp), "=&r" (ret)
		: "m" (rw->lock)
		: "memory");
	} else {
		__asm__ __volatile__(
428
		"	.set	noreorder	# arch_write_trylock	\n"
L
Linus Torvalds 已提交
429 430 431 432 433
		"	li	%2, 0					\n"
		"1:	ll	%1, %3					\n"
		"	bnez	%1, 2f					\n"
		"	lui	%1, 0x8000				\n"
		"	sc	%1, %0					\n"
434 435 436
		"	beqz	%1, 3f					\n"
		"	 li	%2, 1					\n"
		"2:							\n"
437
		__WEAK_LLSC_MB
438 439 440 441
		"	.subsection 2					\n"
		"3:	b	1b					\n"
		"	 li	%2, 0					\n"
		"	.previous					\n"
L
Linus Torvalds 已提交
442 443 444 445 446 447 448 449 450
		"	.set	reorder					\n"
		: "=m" (rw->lock), "=&r" (tmp), "=&r" (ret)
		: "m" (rw->lock)
		: "memory");
	}

	return ret;
}

451 452
#define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
#define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
453

454 455 456
#define arch_spin_relax(lock)	cpu_relax()
#define arch_read_relax(lock)	cpu_relax()
#define arch_write_relax(lock)	cpu_relax()
457

L
Linus Torvalds 已提交
458
#endif /* _ASM_SPINLOCK_H */