spinlock.h 6.68 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13
#ifndef _ASM_IA64_SPINLOCK_H
#define _ASM_IA64_SPINLOCK_H

/*
 * Copyright (C) 1998-2003 Hewlett-Packard Co
 *	David Mosberger-Tang <davidm@hpl.hp.com>
 * Copyright (C) 1999 Walt Drummond <drummond@valinux.com>
 *
 * This file is used for SMP configurations only.
 */

#include <linux/compiler.h>
#include <linux/kernel.h>
Jiri Slaby's avatar
Jiri Slaby committed
14
#include <linux/bitops.h>
Linus Torvalds's avatar
Linus Torvalds committed
15 16 17 18 19

#include <asm/atomic.h>
#include <asm/intrinsics.h>
#include <asm/system.h>

Ingo Molnar's avatar
Ingo Molnar committed
20
#define __raw_spin_lock_init(x)			((x)->lock = 0)
Linus Torvalds's avatar
Linus Torvalds committed
21 22

/*
23 24 25 26 27 28 29 30 31 32
 * 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.
 *
 *   63                     32  31                      0
 *  +----------------------------------------------------+
 *  |  next_ticket_number      |     now_serving         |
 *  +----------------------------------------------------+
Linus Torvalds's avatar
Linus Torvalds committed
33 34
 */

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
#define TICKET_SHIFT	32

static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock)
{
	int	*p = (int *)&lock->lock, turn, now_serving;

	now_serving = *p;
	turn = ia64_fetchadd(1, p+1, acq);

	if (turn == now_serving)
		return;

	do {
		cpu_relax();
	} while (ACCESS_ONCE(*p) != turn);
}

static __always_inline int __ticket_spin_trylock(raw_spinlock_t *lock)
{
	long tmp = ACCESS_ONCE(lock->lock), try;

	if (!(((tmp >> TICKET_SHIFT) ^ tmp) & ((1L << TICKET_SHIFT) - 1))) {
		try = tmp + (1L << TICKET_SHIFT);

		return ia64_cmpxchg(acq, &lock->lock, tmp, try, sizeof (tmp)) == tmp;
	}
	return 0;
}

static __always_inline void __ticket_spin_unlock(raw_spinlock_t *lock)
{
	int	*p = (int *)&lock->lock;

	(void)ia64_fetchadd(1, p, rel);
}
Linus Torvalds's avatar
Linus Torvalds committed
70

71
static inline int __ticket_spin_is_locked(raw_spinlock_t *lock)
Linus Torvalds's avatar
Linus Torvalds committed
72
{
73 74 75
	long tmp = ACCESS_ONCE(lock->lock);

	return !!(((tmp >> TICKET_SHIFT) ^ tmp) & ((1L << TICKET_SHIFT) - 1));
Linus Torvalds's avatar
Linus Torvalds committed
76
}
77

78 79 80
static inline int __ticket_spin_is_contended(raw_spinlock_t *lock)
{
	long tmp = ACCESS_ONCE(lock->lock);
81

82
	return (((tmp >> TICKET_SHIFT) - tmp) & ((1L << TICKET_SHIFT) - 1)) > 1;
83 84
}

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
static inline int __raw_spin_is_locked(raw_spinlock_t *lock)
{
	return __ticket_spin_is_locked(lock);
}

static inline int __raw_spin_is_contended(raw_spinlock_t *lock)
{
	return __ticket_spin_is_contended(lock);
}
#define __raw_spin_is_contended	__raw_spin_is_contended

static __always_inline void __raw_spin_lock(raw_spinlock_t *lock)
{
	__ticket_spin_lock(lock);
}

static __always_inline int __raw_spin_trylock(raw_spinlock_t *lock)
{
	return __ticket_spin_trylock(lock);
}

static __always_inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
	__ticket_spin_unlock(lock);
}

static __always_inline void __raw_spin_lock_flags(raw_spinlock_t *lock,
						  unsigned long flags)
{
	__raw_spin_lock(lock);
}

