1/* $Id: semaphore.c,v 1.1.1.1 2007/08/03 18:52:18 Exp $
2 * semaphore.c: Sparc64 semaphore implementation.
3 *
4 * This is basically the PPC semaphore scheme ported to use
5 * the sparc64 atomic instructions, so see the PPC code for
6 * credits.
7 */
8
9#include <linux/sched.h>
10#include <linux/errno.h>
11#include <linux/init.h>
12
13/*
14 * Atomically update sem->count.
15 * This does the equivalent of the following:
16 *
17 *	old_count = sem->count;
18 *	tmp = MAX(old_count, 0) + incr;
19 *	sem->count = tmp;
20 *	return old_count;
21 */
22static __inline__ int __sem_update_count(struct semaphore *sem, int incr)
23{
24	int old_count, tmp;
25
26	__asm__ __volatile__("\n"
27"	! __sem_update_count old_count(%0) tmp(%1) incr(%4) &sem->count(%3)\n"
28"1:	ldsw	[%3], %0\n"
29"	mov	%0, %1\n"
30"	cmp	%0, 0\n"
31"	movl	%%icc, 0, %1\n"
32"	add	%1, %4, %1\n"
33"	cas	[%3], %0, %1\n"
34"	cmp	%0, %1\n"
35"	membar	#StoreLoad | #StoreStore\n"
36"	bne,pn	%%icc, 1b\n"
37"	 nop\n"
38	: "=&r" (old_count), "=&r" (tmp), "=m" (sem->count)
39	: "r" (&sem->count), "r" (incr), "m" (sem->count)
40	: "cc");
41
42	return old_count;
43}
44
45static void __up(struct semaphore *sem)
46{
47	__sem_update_count(sem, 1);
48	wake_up(&sem->wait);
49}
50
51void up(struct semaphore *sem)
52{
53	/* This atomically does:
54	 * 	old_val = sem->count;
55	 *	new_val = sem->count + 1;
56	 *	sem->count = new_val;
57	 *	if (old_val < 0)
58	 *		__up(sem);
59	 *
60	 * The (old_val < 0) test is equivalent to
61	 * the more straightforward (new_val <= 0),
62	 * but it is easier to test the former because
63	 * of how the CAS instruction works.
64	 */
65
66	__asm__ __volatile__("\n"
67"	! up sem(%0)\n"
68"	membar	#StoreLoad | #LoadLoad\n"
69"1:	lduw	[%0], %%g1\n"
70"	add	%%g1, 1, %%g7\n"
71"	cas	[%0], %%g1, %%g7\n"
72"	cmp	%%g1, %%g7\n"
73"	bne,pn	%%icc, 1b\n"
74"	 addcc	%%g7, 1, %%g0\n"
75"	membar	#StoreLoad | #StoreStore\n"
76"	ble,pn	%%icc, 3f\n"
77"	 nop\n"
78"2:\n"
79"	.subsection 2\n"
80"3:	mov	%0, %%g1\n"
81"	save	%%sp, -160, %%sp\n"
82"	call	%1\n"
83"	 mov	%%g1, %%o0\n"
84"	ba,pt	%%xcc, 2b\n"
85"	 restore\n"
86"	.previous\n"
87	: : "r" (sem), "i" (__up)
88	: "g1", "g2", "g3", "g7", "memory", "cc");
89}
90
91static void __sched __down(struct semaphore * sem)
92{
93	struct task_struct *tsk = current;
94	DECLARE_WAITQUEUE(wait, tsk);
95
96	tsk->state = TASK_UNINTERRUPTIBLE;
97	add_wait_queue_exclusive(&sem->wait, &wait);
98
99	while (__sem_update_count(sem, -1) <= 0) {
100		schedule();
101		tsk->state = TASK_UNINTERRUPTIBLE;
102	}
103	remove_wait_queue(&sem->wait, &wait);
104	tsk->state = TASK_RUNNING;
105
106	wake_up(&sem->wait);
107}
108
109void __sched down(struct semaphore *sem)
110{
111	might_sleep();
112	/* This atomically does:
113	 * 	old_val = sem->count;
114	 *	new_val = sem->count - 1;
115	 *	sem->count = new_val;
116	 *	if (old_val < 1)
117	 *		__down(sem);
118	 *
119	 * The (old_val < 1) test is equivalent to
120	 * the more straightforward (new_val < 0),
121	 * but it is easier to test the former because
122	 * of how the CAS instruction works.
123	 */
124
125	__asm__ __volatile__("\n"
126"	! down sem(%0)\n"
127"1:	lduw	[%0], %%g1\n"
128"	sub	%%g1, 1, %%g7\n"
129"	cas	[%0], %%g1, %%g7\n"
130"	cmp	%%g1, %%g7\n"
131"	bne,pn	%%icc, 1b\n"
132"	 cmp	%%g7, 1\n"
133"	membar	#StoreLoad | #StoreStore\n"
134"	bl,pn	%%icc, 3f\n"
135"	 nop\n"
136"2:\n"
137"	.subsection 2\n"
138"3:	mov	%0, %%g1\n"
139"	save	%%sp, -160, %%sp\n"
140"	call	%1\n"
141"	 mov	%%g1, %%o0\n"
142"	ba,pt	%%xcc, 2b\n"
143"	 restore\n"
144"	.previous\n"
145	: : "r" (sem), "i" (__down)
146	: "g1", "g2", "g3", "g7", "memory", "cc");
147}
148
149int down_trylock(struct semaphore *sem)
150{
151	int ret;
152
153	/* This atomically does:
154	 * 	old_val = sem->count;
155	 *	new_val = sem->count - 1;
156	 *	if (old_val < 1) {
157	 *		ret = 1;
158	 *	} else {
159	 *		sem->count = new_val;
160	 *		ret = 0;
161	 *	}
162	 *
163	 * The (old_val < 1) test is equivalent to
164	 * the more straightforward (new_val < 0),
165	 * but it is easier to test the former because
166	 * of how the CAS instruction works.
167	 */
168
169	__asm__ __volatile__("\n"
170"	! down_trylock sem(%1) ret(%0)\n"
171"1:	lduw	[%1], %%g1\n"
172"	sub	%%g1, 1, %%g7\n"
173"	cmp	%%g1, 1\n"
174"	bl,pn	%%icc, 2f\n"
175"	 mov	1, %0\n"
176"	cas	[%1], %%g1, %%g7\n"
177"	cmp	%%g1, %%g7\n"
178"	bne,pn	%%icc, 1b\n"
179"	 mov	0, %0\n"
180"	membar	#StoreLoad | #StoreStore\n"
181"2:\n"
182	: "=&r" (ret)
183	: "r" (sem)
184	: "g1", "g7", "memory", "cc");
185
186	return ret;
187}
188
189static int __sched __down_interruptible(struct semaphore * sem)
190{
191	int retval = 0;
192	struct task_struct *tsk = current;
193	DECLARE_WAITQUEUE(wait, tsk);
194
195	tsk->state = TASK_INTERRUPTIBLE;
196	add_wait_queue_exclusive(&sem->wait, &wait);
197
198	while (__sem_update_count(sem, -1) <= 0) {
199		if (signal_pending(current)) {
200			__sem_update_count(sem, 0);
201			retval = -EINTR;
202			break;
203		}
204		schedule();
205		tsk->state = TASK_INTERRUPTIBLE;
206	}
207	tsk->state = TASK_RUNNING;
208	remove_wait_queue(&sem->wait, &wait);
209	wake_up(&sem->wait);
210	return retval;
211}
212
213int __sched down_interruptible(struct semaphore *sem)
214{
215	int ret = 0;
216
217	might_sleep();
218	/* This atomically does:
219	 * 	old_val = sem->count;
220	 *	new_val = sem->count - 1;
221	 *	sem->count = new_val;
222	 *	if (old_val < 1)
223	 *		ret = __down_interruptible(sem);
224	 *
225	 * The (old_val < 1) test is equivalent to
226	 * the more straightforward (new_val < 0),
227	 * but it is easier to test the former because
228	 * of how the CAS instruction works.
229	 */
230
231	__asm__ __volatile__("\n"
232"	! down_interruptible sem(%2) ret(%0)\n"
233"1:	lduw	[%2], %%g1\n"
234"	sub	%%g1, 1, %%g7\n"
235"	cas	[%2], %%g1, %%g7\n"
236"	cmp	%%g1, %%g7\n"
237"	bne,pn	%%icc, 1b\n"
238"	 cmp	%%g7, 1\n"
239"	membar	#StoreLoad | #StoreStore\n"
240"	bl,pn	%%icc, 3f\n"
241"	 nop\n"
242"2:\n"
243"	.subsection 2\n"
244"3:	mov	%2, %%g1\n"
245"	save	%%sp, -160, %%sp\n"
246"	call	%3\n"
247"	 mov	%%g1, %%o0\n"
248"	ba,pt	%%xcc, 2b\n"
249"	 restore\n"
250"	.previous\n"
251	: "=r" (ret)
252	: "0" (ret), "r" (sem), "i" (__down_interruptible)
253	: "g1", "g2", "g3", "g7", "memory", "cc");
254	return ret;
255}
256