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