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