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
32 #include "drmP.h"
33
DRM(block)34 int DRM(block)(struct inode *inode, struct file *filp, unsigned int cmd,
35 unsigned long arg)
36 {
37 DRM_DEBUG("\n");
38 return 0;
39 }
40
DRM(unblock)41 int DRM(unblock)(struct inode *inode, struct file *filp, unsigned int cmd,
42 unsigned long arg)
43 {
44 DRM_DEBUG("\n");
45 return 0;
46 }
47
DRM(lock_take)48 int DRM(lock_take)(__volatile__ unsigned int *lock, unsigned int context)
49 {
50 unsigned int old, new, prev;
51
52 do {
53 old = *lock;
54 if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT;
55 else new = context | _DRM_LOCK_HELD;
56 prev = cmpxchg(lock, old, new);
57 } while (prev != old);
58 if (_DRM_LOCKING_CONTEXT(old) == context) {
59 if (old & _DRM_LOCK_HELD) {
60 if (context != DRM_KERNEL_CONTEXT) {
61 DRM_ERROR("%d holds heavyweight lock\n",
62 context);
63 }
64 return 0;
65 }
66 }
67 if (new == (context | _DRM_LOCK_HELD)) {
68 /* Have lock */
69 return 1;
70 }
71 return 0;
72 }
73
74 /* This takes a lock forcibly and hands it to context. Should ONLY be used
75 inside *_unlock to give lock to kernel before calling *_dma_schedule. */
DRM(lock_transfer)76 int DRM(lock_transfer)(drm_device_t *dev,
77 __volatile__ unsigned int *lock, unsigned int context)
78 {
79 unsigned int old, new, prev;
80
81 dev->lock.pid = 0;
82 do {
83 old = *lock;
84 new = context | _DRM_LOCK_HELD;
85 prev = cmpxchg(lock, old, new);
86 } while (prev != old);
87 return 1;
88 }
89
DRM(lock_free)90 int DRM(lock_free)(drm_device_t *dev,
91 __volatile__ unsigned int *lock, unsigned int context)
92 {
93 unsigned int old, new, prev;
94 pid_t pid = dev->lock.pid;
95
96 dev->lock.pid = 0;
97 do {
98 old = *lock;
99 new = 0;
100 prev = cmpxchg(lock, old, new);
101 } while (prev != old);
102 if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
103 DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n",
104 context,
105 _DRM_LOCKING_CONTEXT(old),
106 pid);
107 return 1;
108 }
109 wake_up_interruptible(&dev->lock.lock_queue);
110 return 0;
111 }
112
DRM(flush_queue)113 static int DRM(flush_queue)(drm_device_t *dev, int context)
114 {
115 DECLARE_WAITQUEUE(entry, current);
116 int ret = 0;
117 drm_queue_t *q = dev->queuelist[context];
118
119 DRM_DEBUG("\n");
120
121 atomic_inc(&q->use_count);
122 if (atomic_read(&q->use_count) > 1) {
123 atomic_inc(&q->block_write);
124 add_wait_queue(&q->flush_queue, &entry);
125 atomic_inc(&q->block_count);
126 for (;;) {
127 current->state = TASK_INTERRUPTIBLE;
128 if (!DRM_BUFCOUNT(&q->waitlist)) break;
129 schedule();
130 if (signal_pending(current)) {
131 ret = -EINTR; /* Can't restart */
132 break;
133 }
134 }
135 atomic_dec(&q->block_count);
136 current->state = TASK_RUNNING;
137 remove_wait_queue(&q->flush_queue, &entry);
138 }
139 atomic_dec(&q->use_count);
140
141 /* NOTE: block_write is still incremented!
142 Use drm_flush_unlock_queue to decrement. */
143 return ret;
144 }
145
DRM(flush_unblock_queue)146 static int DRM(flush_unblock_queue)(drm_device_t *dev, int context)
147 {
148 drm_queue_t *q = dev->queuelist[context];
149
150 DRM_DEBUG("\n");
151
152 atomic_inc(&q->use_count);
153 if (atomic_read(&q->use_count) > 1) {
154 if (atomic_read(&q->block_write)) {
155 atomic_dec(&q->block_write);
156 wake_up_interruptible(&q->write_queue);
157 }
158 }
159 atomic_dec(&q->use_count);
160 return 0;
161 }
162
DRM(flush_block_and_flush)163 int DRM(flush_block_and_flush)(drm_device_t *dev, int context,
164 drm_lock_flags_t flags)
165 {
166 int ret = 0;
167 int i;
168
169 DRM_DEBUG("\n");
170
171 if (flags & _DRM_LOCK_FLUSH) {
172 ret = DRM(flush_queue)(dev, DRM_KERNEL_CONTEXT);
173 if (!ret) ret = DRM(flush_queue)(dev, context);
174 }
175 if (flags & _DRM_LOCK_FLUSH_ALL) {
176 for (i = 0; !ret && i < dev->queue_count; i++) {
177 ret = DRM(flush_queue)(dev, i);
178 }
179 }
180 return ret;
181 }
182
DRM(flush_unblock)183 int DRM(flush_unblock)(drm_device_t *dev, int context, drm_lock_flags_t flags)
184 {
185 int ret = 0;
186 int i;
187
188 DRM_DEBUG("\n");
189
190 if (flags & _DRM_LOCK_FLUSH) {
191 ret = DRM(flush_unblock_queue)(dev, DRM_KERNEL_CONTEXT);
192 if (!ret) ret = DRM(flush_unblock_queue)(dev, context);
193 }
194 if (flags & _DRM_LOCK_FLUSH_ALL) {
195 for (i = 0; !ret && i < dev->queue_count; i++) {
196 ret = DRM(flush_unblock_queue)(dev, i);
197 }
198 }
199
200 return ret;
201 }
202
DRM(finish)203 int DRM(finish)(struct inode *inode, struct file *filp, unsigned int cmd,
204 unsigned long arg)
205 {
206 drm_file_t *priv = filp->private_data;
207 drm_device_t *dev = priv->dev;
208 int ret = 0;
209 drm_lock_t lock;
210
211 DRM_DEBUG("\n");
212
213 if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
214 return -EFAULT;
215 ret = DRM(flush_block_and_flush)(dev, lock.context, lock.flags);
216 DRM(flush_unblock)(dev, lock.context, lock.flags);
217 return ret;
218 }
219
220 /* If we get here, it means that the process has called DRM_IOCTL_LOCK
221 without calling DRM_IOCTL_UNLOCK.
222
223 If the lock is not held, then let the signal proceed as usual.
224
225 If the lock is held, then set the contended flag and keep the signal
226 blocked.
227
228
229 Return 1 if the signal should be delivered normally.
230 Return 0 if the signal should be blocked. */
231
DRM(notifier)232 int DRM(notifier)(void *priv)
233 {
234 drm_sigdata_t *s = (drm_sigdata_t *)priv;
235 unsigned int old, new, prev;
236
237
238 /* Allow signal delivery if lock isn't held */
239 if (!s->lock || !_DRM_LOCK_IS_HELD(s->lock->lock)
240 || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) return 1;
241
242 /* Otherwise, set flag to force call to
243 drmUnlock */
244 do {
245 old = s->lock->lock;
246 new = old | _DRM_LOCK_CONT;
247 prev = cmpxchg(&s->lock->lock, old, new);
248 } while (prev != old);
249 return 0;
250 }
251