• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
__down_read(struct rw_semaphore * sem)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  */
__down_read_trylock(struct rw_semaphore * sem)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  */
___down_write(struct rw_semaphore * sem)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 
__down_write(struct rw_semaphore * sem)112 static inline void __down_write(struct rw_semaphore *sem)
113 {
114 	if (___down_write(sem))
115 		rwsem_down_write_failed(sem);
116 }
117 
__down_write_killable(struct rw_semaphore * sem)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  */
__down_write_trylock(struct rw_semaphore * sem)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  */
__up_read(struct rw_semaphore * sem)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  */
__up_write(struct rw_semaphore * sem)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  */
__downgrade_write(struct rw_semaphore * sem)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 */
212