xref: /titanic_50/usr/src/uts/common/io/drm/drm_lock.c (revision 292f4c1c373bd6e2c3c0b6e199a87392f265291f)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * lock.c -- IOCTLs for locking -*- linux-c -*-
8  * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
9  */
10 /*
11  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
12  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
13  * All Rights Reserved.
14  *
15  * Permission is hereby granted, free of charge, to any person obtaining a
16  * copy of this software and associated documentation files (the "Software"),
17  * to deal in the Software without restriction, including without limitation
18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19  * and/or sell copies of the Software, and to permit persons to whom the
20  * Software is furnished to do so, subject to the following conditions:
21  *
22  * The above copyright notice and this permission notice (including the next
23  * paragraph) shall be included in all copies or substantial portions of the
24  * Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
29  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
30  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
31  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
32  * OTHER DEALINGS IN THE SOFTWARE.
33  *
34  * Authors:
35  *    Rickard E. (Rik) Faith <faith@valinux.com>
36  *    Gareth Hughes <gareth@valinux.com>
37  *
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 #include "drmP.h"
43 
44 int
45 drm_lock_held(void *info)
46 {
47 	__volatile__ unsigned int *lock;
48 
49 	lock = (__volatile__ unsigned int *)info;
50 	return (_DRM_LOCK_IS_HELD(*lock));
51 }
52 
53 int
54 drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
55 {
56 	unsigned int old, new;
57 
58 	do {
59 		old = *lock;
60 		if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT;
61 		else			  new = context | _DRM_LOCK_HELD;
62 	} while (!atomic_cmpset_int(lock, old, new));
63 
64 	if (_DRM_LOCKING_CONTEXT(old) == context) {
65 		if (old & _DRM_LOCK_HELD) {
66 			if (context != DRM_KERNEL_CONTEXT) {
67 				DRM_ERROR("%d holds heavyweight lock\n",
68 				    context);
69 			}
70 			return (0);
71 		}
72 	}
73 	if (new == (context | _DRM_LOCK_HELD)) {
74 				/* Have lock */
75 		return (1);
76 	}
77 	return (0);
78 }
79 
80 /*
81  * This takes a lock forcibly and hands it to context.	Should ONLY be used
82  * inside *_unlock to give lock to kernel before calling *_dma_schedule.
83  */
84 int
85 drm_lock_transfer(drm_device_t *dev, __volatile__ unsigned int *lock,
86     unsigned int context)
87 {
88 	unsigned int old, new;
89 
90 	dev->lock.filp = NULL;
91 	do {
92 		old  = *lock;
93 		new  = context | _DRM_LOCK_HELD;
94 	} while (!atomic_cmpset_int(lock, old, new));
95 
96 	return (1);
97 }
98 
99 int
100 drm_lock_free(drm_device_t *dev, __volatile__ unsigned int *lock,
101     unsigned int context)
102 {
103 	unsigned int old, new;
104 
105 	mutex_enter(&(dev->lock.lock_mutex));
106 	dev->lock.filp = NULL;
107 	do {
108 		old  = *lock;
109 		new  = 0;
110 	} while (!atomic_cmpset_int(lock, old, new));
111 
112 	if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
113 		DRM_ERROR("%d freed heavyweight lock held by %d\n",
114 		    context, _DRM_LOCKING_CONTEXT(old));
115 		return (1);
116 	}
117 	cv_broadcast(&(dev->lock.lock_cv));
118 	mutex_exit(&(dev->lock.lock_mutex));
119 	return (0);
120 }
121 
122 /*ARGSUSED*/
123 int
124 drm_lock(DRM_IOCTL_ARGS)
125 {
126 	DRM_DEVICE;
127 	drm_lock_t lock;
128 	int ret = 0;
129 
130 	DRM_COPY_FROM_USER_IOCTL(lock, (drm_lock_t *)data, sizeof (lock));
131 
132 	if (lock.context == DRM_KERNEL_CONTEXT) {
133 		return (DRM_ERR(EINVAL));
134 	}
135 
136 	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
137 	    lock.context, DRM_CURRENTPID, dev->lock.hw_lock->lock, lock.flags);
138 
139 	if (dev->use_dma_queue && lock.context < 0)
140 		return (DRM_ERR(EINVAL));
141 
142 	mutex_enter(&(dev->lock.lock_mutex));
143 	DRM_DEBUG("mutex enter\n");
144 	for (;;) {
145 		if (drm_lock_take(&dev->lock.hw_lock->lock, lock.context)) {
146 			DRM_DEBUG("drm_lock_take enter\n");
147 			dev->lock.filp = (void *)(uintptr_t)DRM_CURRENTPID;
148 			dev->lock.lock_time = jiffies;
149 			atomic_inc_32(&dev->counts[_DRM_STAT_LOCKS]);
150 			break;  /* Got lock */
151 		}
152 		DRM_DEBUG("Drm_lock: cv_wait\n");
153 		ret = cv_wait_sig(&(dev->lock.lock_cv),
154 		    &(dev->lock.lock_mutex));
155 
156 		if (ret == 0) {
157 			mutex_exit(&(dev->lock.lock_mutex));
158 			return (-1);
159 		}
160 	}
161 	DRM_DEBUG("before mutex_exit\n");
162 	mutex_exit(&(dev->lock.lock_mutex));
163 	DRM_DEBUG("drm_lock: %d %s\n", lock.context,
164 	    ret ? "interrupted" : "has lock");
165 
166 	if (dev->dma_quiescent != NULL &&
167 	    (lock.flags & _DRM_LOCK_QUIESCENT))
168 		dev->dma_quiescent(dev);
169 
170 	return (0);
171 }
172 
173 /*ARGSUSED*/
174 int
175 drm_unlock(DRM_IOCTL_ARGS)
176 {
177 	DRM_DEVICE;
178 	drm_lock_t lock;
179 
180 	DRM_COPY_FROM_USER_IOCTL(lock, (drm_lock_t *)data, sizeof (lock));
181 
182 	if (lock.context == DRM_KERNEL_CONTEXT) {
183 		DRM_ERROR("Process %d using kernel context %d\n",
184 		    DRM_CURRENTPID, lock.context);
185 		return (DRM_ERR(EINVAL));
186 	}
187 
188 	atomic_inc_32(&dev->counts[_DRM_STAT_UNLOCKS]);
189 
190 	DRM_LOCK();
191 	(void) drm_lock_transfer(dev, &dev->lock.hw_lock->lock,
192 	    DRM_KERNEL_CONTEXT);
193 
194 	if (drm_lock_free(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) {
195 		DRM_ERROR("drm_unlock\n");
196 	}
197 	DRM_UNLOCK();
198 
199 	return (0);
200 }
201