xref: /freebsd/sys/dev/drm2/ttm/ttm_lock.c (revision bdcbfde31e8e9b343f113a1956384bdf30d1ed62)
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 #include <dev/drm2/ttm/ttm_lock.h>
40 #include <dev/drm2/ttm/ttm_module.h>
41 
42 #define TTM_WRITE_LOCK_PENDING    (1 << 0)
43 #define TTM_VT_LOCK_PENDING       (1 << 1)
44 #define TTM_SUSPEND_LOCK_PENDING  (1 << 2)
45 #define TTM_VT_LOCK               (1 << 3)
46 #define TTM_SUSPEND_LOCK          (1 << 4)
47 
48 void ttm_lock_init(struct ttm_lock *lock)
49 {
50 	mtx_init(&lock->lock, "ttmlk", NULL, MTX_DEF);
51 	lock->rw = 0;
52 	lock->flags = 0;
53 	lock->kill_takers = false;
54 	lock->signal = SIGKILL;
55 }
56 
57 static void
58 ttm_lock_send_sig(int signo)
59 {
60 	struct proc *p;
61 
62 	p = curproc;	/* XXXKIB curthread ? */
63 	PROC_LOCK(p);
64 	kern_psignal(p, signo);
65 	PROC_UNLOCK(p);
66 }
67 
68 void ttm_read_unlock(struct ttm_lock *lock)
69 {
70 	mtx_lock(&lock->lock);
71 	if (--lock->rw == 0)
72 		wakeup(lock);
73 	mtx_unlock(&lock->lock);
74 }
75 
76 static bool __ttm_read_lock(struct ttm_lock *lock)
77 {
78 	bool locked = false;
79 
80 	if (unlikely(lock->kill_takers)) {
81 		ttm_lock_send_sig(lock->signal);
82 		return false;
83 	}
84 	if (lock->rw >= 0 && lock->flags == 0) {
85 		++lock->rw;
86 		locked = true;
87 	}
88 	return locked;
89 }
90 
91 int
92 ttm_read_lock(struct ttm_lock *lock, bool interruptible)
93 {
94 	const char *wmsg;
95 	int flags, ret;
96 
97 	ret = 0;
98 	if (interruptible) {
99 		flags = PCATCH;
100 		wmsg = "ttmri";
101 	} else {
102 		flags = 0;
103 		wmsg = "ttmr";
104 	}
105 	mtx_lock(&lock->lock);
106 	while (!__ttm_read_lock(lock)) {
107 		ret = -msleep(lock, &lock->lock, flags, wmsg, 0);
108 		if (ret == -EINTR || ret == -ERESTART)
109 			ret = -ERESTARTSYS;
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 == -EINTR || ret == -ERESTART)
155 			ret = -ERESTARTSYS;
156 		if (ret != 0)
157 			break;
158 	}
159 	MPASS(!locked || ret == 0);
160 	mtx_unlock(&lock->lock);
161 
162 	return (locked) ? 0 : -EBUSY;
163 }
164 
165 void ttm_write_unlock(struct ttm_lock *lock)
166 {
167 	mtx_lock(&lock->lock);
168 	lock->rw = 0;
169 	wakeup(lock);
170 	mtx_unlock(&lock->lock);
171 }
172 
173 static bool __ttm_write_lock(struct ttm_lock *lock)
174 {
175 	bool locked = false;
176 
177 	if (unlikely(lock->kill_takers)) {
178 		ttm_lock_send_sig(lock->signal);
179 		return false;
180 	}
181 	if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) {
182 		lock->rw = -1;
183 		lock->flags &= ~TTM_WRITE_LOCK_PENDING;
184 		locked = true;
185 	} else {
186 		lock->flags |= TTM_WRITE_LOCK_PENDING;
187 	}
188 	return locked;
189 }
190 
191 int
192 ttm_write_lock(struct ttm_lock *lock, bool interruptible)
193 {
194 	const char *wmsg;
195 	int flags, ret;
196 
197 	ret = 0;
198 	if (interruptible) {
199 		flags = PCATCH;
200 		wmsg = "ttmwi";
201 	} else {
202 		flags = 0;
203 		wmsg = "ttmw";
204 	}
205 	mtx_lock(&lock->lock);
206 	/* XXXKIB: linux uses __ttm_read_lock for uninterruptible sleeps */
207 	while (!__ttm_write_lock(lock)) {
208 		ret = -msleep(lock, &lock->lock, flags, wmsg, 0);
209 		if (ret == -EINTR || ret == -ERESTART)
210 			ret = -ERESTARTSYS;
211 		if (interruptible && ret != 0) {
212 			lock->flags &= ~TTM_WRITE_LOCK_PENDING;
213 			wakeup(lock);
214 			break;
215 		}
216 	}
217 	mtx_unlock(&lock->lock);
218 
219 	return (ret);
220 }
221 
222 void ttm_write_lock_downgrade(struct ttm_lock *lock)
223 {
224 	mtx_lock(&lock->lock);
225 	lock->rw = 1;
226 	wakeup(lock);
227 	mtx_unlock(&lock->lock);
228 }
229 
230 static int __ttm_vt_unlock(struct ttm_lock *lock)
231 {
232 	int ret = 0;
233 
234 	mtx_lock(&lock->lock);
235 	if (unlikely(!(lock->flags & TTM_VT_LOCK)))
236 		ret = -EINVAL;
237 	lock->flags &= ~TTM_VT_LOCK;
238 	wakeup(lock);
239 	mtx_unlock(&lock->lock);
240 
241 	return ret;
242 }
243 
244 static void ttm_vt_lock_remove(struct ttm_base_object **p_base)
245 {
246 	struct ttm_base_object *base = *p_base;
247 	struct ttm_lock *lock = container_of(base, struct ttm_lock, base);
248 	int ret __diagused;
249 
250 	*p_base = NULL;
251 	ret = __ttm_vt_unlock(lock);
252 	MPASS(ret == 0);
253 }
254 
255 static bool __ttm_vt_lock(struct ttm_lock *lock)
256 {
257 	bool locked = false;
258 
259 	if (lock->rw == 0) {
260 		lock->flags &= ~TTM_VT_LOCK_PENDING;
261 		lock->flags |= TTM_VT_LOCK;
262 		locked = true;
263 	} else {
264 		lock->flags |= TTM_VT_LOCK_PENDING;
265 	}
266 	return locked;
267 }
268 
269 int ttm_vt_lock(struct ttm_lock *lock,
270 		bool interruptible,
271 		struct ttm_object_file *tfile)
272 {
273 	const char *wmsg;
274 	int flags, ret;
275 
276 	ret = 0;
277 	if (interruptible) {
278 		flags = PCATCH;
279 		wmsg = "ttmwi";
280 	} else {
281 		flags = 0;
282 		wmsg = "ttmw";
283 	}
284 	mtx_lock(&lock->lock);
285 	while (!__ttm_vt_lock(lock)) {
286 		ret = -msleep(lock, &lock->lock, flags, wmsg, 0);
287 		if (ret == -EINTR || ret == -ERESTART)
288 			ret = -ERESTARTSYS;
289 		if (interruptible && ret != 0) {
290 			lock->flags &= ~TTM_VT_LOCK_PENDING;
291 			wakeup(lock);
292 			break;
293 		}
294 	}
295 
296 	/*
297 	 * Add a base-object, the destructor of which will
298 	 * make sure the lock is released if the client dies
299 	 * while holding it.
300 	 */
301 
302 	ret = ttm_base_object_init(tfile, &lock->base, false,
303 				   ttm_lock_type, &ttm_vt_lock_remove, NULL);
304 	if (ret)
305 		(void)__ttm_vt_unlock(lock);
306 	else
307 		lock->vt_holder = tfile;
308 
309 	return (ret);
310 }
311 
312 int ttm_vt_unlock(struct ttm_lock *lock)
313 {
314 	return ttm_ref_object_base_unref(lock->vt_holder,
315 					 lock->base.hash.key, TTM_REF_USAGE);
316 }
317 
318 void ttm_suspend_unlock(struct ttm_lock *lock)
319 {
320 	mtx_lock(&lock->lock);
321 	lock->flags &= ~TTM_SUSPEND_LOCK;
322 	wakeup(lock);
323 	mtx_unlock(&lock->lock);
324 }
325 
326 static bool __ttm_suspend_lock(struct ttm_lock *lock)
327 {
328 	bool locked = false;
329 
330 	if (lock->rw == 0) {
331 		lock->flags &= ~TTM_SUSPEND_LOCK_PENDING;
332 		lock->flags |= TTM_SUSPEND_LOCK;
333 		locked = true;
334 	} else {
335 		lock->flags |= TTM_SUSPEND_LOCK_PENDING;
336 	}
337 	return locked;
338 }
339 
340 void ttm_suspend_lock(struct ttm_lock *lock)
341 {
342 	mtx_lock(&lock->lock);
343 	while (!__ttm_suspend_lock(lock))
344 		msleep(lock, &lock->lock, 0, "ttms", 0);
345 	mtx_unlock(&lock->lock);
346 }
347