xref: /netbsd-src/sys/external/bsd/drm2/linux/linux_rwsem.c (revision 246be0cd584889acd481c85c6f6934743094ed15)
1 /*	$NetBSD: linux_rwsem.c,v 1.4 2021/12/19 11:21:54 riastradh Exp $	*/
2 
3 /*-
4  * Copyright (c) 2021 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: linux_rwsem.c,v 1.4 2021/12/19 11:21:54 riastradh Exp $");
31 
32 #include <sys/types.h>
33 
34 #include <sys/condvar.h>
35 #include <sys/lwp.h>
36 #include <sys/lockdebug.h>
37 #include <sys/rwlock.h>
38 
39 #include <machine/limits.h>
40 
41 #include <lib/libkern/libkern.h>
42 
43 #include <linux/rwsem.h>
44 
45 #define	RWSEM_WANTLOCK(RWSEM)						      \
46 	LOCKDEBUG_WANTLOCK((RWSEM)->rws_debug, (RWSEM),			      \
47 	    (uintptr_t)__builtin_return_address(0), 0)
48 #define	RWSEM_LOCKED_EX(RWSEM)						      \
49 	LOCKDEBUG_LOCKED((RWSEM)->rws_debug, (RWSEM), NULL,		      \
50 	    (uintptr_t)__builtin_return_address(0), 0)
51 #define	RWSEM_LOCKED_SH(RWSEM)						      \
52 	LOCKDEBUG_LOCKED((RWSEM)->rws_debug, (RWSEM), NULL,		      \
53 	    (uintptr_t)__builtin_return_address(0), 1)
54 #define	RWSEM_UNLOCKED_EX(RWSEM)					      \
55 	LOCKDEBUG_UNLOCKED((RWSEM)->rws_debug, (RWSEM),			      \
56 	    (uintptr_t)__builtin_return_address(0), 0)
57 #define	RWSEM_UNLOCKED_SH(RWSEM)					      \
58 	LOCKDEBUG_UNLOCKED((RWSEM)->rws_debug, (RWSEM),			      \
59 	    (uintptr_t)__builtin_return_address(0), 1)
60 
61 #ifdef LOCKDEBUG
62 static void
rwsem_dump(const volatile void * cookie,lockop_printer_t pr)63 rwsem_dump(const volatile void *cookie, lockop_printer_t pr)
64 {
65 	const volatile struct rw_semaphore *rwsem = cookie;
66 
67 	pr("%-13s: %p", "writer", rwsem->rws_writer);
68 	pr("%-13s: %u", "readers", rwsem->rws_readers);
69 	pr("%-13s: %s", "writewanted", rwsem->rws_writewanted ? "yes" : "no");
70 }
71 
72 static lockops_t rwsem_lockops = {
73 	.lo_name = "Linux read/write semaphore",
74 	.lo_type = LOCKOPS_SLEEP,
75 	.lo_dump = rwsem_dump,
76 };
77 #endif
78 
79 void
init_rwsem(struct rw_semaphore * rwsem)80 init_rwsem(struct rw_semaphore *rwsem)
81 {
82 
83 	mutex_init(&rwsem->rws_lock, MUTEX_DEFAULT, IPL_VM);
84 	cv_init(&rwsem->rws_cv, "lnxrwsem");
85 	rwsem->rws_writer = NULL;
86 	rwsem->rws_readers = 0;
87 
88 #ifdef LOCKDEBUG
89 	rwsem->rws_debug = LOCKDEBUG_ALLOC(rwsem, &rwsem_lockops,
90 	    (uintptr_t)__builtin_return_address(0));
91 #endif
92 }
93 
94 void
destroy_rwsem(struct rw_semaphore * rwsem)95 destroy_rwsem(struct rw_semaphore *rwsem)
96 {
97 
98 	KASSERT(rwsem->rws_readers == 0);
99 	KASSERT(rwsem->rws_writer == NULL);
100 
101 #ifdef LOCKDEBUG
102 	LOCKDEBUG_FREE(rwsem->rws_debug, rwsem);
103 #endif
104 
105 	cv_destroy(&rwsem->rws_cv);
106 	mutex_destroy(&rwsem->rws_lock);
107 }
108 
109 void
down_read(struct rw_semaphore * rwsem)110 down_read(struct rw_semaphore *rwsem)
111 {
112 
113 	RWSEM_WANTLOCK(rwsem);
114 
115 	mutex_enter(&rwsem->rws_lock);
116 	while (rwsem->rws_writer || rwsem->rws_writewanted)
117 		cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
118 	KASSERT(rwsem->rws_readers < UINT_MAX);
119 	rwsem->rws_readers++;
120 	mutex_exit(&rwsem->rws_lock);
121 
122 	RWSEM_LOCKED_SH(rwsem);
123 }
124 
125 bool
down_read_trylock(struct rw_semaphore * rwsem)126 down_read_trylock(struct rw_semaphore *rwsem)
127 {
128 	bool ret = false;
129 
130 	/*
131 	 * Note: Linux apparently relies on down_read_trylock to
132 	 * quietly succeed when the caller already holds a reader lock.
133 	 * This is why we can't use rwlock(9), which absolutely
134 	 * prohibits recursive use and crashes immediately under
135 	 * LOCKDEBUG if you try it.
136 	 */
137 
138 	mutex_enter(&rwsem->rws_lock);
139 	if (rwsem->rws_writer == NULL && !rwsem->rws_writewanted) {
140 		KASSERT(rwsem->rws_readers < UINT_MAX);
141 		rwsem->rws_readers++;
142 		ret = true;
143 	}
144 	mutex_exit(&rwsem->rws_lock);
145 
146 	if (ret) {
147 		RWSEM_LOCKED_SH(rwsem);
148 	}
149 
150 	return ret;
151 }
152 
153 void
up_read(struct rw_semaphore * rwsem)154 up_read(struct rw_semaphore *rwsem)
155 {
156 
157 	RWSEM_UNLOCKED_SH(rwsem);
158 
159 	mutex_enter(&rwsem->rws_lock);
160 	KASSERT(rwsem->rws_readers);
161 	KASSERT(rwsem->rws_writer == NULL);
162 	if (--rwsem->rws_readers == 0)
163 		cv_broadcast(&rwsem->rws_cv);
164 	mutex_exit(&rwsem->rws_lock);
165 }
166 
167 void
down_write(struct rw_semaphore * rwsem)168 down_write(struct rw_semaphore *rwsem)
169 {
170 
171 	RWSEM_WANTLOCK(rwsem);
172 
173 	mutex_enter(&rwsem->rws_lock);
174 
175 	/* If another writer is waiting, get in the queue.  */
176 	while (rwsem->rws_writewanted)
177 		cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
178 
179 	/*
180 	 * No other writers waiting.  Our turn.  Announce our intent to
181 	 * readers, and wait for the writer or readers to finish.
182 	 */
183 	rwsem->rws_writewanted = true;
184 	while (rwsem->rws_writer || rwsem->rws_readers) {
185 		KASSERTMSG(rwsem->rws_writer != curlwp,
186 		    "locking against myself: rwsem=%p lwp=%p", rwsem, curlwp);
187 		cv_wait(&rwsem->rws_cv, &rwsem->rws_lock);
188 	}
189 
190 	/* At last, it is ours!  */
191 	KASSERT(rwsem->rws_readers == 0);
192 	KASSERT(rwsem->rws_writer == NULL);
193 	KASSERT(rwsem->rws_writewanted);
194 	rwsem->rws_writewanted = false;
195 	rwsem->rws_writer = curlwp;
196 
197 	mutex_exit(&rwsem->rws_lock);
198 
199 	RWSEM_LOCKED_EX(rwsem);
200 }
201 
202 void
up_write(struct rw_semaphore * rwsem)203 up_write(struct rw_semaphore *rwsem)
204 {
205 
206 	RWSEM_UNLOCKED_EX(rwsem);
207 
208 	mutex_enter(&rwsem->rws_lock);
209 	KASSERT(rwsem->rws_writer == curlwp);
210 	KASSERT(rwsem->rws_readers == 0);
211 	rwsem->rws_writer = NULL;
212 	cv_broadcast(&rwsem->rws_cv);
213 	mutex_exit(&rwsem->rws_lock);
214 }
215 
216 void
downgrade_write(struct rw_semaphore * rwsem)217 downgrade_write(struct rw_semaphore *rwsem)
218 {
219 
220 	RWSEM_UNLOCKED_EX(rwsem);
221 
222 	mutex_enter(&rwsem->rws_lock);
223 	KASSERT(rwsem->rws_writer == curlwp);
224 	KASSERT(rwsem->rws_readers == 0);
225 	rwsem->rws_writer = NULL;
226 	rwsem->rws_readers = 1;
227 	cv_broadcast(&rwsem->rws_cv);
228 	mutex_exit(&rwsem->rws_lock);
229 
230 	RWSEM_LOCKED_SH(rwsem);
231 }
232