xref: /titanic_44/usr/src/uts/common/io/drm/drm_lock.c (revision 21bf64a78855d076f09716ea1c06175d954e934c)
1 /*
2  * Copyright 2008 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_take(drm_lock_data_t *lock_data, unsigned int context)
46 {
47 	unsigned int old, new, prev;
48 	volatile unsigned int *lock = &lock_data->hw_lock->lock;
49 
50 	do {
51 		old = *lock;
52 		if (old & _DRM_LOCK_HELD)
53 			new = old | _DRM_LOCK_CONT;
54 		else
55 			new = context | _DRM_LOCK_HELD;
56 		prev = atomic_cas_uint(lock, old, new);
57 	} while (prev != old);
58 
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 ((_DRM_LOCKING_CONTEXT(new)) == context &&
69 	    _DRM_LOCK_IS_HELD(new)) {
70 				/* Have lock */
71 		return (1);
72 	}
73 	return (0);
74 }
75 
76 /*
77  * This takes a lock forcibly and hands it to context.	Should ONLY be used
78  * inside *_unlock to give lock to kernel before calling *_dma_schedule.
79  */
80 int
81 drm_lock_transfer(drm_device_t *dev, volatile unsigned int *lock,
82     unsigned int context)
83 {
84 	unsigned int old, new, prev;
85 
86 	dev->lock.filp = NULL;
87 	do {
88 		old  = *lock;
89 		new  = context | _DRM_LOCK_HELD;
90 		prev = atomic_cas_uint(lock, old, new);
91 	} while (prev != old);
92 
93 	return (1);
94 }
95 
96 int
97 drm_lock_free(drm_device_t *dev, volatile unsigned int *lock,
98     unsigned int context)
99 {
100 	unsigned int old, new, prev;
101 
102 	mutex_enter(&(dev->lock.lock_mutex));
103 	dev->lock.filp = NULL;
104 	do {
105 		old  = *lock;
106 		new = 0;
107 		prev = atomic_cas_uint(lock, old, new);
108 	} while (prev != old);
109 
110 	if (_DRM_LOCK_IS_HELD(old) &&
111 	    (_DRM_LOCKING_CONTEXT(old) != context)) {
112 		DRM_ERROR("%d freed heavyweight lock held by %d\n",
113 		    context, _DRM_LOCKING_CONTEXT(old));
114 		mutex_exit(&(dev->lock.lock_mutex));
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_COPYFROM_WITH_RETURN(&lock, (void *)data, sizeof (lock));
131 
132 	if (lock.context == DRM_KERNEL_CONTEXT) {
133 		return (EINVAL);
134 	}
135 
136 	if (dev->driver->use_dma_queue && lock.context < 0)
137 		return (EINVAL);
138 
139 	mutex_enter(&(dev->lock.lock_mutex));
140 	for (;;) {
141 		if (drm_lock_take(&dev->lock, lock.context)) {
142 			dev->lock.filp = fpriv;
143 			dev->lock.lock_time = jiffies;
144 			break;  /* Got lock */
145 		}
146 		ret = cv_wait_sig(&(dev->lock.lock_cv),
147 		    &(dev->lock.lock_mutex));
148 
149 		if (ret == 0) {
150 			mutex_exit(&(dev->lock.lock_mutex));
151 			return (EINTR);
152 		}
153 	}
154 	mutex_exit(&(dev->lock.lock_mutex));
155 
156 	if (dev->driver->dma_quiescent != NULL &&
157 	    (lock.flags & _DRM_LOCK_QUIESCENT))
158 		dev->driver->dma_quiescent(dev);
159 
160 	return (0);
161 }
162 
163 /*ARGSUSED*/
164 int
165 drm_unlock(DRM_IOCTL_ARGS)
166 {
167 	DRM_DEVICE;
168 	drm_lock_t lock;
169 
170 	DRM_COPYFROM_WITH_RETURN(&lock, (void *)data, sizeof (lock));
171 
172 	if (lock.context == DRM_KERNEL_CONTEXT) {
173 		DRM_ERROR("Process %d using kernel context %d\n",
174 		    DRM_CURRENTPID, lock.context);
175 		return (EINVAL);
176 	}
177 	atomic_inc_32(&dev->counts[_DRM_STAT_UNLOCKS]);
178 
179 	DRM_LOCK();
180 	if (drm_lock_free(dev, &dev->lock.hw_lock->lock, lock.context)) {
181 		DRM_ERROR("drm_unlock\n");
182 	}
183 	DRM_UNLOCK();
184 	return (0);
185 }
186