summaryrefslogtreecommitdiffstats
path: root/include/asm-ia64/semaphore.h
blob: a50ee01e39f64df30b0438adacd4b543606d98eb (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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#ifndef _ASM_IA64_SEMAPHORE_H
#define _ASM_IA64_SEMAPHORE_H

/*
 * Copyright (C) 1998-2000 Hewlett-Packard Co
 * Copyright (C) 1998-2000 David Mosberger-Tang <davidm@hpl.hp.com>
 */

#include <linux/wait.h>

#include <asm/atomic.h>

struct semaphore {
	atomic_t count;
	int sleepers;
	wait_queue_head_t wait;
#if WAITQUEUE_DEBUG
	long __magic;		/* initialized by __SEM_DEBUG_INIT() */
#endif
};

#if WAITQUEUE_DEBUG
# define __SEM_DEBUG_INIT(name)		, (long) &(name).__magic
#else
# define __SEM_DEBUG_INIT(name)
#endif

#define __SEMAPHORE_INITIALIZER(name,count)					\
{										\
	ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait)	\
	__SEM_DEBUG_INIT(name)							\
}

#define __MUTEX_INITIALIZER(name)	__SEMAPHORE_INITIALIZER(name,1)

#define __DECLARE_SEMAPHORE_GENERIC(name,count)					\
	struct semaphore name = __SEMAPHORE_INITIALIZER(name, count)

#define DECLARE_MUTEX(name)		__DECLARE_SEMAPHORE_GENERIC(name, 1)
#define DECLARE_MUTEX_LOCKED(name)	__DECLARE_SEMAPHORE_GENERIC(name, 0)

extern inline void
sema_init (struct semaphore *sem, int val)
{
	*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
}

static inline void
init_MUTEX (struct semaphore *sem)
{
	sema_init(sem, 1);
}

static inline void
init_MUTEX_LOCKED (struct semaphore *sem)
{
	sema_init(sem, 0);
}

extern void __down (struct semaphore * sem);
extern int  __down_interruptible (struct semaphore * sem);
extern int  __down_trylock (struct semaphore * sem);
extern void __up (struct semaphore * sem);

extern spinlock_t semaphore_wake_lock;

/*
 * Atomically decrement the semaphore's count.  If it goes negative,
 * block the calling thread in the TASK_UNINTERRUPTIBLE state.
 */
extern inline void
down (struct semaphore *sem)
{
#if WAITQUEUE_DEBUG
	CHECK_MAGIC(sem->__magic);
#endif
	if (atomic_dec_return(&sem->count) < 0)
		__down(sem);
}

/*
 * Atomically decrement the semaphore's count.  If it goes negative,
 * block the calling thread in the TASK_INTERRUPTIBLE state.
 */
extern inline int
down_interruptible (struct semaphore * sem)
{
	int ret = 0;

#if WAITQUEUE_DEBUG
	CHECK_MAGIC(sem->__magic);
#endif
	if (atomic_dec_return(&sem->count) < 0)
		ret = __down_interruptible(sem);
	return ret;
}

extern inline int
down_trylock (struct semaphore *sem)
{
	int ret = 0;

#if WAITQUEUE_DEBUG
	CHECK_MAGIC(sem->__magic);
#endif
	if (atomic_dec_return(&sem->count) < 0)
		ret = __down_trylock(sem);
	return ret;
}

extern inline void
up (struct semaphore * sem)
{
#if WAITQUEUE_DEBUG
	CHECK_MAGIC(sem->__magic);
#endif
	if (atomic_inc_return(&sem->count) <= 0)
		__up(sem);
}	

/*
 * rw mutexes (should that be mutices? =) -- throw rw spinlocks and
 * semaphores together, and this is what we end up with...
 *
 * The lock is initialized to BIAS.  This way, a writer subtracts BIAS
 * ands gets 0 for the case of an uncontended lock.  Readers decrement
 * by 1 and see a positive value when uncontended, negative if there
 * are writers waiting (in which case it goes to sleep).  BIAS must be
 * chosen such that subtracting BIAS once per CPU will result either
 * in zero (uncontended case) or in a negative value (contention
 * case).  On the other hand, BIAS must be at least as big as the
 * number of processes in the system.
 *
 * On IA-64, we use a BIAS value of 0x100000000, which supports up to
 * 2 billion (2^31) processors and 4 billion processes.
 *
 * In terms of fairness, when there is heavy use of the lock, we want
 * to see the lock being passed back and forth between readers and
 * writers (like in a producer/consumer style of communication).
 *
 *	      -ben (with clarifications & IA-64 comments by davidm)
 */
#define RW_LOCK_BIAS		0x100000000ul

struct rw_semaphore {
	volatile long		count;
	volatile __u8		write_bias_granted;
	volatile __u8		read_bias_granted;
	__u16			pad1;
	__u32			pad2;
	wait_queue_head_t	wait;
	wait_queue_head_t	write_bias_wait;
#if WAITQUEUE_DEBUG
	long			__magic;
	atomic_t		readers;
	atomic_t		writers;
#endif
};

