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