static inline void __raw_spin_unlock_wait(raw_spinlock_t *lock)
{
	while (__raw_spin_is_locked(lock))
		cpu_relax();
}
Linus Torvalds's avatar
Linus Torvalds committed
122

Ingo Molnar's avatar
Ingo Molnar committed
123 124
#define __raw_read_can_lock(rw)		(*(volatile int *)(rw) >= 0)
#define __raw_write_can_lock(rw)	(*(volatile int *)(rw) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
125

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
#ifdef ASM_SUPPORTED

static __always_inline void
__raw_read_lock_flags(raw_rwlock_t *lock, unsigned long flags)
{
	__asm__ __volatile__ (
		"tbit.nz p6, p0 = %1,%2\n"
		"br.few 3f\n"
		"1:\n"
		"fetchadd4.rel r2 = [%0], -1;;\n"
		"(p6) ssm psr.i\n"
		"2:\n"
		"hint @pause\n"
		"ld4 r2 = [%0];;\n"
		"cmp4.lt p7,p0 = r2, r0\n"
		"(p7) br.cond.spnt.few 2b\n"
		"(p6) rsm psr.i\n"
		";;\n"
		"3:\n"
		"fetchadd4.acq r2 = [%0], 1;;\n"
		"cmp4.lt p7,p0 = r2, r0\n"
		"(p7) br.cond.spnt.few 1b\n"
		: : "r"(lock), "r"(flags), "i"(IA64_PSR_I_BIT)
		: "p6", "p7", "r2", "memory");
}

#define __raw_read_lock(lock) __raw_read_lock_flags(lock, 0)

#else /* !ASM_SUPPORTED */

#define __raw_read_lock_flags(rw, flags) __raw_read_lock(rw)

Ingo Molnar's avatar
Ingo Molnar committed
158
#define __raw_read_lock(rw)								\
Linus Torvalds's avatar
Linus Torvalds committed
159
do {											\
Ingo Molnar's avatar
Ingo Molnar committed
160
	raw_rwlock_t *__read_lock_ptr = (rw);						\
Linus Torvalds's avatar
Linus Torvalds committed
161 162 163 164 165 166 167 168
											\
	while (unlikely(ia64_fetchadd(1, (int *) __read_lock_ptr, acq) < 0)) {		\
		ia64_fetchadd(-1, (int *) __read_lock_ptr, rel);			\
		while (*(volatile int *)__read_lock_ptr < 0)				\
			cpu_relax();							\
	}										\
} while (0)

169 170
#endif /* !ASM_SUPPORTED */

Ingo Molnar's avatar
Ingo Molnar committed
171
#define __raw_read_unlock(rw)					\
Linus Torvalds's avatar
Linus Torvalds committed
172
do {								\
Ingo Molnar's avatar
Ingo Molnar committed
173
	raw_rwlock_t *__read_lock_ptr = (rw);			\
Linus Torvalds's avatar
Linus Torvalds committed
174 175 176 177
	ia64_fetchadd(-1, (int *) __read_lock_ptr, rel);	\
} while (0)

#ifdef ASM_SUPPORTED
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204

static __always_inline void
__raw_write_lock_flags(raw_rwlock_t *lock, unsigned long flags)
{
	__asm__ __volatile__ (
		"tbit.nz p6, p0 = %1, %2\n"
		"mov ar.ccv = r0\n"
		"dep r29 = -1, r0, 31, 1\n"
		"br.few 3f;;\n"
		"1:\n"
		"(p6) ssm psr.i\n"
		"2:\n"
		"hint @pause\n"
		"ld4 r2 = [%0];;\n"
		"cmp4.eq p0,p7 = r0, r2\n"
		"(p7) br.cond.spnt.few 2b\n"
		"(p6) rsm psr.i\n"
		";;\n"
		"3:\n"
		"cmpxchg4.acq r2 = [%0], r29, ar.ccv;;\n"
		"cmp4.eq p0,p7 = r0, r2\n"
		"(p7) br.cond.spnt.few 1b;;\n"
		: : "r"(lock), "r"(flags), "i"(IA64_PSR_I_BIT)
		: "ar.ccv", "p6", "p7", "r2", "r29", "memory");
}

