make oldconfig will rebuild these...
[linux-2.4.21-pre4.git] / include / asm-mips / semaphore-helper.h
1 /*
2  * SMP- and interrupt-safe semaphores helper functions.
3  *
4  * Copyright (C) 1996 Linus Torvalds
5  * Copyright (C) 1999 Andrea Arcangeli
6  * Copyright (C) 1999 Ralf Baechle
7  * Copyright (C) 1999 Silicon Graphics, Inc.
8  * Copyright (C) 2000 MIPS Technologies, Inc.
9  */
10 #ifndef _ASM_SEMAPHORE_HELPER_H
11 #define _ASM_SEMAPHORE_HELPER_H
12
13 #include <linux/config.h>
14
15 #define sem_read(a) ((a)->counter)
16 #define sem_inc(a) (((a)->counter)++)
17 #define sem_dec(a) (((a)->counter)--)
18 /*
19  * These two _must_ execute atomically wrt each other.
20  */
21 static inline void wake_one_more(struct semaphore * sem)
22 {
23         atomic_inc(&sem->waking);
24 }
25
26 #ifdef CONFIG_CPU_HAS_LLSC
27
28 static inline int waking_non_zero(struct semaphore *sem)
29 {
30         int ret, tmp;
31
32         __asm__ __volatile__(
33         "1:\tll\t%1, %2\t\t\t# waking_non_zero\n\t"
34         "blez\t%1, 2f\n\t"
35         "subu\t%0, %1, 1\n\t"
36         "sc\t%0, %2\n\t"
37         "beqz\t%0, 1b\n"
38         "2:"
39         : "=r" (ret), "=r" (tmp), "+m" (sem->waking)
40         : "0"(0));
41
42         return ret;
43 }
44
45 #else /* !CONFIG_CPU_HAS_LLSC */
46
47 /*
48  * It doesn't make sense, IMHO, to endlessly turn interrupts off and on again.
49  * Do it once and that's it. ll/sc *has* it's advantages. HK
50  */
51
52 static inline int waking_non_zero(struct semaphore *sem)
53 {
54         unsigned long flags;
55         int ret = 0;
56
57         save_and_cli(flags);
58         if (sem_read(&sem->waking) > 0) {
59                 sem_dec(&sem->waking);
60                 ret = 1;
61         }
62         restore_flags(flags);
63         return ret;
64 }
65 #endif /* !CONFIG_CPU_HAS_LLSC */
66
67 #ifdef CONFIG_CPU_HAS_LLDSCD
68
69 /*
70  * waking_non_zero_interruptible:
71  *      1       got the lock
72  *      0       go to sleep
73  *      -EINTR  interrupted
74  *
75  * We must undo the sem->count down_interruptible decrement
76  * simultaneously and atomically with the sem->waking adjustment,
77  * otherwise we can race with wake_one_more.
78  *
79  * This is accomplished by doing a 64-bit lld/scd on the 2 32-bit words.
80  *
81  * This is crazy.  Normally it's strictly forbidden to use 64-bit operations
82  * in the 32-bit MIPS kernel.  In this case it's however ok because if an
83  * interrupt has destroyed the upper half of registers sc will fail.
84  * Note also that this will not work for MIPS32 CPUs!
85  *
86  * Pseudocode:
87  *
88  * If(sem->waking > 0) {
89  *      Decrement(sem->waking)
90  *      Return(SUCCESS)
91  * } else If(signal_pending(tsk)) {
92  *      Increment(sem->count)
93  *      Return(-EINTR)
94  * } else {
95  *      Return(SLEEP)
96  * }
97  */
98
99 static inline int
100 waking_non_zero_interruptible(struct semaphore *sem, struct task_struct *tsk)
101 {
102         long ret, tmp;
103
104         __asm__ __volatile__(
105         ".set\tpush\n\t"
106         ".set\tmips3\n\t"
107         ".set\tnoat\n"
108         "0:\tlld\t%1, %2\n\t"
109         "li\t%0, 0\n\t"
110         "sll\t$1, %1, 0\n\t"
111         "blez\t$1, 1f\n\t"
112         "daddiu\t%1, %1, -1\n\t"
113         "li\t%0, 1\n\t"
114         "b\t2f\n"
115         "1:\tbeqz\t%3, 2f\n\t"
116         "li\t%0, %4\n\t"
117         "dli\t$1, 0x0000000100000000\n\t"
118         "daddu\t%1, %1, $1\n"
119         "2:\tscd\t%1, %2\n\t"
120         "beqz\t%1, 0b\n\t"
121         ".set\tpop"
122         : "=&r" (ret), "=&r" (tmp), "=m" (*sem)
123         : "r" (signal_pending(tsk)), "i" (-EINTR));
124
125         return ret;
126 }
127
128 /*
129  * waking_non_zero_trylock is unused.  we do everything in
130  * down_trylock and let non-ll/sc hosts bounce around.
131  */
132
133 static inline int waking_non_zero_trylock(struct semaphore *sem)
134 {
135 #if WAITQUEUE_DEBUG
136         CHECK_MAGIC(sem->__magic);
137 #endif
138
139         return 0;
140 }
141
142 #else /* !CONFIG_CPU_HAS_LLDSCD */
143
144 static inline int waking_non_zero_interruptible(struct semaphore *sem,
145                                                 struct task_struct *tsk)
146 {
147         int ret = 0;
148         unsigned long flags;
149
150         save_and_cli(flags);
151         if (sem_read(&sem->waking) > 0) {
152                 sem_dec(&sem->waking);
153                 ret = 1;
154         } else if (signal_pending(tsk)) {
155                 sem_inc(&sem->count);
156                 ret = -EINTR;
157         }
158         restore_flags(flags);
159         return ret;
160 }
161
162 static inline int waking_non_zero_trylock(struct semaphore *sem)
163 {
164         int ret = 1;
165         unsigned long flags;
166
167         save_and_cli(flags);
168         if (sem_read(&sem->waking) <= 0)
169                 sem_inc(&sem->count);
170         else {
171                 sem_dec(&sem->waking);
172                 ret = 0;
173         }
174         restore_flags(flags);
175
176         return ret;
177 }
178
179 #endif /* !CONFIG_CPU_HAS_LLDSCD */
180
181 #endif /* _ASM_SEMAPHORE_HELPER_H */