Import of XFree86 3.9.18
[gstreamer-omap:libdrm.git] / linux / lock.c
1 /* lock.c -- IOCTLs for locking -*- linux-c -*-
2  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
3  * Revised: Sun Feb 13 23:38:25 2000 by kevin@precisioninsight.com
4  *
5  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
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  * PRECISION INSIGHT 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 OTHER
25  * DEALINGS IN THE SOFTWARE.
26  * 
27  * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.5 1999/08/30 13:05:00 faith Exp $
28  * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.4 2000/02/14 06:27:27 martin Exp $
29  *
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         DRM_DEBUG("%d attempts\n", context);
54         do {
55                 old = *lock;
56                 if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT;
57                 else                      new = context | _DRM_LOCK_HELD;
58                 prev = cmpxchg(lock, old, new);
59         } while (prev != old);
60         if (_DRM_LOCKING_CONTEXT(old) == context) {
61                 if (old & _DRM_LOCK_HELD) {
62                         if (context != DRM_KERNEL_CONTEXT) {
63                                 DRM_ERROR("%d holds heavyweight lock\n",
64                                           context);
65                         }
66                         return 0;
67                 }
68         }
69         if (new == (context | _DRM_LOCK_HELD)) {
70                                 /* Have lock */
71                 DRM_DEBUG("%d\n", context);
72                 return 1;
73         }
74         DRM_DEBUG("%d unable to get lock held by %d\n",
75                   context, _DRM_LOCKING_CONTEXT(old));
76         return 0;
77 }
78
79 /* This takes a lock forcibly and hands it to context.  Should ONLY be used
80    inside *_unlock to give lock to kernel before calling *_dma_schedule. */
81 int drm_lock_transfer(drm_device_t *dev,
82                       __volatile__ unsigned int *lock, unsigned int context)
83 {
84         unsigned int old, new, prev;
85
86         dev->lock.pid = 0;
87         do {
88                 old  = *lock;
89                 new  = context | _DRM_LOCK_HELD;
90                 prev = cmpxchg(lock, old, new);
91         } while (prev != old);
92         DRM_DEBUG("%d => %d\n", _DRM_LOCKING_CONTEXT(old), context);
93         return 1;
94 }
95
96 int drm_lock_free(drm_device_t *dev,
97                   __volatile__ unsigned int *lock, unsigned int context)
98 {
99         unsigned int old, new, prev;
100         pid_t        pid = dev->lock.pid;
101
102         DRM_DEBUG("%d\n", context);
103         dev->lock.pid = 0;
104         do {
105                 old  = *lock;
106                 new  = 0;
107                 prev = cmpxchg(lock, old, new);
108         } while (prev != old);
109         if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
110                 DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n",
111                           context,
112                           _DRM_LOCKING_CONTEXT(old),
113                           pid);
114                 return 1;
115         }
116         wake_up_interruptible(&dev->lock.lock_queue);
117         return 0;
118 }
119
120 static int drm_flush_queue(drm_device_t *dev, int context)
121 {
122         DECLARE_WAITQUEUE(entry, current);
123         int               ret   = 0;
124         drm_queue_t       *q    = dev->queuelist[context];
125         
126         DRM_DEBUG("\n");
127         
128         atomic_inc(&q->use_count);
129         if (atomic_read(&q->use_count) > 1) {
130                 atomic_inc(&q->block_write);
131                 current->state = TASK_INTERRUPTIBLE;
132                 add_wait_queue(&q->flush_queue, &entry);
133                 atomic_inc(&q->block_count);
134                 for (;;) {
135                         if (!DRM_BUFCOUNT(&q->waitlist)) break;
136                         schedule();
137                         if (signal_pending(current)) {
138                                 ret = -EINTR; /* Can't restart */
139                                 break;
140                         }
141                 }
142                 atomic_dec(&q->block_count);
143                 current->state = TASK_RUNNING;
144                 remove_wait_queue(&q->flush_queue, &entry);
145         }
146         atomic_dec(&q->use_count);
147         atomic_inc(&q->total_flushed);
148                 
149                                 /* NOTE: block_write is still incremented!
150                                    Use drm_flush_unlock_queue to decrement. */
151         return ret;
152 }
153
154 static int drm_flush_unblock_queue(drm_device_t *dev, int context)
155 {
156         drm_queue_t       *q    = dev->queuelist[context];
157         
158         DRM_DEBUG("\n");
159         
160         atomic_inc(&q->use_count);
161         if (atomic_read(&q->use_count) > 1) {
162                 if (atomic_read(&q->block_write)) {
163                         atomic_dec(&q->block_write);
164                         wake_up_interruptible(&q->write_queue);
165                 }
166         }
167         atomic_dec(&q->use_count);
168         return 0;
169 }
170
171 int drm_flush_block_and_flush(drm_device_t *dev, int context,
172                               drm_lock_flags_t flags)
173 {
174         int ret = 0;
175         int i;
176         
177         DRM_DEBUG("\n");
178         
179         if (flags & _DRM_LOCK_FLUSH) {
180                 ret = drm_flush_queue(dev, DRM_KERNEL_CONTEXT);
181                 if (!ret) ret = drm_flush_queue(dev, context);
182         }
183         if (flags & _DRM_LOCK_FLUSH_ALL) {
184                 for (i = 0; !ret && i < dev->queue_count; i++) {
185                         ret = drm_flush_queue(dev, i);
186                 }
187         }
188         return ret;
189 }
190
191 int drm_flush_unblock(drm_device_t *dev, int context, drm_lock_flags_t flags)
192 {
193         int ret = 0;
194         int i;
195         
196         DRM_DEBUG("\n");
197         
198         if (flags & _DRM_LOCK_FLUSH) {
199                 ret = drm_flush_unblock_queue(dev, DRM_KERNEL_CONTEXT);
200                 if (!ret) ret = drm_flush_unblock_queue(dev, context);
201         }
202         if (flags & _DRM_LOCK_FLUSH_ALL) {
203                 for (i = 0; !ret && i < dev->queue_count; i++) {
204                         ret = drm_flush_unblock_queue(dev, i);
205                 }
206         }
207                 
208         return ret;
209 }
210
211 int drm_finish(struct inode *inode, struct file *filp, unsigned int cmd,
212                unsigned long arg)
213 {
214         drm_file_t        *priv   = filp->private_data;
215         drm_device_t      *dev    = priv->dev;
216         int               ret     = 0;
217         drm_lock_t        lock;
218
219         DRM_DEBUG("\n");
220
221         copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
222         ret = drm_flush_block_and_flush(dev, lock.context, lock.flags);
223         drm_flush_unblock(dev, lock.context, lock.flags);
224         return ret;
225 }