#define __raw_write_lock(rw) __raw_write_lock_flags(rw, 0)
Linus Torvalds's avatar
Linus Torvalds committed
205

Ingo Molnar's avatar
Ingo Molnar committed
206
#define __raw_write_trylock(rw)							\
Linus Torvalds's avatar
Linus Torvalds committed
207 208 209 210 211 212 213 214 215 216 217
({										\
	register long result;							\
										\
	__asm__ __volatile__ (							\
		"mov ar.ccv = r0\n"						\
		"dep r29 = -1, r0, 31, 1;;\n"					\
		"cmpxchg4.acq %0 = [%1], r29, ar.ccv\n"				\
		: "=r"(result) : "r"(rw) : "ar.ccv", "r29", "memory");		\
	(result == 0);								\
})

Ingo Molnar's avatar
Ingo Molnar committed
218
static inline void __raw_write_unlock(raw_rwlock_t *x)
219 220 221 222 223 224
{
	u8 *y = (u8 *)x;
	barrier();
	asm volatile ("st1.rel.nta [%0] = r0\n\t" :: "r"(y+3) : "memory" );
}

Linus Torvalds's avatar
Linus Torvalds committed
225 226
#else /* !ASM_SUPPORTED */

227 228
#define __raw_write_lock_flags(l, flags) __raw_write_lock(l)

Ingo Molnar's avatar
Ingo Molnar committed
229
#define __raw_write_lock(l)								\
Linus Torvalds's avatar
Linus Torvalds committed
230 231 232 233 234 235 236 237 238 239
({											\
	__u64 ia64_val, ia64_set_val = ia64_dep_mi(-1, 0, 31, 1);			\
	__u32 *ia64_write_lock_ptr = (__u32 *) (l);					\
	do {										\
		while (*ia64_write_lock_ptr)						\
			ia64_barrier();							\
		ia64_val = ia64_cmpxchg4_acq(ia64_write_lock_ptr, ia64_set_val, 0);	\
	} while (ia64_val);								\
})

Ingo Molnar's avatar
Ingo Molnar committed
240
#define __raw_write_trylock(rw)						\
Linus Torvalds's avatar
Linus Torvalds committed
241 242 243 244 245 246 247
({									\
	__u64 ia64_val;							\
	__u64 ia64_set_val = ia64_dep_mi(-1, 0, 31,1);			\
	ia64_val = ia64_cmpxchg4_acq((__u32 *)(rw), ia64_set_val, 0);	\
	(ia64_val == 0);						\
})

Ingo Molnar's avatar
Ingo Molnar committed
248
static inline void __raw_write_unlock(raw_rwlock_t *x)
249 250 251 252 253
{
	barrier();
	x->write_lock = 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
254 255
#endif /* !ASM_SUPPORTED */

256 257 258 259 260 261 262 263 264 265 266
static inline int __raw_read_trylock(raw_rwlock_t *x)
{
	union {
		raw_rwlock_t lock;
		__u32 word;
	} old, new;
	old.lock = new.lock = *x;
	old.lock.write_lock = new.lock.write_lock = 0;
	++new.lock.read_counter;
	return (u32)ia64_cmpxchg4_acq((__u32 *)(x), new.word, old.word) == old.word;
}
Linus Torvalds's avatar
Linus Torvalds committed
267

268 269 270 271
#define _raw_spin_relax(lock)	cpu_relax()
#define _raw_read_relax(lock)	cpu_relax()
#define _raw_write_relax(lock)	cpu_relax()

Linus Torvalds's avatar
Linus Torvalds committed
272
#endif /*  _ASM_IA64_SPINLOCK_H */