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