Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Alpha semaphore implementation. | |
3 | * | |
4 | * (C) Copyright 1996 Linus Torvalds | |
5 | * (C) Copyright 1999, 2000 Richard Henderson | |
6 | */ | |
7 | ||
8 | #include <linux/errno.h> | |
9 | #include <linux/sched.h> | |
10 | #include <linux/init.h> | |
11 | ||
12 | /* | |
13 | * This is basically the PPC semaphore scheme ported to use | |
14 | * the Alpha ll/sc sequences, so see the PPC code for | |
15 | * credits. | |
16 | */ | |
17 | ||
18 | /* | |
19 | * Atomically update sem->count. | |
20 | * This does the equivalent of the following: | |
21 | * | |
22 | * old_count = sem->count; | |
23 | * tmp = MAX(old_count, 0) + incr; | |
24 | * sem->count = tmp; | |
25 | * return old_count; | |
26 | */ | |
27 | static inline int __sem_update_count(struct semaphore *sem, int incr) | |
28 | { | |
29 | long old_count, tmp = 0; | |
30 | ||
31 | __asm__ __volatile__( | |
32 | "1: ldl_l %0,%2\n" | |
33 | " cmovgt %0,%0,%1\n" | |
34 | " addl %1,%3,%1\n" | |
35 | " stl_c %1,%2\n" | |
36 | " beq %1,2f\n" | |
37 | " mb\n" | |
38 | ".subsection 2\n" | |
39 | "2: br 1b\n" | |
40 | ".previous" | |
41 | : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) | |
42 | : "Ir" (incr), "1" (tmp), "m" (sem->count)); | |
43 | ||
44 | return old_count; | |
45 | } | |
46 | ||
47 | /* | |
48 | * Perform the "down" function. Return zero for semaphore acquired, | |
49 | * return negative for signalled out of the function. | |
50 | * | |
51 | * If called from down, the return is ignored and the wait loop is | |
52 | * not interruptible. This means that a task waiting on a semaphore | |
53 | * using "down()" cannot be killed until someone does an "up()" on | |
54 | * the semaphore. | |
55 | * | |
56 | * If called from down_interruptible, the return value gets checked | |
57 | * upon return. If the return value is negative then the task continues | |
58 | * with the negative value in the return register (it can be tested by | |
59 | * the caller). | |
60 | * | |
61 | * Either form may be used in conjunction with "up()". | |
62 | */ | |
63 | ||
64 | void __sched | |
65 | __down_failed(struct semaphore *sem) | |
66 | { | |
67 | struct task_struct *tsk = current; | |
68 | DECLARE_WAITQUEUE(wait, tsk); | |
69 | ||
70 | #ifdef CONFIG_DEBUG_SEMAPHORE | |
71 | printk("%s(%d): down failed(%p)\n", | |
19c5870c | 72 | tsk->comm, task_pid_nr(tsk), sem); |
1da177e4 LT |
73 | #endif |
74 | ||
75 | tsk->state = TASK_UNINTERRUPTIBLE; | |
76 | wmb(); | |
77 | add_wait_queue_exclusive(&sem->wait, &wait); | |
78 | ||
79 | /* | |
80 | * Try to get the semaphore. If the count is > 0, then we've | |
81 | * got the semaphore; we decrement count and exit the loop. | |
82 | * If the count is 0 or negative, we set it to -1, indicating | |
83 | * that we are asleep, and then sleep. | |
84 | */ | |
85 | while (__sem_update_count(sem, -1) <= 0) { | |
86 | schedule(); | |
87 | set_task_state(tsk, TASK_UNINTERRUPTIBLE); | |
88 | } | |
89 | remove_wait_queue(&sem->wait, &wait); | |
90 | tsk->state = TASK_RUNNING; | |
91 | ||
92 | /* | |
93 | * If there are any more sleepers, wake one of them up so | |
94 | * that it can either get the semaphore, or set count to -1 | |
95 | * indicating that there are still processes sleeping. | |
96 | */ | |
97 | wake_up(&sem->wait); | |
98 | ||
99 | #ifdef CONFIG_DEBUG_SEMAPHORE | |
100 | printk("%s(%d): down acquired(%p)\n", | |
19c5870c | 101 | tsk->comm, task_pid_nr(tsk), sem); |
1da177e4 LT |
102 | #endif |
103 | } | |
104 | ||
105 | int __sched | |
106 | __down_failed_interruptible(struct semaphore *sem) | |
107 | { | |
108 | struct task_struct *tsk = current; | |
109 | DECLARE_WAITQUEUE(wait, tsk); | |
110 | long ret = 0; | |
111 | ||
112 | #ifdef CONFIG_DEBUG_SEMAPHORE | |
113 | printk("%s(%d): down failed(%p)\n", | |
19c5870c | 114 | tsk->comm, task_pid_nr(tsk), sem); |
1da177e4 LT |
115 | #endif |
116 | ||
117 | tsk->state = TASK_INTERRUPTIBLE; | |
118 | wmb(); | |
119 | add_wait_queue_exclusive(&sem->wait, &wait); | |
120 | ||
121 | while (__sem_update_count(sem, -1) <= 0) { | |
122 | if (signal_pending(current)) { | |
123 | /* | |
124 | * A signal is pending - give up trying. | |
125 | * Set sem->count to 0 if it is negative, | |
126 | * since we are no longer sleeping. | |
127 | */ | |
128 | __sem_update_count(sem, 0); | |
129 | ret = -EINTR; | |
130 | break; | |
131 | } | |
132 | schedule(); | |
133 | set_task_state(tsk, TASK_INTERRUPTIBLE); | |
134 | } | |
135 | ||
136 | remove_wait_queue(&sem->wait, &wait); | |
137 | tsk->state = TASK_RUNNING; | |
138 | wake_up(&sem->wait); | |
139 | ||
140 | #ifdef CONFIG_DEBUG_SEMAPHORE | |
141 | printk("%s(%d): down %s(%p)\n", | |
19c5870c | 142 | current->comm, task_pid_nr(current), |
1da177e4 LT |
143 | (ret < 0 ? "interrupted" : "acquired"), sem); |
144 | #endif | |
145 | return ret; | |
146 | } | |
147 | ||
148 | void | |
149 | __up_wakeup(struct semaphore *sem) | |
150 | { | |
151 | /* | |
152 | * Note that we incremented count in up() before we came here, | |
153 | * but that was ineffective since the result was <= 0, and | |
154 | * any negative value of count is equivalent to 0. | |
155 | * This ends up setting count to 1, unless count is now > 0 | |
156 | * (i.e. because some other cpu has called up() in the meantime), | |
157 | * in which case we just increment count. | |
158 | */ | |
159 | __sem_update_count(sem, 1); | |
160 | wake_up(&sem->wait); | |
161 | } | |
162 | ||
163 | void __sched | |
164 | down(struct semaphore *sem) | |
165 | { | |
166 | #ifdef WAITQUEUE_DEBUG | |
167 | CHECK_MAGIC(sem->__magic); | |
168 | #endif | |
169 | #ifdef CONFIG_DEBUG_SEMAPHORE | |
170 | printk("%s(%d): down(%p) <count=%d> from %p\n", | |
19c5870c | 171 | current->comm, task_pid_nr(current), sem, |
1da177e4 LT |
172 | atomic_read(&sem->count), __builtin_return_address(0)); |
173 | #endif | |
174 | __down(sem); | |
175 | } | |
176 | ||
177 | int __sched | |
178 | down_interruptible(struct semaphore *sem) | |
179 | { | |
180 | #ifdef WAITQUEUE_DEBUG | |
181 | CHECK_MAGIC(sem->__magic); | |
182 | #endif | |
183 | #ifdef CONFIG_DEBUG_SEMAPHORE | |
184 | printk("%s(%d): down(%p) <count=%d> from %p\n", | |
19c5870c | 185 | current->comm, task_pid_nr(current), sem, |
1da177e4 LT |
186 | atomic_read(&sem->count), __builtin_return_address(0)); |
187 | #endif | |
188 | return __down_interruptible(sem); | |
189 | } | |
190 | ||
191 | int | |
192 | down_trylock(struct semaphore *sem) | |
193 | { | |
194 | int ret; | |
195 | ||
196 | #ifdef WAITQUEUE_DEBUG | |
197 | CHECK_MAGIC(sem->__magic); | |
198 | #endif | |
199 | ||
200 | ret = __down_trylock(sem); | |
201 | ||
202 | #ifdef CONFIG_DEBUG_SEMAPHORE | |
203 | printk("%s(%d): down_trylock %s from %p\n", | |
19c5870c | 204 | current->comm, task_pid_nr(current), |
1da177e4 LT |
205 | ret ? "failed" : "acquired", |
206 | __builtin_return_address(0)); | |
207 | #endif | |
208 | ||
209 | return ret; | |
210 | } | |
211 | ||
212 | void | |
213 | up(struct semaphore *sem) | |
214 | { | |
215 | #ifdef WAITQUEUE_DEBUG | |
216 | CHECK_MAGIC(sem->__magic); | |
217 | #endif | |
218 | #ifdef CONFIG_DEBUG_SEMAPHORE | |
219 | printk("%s(%d): up(%p) <count=%d> from %p\n", | |
19c5870c | 220 | current->comm, task_pid_nr(current), sem, |
1da177e4 LT |
221 | atomic_read(&sem->count), __builtin_return_address(0)); |
222 | #endif | |
223 | __up(sem); | |
224 | } |