Merge with /home/shaggy/git/linus-clean/
[linux-2.6] / arch / ppc64 / kernel / semaphore.c
1 /*
2  * 
3  *
4  * PowerPC-specific semaphore code.
5  *
6  * Copyright (C) 1999 Cort Dougan <cort@cs.nmt.edu>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version
11  * 2 of the License, or (at your option) any later version.
12  *
13  * April 2001 - Reworked by Paul Mackerras <paulus@samba.org>
14  * to eliminate the SMP races in the old version between the updates
15  * of `count' and `waking'.  Now we use negative `count' values to
16  * indicate that some process(es) are waiting for the semaphore.
17  */
18
19 #include <linux/sched.h>
20 #include <linux/init.h>
21 #include <linux/module.h>
22
23 #include <asm/atomic.h>
24 #include <asm/semaphore.h>
25 #include <asm/errno.h>
26
27 /*
28  * Atomically update sem->count.
29  * This does the equivalent of the following:
30  *
31  *      old_count = sem->count;
32  *      tmp = MAX(old_count, 0) + incr;
33  *      sem->count = tmp;
34  *      return old_count;
35  */
36 static inline int __sem_update_count(struct semaphore *sem, int incr)
37 {
38         int old_count, tmp;
39
40         __asm__ __volatile__("\n"
41 "1:     lwarx   %0,0,%3\n"
42 "       srawi   %1,%0,31\n"
43 "       andc    %1,%0,%1\n"
44 "       add     %1,%1,%4\n"
45 "       stwcx.  %1,0,%3\n"
46 "       bne     1b"
47         : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count)
48         : "r" (&sem->count), "r" (incr), "m" (sem->count)
49         : "cc");
50
51         return old_count;
52 }
53
54 void __up(struct semaphore *sem)
55 {
56         /*
57          * Note that we incremented count in up() before we came here,
58          * but that was ineffective since the result was <= 0, and
59          * any negative value of count is equivalent to 0.
60          * This ends up setting count to 1, unless count is now > 0
61          * (i.e. because some other cpu has called up() in the meantime),
62          * in which case we just increment count.
63          */
64         __sem_update_count(sem, 1);
65         wake_up(&sem->wait);
66 }
67 EXPORT_SYMBOL(__up);
68
69 /*
70  * Note that when we come in to __down or __down_interruptible,
71  * we have already decremented count, but that decrement was
72  * ineffective since the result was < 0, and any negative value
73  * of count is equivalent to 0.
74  * Thus it is only when we decrement count from some value > 0
75  * that we have actually got the semaphore.
76  */
77 void __sched __down(struct semaphore *sem)
78 {
79         struct task_struct *tsk = current;
80         DECLARE_WAITQUEUE(wait, tsk);
81
82         __set_task_state(tsk, TASK_UNINTERRUPTIBLE);
83         add_wait_queue_exclusive(&sem->wait, &wait);
84
85         /*
86          * Try to get the semaphore.  If the count is > 0, then we've
87          * got the semaphore; we decrement count and exit the loop.
88          * If the count is 0 or negative, we set it to -1, indicating
89          * that we are asleep, and then sleep.
90          */
91         while (__sem_update_count(sem, -1) <= 0) {
92                 schedule();
93                 set_task_state(tsk, TASK_UNINTERRUPTIBLE);
94         }
95         remove_wait_queue(&sem->wait, &wait);
96         __set_task_state(tsk, TASK_RUNNING);
97
98         /*
99          * If there are any more sleepers, wake one of them up so
100          * that it can either get the semaphore, or set count to -1
101          * indicating that there are still processes sleeping.
102          */
103         wake_up(&sem->wait);
104 }
105 EXPORT_SYMBOL(__down);
106
107 int __sched __down_interruptible(struct semaphore * sem)
108 {
109         int retval = 0;
110         struct task_struct *tsk = current;
111         DECLARE_WAITQUEUE(wait, tsk);
112
113         __set_task_state(tsk, TASK_INTERRUPTIBLE);
114         add_wait_queue_exclusive(&sem->wait, &wait);
115
116         while (__sem_update_count(sem, -1) <= 0) {
117                 if (signal_pending(current)) {
118                         /*
119                          * A signal is pending - give up trying.
120                          * Set sem->count to 0 if it is negative,
121                          * since we are no longer sleeping.
122                          */
123                         __sem_update_count(sem, 0);
124                         retval = -EINTR;
125                         break;
126                 }
127                 schedule();
128                 set_task_state(tsk, TASK_INTERRUPTIBLE);
129         }
130         remove_wait_queue(&sem->wait, &wait);
131         __set_task_state(tsk, TASK_RUNNING);
132
133         wake_up(&sem->wait);
134         return retval;
135 }
136 EXPORT_SYMBOL(__down_interruptible);