locking/rwsem: Get rid of __down_write_nested()
[cascardo/linux.git] / arch / s390 / include / asm / rwsem.h
1 #ifndef _S390_RWSEM_H
2 #define _S390_RWSEM_H
3
4 /*
5  *  S390 version
6  *    Copyright IBM Corp. 2002
7  *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
8  *
9  *  Based on asm-alpha/semaphore.h and asm-i386/rwsem.h
10  */
11
12 /*
13  *
14  * The MSW of the count is the negated number of active writers and waiting
15  * lockers, and the LSW is the total number of active locks
16  *
17  * The lock count is initialized to 0 (no active and no waiting lockers).
18  *
19  * When a writer subtracts WRITE_BIAS, it'll get 0xffff0001 for the case of an
20  * uncontended lock. This can be determined because XADD returns the old value.
21  * Readers increment by 1 and see a positive value when uncontended, negative
22  * if there are writers (and maybe) readers waiting (in which case it goes to
23  * sleep).
24  *
25  * The value of WAITING_BIAS supports up to 32766 waiting processes. This can
26  * be extended to 65534 by manually checking the whole MSW rather than relying
27  * on the S flag.
28  *
29  * The value of ACTIVE_BIAS supports up to 65535 active processes.
30  *
31  * This should be totally fair - if anything is waiting, a process that wants a
32  * lock will go to the back of the queue. When the currently active lock is
33  * released, if there's a writer at the front of the queue, then that and only
34  * that will be woken up; if there's a bunch of consecutive readers at the
35  * front, then they'll all be woken up, but no other readers will be.
36  */
37
38 #ifndef _LINUX_RWSEM_H
39 #error "please don't include asm/rwsem.h directly, use linux/rwsem.h instead"
40 #endif
41
42 #define RWSEM_UNLOCKED_VALUE    0x0000000000000000L
43 #define RWSEM_ACTIVE_BIAS       0x0000000000000001L
44 #define RWSEM_ACTIVE_MASK       0x00000000ffffffffL
45 #define RWSEM_WAITING_BIAS      (-0x0000000100000000L)
46 #define RWSEM_ACTIVE_READ_BIAS  RWSEM_ACTIVE_BIAS
47 #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
48
49 /*
50  * lock for reading
51  */
52 static inline void __down_read(struct rw_semaphore *sem)
53 {
54         signed long old, new;
55
56         asm volatile(
57                 "       lg      %0,%2\n"
58                 "0:     lgr     %1,%0\n"
59                 "       aghi    %1,%4\n"
60                 "       csg     %0,%1,%2\n"
61                 "       jl      0b"
62                 : "=&d" (old), "=&d" (new), "=Q" (sem->count)
63                 : "Q" (sem->count), "i" (RWSEM_ACTIVE_READ_BIAS)
64                 : "cc", "memory");
65         if (old < 0)
66                 rwsem_down_read_failed(sem);
67 }
68
69 /*
70  * trylock for reading -- returns 1 if successful, 0 if contention
71  */
72 static inline int __down_read_trylock(struct rw_semaphore *sem)
73 {
74         signed long old, new;
75
76         asm volatile(
77                 "       lg      %0,%2\n"
78                 "0:     ltgr    %1,%0\n"
79                 "       jm      1f\n"
80                 "       aghi    %1,%4\n"
81                 "       csg     %0,%1,%2\n"
82                 "       jl      0b\n"
83                 "1:"
84                 : "=&d" (old), "=&d" (new), "=Q" (sem->count)
85                 : "Q" (sem->count), "i" (RWSEM_ACTIVE_READ_BIAS)
86                 : "cc", "memory");
87         return old >= 0 ? 1 : 0;
88 }
89
90 /*
91  * lock for writing
92  */
93 static inline void __down_write(struct rw_semaphore *sem)
94 {
95         signed long old, new, tmp;
96
97         tmp = RWSEM_ACTIVE_WRITE_BIAS;
98         asm volatile(
99                 "       lg      %0,%2\n"
100                 "0:     lgr     %1,%0\n"
101                 "       ag      %1,%4\n"
102                 "       csg     %0,%1,%2\n"
103                 "       jl      0b"
104                 : "=&d" (old), "=&d" (new), "=Q" (sem->count)
105                 : "Q" (sem->count), "m" (tmp)
106                 : "cc", "memory");
107         if (old != 0)
108                 rwsem_down_write_failed(sem);
109 }
110
111 /*
112  * trylock for writing -- returns 1 if successful, 0 if contention
113  */
114 static inline int __down_write_trylock(struct rw_semaphore *sem)
115 {
116         signed long old;
117
118         asm volatile(
119                 "       lg      %0,%1\n"
120                 "0:     ltgr    %0,%0\n"
121                 "       jnz     1f\n"
122                 "       csg     %0,%3,%1\n"
123                 "       jl      0b\n"
124                 "1:"
125                 : "=&d" (old), "=Q" (sem->count)
126                 : "Q" (sem->count), "d" (RWSEM_ACTIVE_WRITE_BIAS)
127                 : "cc", "memory");
128         return (old == RWSEM_UNLOCKED_VALUE) ? 1 : 0;
129 }
130
131 /*
132  * unlock after reading
133  */
134 static inline void __up_read(struct rw_semaphore *sem)
135 {
136         signed long old, new;
137
138         asm volatile(
139                 "       lg      %0,%2\n"
140                 "0:     lgr     %1,%0\n"
141                 "       aghi    %1,%4\n"
142                 "       csg     %0,%1,%2\n"
143                 "       jl      0b"
144                 : "=&d" (old), "=&d" (new), "=Q" (sem->count)
145                 : "Q" (sem->count), "i" (-RWSEM_ACTIVE_READ_BIAS)
146                 : "cc", "memory");
147         if (new < 0)
148                 if ((new & RWSEM_ACTIVE_MASK) == 0)
149                         rwsem_wake(sem);
150 }
151
152 /*
153  * unlock after writing
154  */
155 static inline void __up_write(struct rw_semaphore *sem)
156 {
157         signed long old, new, tmp;
158
159         tmp = -RWSEM_ACTIVE_WRITE_BIAS;
160         asm volatile(
161                 "       lg      %0,%2\n"
162                 "0:     lgr     %1,%0\n"
163                 "       ag      %1,%4\n"
164                 "       csg     %0,%1,%2\n"
165                 "       jl      0b"
166                 : "=&d" (old), "=&d" (new), "=Q" (sem->count)
167                 : "Q" (sem->count), "m" (tmp)
168                 : "cc", "memory");
169         if (new < 0)
170                 if ((new & RWSEM_ACTIVE_MASK) == 0)
171                         rwsem_wake(sem);
172 }
173
174 /*
175  * downgrade write lock to read lock
176  */
177 static inline void __downgrade_write(struct rw_semaphore *sem)
178 {
179         signed long old, new, tmp;
180
181         tmp = -RWSEM_WAITING_BIAS;
182         asm volatile(
183                 "       lg      %0,%2\n"
184                 "0:     lgr     %1,%0\n"
185                 "       ag      %1,%4\n"
186                 "       csg     %0,%1,%2\n"
187                 "       jl      0b"
188                 : "=&d" (old), "=&d" (new), "=Q" (sem->count)
189                 : "Q" (sem->count), "m" (tmp)
190                 : "cc", "memory");
191         if (new > 1)
192                 rwsem_downgrade_wake(sem);
193 }
194
195 /*
196  * implement atomic add functionality
197  */
198 static inline void rwsem_atomic_add(long delta, struct rw_semaphore *sem)
199 {
200         signed long old, new;
201
202         asm volatile(
203                 "       lg      %0,%2\n"
204                 "0:     lgr     %1,%0\n"
205                 "       agr     %1,%4\n"
206                 "       csg     %0,%1,%2\n"
207                 "       jl      0b"
208                 : "=&d" (old), "=&d" (new), "=Q" (sem->count)
209                 : "Q" (sem->count), "d" (delta)
210                 : "cc", "memory");
211 }
212
213 /*
214  * implement exchange and add functionality
215  */
216 static inline long rwsem_atomic_update(long delta, struct rw_semaphore *sem)
217 {
218         signed long old, new;
219
220         asm volatile(
221                 "       lg      %0,%2\n"
222                 "0:     lgr     %1,%0\n"
223                 "       agr     %1,%4\n"
224                 "       csg     %0,%1,%2\n"
225                 "       jl      0b"
226                 : "=&d" (old), "=&d" (new), "=Q" (sem->count)
227                 : "Q" (sem->count), "d" (delta)
228                 : "cc", "memory");
229         return new;
230 }
231
232 #endif /* _S390_RWSEM_H */