summaryrefslogtreecommitdiff
path: root/release/src/linux/linux/include/asm-sparc64/rwsem.h
blob: ed250ff2d662ace3da5cf929e34adbc50dbc5fe3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
70
71
72
73
74
75
76
77
78
79
80
81
82
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
122
123
124
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
/* $Id: rwsem.h,v 1.1.1.4 2003/10/14 08:09:23 sparq Exp $
 * rwsem.h: R/W semaphores implemented using CAS
 *
 * Written by David S. Miller (davem@redhat.com), 2001.
 * Derived from asm-i386/rwsem.h
 */
#ifndef _SPARC64_RWSEM_H
#define _SPARC64_RWSEM_H

#ifndef _LINUX_RWSEM_H
#error please dont include asm/rwsem.h directly, use linux/rwsem.h instead
#endif

#ifdef __KERNEL__

#include <linux/list.h>
#include <linux/spinlock.h>

struct rwsem_waiter;

extern struct rw_semaphore *FASTCALL(rwsem_down_read_failed(struct rw_semaphore *sem));
extern struct rw_semaphore *FASTCALL(rwsem_down_write_failed(struct rw_semaphore *sem));
extern struct rw_semaphore *FASTCALL(rwsem_wake(struct rw_semaphore *));

struct rw_semaphore {
	signed int count;
#define RWSEM_UNLOCKED_VALUE		0x00000000
#define RWSEM_ACTIVE_BIAS		0x00000001
#define RWSEM_ACTIVE_MASK		0x0000ffff
#define RWSEM_WAITING_BIAS		0xffff0000
#define RWSEM_ACTIVE_READ_BIAS		RWSEM_ACTIVE_BIAS
#define RWSEM_ACTIVE_WRITE_BIAS		(RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
	spinlock_t		wait_lock;
	struct list_head	wait_list;
};

#define __RWSEM_INITIALIZER(name) \
{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) }

#define DECLARE_RWSEM(name) \
	struct rw_semaphore name = __RWSEM_INITIALIZER(name)

static inline void init_rwsem(struct rw_semaphore *sem)
{
	sem->count = RWSEM_UNLOCKED_VALUE;
	spin_lock_init(&sem->wait_lock);
	INIT_LIST_HEAD(&sem->wait_list);
}

static inline void __down_read(struct rw_semaphore *sem)
{
	__asm__ __volatile__(
		"! beginning __down_read\n"
		"1:\tlduw	[%0], %%g5\n\t"
		"add		%%g5, 1, %%g7\n\t"
		"cas		[%0], %%g5, %%g7\n\t"
		"cmp		%%g5, %%g7\n\t"
		"bne,pn		%%icc, 1b\n\t"
		" add		%%g7, 1, %%g7\n\t"
		"cmp		%%g7, 0\n\t"
		"bl,pn		%%icc, 3f\n\t"
		" membar	#StoreLoad | #StoreStore\n"
		"2:\n\t"
		".subsection	2\n"
		"3:\tmov	%0, %%g5\n\t"
		"save		%%sp, -160, %%sp\n\t"
		"mov		%%g1, %%l1\n\t"
		"mov		%%g2, %%l2\n\t"
		"mov		%%g3, %%l3\n\t"
		"call		%1\n\t"
		" mov		%%g5, %%o0\n\t"
		"mov		%%l1, %%g1\n\t"
		"mov		%%l2, %%g2\n\t"
		"ba,pt		%%xcc, 2b\n\t"
		" restore	%%l3, %%g0, %%g3\n\t"
		".previous\n\t"
		"! ending __down_read"
		: : "r" (sem), "i" (rwsem_down_read_failed)
		: "g5", "g7", "memory", "cc");
}

