xref: /titanic_44/usr/src/uts/common/io/drm/drm_lock.c (revision 1e4c938b57d1656808e4112127ff1dce3eba5314)
1 /*
2  * Copyright 2009 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 #include "drmP.h"
41 
42 int
43 drm_lock_take(drm_lock_data_t *lock_data, unsigned int context)
44 {
45 	unsigned int old, new;
46 	volatile unsigned int *lock = &lock_data->hw_lock->lock;
47 
48 	do {
49 		old = *lock;
50 		if (old & _DRM_LOCK_HELD)
51 			new = old | _DRM_LOCK_CONT;
52 		else
53 			new = context | _DRM_LOCK_HELD;
54 	} while (!atomic_cmpset_int(lock, old, new));
55 
56 	if (_DRM_LOCKING_CONTEXT(old) == context) {
57 		if (old & _DRM_LOCK_HELD) {
58 			if (context != DRM_KERNEL_CONTEXT) {
59 				DRM_ERROR("%d holds heavyweight lock\n",
60 				    context);
61 			}
62 			return (0);
63 		}
64 	}
65 	if (new == (context | _DRM_LOCK_HELD)) {
66 		/* Have lock */
67 		return (1);
68 	}
69 	return (0);
70 }
71 
72 /*
73  * This takes a lock forcibly and hands it to context.	Should ONLY be used
74  * inside *_unlock to give lock to kernel before calling *_dma_schedule.
75  */
76 int
77 drm_lock_transfer(drm_device_t *dev, drm_lock_data_t *lock_data,
78 			unsigned int context)
79 {
80 	unsigned int old, new;
81 	volatile unsigned int *lock = &lock_data->hw_lock->lock;
82 
83 	dev->lock.filp = NULL;
84 	do {
85 		old  = *lock;
86 		new  = context | _DRM_LOCK_HELD;
87 	} while (!atomic_cmpset_int(lock, old, new));
88 
89 	return (1);
90 }
91 
92 int
93 drm_lock_free(drm_device_t *dev, volatile unsigned int *lock,
94     unsigned int context)
95 {
96 	unsigned int old, new;
97 
98 	mutex_enter(&(dev->lock.lock_mutex));
99 	dev->lock.filp = NULL;
100 	do {
101 		old  = *lock;
102 		new = 0;
103 	} while (!atomic_cmpset_int(lock, old, new));
104 
105 	if (_DRM_LOCK_IS_HELD(old) &&
106 	    (_DRM_LOCKING_CONTEXT(old) != context)) {
107 		DRM_ERROR("%d freed heavyweight lock held by %d\n",
108 		    context, _DRM_LOCKING_CONTEXT(old));
109 		mutex_exit(&(dev->lock.lock_mutex));
110 		return (1);
111 	}
112 	cv_broadcast(&(dev->lock.lock_cv));
113 	mutex_exit(&(dev->lock.lock_mutex));
114 	return (0);
115 }
116 
117 /*ARGSUSED*/
118 int
119 drm_lock(DRM_IOCTL_ARGS)
120 {
121 	DRM_DEVICE;
122 	drm_lock_t lock;
123 	int ret = 0;
124 
125 	DRM_COPYFROM_WITH_RETURN(&lock, (void *)data, sizeof (lock));
126 
127 	if (lock.context == DRM_KERNEL_CONTEXT) {
128 		DRM_ERROR("Process %d using kernel context %d\n",
129 		    DRM_CURRENTPID, lock.context);
130 		return (EINVAL);
131 	}
132 
133 	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
134 	    lock.context, DRM_CURRENTPID, dev->lock.hw_lock->lock,
135 	    lock.flags);
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 = ddi_get_lbolt();
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 	DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
156 
157 	if (dev->driver->dma_quiescent != NULL &&
158 	    (lock.flags & _DRM_LOCK_QUIESCENT))
159 		dev->driver->dma_quiescent(dev);
160 
161 	return (0);
162 }
163 
164 /*ARGSUSED*/
165 int
166 drm_unlock(DRM_IOCTL_ARGS)
167 {
168 	DRM_DEVICE;
169 	drm_lock_t lock;
170 
171 	DRM_COPYFROM_WITH_RETURN(&lock, (void *)data, sizeof (lock));
172 
173 	DRM_DEBUG("%d (pid %d) requests unlock (0x%08x), flags = 0x%08x\n",
174 	    lock.context, DRM_CURRENTPID, dev->lock.hw_lock->lock,
175 	    lock.flags);
176 
177 	if (lock.context == DRM_KERNEL_CONTEXT) {
178 		DRM_ERROR("Process %d using kernel context %d\n",
179 		    DRM_CURRENTPID, lock.context);
180 		return (EINVAL);
181 	}
182 	atomic_inc_32(&dev->counts[_DRM_STAT_UNLOCKS]);
183 
184 	DRM_LOCK();
185 	if (drm_lock_free(dev, &dev->lock.hw_lock->lock, lock.context)) {
186 		DRM_ERROR("drm_unlock\n");
187 	}
188 	DRM_UNLOCK();
189 	return (0);
190 }
191