xref: /freebsd/sys/dev/drm2/ttm/ttm_lock.c (revision 595e514d0df2bac5b813d35f83e32875dbf16a83)
1 /**************************************************************************
2  *
3  * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24  * USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 /*
28  * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
29  */
30 /*
31  * Copyright (c) 2013 The FreeBSD Foundation
32  * All rights reserved.
33  *
34  * Portions of this software were developed by Konstantin Belousov
35  * <kib@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
36  */
37 
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
41 #include <dev/drm2/ttm/ttm_lock.h>
42 #include <dev/drm2/ttm/ttm_module.h>
43 
44 #define TTM_WRITE_LOCK_PENDING    (1 << 0)
45 #define TTM_VT_LOCK_PENDING       (1 << 1)
46 #define TTM_SUSPEND_LOCK_PENDING  (1 << 2)
47 #define TTM_VT_LOCK               (1 << 3)
48 #define TTM_SUSPEND_LOCK          (1 << 4)
49 
50 void ttm_lock_init(struct ttm_lock *lock)
51 {
52 	mtx_init(&lock->lock, "ttmlk", NULL, MTX_DEF);
53 	lock->rw = 0;
54 	lock->flags = 0;
55 	lock->kill_takers = false;
56 	lock->signal = SIGKILL;
57 }
58 
59 static void
60 ttm_lock_send_sig(int signo)
61 {
62 	struct proc *p;
63 
64 	p = curproc;	/* XXXKIB curthread ? */
65 	PROC_LOCK(p);
66 	kern_psignal(p, signo);
67 	PROC_UNLOCK(p);
68 }
69 
70 void ttm_read_unlock(struct ttm_lock *lock)
71 {
72 	mtx_lock(&lock->lock);
73 	if (--lock->rw == 0)
74 		wakeup(lock);
75 	mtx_unlock(&lock->lock);
76 }
77 
78 static bool __ttm_read_lock(struct ttm_lock *lock)
79 {
80 	bool locked = false;
81 
82 	if (unlikely(lock->kill_takers)) {
83 		ttm_lock_send_sig(lock->signal);
84 		return false;
85 	}
86 	if (lock->rw >= 0 && lock->flags == 0) {
87 		++lock->rw;
88 		locked = true;
89 	}
90 	return locked;
91 }
92 
93 int
94 ttm_read_lock(struct ttm_lock *lock, bool interruptible)
95 {
96 	const char *wmsg;
97 	int flags, ret;
98 
99 	ret = 0;
100 	if (interruptible) {
101 		flags = PCATCH;
102 		wmsg = "ttmri";
103 	} else {
104 		flags = 0;
105 		wmsg = "ttmr";
106 	}
107 	mtx_lock(&lock->lock);
108 	while (!__ttm_read_lock(lock)) {
109 		ret = msleep(lock, &lock->lock, flags, wmsg, 0);
110 		if (ret != 0)
111 			break;
112 	}
113 	return (-ret);
114 }
115 
116 static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked)
117 {
118 	bool block = true;
119 
120 	*locked = false;
121 
122 	if (unlikely(lock->kill_takers)) {
123 		ttm_lock_send_sig(lock->signal);
124 		return false;
125 	}
126 	if (lock->rw >= 0 && lock->flags == 0) {
127 		++lock->rw;
128 		block = false;
129 		*locked = true;
130 	} else if (lock->flags == 0) {
131 		block = false;
132 	}
133 
134 	return !block;
135 }
136 
137 int ttm_read_trylock(struct ttm_lock *lock, bool interruptible)
138 {
139 	const char *wmsg;
140 	int flags, ret;
141 	bool locked;
142 
143 	ret = 0;
144 	if (interruptible) {
145 		flags = PCATCH;
146 		wmsg = "ttmrti";
147 	} else {
148 		flags = 0;
149 		wmsg = "ttmrt";
150 	}
151 	mtx_lock(&lock->lock);
152 	while (!__ttm_read_trylock(lock, &locked)) {
153 		ret = msleep(lock, &lock->lock, flags, wmsg, 0);
154 		if (ret != 0)
155 			break;
156 	}
157 	MPASS(!locked || ret == 0);
158 	mtx_unlock(&lock->lock);
159 
160 	return (locked) ? 0 : -EBUSY;
161 }
162 
163 void ttm_write_unlock(struct ttm_lock *lock)
164 {
165 	mtx_lock(&lock->lock);
166 	lock->rw = 0;
167 	wakeup(lock);
168 	mtx_unlock(&lock->lock);
169 }
170 
171 static bool __ttm_write_lock(struct ttm_lock *lock)
172 {
173 	bool locked = false;
174 
175 	if (unlikely(lock->kill_takers)) {
176 		ttm_lock_send_sig(lock->signal);
177 		return false;
178 	}
179 	if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) {
180 		lock->rw = -1;
181 		lock->flags &= ~TTM_WRITE_LOCK_PENDING;
182 		locked = true;
183 	} else {
184 		lock->flags |= TTM_WRITE_LOCK_PENDING;
185 	}
186 	return locked;
187 }
188 
189 int
190 ttm_write_lock(struct ttm_lock *lock, bool interruptible)
191 {
192 	const char *wmsg;
193 	int flags, ret;
194 
195 	ret = 0;
196 	if (interruptible) {
197 		flags = PCATCH;
198 		wmsg = "ttmwi";
199 	} else {
200 		flags = 0;
201 		wmsg = "ttmw";
202 	}
203 	mtx_lock(&lock->lock);
204 	/* XXXKIB: linux uses __ttm_read_lock for uninterruptible sleeps */
205 	while (!__ttm_write_lock(lock)) {
206 		ret = msleep(lock, &lock->lock, flags, wmsg, 0);
207 		if (interruptible && ret != 0) {
208 			lock->flags &= ~TTM_WRITE_LOCK_PENDING;
209 			wakeup(lock);
210 			break;
211 		}
212 	}
213 	mtx_unlock(&lock->lock);
214 
215 	return (-ret);
216 }
217 
218 void ttm_write_lock_downgrade(struct ttm_lock *lock)
219 {
220 	mtx_lock(&lock->lock);
221 	lock->rw = 1;
222 	wakeup(lock);
223 	mtx_unlock(&lock->lock);
224 }
225 
226 static int __ttm_vt_unlock(struct ttm_lock *lock)
227 {
228 	int ret = 0;
229 
230 	mtx_lock(&lock->lock);
231 	if (unlikely(!(lock->flags & TTM_VT_LOCK)))
232 		ret = -EINVAL;
233 	lock->flags &= ~TTM_VT_LOCK;
234 	wakeup(lock);
235 	mtx_unlock(&lock->lock);
236 
237 	return ret;
238 }
239 
240 static void ttm_vt_lock_remove(struct ttm_base_object **p_base)
241 {
242 	struct ttm_base_object *base = *p_base;
243 	struct ttm_lock *lock = container_of(base, struct ttm_lock, base);
244 	int ret;
245 
246 	*p_base = NULL;
247 	ret = __ttm_vt_unlock(lock);
248 	MPASS(ret == 0);
249 }
250 
251 static bool __ttm_vt_lock(struct ttm_lock *lock)
252 {
253 	bool locked = false;
254 
255 	if (lock->rw == 0) {
256 		lock->flags &= ~TTM_VT_LOCK_PENDING;
257 		lock->flags |= TTM_VT_LOCK;
258 		locked = true;
259 	} else {
260 		lock->flags |= TTM_VT_LOCK_PENDING;
261 	}
262 	return locked;
263 }
264 
265 int ttm_vt_lock(struct ttm_lock *lock,
266 		bool interruptible,
267 		struct ttm_object_file *tfile)
268 {
269 	const char *wmsg;
270 	int flags, ret;
271 
272 	ret = 0;
273 	if (interruptible) {
274 		flags = PCATCH;
275 		wmsg = "ttmwi";
276 	} else {
277 		flags = 0;
278 		wmsg = "ttmw";
279 	}
280 	mtx_lock(&lock->lock);
281 	while (!__ttm_vt_lock(lock)) {
282 		ret = msleep(lock, &lock->lock, flags, wmsg, 0);
283 		if (interruptible && ret != 0) {
284 			lock->flags &= ~TTM_VT_LOCK_PENDING;
285 			wakeup(lock);
286 			break;
287 		}
288 	}
289 
290 	/*
291 	 * Add a base-object, the destructor of which will
292 	 * make sure the lock is released if the client dies
293 	 * while holding it.
294 	 */
295 
296 	ret = ttm_base_object_init(tfile, &lock->base, false,
297 				   ttm_lock_type, &ttm_vt_lock_remove, NULL);
298 	if (ret)
299 		(void)__ttm_vt_unlock(lock);
300 	else
301 		lock->vt_holder = tfile;
302 
303 	return (-ret);
304 }
305 
306 int ttm_vt_unlock(struct ttm_lock *lock)
307 {
308 	return ttm_ref_object_base_unref(lock->vt_holder,
309 					 lock->base.hash.key, TTM_REF_USAGE);
310 }
311 
312 void ttm_suspend_unlock(struct ttm_lock *lock)
313 {
314 	mtx_lock(&lock->lock);
315 	lock->flags &= ~TTM_SUSPEND_LOCK;
316 	wakeup(lock);
317 	mtx_unlock(&lock->lock);
318 }
319 
320 static bool __ttm_suspend_lock(struct ttm_lock *lock)
321 {
322 	bool locked = false;
323 
324 	if (lock->rw == 0) {
325 		lock->flags &= ~TTM_SUSPEND_LOCK_PENDING;
326 		lock->flags |= TTM_SUSPEND_LOCK;
327 		locked = true;
328 	} else {
329 		lock->flags |= TTM_SUSPEND_LOCK_PENDING;
330 	}
331 	return locked;
332 }
333 
334 void ttm_suspend_lock(struct ttm_lock *lock)
335 {
336 	mtx_lock(&lock->lock);
337 	while (!__ttm_suspend_lock(lock))
338 		msleep(lock, &lock->lock, 0, "ttms", 0);
339 	mtx_unlock(&lock->lock);
340 }
341