static __inline__ int __down_read_trylock(struct rw_semaphore *sem)
{
	int result;

	__asm__ __volatile__(
		"! beginning __down_read_trylock\n"
		"1:\tlduw	[%1], %%g5\n\t"
		"add		%%g5, 1, %%g7\n\t"
		"cmp		%%g7, 0\n\t"
		"bl,pn		%%icc, 2f\n\t"
		" mov		0, %0\n\t"
		"cas		[%1], %%g5, %%g7\n\t"
		"cmp		%%g5, %%g7\n\t"
		"bne,pn		%%icc, 1b\n\t"
		" mov		1, %0\n\t"
		"membar		#StoreLoad | #StoreStore\n"
		"2:\n\t"
		"! ending __down_read_trylock"
		: "=&r" (result)
                : "r" (sem)
		: "g5", "g7", "memory", "cc");

	return result;
}

static inline void __down_write(struct rw_semaphore *sem)
{
	__asm__ __volatile__(
		"! beginning __down_write\n\t"
		"sethi		%%hi(%2), %%g1\n\t"
		"or		%%g1, %%lo(%2), %%g1\n"
		"1:\tlduw	[%0], %%g5\n\t"
		"add		%%g5, %%g1, %%g7\n\t"
		"cas		[%0], %%g5, %%g7\n\t"
		"cmp		%%g5, %%g7\n\t"
		"bne,pn		%%icc, 1b\n\t"
		" cmp		%%g7, 0\n\t"
		"bne,pn		%%icc, 3f\n\t"
		" membar	#StoreLoad | #StoreStore\n"
		"2:\n\t"
		".subsection	2\n"
		"3:\tmov	%0, %%g5\n\t"
		"save		%%sp, -160, %%sp\n\t"
		"mov		%%g2, %%l2\n\t"
		"mov		%%g3, %%l3\n\t"
		"call		%1\n\t"
		" mov		%%g5, %%o0\n\t"
		"mov		%%l2, %%g2\n\t"
		"ba,pt		%%xcc, 2b\n\t"
		" restore	%%l3, %%g0, %%g3\n\t"
		".previous\n\t"
		"! ending __down_write"
		: : "r" (sem), "i" (rwsem_down_write_failed),
		    "i" (RWSEM_ACTIVE_WRITE_BIAS)
		: "g1", "g5", "g7", "memory", "cc");
}

static __inline__ int __down_write_trylock(struct rw_semaphore *sem)
{
	int result;

	__asm__ __volatile__(
		"! beginning __down_write_trylock\n\t"
		"sethi		%%hi(%2), %%g1\n\t"
		"or		%%g1, %%lo(%2), %%g1\n"
		"1:\tlduw	[%1], %%g5\n\t"
		"cmp		%%g5, 0\n\t"
		"bne,pn		%%icc, 2f\n\t"
		" mov		0, %0\n\t"
		"add		%%g5, %%g1, %%g7\n\t"
		"cas		[%1], %%g5, %%g7\n\t"
		"cmp		%%g5, %%g7\n\t"
		"bne,pn		%%icc, 1b\n\t"
		" mov		1, %0\n\t"
		"membar		#StoreLoad | #StoreStore\n"
		"2:\n\t"
		"! ending __down_write_trylock"
		: "=&r" (result)
		: "r" (sem), "i" (RWSEM_ACTIVE_WRITE_BIAS)
		: "g1", "g5", "g7", "memory", "cc");

	return result;
}

static inline void __up_read(struct rw_semaphore *sem)
{
	__asm__ __volatile__(
		"! beginning __up_read\n\t"
		"1:\tlduw	[%0], %%g5\n\t"
		"sub		%%g5, 1, %%g7\n\t"
		"cas		[%0], %%g5, %%g7\n\t"
		"cmp		%%g5, %%g7\n\t"
		"bne,pn		%%icc, 1b\n\t"
		" cmp		%%g7, 0\n\t"
		"bl,pn		%%icc, 3f\n\t"
		" membar	#StoreLoad | #StoreStore\n"
		"2:\n\t"
		".subsection	2\n"
		"3:\tsethi	%%hi(%2), %%g1\n\t"
		"sub		%%g7, 1, %%g7\n\t"
		"or		%%g1, %%lo(%2), %%g1\n\t"
		"andcc		%%g7, %%g1, %%g0\n\t"
		"bne,pn		%%icc, 2b\n\t"
		" mov		%0, %%g5\n\t"
		"save		%%sp, -160, %%sp\n\t"
		"mov		%%g2, %%l2\n\t"
		"mov		%%g3, %%l3\n\t"
		"call		%1\n\t"
		" mov		%%g5, %%o0\n\t"
		"mov		%%l2, %%g2\n\t"
		"ba,pt		%%xcc, 2b\n\t"
		" restore	%%l3, %%g0, %%g3\n\t"
		".previous\n\t"
		"! ending __up_read"
		: : "r" (sem), "i" (rwsem_wake),
		    "i" (RWSEM_ACTIVE_MASK)
		: "g1", "g5", "g7", "memory", "cc");
}

