File: /usr/src/linux/drivers/char/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     
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.pid = 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     	pid_t        pid = dev->lock.pid;
96     
97     	dev->lock.pid = 0;
98     	do {
99     		old  = *lock;
100     		new  = 0;
101     		prev = cmpxchg(lock, old, new);
102     	} while (prev != old);
103     	if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
104     		DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n",
105     			  context,
106     			  _DRM_LOCKING_CONTEXT(old),
107     			  pid);
108     		return 1;
109     	}
110     	wake_up_interruptible(&dev->lock.lock_queue);
111     	return 0;
112     }
113     
114     static int DRM(flush_queue)(drm_device_t *dev, int context)
115     {
116     	DECLARE_WAITQUEUE(entry, current);
117     	int		  ret	= 0;
118     	drm_queue_t	  *q	= dev->queuelist[context];
119     
120     	DRM_DEBUG("\n");
121     
122     	atomic_inc(&q->use_count);
123     	if (atomic_read(&q->use_count) > 1) {
124     		atomic_inc(&q->block_write);
125     		add_wait_queue(&q->flush_queue, &entry);
126     		atomic_inc(&q->block_count);
127     		for (;;) {
128     			current->state = TASK_INTERRUPTIBLE;
129     			if (!DRM_BUFCOUNT(&q->waitlist)) break;
130     			schedule();
131     			if (signal_pending(current)) {
132     				ret = -EINTR; /* Can't restart */
133     				break;
134     			}
135     		}
136     		atomic_dec(&q->block_count);
137     		current->state = TASK_RUNNING;
138     		remove_wait_queue(&q->flush_queue, &entry);
139     	}
140     	atomic_dec(&q->use_count);
141     
142     				/* NOTE: block_write is still incremented!
143     				   Use drm_flush_unlock_queue to decrement. */
144     	return ret;
145     }
146     
147     static int DRM(flush_unblock_queue)(drm_device_t *dev, int context)
148     {
149     	drm_queue_t	  *q	= dev->queuelist[context];
150     
151     	DRM_DEBUG("\n");
152     
153     	atomic_inc(&q->use_count);
154     	if (atomic_read(&q->use_count) > 1) {
155     		if (atomic_read(&q->block_write)) {
156     			atomic_dec(&q->block_write);
157     			wake_up_interruptible(&q->write_queue);
158     		}
159     	}
160     	atomic_dec(&q->use_count);
161     	return 0;
162     }
163     
164     int DRM(flush_block_and_flush)(drm_device_t *dev, int context,
165     			       drm_lock_flags_t flags)
166     {
167     	int ret = 0;
168     	int i;
169     
170     	DRM_DEBUG("\n");
171     
172     	if (flags & _DRM_LOCK_FLUSH) {
173     		ret = DRM(flush_queue)(dev, DRM_KERNEL_CONTEXT);
174     		if (!ret) ret = DRM(flush_queue)(dev, context);
175     	}
176     	if (flags & _DRM_LOCK_FLUSH_ALL) {
177     		for (i = 0; !ret && i < dev->queue_count; i++) {
178     			ret = DRM(flush_queue)(dev, i);
179     		}
180     	}
181     	return ret;
182     }
183     
184     int DRM(flush_unblock)(drm_device_t *dev, int context, drm_lock_flags_t flags)
185     {
186     	int ret = 0;
187     	int i;
188     
189     	DRM_DEBUG("\n");
190     
191     	if (flags & _DRM_LOCK_FLUSH) {
192     		ret = DRM(flush_unblock_queue)(dev, DRM_KERNEL_CONTEXT);
193     		if (!ret) ret = DRM(flush_unblock_queue)(dev, context);
194     	}
195     	if (flags & _DRM_LOCK_FLUSH_ALL) {
196     		for (i = 0; !ret && i < dev->queue_count; i++) {
197     			ret = DRM(flush_unblock_queue)(dev, i);
198     		}
199     	}
200     
201     	return ret;
202     }
203     
204     int DRM(finish)(struct inode *inode, struct file *filp, unsigned int cmd,
205     		unsigned long arg)
206     {
207     	drm_file_t	  *priv	  = filp->private_data;
208     	drm_device_t	  *dev	  = priv->dev;
209     	int		  ret	  = 0;
210     	drm_lock_t	  lock;
211     
212     	DRM_DEBUG("\n");
213     
214     	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
215     		return -EFAULT;
216     	ret = DRM(flush_block_and_flush)(dev, lock.context, lock.flags);
217     	DRM(flush_unblock)(dev, lock.context, lock.flags);
218     	return ret;
219     }
220     
221     /* If we get here, it means that the process has called DRM_IOCTL_LOCK
222        without calling DRM_IOCTL_UNLOCK.
223     
224        If the lock is not held, then let the signal proceed as usual.
225     
226        If the lock is held, then set the contended flag and keep the signal
227        blocked.
228     
229     
230        Return 1 if the signal should be delivered normally.
231        Return 0 if the signal should be blocked.  */
232     
233     int DRM(notifier)(void *priv)
234     {
235     	drm_sigdata_t *s = (drm_sigdata_t *)priv;
236     	unsigned int  old, new, prev;
237     
238     
239     				/* Allow signal delivery if lock isn't held */
240     	if (!_DRM_LOCK_IS_HELD(s->lock->lock)
241     	    || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) return 1;
242     
243     				/* Otherwise, set flag to force call to
244                                        drmUnlock */
245     	do {
246     		old  = s->lock->lock;
247     		new  = old | _DRM_LOCK_CONT;
248     		prev = cmpxchg(&s->lock->lock, old, new);
249     	} while (prev != old);
250     	return 0;
251     }
252