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
ttm_lock_init(struct ttm_lock * lock)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
ttm_lock_send_sig(int signo)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
ttm_read_unlock(struct ttm_lock * lock)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
__ttm_read_lock(struct ttm_lock * lock)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
ttm_read_lock(struct ttm_lock * lock,bool interruptible)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
__ttm_read_trylock(struct ttm_lock * lock,bool * locked)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
ttm_read_trylock(struct ttm_lock * lock,bool interruptible)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
ttm_write_unlock(struct ttm_lock * lock)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
__ttm_write_lock(struct ttm_lock * lock)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
ttm_write_lock(struct ttm_lock * lock,bool interruptible)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
ttm_write_lock_downgrade(struct ttm_lock * lock)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
__ttm_vt_unlock(struct ttm_lock * lock)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
ttm_vt_lock_remove(struct ttm_base_object ** p_base)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
__ttm_vt_lock(struct ttm_lock * lock)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
ttm_vt_lock(struct ttm_lock * lock,bool interruptible,struct ttm_object_file * tfile)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
ttm_vt_unlock(struct ttm_lock * lock)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
ttm_suspend_unlock(struct ttm_lock * lock)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
__ttm_suspend_lock(struct ttm_lock * lock)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
ttm_suspend_lock(struct ttm_lock * lock)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