drm_lock.c revision 1.1.2.1 1 /* $NetBSD: drm_lock.c,v 1.1.2.1 2013/07/24 02:33:17 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * DRM lock. Each drm master has a lock to provide mutual exclusion
34 * for access to something about the hardware (XXX what?). The lock
35 * can be held by the kernel or by a drm file (XXX not by a process!
36 * and multiple processes may share a common drm file). (XXX What
37 * excludes different kthreads?) The DRM_LOCK ioctl grants the lock to
38 * the userland. The kernel may need to steal this lock in order to
39 * close a drm file; I believe drm_global_mutex excludes different
40 * kernel threads from doing this simultaneously.
41 *
42 * XXX This description is incoherent and incomplete.
43 */
44
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: drm_lock.c,v 1.1.2.1 2013/07/24 02:33:17 riastradh Exp $");
47
48 #include <sys/types.h>
49 #include <sys/errno.h>
50 #include <sys/systm.h>
51
52 #include <drm/drmP.h>
53
54 static bool drm_lock_acquire(struct drm_lock_data *, int);
55 static void drm_lock_release(struct drm_lock_data *, int);
56 static int drm_lock_block_signals(struct drm_device *, struct drm_lock *,
57 struct drm_file *);
58 static void drm_lock_unblock_signals(struct drm_device *,
59 struct drm_lock *, struct drm_file *);
60
61 /*
62 * Take the lock on behalf of userland.
63 */
64 int
65 drm_lock(struct drm_device *dev, void *data, struct drm_file *file)
66 {
67 struct drm_lock *lock_request = data;
68 struct drm_master *master = file->master;
69 bool acquired;
70 int error;
71
72 KASSERT(mutex_is_locked(&drm_global_mutex));
73
74 /* Refuse to lock on behalf of the kernel. */
75 if (lock_request->context == DRM_KERNEL_CONTEXT)
76 return -EINVAL;
77
78 /* Count it in the file and device statistics (XXX why here?). */
79 file->lock_count++;
80 atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
81
82 /* Wait until the hardware lock is gone or we can acquire it. */
83 spin_lock(&master->lock.spinlock);
84 DRM_SPIN_WAIT_UNTIL(error, &master->lock.lock_queue,
85 &master->lock.spinlock,
86 ((master->lock.hw_lock == NULL) ||
87 (acquired = drm_lock_acquire(&master->lock,
88 lock_request->context))));
89 if (error)
90 goto out;
91
92 /* If the lock is gone, give up. */
93 if (master->lock.hw_lock == NULL) {
94 #if 0 /* XXX Linux sends SIGTERM, but why? */
95 mutex_enter(proc_lock);
96 psignal(curproc, SIGTERM);
97 mutex_exit(proc_lock);
98 error = -EINTR;
99 #else
100 error = -ENXIO;
101 #endif
102 goto out;
103 }
104
105 /* Mark the lock as owned by file. */
106 master->lock.file_priv = file;
107 #ifndef __NetBSD__
108 master->lock.lock_time = jiffies; /* XXX Unused? */
109 #endif
110
111 /* Block signals while the lock is held. */
112 error = drm_lock_block_signals(dev, lock_request, file);
113 if (error)
114 goto fail0;
115
116 /* Enter the DMA quiescent state if requested and available. */
117 if (ISSET(lock_request->flags, _DRM_LOCK_QUIESCENT) &&
118 (dev->driver->dma_quiescent != NULL)) {
119 error = (*dev->driver->dma_quiescent)(dev);
120 if (error)
121 goto fail1;
122 }
123
124 /* Success! */
125 error = 0;
126 goto out;
127
128 fail1: drm_lock_unblock_signals(dev, lock_request, file);
129 fail0: drm_lock_release(&master->lock, lock_request->context);
130 master->lock.file_priv = NULL;
131 out: spin_unlock(&master->lock.spinlock);
132 return error;
133 }
134
135 /*
136 * Try to relinquish a lock that userland thinks it holds, per
137 * userland's request. Fail if it doesn't actually hold the lock.
138 */
139 int
140 drm_unlock(struct drm_device *dev, void *data, struct drm_file *file)
141 {
142 struct drm_lock *lock_request = data;
143 struct drm_master *master = file->master;
144 int error;
145
146 KASSERT(mutex_is_locked(&drm_global_mutex));
147
148 /* Refuse to unlock on behalf of the kernel. */
149 if (lock_request->context == DRM_KERNEL_CONTEXT)
150 return -EINVAL;
151
152 /* Count it in the device statistics. */
153 atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]);
154
155 /* Lock the internal spin lock to make changes. */
156 spin_lock(&master->lock.spinlock);
157
158 /* Make sure it's actually locked. */
159 if (!_DRM_LOCK_IS_HELD(master->lock.hw_lock->lock)) {
160 error = -EINVAL; /* XXX Right error? */
161 goto out;
162 }
163
164 /* Make sure it's locked in the right context. */
165 if (_DRM_LOCKING_CONTEXT(master->lock.hw_lock->lock) !=
166 lock_request->context) {
167 error = -EACCES; /* XXX Right error? */
168 goto out;
169 }
170
171 /* Make sure it's locked by us. */
172 if (master->lock.file_priv != file) {
173 error = -EACCES; /* XXX Right error? */
174 goto out;
175 }
176
177 /* Actually release the lock. */
178 drm_lock_release(&master->lock, lock_request->context);
179
180 /* Clear the lock's file pointer, just in case. */
181 master->lock.file_priv = NULL;
182
183 /* Unblock the signals we blocked in drm_lock. */
184 drm_lock_unblock_signals(dev, lock_request, file);
185
186 /* Success! */
187 error = 0;
188
189 out: spin_unlock(&master->lock.spinlock);
190 return error;
191 }
192
193 /*
194 * Take the lock for the kernel's use.
195 */
196 void
197 drm_idlelock_take(struct drm_lock_data *lock_data __unused)
198 {
199 KASSERT(mutex_is_locked(&drm_global_mutex));
200 panic("drm_idlelock_take is not yet implemented"); /* XXX */
201 }
202
203 /*
204 * Release the lock from the kernel.
205 */
206 void
207 drm_idlelock_release(struct drm_lock_data *lock_data __unused)
208 {
209 KASSERT(mutex_is_locked(&drm_global_mutex));
210 panic("drm_idlelock_release is not yet implemented"); /* XXX */
211 }
212
213 /*
214 * Try to acquire the lock. Return true if successful, false if not.
215 *
216 * Lock's spinlock must be held.
217 */
218 static bool
219 drm_lock_acquire(struct drm_lock_data *lock_data, int context)
220 {
221
222 KASSERT(spin_is_locked(&lock_data->spinlock));
223
224 /* Test and set. */
225 if (_DRM_LOCK_IS_HELD(lock_data->hw_lock->lock)) {
226 return false;
227 } else {
228 lock_data->hw_lock->lock = (context | _DRM_LOCK_HELD);
229 return true;
230 }
231 }
232
233 /*
234 * Release the lock held in the given context. Wake any waiters,
235 * preferring kernel waiters over userland waiters.
236 *
237 * Lock's spinlock must be held and lock must be held in this context.
238 */
239 static void
240 drm_lock_release(struct drm_lock_data *lock_data, int context)
241 {
242
243 (void)context; /* ignore */
244 KASSERT(spin_is_locked(&lock_data->spinlock));
245 KASSERT(_DRM_LOCK_IS_HELD(lock_data->hw_lock->lock));
246 KASSERT(_DRM_LOCKING_CONTEXT(lock_data->hw_lock->lock) == context);
247
248 /* Are there any kernel waiters? */
249 if (lock_data->n_kernel_waiters > 0) {
250 /* If there's a kernel waiter, grant it ownership. */
251 lock_data->hw_lock->lock = (DRM_KERNEL_CONTEXT |
252 _DRM_LOCK_HELD);
253 lock_data->n_kernel_waiters--;
254 DRM_SPIN_WAKEUP_ONE(&lock_data->kernel_lock_queue,
255 &lock_data->spinlock);
256 } else {
257 /* Otherwise, free it up and wake someone else. */
258 lock_data->hw_lock->lock = 0;
259 DRM_SPIN_WAKEUP_ONE(&lock_data->lock_queue,
260 &lock_data->spinlock);
261 }
262 }
263
264 /*
265 * Block signals for a process that holds a drm lock.
266 *
267 * XXX It's not processes but files that hold drm locks, so blocking
268 * signals in a process seems wrong, and it's not clear that blocking
269 * signals automatically is remotely sensible anyway.
270 */
271 static int
272 drm_lock_block_signals(struct drm_device *dev __unused,
273 struct drm_lock *lock_request __unused, struct drm_file *file __unused)
274 {
275 return 0;
276 }
277
278 /*
279 * Unblock the signals that drm_lock_block_signals blocked.
280 */
281 static void
282 drm_lock_unblock_signals(struct drm_device *dev __unused,
283 struct drm_lock *lock_request __unused, struct drm_file *file __unused)
284 {
285 }
286