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