ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / arm / kernel / semaphore.c
1 /*
2  *  ARM semaphore implementation, taken from
3  *
4  *  i386 semaphore implementation.
5  *
6  *  (C) Copyright 1999 Linus Torvalds
7  *
8  *  Modified for ARM by Russell King
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  */
14 #include <linux/sched.h>
15 #include <linux/errno.h>
16 #include <linux/init.h>
17
18 #include <asm/semaphore.h>
19
20 /*
21  * Semaphores are implemented using a two-way counter:
22  * The "count" variable is decremented for each process
23  * that tries to acquire the semaphore, while the "sleeping"
24  * variable is a count of such acquires.
25  *
26  * Notably, the inline "up()" and "down()" functions can
27  * efficiently test if they need to do any extra work (up
28  * needs to do something only if count was negative before
29  * the increment operation.
30  *
31  * "sleeping" and the contention routine ordering is
32  * protected by the semaphore spinlock.
33  *
34  * Note that these functions are only called when there is
35  * contention on the lock, and as such all this is the
36  * "non-critical" part of the whole semaphore business. The
37  * critical part is the inline stuff in <asm/semaphore.h>
38  * where we want to avoid any extra jumps and calls.
39  */
40
41 /*
42  * Logic:
43  *  - only on a boundary condition do we need to care. When we go
44  *    from a negative count to a non-negative, we wake people up.
45  *  - when we go from a non-negative count to a negative do we
46  *    (a) synchronize with the "sleeper" count and (b) make sure
47  *    that we're on the wakeup list before we synchronize so that
48  *    we cannot lose wakeup events.
49  */
50
51 void __up(struct semaphore *sem)
52 {
53         wake_up(&sem->wait);
54 }
55
56 static spinlock_t semaphore_lock = SPIN_LOCK_UNLOCKED;
57
58 void __sched __down(struct semaphore * sem)
59 {
60         struct task_struct *tsk = current;
61         DECLARE_WAITQUEUE(wait, tsk);
62         tsk->state = TASK_UNINTERRUPTIBLE;
63         add_wait_queue_exclusive(&sem->wait, &wait);
64
65         spin_lock_irq(&semaphore_lock);
66         sem->sleepers++;
67         for (;;) {
68                 int sleepers = sem->sleepers;
69
70                 /*
71                  * Add "everybody else" into it. They aren't
72                  * playing, because we own the spinlock.
73                  */
74                 if (!atomic_add_negative(sleepers - 1, &sem->count)) {
75                         sem->sleepers = 0;
76                         break;
77                 }
78                 sem->sleepers = 1;      /* us - see -1 above */
79                 spin_unlock_irq(&semaphore_lock);
80
81                 schedule();
82                 tsk->state = TASK_UNINTERRUPTIBLE;
83                 spin_lock_irq(&semaphore_lock);
84         }
85         spin_unlock_irq(&semaphore_lock);
86         remove_wait_queue(&sem->wait, &wait);
87         tsk->state = TASK_RUNNING;
88         wake_up(&sem->wait);
89 }
90
91 int __sched __down_interruptible(struct semaphore * sem)
92 {
93         int retval = 0;
94         struct task_struct *tsk = current;
95         DECLARE_WAITQUEUE(wait, tsk);
96         tsk->state = TASK_INTERRUPTIBLE;
97         add_wait_queue_exclusive(&sem->wait, &wait);
98
99         spin_lock_irq(&semaphore_lock);
100         sem->sleepers ++;
101         for (;;) {
102                 int sleepers = sem->sleepers;
103
104                 /*
105                  * With signals pending, this turns into
106                  * the trylock failure case - we won't be
107                  * sleeping, and we* can't get the lock as
108                  * it has contention. Just correct the count
109                  * and exit.
110                  */
111                 if (signal_pending(current)) {
112                         retval = -EINTR;
113                         sem->sleepers = 0;
114                         atomic_add(sleepers, &sem->count);
115                         break;
116                 }
117
118                 /*
119                  * Add "everybody else" into it. They aren't
120                  * playing, because we own the spinlock. The
121                  * "-1" is because we're still hoping to get
122                  * the lock.
123                  */
124                 if (!atomic_add_negative(sleepers - 1, &sem->count)) {
125                         sem->sleepers = 0;
126                         break;
127                 }
128                 sem->sleepers = 1;      /* us - see -1 above */
129                 spin_unlock_irq(&semaphore_lock);
130
131                 schedule();
132                 tsk->state = TASK_INTERRUPTIBLE;
133                 spin_lock_irq(&semaphore_lock);
134         }
135         spin_unlock_irq(&semaphore_lock);
136         tsk->state = TASK_RUNNING;
137         remove_wait_queue(&sem->wait, &wait);
138         wake_up(&sem->wait);
139         return retval;
140 }
141
142 /*
143  * Trylock failed - make sure we correct for
144  * having decremented the count.
145  *
146  * We could have done the trylock with a
147  * single "cmpxchg" without failure cases,
148  * but then it wouldn't work on a 386.
149  */
150 int __down_trylock(struct semaphore * sem)
151 {
152         int sleepers;
153         unsigned long flags;
154
155         spin_lock_irqsave(&semaphore_lock, flags);
156         sleepers = sem->sleepers + 1;
157         sem->sleepers = 0;
158
159         /*
160          * Add "everybody else" and us into it. They aren't
161          * playing, because we own the spinlock.
162          */
163         if (!atomic_add_negative(sleepers, &sem->count))
164                 wake_up(&sem->wait);
165
166         spin_unlock_irqrestore(&semaphore_lock, flags);
167         return 1;
168 }
169
170 /*
171  * The semaphore operations have a special calling sequence that
172  * allow us to do a simpler in-line version of them. These routines
173  * need to convert that sequence back into the C sequence when
174  * there is contention on the semaphore.
175  *
176  * ip contains the semaphore pointer on entry. Save the C-clobbered
177  * registers (r0 to r3 and lr), but not ip, as we use it as a return
178  * value in some cases..
179  */
180 asm("   .section .sched.text                    \n\
181         .align  5                               \n\
182         .globl  __down_failed                   \n\
183 __down_failed:                                  \n\
184         stmfd   sp!, {r0 - r3, lr}              \n\
185         mov     r0, ip                          \n\
186         bl      __down                          \n\
187         ldmfd   sp!, {r0 - r3, pc}              \n\
188                                                 \n\
189         .align  5                               \n\
190         .globl  __down_interruptible_failed     \n\
191 __down_interruptible_failed:                    \n\
192         stmfd   sp!, {r0 - r3, lr}              \n\
193         mov     r0, ip                          \n\
194         bl      __down_interruptible            \n\
195         mov     ip, r0                          \n\
196         ldmfd   sp!, {r0 - r3, pc}              \n\
197                                                 \n\
198         .align  5                               \n\
199         .globl  __down_trylock_failed           \n\
200 __down_trylock_failed:                          \n\
201         stmfd   sp!, {r0 - r3, lr}              \n\
202         mov     r0, ip                          \n\
203         bl      __down_trylock                  \n\
204         mov     ip, r0                          \n\
205         ldmfd   sp!, {r0 - r3, pc}              \n\
206                                                 \n\
207         .align  5                               \n\
208         .globl  __up_wakeup                     \n\
209 __up_wakeup:                                    \n\
210         stmfd   sp!, {r0 - r3, lr}              \n\
211         mov     r0, ip                          \n\
212         bl      __up                            \n\
213         ldmfd   sp!, {r0 - r3, pc}              \n\
214         ");
215