static inline void __up_write(struct rw_semaphore *sem)
{
	__asm__ __volatile__(
		"! beginning __up_write\n\t"
		"sethi		%%hi(%2), %%g1\n\t"
		"or		%%g1, %%lo(%2), %%g1\n"
		"1:\tlduw	[%0], %%g5\n\t"
		"sub		%%g5, %%g1, %%g7\n\t"
		"cas		[%0], %%g5, %%g7\n\t"
		"cmp		%%g5, %%g7\n\t"
		"bne,pn		%%icc, 1b\n\t"
		" sub		%%g7, %%g1, %%g7\n\t"
		"cmp		%%g7, 0\n\t"
		"bl,pn		%%icc, 3f\n\t"
		" membar	#StoreLoad | #StoreStore\n"
		"2:\n\t"
		".subsection 2\n"
		"3:\tmov	%0, %%g5\n\t"
		"save		%%sp, -160, %%sp\n\t"
		"mov		%%g2, %%l2\n\t"
		"mov		%%g3, %%l3\n\t"
		"call		%1\n\t"
		" mov		%%g5, %%o0\n\t"
		"mov		%%l2, %%g2\n\t"
		"ba,pt		%%xcc, 2b\n\t"
		" restore	%%l3, %%g0, %%g3\n\t"
		".previous\n\t"
		"! ending __up_write"
		: : "r" (sem), "i" (rwsem_wake),
		    "i" (RWSEM_ACTIVE_WRITE_BIAS)
		: "g1", "g5", "g7", "memory", "cc");
}

static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem)
{
	int tmp = delta;

	__asm__ __volatile__(
		"1:\tlduw	[%2], %%g5\n\t"
		"add		%%g5, %1, %%g7\n\t"
		"cas		[%2], %%g5, %%g7\n\t"
		"cmp		%%g5, %%g7\n\t"
		"bne,pn		%%icc, 1b\n\t"
		" membar	#StoreLoad | #StoreStore\n\t"
		"mov		%%g7, %0\n\t"
		: "=&r" (tmp)
		: "0" (tmp), "r" (sem)
		: "g5", "g7", "memory");

	return tmp + delta;
}

#define rwsem_atomic_add rwsem_atomic_update

static inline __u16 rwsem_cmpxchgw(struct rw_semaphore *sem, __u16 __old, __u16 __new)
{
	u32 old = (sem->count & 0xffff0000) | (u32) __old;
	u32 new = (old & 0xffff0000) | (u32) __new;
	u32 prev;

again:
	__asm__ __volatile__("cas	[%2], %3, %0\n\t"
			     "membar	#StoreLoad | #StoreStore"
			     : "=&r" (prev)
			     : "0" (new), "r" (sem), "r" (old)
			     : "memory");

	/* To give the same semantics as x86 cmpxchgw, keep trying
	 * if only the upper 16-bits changed.
	 */
	if (prev != old &&
	    ((prev & 0xffff) == (old & 0xffff)))
		goto again;

	return prev & 0xffff;
}

static inline signed long rwsem_cmpxchg(struct rw_semaphore *sem, signed long old, signed long new)
{
	return cmpxchg(&sem->count,old,new);
}

#endif /* __KERNEL__ */

#endif /* _SPARC64_RWSEM_H */