FreeBSD/Linux Kernel Cross Reference
sys/dev/drm/drm_lock.h
1 /* lock.c -- IOCTLs for locking -*- linux-c -*-
2 * Created: Tue Feb 2 08:37:54 1999 by faith@valinux.com
3 *
4 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * 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 NONINFRINGEMENT. IN NO EVENT SHALL
22 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Authors:
28 * Rickard E. (Rik) Faith <faith@valinux.com>
29 * Gareth Hughes <gareth@valinux.com>
30 *
31 * $FreeBSD: releng/5.0/sys/dev/drm/drm_lock.h 95584 2002-04-27 20:47:57Z anholt $
32 */
33
34 #define __NO_VERSION__
35 #include "dev/drm/drmP.h"
36
37 int DRM(block)( DRM_OS_IOCTL )
38 {
39 DRM_DEBUG("\n");
40 return 0;
41 }
42
43 int DRM(unblock)( DRM_OS_IOCTL )
44 {
45 DRM_DEBUG("\n");
46 return 0;
47 }
48
49 int DRM(lock_take)(__volatile__ unsigned int *lock, unsigned int context)
50 {
51 unsigned int old, new;
52 #ifdef __linux__
53 unsigned int prev;
54 #endif /* __linux__ */
55
56 #ifdef __FreeBSD__
57 char failed;
58 #endif /* __FreeBSD__ */
59
60 do {
61 old = *lock;
62 if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT;
63 else new = context | _DRM_LOCK_HELD;
64 #ifdef __linux__
65 prev = cmpxchg(lock, old, new);
66 } while (prev != old);
67 #endif /* __linux__ */
68 #ifdef __FreeBSD__
69 _DRM_CAS(lock, old, new, failed);
70 } while (failed);
71 #endif /* __FreeBSD__ */
72 if (_DRM_LOCKING_CONTEXT(old) == context) {
73 if (old & _DRM_LOCK_HELD) {
74 if (context != DRM_KERNEL_CONTEXT) {
75 DRM_ERROR("%d holds heavyweight lock\n",
76 context);
77 }
78 return 0;
79 }
80 }
81 if (new == (context | _DRM_LOCK_HELD)) {
82 /* Have lock */
83 return 1;
84 }
85 return 0;
86 }
87
88 /* This takes a lock forcibly and hands it to context. Should ONLY be used
89 inside *_unlock to give lock to kernel before calling *_dma_schedule. */
90 int DRM(lock_transfer)(drm_device_t *dev,
91 __volatile__ unsigned int *lock, unsigned int context)
92 {
93 unsigned int old, new;
94 #ifdef __linux__
95 unsigned int prev;
96 #endif /* __linux__ */
97 #ifdef __FreeBSD__
98 char failed;
99 #endif /* __FreeBSD__ */
100
101 dev->lock.pid = 0;
102 do {
103 old = *lock;
104 new = context | _DRM_LOCK_HELD;
105 #ifdef __linux__
106 prev = cmpxchg(lock, old, new);
107 } while (prev != old);
108 #endif /* __linux__ */
109 #ifdef __FreeBSD__
110 _DRM_CAS(lock, old, new, failed);
111 } while (failed);
112 #endif /* __FreeBSD__ */
113 return 1;
114 }
115
116 int DRM(lock_free)(drm_device_t *dev,
117 __volatile__ unsigned int *lock, unsigned int context)
118 {
119 unsigned int old, new;
120 #ifdef __linux__
121 unsigned int prev;
122 #endif /* __linux__ */
123 pid_t pid = dev->lock.pid;
124 #ifdef __FreeBSD__
125 char failed;
126 #endif /* __FreeBSD__ */
127
128 dev->lock.pid = 0;
129 do {
130 old = *lock;
131 new = 0;
132 #ifdef __linux__
133 prev = cmpxchg(lock, old, new);
134 } while (prev != old);
135 #endif /* __linux__ */
136 #ifdef __FreeBSD__
137 _DRM_CAS(lock, old, new, failed);
138 } while (failed);
139 #endif /* __FreeBSD__ */
140 if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
141 DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n",
142 context,
143 _DRM_LOCKING_CONTEXT(old),
144 pid);
145 return 1;
146 }
147 DRM_OS_WAKEUP_INT(&dev->lock.lock_queue);
148 return 0;
149 }
150
151 static int DRM(flush_queue)(drm_device_t *dev, int context)
152 {
153 #ifdef __linux__
154 DECLARE_WAITQUEUE(entry, current);
155 #endif /* __linux__ */
156 #ifdef __FreeBSD__
157 int error;
158 #endif /* __FreeBSD__ */
159 int ret = 0;
160 drm_queue_t *q = dev->queuelist[context];
161
162 DRM_DEBUG("\n");
163
164 atomic_inc(&q->use_count);
165 if (atomic_read(&q->use_count) > 1) {
166 atomic_inc(&q->block_write);
167 #ifdef __linux__
168 add_wait_queue(&q->flush_queue, &entry);
169 atomic_inc(&q->block_count);
170 for (;;) {
171 current->state = TASK_INTERRUPTIBLE;
172 if (!DRM_BUFCOUNT(&q->waitlist)) break;
173 schedule();
174 if (signal_pending(current)) {
175 ret = -EINTR; /* Can't restart */
176 break;
177 }
178 }
179 #endif /* __linux__ */
180 #ifdef __FreeBSD__
181 atomic_inc(&q->block_count);
182 error = tsleep(&q->flush_queue, PZERO|PCATCH, "drmfq", 0);
183 if (error)
184 return error;
185 #endif /* __FreeBSD__ */
186 atomic_dec(&q->block_count);
187 #ifdef __linux__
188 current->state = TASK_RUNNING;
189 remove_wait_queue(&q->flush_queue, &entry);
190 #endif /* __linux__ */
191 }
192 atomic_dec(&q->use_count);
193
194 /* NOTE: block_write is still incremented!
195 Use drm_flush_unlock_queue to decrement. */
196 return ret;
197 }
198
199 static int DRM(flush_unblock_queue)(drm_device_t *dev, int context)
200 {
201 drm_queue_t *q = dev->queuelist[context];
202
203 DRM_DEBUG("\n");
204
205 atomic_inc(&q->use_count);
206 if (atomic_read(&q->use_count) > 1) {
207 if (atomic_read(&q->block_write)) {
208 atomic_dec(&q->block_write);
209 DRM_OS_WAKEUP_INT(&q->write_queue);
210 }
211 }
212 atomic_dec(&q->use_count);
213 return 0;
214 }
215
216 int DRM(flush_block_and_flush)(drm_device_t *dev, int context,
217 drm_lock_flags_t flags)
218 {
219 int ret = 0;
220 int i;
221
222 DRM_DEBUG("\n");
223
224 if (flags & _DRM_LOCK_FLUSH) {
225 ret = DRM(flush_queue)(dev, DRM_KERNEL_CONTEXT);
226 if (!ret) ret = DRM(flush_queue)(dev, context);
227 }
228 if (flags & _DRM_LOCK_FLUSH_ALL) {
229 for (i = 0; !ret && i < dev->queue_count; i++) {
230 ret = DRM(flush_queue)(dev, i);
231 }
232 }
233 return ret;
234 }
235
236 int DRM(flush_unblock)(drm_device_t *dev, int context, drm_lock_flags_t flags)
237 {
238 int ret = 0;
239 int i;
240
241 DRM_DEBUG("\n");
242
243 if (flags & _DRM_LOCK_FLUSH) {
244 ret = DRM(flush_unblock_queue)(dev, DRM_KERNEL_CONTEXT);
245 if (!ret) ret = DRM(flush_unblock_queue)(dev, context);
246 }
247 if (flags & _DRM_LOCK_FLUSH_ALL) {
248 for (i = 0; !ret && i < dev->queue_count; i++) {
249 ret = DRM(flush_unblock_queue)(dev, i);
250 }
251 }
252
253 return ret;
254 }
255
256 int DRM(finish)( DRM_OS_IOCTL )
257 {
258 DRM_OS_DEVICE;
259 int ret = 0;
260 drm_lock_t lock;
261
262 DRM_DEBUG("\n");
263
264 DRM_OS_KRNFROMUSR( lock, (drm_lock_t *)data, sizeof(lock) );
265
266 ret = DRM(flush_block_and_flush)(dev, lock.context, lock.flags);
267 DRM(flush_unblock)(dev, lock.context, lock.flags);
268 return ret;
269 }
270
271 /* If we get here, it means that the process has called DRM_IOCTL_LOCK
272 without calling DRM_IOCTL_UNLOCK.
273
274 If the lock is not held, then let the signal proceed as usual.
275
276 If the lock is held, then set the contended flag and keep the signal
277 blocked.
278
279
280 Return 1 if the signal should be delivered normally.
281 Return 0 if the signal should be blocked. */
282
283 int DRM(notifier)(void *priv)
284 {
285 drm_sigdata_t *s = (drm_sigdata_t *)priv;
286 unsigned int old, new;
287 #ifdef __linux__
288 unsigned int prev;
289 #endif /* __linux__ */
290 #ifdef __FreeBSD__
291 char failed;
292 #endif /* __FreeBSD__ */
293
294
295 /* Allow signal delivery if lock isn't held */
296 if (!_DRM_LOCK_IS_HELD(s->lock->lock)
297 || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) return 1;
298
299 /* Otherwise, set flag to force call to
300 drmUnlock */
301 do {
302 old = s->lock->lock;
303 new = old | _DRM_LOCK_CONT;
304 #ifdef __linux__
305 prev = cmpxchg(&s->lock->lock, old, new);
306 } while (prev != old);
307 #endif /* __linux__ */
308 #ifdef __FreeBSD__
309 _DRM_CAS(&s->lock->lock, old, new, failed);
310 } while (failed);
311 #endif /* __FreeBSD__ */
312 return 0;
313 }
314
Cache object: 80799e465b735d376b1017555b7465c5
|