OSDN Git Service

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