#if WAITQUEUE_DEBUG
# define __RWSEM_DEBUG_INIT	, ATOMIC_INIT(0), ATOMIC_INIT(0)
#else
# define __RWSEM_DEBUG_INIT
#endif

#define __RWSEM_INITIALIZER(name,count)						\
{										\
	(count), 0, 0, 0, 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait),	\
	__WAIT_QUEUE_HEAD_INITIALIZER((name).write_bias_wait)			\
	__SEM_DEBUG_INIT(name) __RWSEM_DEBUG_INIT				\
}

#define __DECLARE_RWSEM_GENERIC(name,count)					\
	struct rw_semaphore name = __RWSEM_INITIALIZER(name,count)

#define DECLARE_RWSEM(name)			__DECLARE_RWSEM_GENERIC(name, RW_LOCK_BIAS)
#define DECLARE_RWSEM_READ_LOCKED(name)		__DECLARE_RWSEM_GENERIC(name, RW_LOCK_BIAS - 1)
#define DECLARE_RWSEM_WRITE_LOCKED(name)	__DECLARE_RWSEM_GENERIC(name, 0)

extern void __down_read_failed (struct rw_semaphore *sem, long count);
extern void __down_write_failed (struct rw_semaphore *sem, long count);
extern void __rwsem_wake (struct rw_semaphore *sem, long count);

extern inline void
init_rwsem (struct rw_semaphore *sem)
{
	sem->count = RW_LOCK_BIAS;
	sem->read_bias_granted = 0;
	sem->write_bias_granted = 0;
	init_waitqueue_head(&sem->wait);
	init_waitqueue_head(&sem->write_bias_wait);
#if WAITQUEUE_DEBUG
	sem->__magic = (long)&sem->__magic;
	atomic_set(&sem->readers, 0);
	atomic_set(&sem->writers, 0);
#endif
}

extern inline void
down_read (struct rw_semaphore *sem)
{
	long count;

#if WAITQUEUE_DEBUG
	CHECK_MAGIC(sem->__magic);
#endif

	count = ia64_fetch_and_add(-1, &sem->count);
	if (count < 0)
		__down_read_failed(sem, count);

#if WAITQUEUE_DEBUG
	if (sem->write_bias_granted)
		BUG();
	if (atomic_read(&sem->writers))
		BUG();
	atomic_inc(&sem->readers);
#endif
}

extern inline void
down_write (struct rw_semaphore *sem)
{
	long old_count, new_count;

#if WAITQUEUE_DEBUG
	CHECK_MAGIC(sem->__magic);
#endif

	do {
		old_count = sem->count;
		new_count = old_count - RW_LOCK_BIAS;
	} while (cmpxchg(&sem->count, old_count, new_count) != old_count);

	if (new_count != 0)
		__down_write_failed(sem, new_count);
#if WAITQUEUE_DEBUG
	if (atomic_read(&sem->writers))
		BUG();
	if (atomic_read(&sem->readers))
		BUG();
	if (sem->read_bias_granted)
		BUG();
	if (sem->write_bias_granted)
		BUG();
	atomic_inc(&sem->writers);
#endif
}

/*
 * When a reader does a release, the only significant
 * case is when there was a writer waiting, and we've
 * bumped the count to 0: we must wake the writer up.
 */
extern inline void
__up_read (struct rw_semaphore *sem)
{
	long count;

	count = ia64_fetch_and_add(1, &sem->count);
	if (count == 0)
		/*
		 * Other processes are blocked already; resolve
		 * contention by letting either a writer or a reader
		 * proceed...
		 */
		__rwsem_wake(sem, count);
}

/*
 * Releasing the writer is easy -- just release it and
 * wake up any sleepers.
 */
extern inline void
__up_write (struct rw_semaphore *sem)
{
	long old_count, new_count;

	do {
		old_count = sem->count;
		new_count = old_count + RW_LOCK_BIAS;
	} while (cmpxchg(&sem->count, old_count, new_count) != old_count);

	/*
	 * Note: new_count <u RW_LOCK_BIAS <=> old_count < 0 && new_count >= 0.
	 *	 (where <u is "unsigned less-than").
	 */
	if ((unsigned long) new_count < RW_LOCK_BIAS)
		/* someone is blocked already, resolve contention... */
		__rwsem_wake(sem, new_count);
}

extern inline void
up_read (struct rw_semaphore *sem)
{
#if WAITQUEUE_DEBUG
	if (sem->write_bias_granted)
		BUG();
	if (atomic_read(&sem->writers))
		BUG();
	atomic_dec(&sem->readers);
#endif
	__up_read(sem);
}

extern inline void
up_write (struct rw_semaphore *sem)
{
#if WAITQUEUE_DEBUG
	if (sem->read_bias_granted)
		BUG();
	if (sem->write_bias_granted)
		BUG();
	if (atomic_read(&sem->readers))
		BUG();
	if (atomic_read(&sem->writers) != 1)
		BUG();
	atomic_dec(&sem->writers);
#endif
	__up_write(sem);
}

#endif /* _ASM_IA64_SEMAPHORE_H */