drm/radeon: Update to Linux 3.17 (v2)
[dragonfly.git] / sys / dev / drm / ttm / ttm_lock.c
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);