Revert "Merge branch 'vmwgfx-next' of git://people.freedesktop.org/~thomash/linux...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / vmwgfx / ttm_lock.c
1 /* SPDX-License-Identifier: GPL-2.0 OR MIT */
2 /**************************************************************************
3  *
4  * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sub license, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the
16  * next paragraph) shall be included in all copies or substantial portions
17  * of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25  * USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  **************************************************************************/
28 /*
29  * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
30  */
31
32 #include <drm/ttm/ttm_module.h>
33 #include <linux/atomic.h>
34 #include <linux/errno.h>
35 #include <linux/wait.h>
36 #include <linux/sched/signal.h>
37 #include "ttm_lock.h"
38 #include "ttm_object.h"
39
40 #define TTM_WRITE_LOCK_PENDING    (1 << 0)
41 #define TTM_VT_LOCK_PENDING       (1 << 1)
42 #define TTM_SUSPEND_LOCK_PENDING  (1 << 2)
43 #define TTM_VT_LOCK               (1 << 3)
44 #define TTM_SUSPEND_LOCK          (1 << 4)
45
46 void ttm_lock_init(struct ttm_lock *lock)
47 {
48         spin_lock_init(&lock->lock);
49         init_waitqueue_head(&lock->queue);
50         lock->rw = 0;
51         lock->flags = 0;
52         lock->kill_takers = false;
53         lock->signal = SIGKILL;
54 }
55
56 void ttm_read_unlock(struct ttm_lock *lock)
57 {
58         spin_lock(&lock->lock);
59         if (--lock->rw == 0)
60                 wake_up_all(&lock->queue);
61         spin_unlock(&lock->lock);
62 }
63
64 static bool __ttm_read_lock(struct ttm_lock *lock)
65 {
66         bool locked = false;
67
68         spin_lock(&lock->lock);
69         if (unlikely(lock->kill_takers)) {
70                 send_sig(lock->signal, current, 0);
71                 spin_unlock(&lock->lock);
72                 return false;
73         }
74         if (lock->rw >= 0 && lock->flags == 0) {
75                 ++lock->rw;
76                 locked = true;
77         }
78         spin_unlock(&lock->lock);
79         return locked;
80 }
81
82 int ttm_read_lock(struct ttm_lock *lock, bool interruptible)
83 {
84         int ret = 0;
85
86         if (interruptible)
87                 ret = wait_event_interruptible(lock->queue,
88                                                __ttm_read_lock(lock));
89         else
90                 wait_event(lock->queue, __ttm_read_lock(lock));
91         return ret;
92 }
93
94 static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked)
95 {
96         bool block = true;
97
98         *locked = false;
99
100         spin_lock(&lock->lock);
101         if (unlikely(lock->kill_takers)) {
102                 send_sig(lock->signal, current, 0);
103                 spin_unlock(&lock->lock);
104                 return false;
105         }
106         if (lock->rw >= 0 && lock->flags == 0) {
107                 ++lock->rw;
108                 block = false;
109                 *locked = true;
110         } else if (lock->flags == 0) {
111                 block = false;
112         }
113         spin_unlock(&lock->lock);
114
115         return !block;
116 }
117
118 int ttm_read_trylock(struct ttm_lock *lock, bool interruptible)
119 {
120         int ret = 0;
121         bool locked;
122
123         if (interruptible)
124                 ret = wait_event_interruptible
125                         (lock->queue, __ttm_read_trylock(lock, &locked));
126         else
127                 wait_event(lock->queue, __ttm_read_trylock(lock, &locked));
128
129         if (unlikely(ret != 0)) {
130                 BUG_ON(locked);
131                 return ret;
132         }
133
134         return (locked) ? 0 : -EBUSY;
135 }
136
137 void ttm_write_unlock(struct ttm_lock *lock)
138 {
139         spin_lock(&lock->lock);
140         lock->rw = 0;
141         wake_up_all(&lock->queue);
142         spin_unlock(&lock->lock);
143 }
144
145 static bool __ttm_write_lock(struct ttm_lock *lock)
146 {
147         bool locked = false;
148
149         spin_lock(&lock->lock);
150         if (unlikely(lock->kill_takers)) {
151                 send_sig(lock->signal, current, 0);
152                 spin_unlock(&lock->lock);
153                 return false;
154         }
155         if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) {
156                 lock->rw = -1;
157                 lock->flags &= ~TTM_WRITE_LOCK_PENDING;
158                 locked = true;
159         } else {
160                 lock->flags |= TTM_WRITE_LOCK_PENDING;
161         }
162         spin_unlock(&lock->lock);
163         return locked;
164 }
165
166 int ttm_write_lock(struct ttm_lock *lock, bool interruptible)
167 {
168         int ret = 0;
169
170         if (interruptible) {
171                 ret = wait_event_interruptible(lock->queue,
172                                                __ttm_write_lock(lock));
173                 if (unlikely(ret != 0)) {
174                         spin_lock(&lock->lock);
175                         lock->flags &= ~TTM_WRITE_LOCK_PENDING;
176                         wake_up_all(&lock->queue);
177                         spin_unlock(&lock->lock);
178                 }
179         } else
180                 wait_event(lock->queue, __ttm_write_lock(lock));
181
182         return ret;
183 }
184
185 static int __ttm_vt_unlock(struct ttm_lock *lock)
186 {
187         int ret = 0;
188
189         spin_lock(&lock->lock);
190         if (unlikely(!(lock->flags & TTM_VT_LOCK)))
191                 ret = -EINVAL;
192         lock->flags &= ~TTM_VT_LOCK;
193         wake_up_all(&lock->queue);
194         spin_unlock(&lock->lock);
195
196         return ret;
197 }
198
199 static void ttm_vt_lock_remove(struct ttm_base_object **p_base)
200 {
201         struct ttm_base_object *base = *p_base;
202         struct ttm_lock *lock = container_of(base, struct ttm_lock, base);
203         int ret;
204
205         *p_base = NULL;
206         ret = __ttm_vt_unlock(lock);
207         BUG_ON(ret != 0);
208 }
209
210 static bool __ttm_vt_lock(struct ttm_lock *lock)
211 {
212         bool locked = false;
213
214         spin_lock(&lock->lock);
215         if (lock->rw == 0) {
216                 lock->flags &= ~TTM_VT_LOCK_PENDING;
217                 lock->flags |= TTM_VT_LOCK;
218                 locked = true;
219         } else {
220                 lock->flags |= TTM_VT_LOCK_PENDING;
221         }
222         spin_unlock(&lock->lock);
223         return locked;
224 }
225
226 int ttm_vt_lock(struct ttm_lock *lock,
227                 bool interruptible,
228                 struct ttm_object_file *tfile)
229 {
230         int ret = 0;
231
232         if (interruptible) {
233                 ret = wait_event_interruptible(lock->queue,
234                                                __ttm_vt_lock(lock));
235                 if (unlikely(ret != 0)) {
236                         spin_lock(&lock->lock);
237                         lock->flags &= ~TTM_VT_LOCK_PENDING;
238                         wake_up_all(&lock->queue);
239                         spin_unlock(&lock->lock);
240                         return ret;
241                 }
242         } else
243                 wait_event(lock->queue, __ttm_vt_lock(lock));
244
245         /*
246          * Add a base-object, the destructor of which will
247          * make sure the lock is released if the client dies
248          * while holding it.
249          */
250
251         ret = ttm_base_object_init(tfile, &lock->base, false,
252                                    ttm_lock_type, &ttm_vt_lock_remove, NULL);
253         if (ret)
254                 (void)__ttm_vt_unlock(lock);
255         else
256                 lock->vt_holder = tfile;
257
258         return ret;
259 }
260
261 int ttm_vt_unlock(struct ttm_lock *lock)
262 {
263         return ttm_ref_object_base_unref(lock->vt_holder,
264                                          lock->base.handle, TTM_REF_USAGE);
265 }
266
267 void ttm_suspend_unlock(struct ttm_lock *lock)
268 {
269         spin_lock(&lock->lock);
270         lock->flags &= ~TTM_SUSPEND_LOCK;
271         wake_up_all(&lock->queue);
272         spin_unlock(&lock->lock);
273 }
274
275 static bool __ttm_suspend_lock(struct ttm_lock *lock)
276 {
277         bool locked = false;
278
279         spin_lock(&lock->lock);
280         if (lock->rw == 0) {
281                 lock->flags &= ~TTM_SUSPEND_LOCK_PENDING;
282                 lock->flags |= TTM_SUSPEND_LOCK;
283                 locked = true;
284         } else {
285                 lock->flags |= TTM_SUSPEND_LOCK_PENDING;
286         }
287         spin_unlock(&lock->lock);
288         return locked;
289 }
290
291 void ttm_suspend_lock(struct ttm_lock *lock)
292 {
293         wait_event(lock->queue, __ttm_suspend_lock(lock));
294 }