1/**
2 * \file drm_context.c
3 * IOCTLs for generic contexts
4 *
5 * \author Rickard E. (Rik) Faith <faith@valinux.com>
6 * \author Gareth Hughes <gareth@valinux.com>
7 */
8
9/*
10 * Created: Fri Nov 24 18:31:37 2000 by gareth@valinux.com
11 *
12 * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
13 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
14 * All Rights Reserved.
15 *
16 * Permission is hereby granted, free of charge, to any person obtaining a
17 * copy of this software and associated documentation files (the "Software"),
18 * to deal in the Software without restriction, including without limitation
19 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20 * and/or sell copies of the Software, and to permit persons to whom the
21 * Software is furnished to do so, subject to the following conditions:
22 *
23 * The above copyright notice and this permission notice (including the next
24 * paragraph) shall be included in all copies or substantial portions of the
25 * Software.
26 *
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
30 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
31 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
32 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33 * OTHER DEALINGS IN THE SOFTWARE.
34 */
35
36/*
37 * ChangeLog:
38 * 2001-11-16 Torsten Duwe <duwe@caldera.de>
39 * added context constructor/destructor hooks,
40 * needed by SiS driver's memory management.
41 */
42
43#include <linux/err.h>
44
45#include <drm/drmP.h>
46
47/******************************************************************/
48/** \name Context bitmap support */
49/*@{*/
50
51/**
52 * Free a handle from the context bitmap.
53 *
54 * \param dev DRM device.
55 * \param ctx_handle context handle.
56 *
57 * Clears the bit specified by \p ctx_handle in drm_device::ctx_bitmap and the entry
58 * in drm_device::ctx_idr, while holding the drm_device::struct_mutex
59 * lock.
60 */
61void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
62{
63 mutex_lock(&dev->struct_mutex);
64 idr_remove(&dev->ctx_idr, ctx_handle);
65 mutex_unlock(&dev->struct_mutex);
66}
67
68/**
69 * Context bitmap allocation.
70 *
71 * \param dev DRM device.
72 * \return (non-negative) context handle on success or a negative number on failure.
73 *
74 * Allocate a new idr from drm_device::ctx_idr while holding the
75 * drm_device::struct_mutex lock.
76 */
77static int drm_ctxbitmap_next(struct drm_device * dev)
78{
79 int ret;
80
81 mutex_lock(&dev->struct_mutex);
82 ret = idr_alloc(&dev->ctx_idr, NULL, DRM_RESERVED_CONTEXTS, 0,
83 GFP_KERNEL);
84 mutex_unlock(&dev->struct_mutex);
85 return ret;
86}
87
88/**
89 * Context bitmap initialization.
90 *
91 * \param dev DRM device.
92 *
93 * Initialise the drm_device::ctx_idr
94 */
95int drm_ctxbitmap_init(struct drm_device * dev)
96{
97 idr_init(&dev->ctx_idr);
98 return 0;
99}
100
101/**
102 * Context bitmap cleanup.
103 *
104 * \param dev DRM device.
105 *
106 * Free all idr members using drm_ctx_sarea_free helper function
107 * while holding the drm_device::struct_mutex lock.
108 */
109void drm_ctxbitmap_cleanup(struct drm_device * dev)
110{
111 mutex_lock(&dev->struct_mutex);
112 idr_destroy(&dev->ctx_idr);
113 mutex_unlock(&dev->struct_mutex);
114}
115
116/*@}*/
117
118/******************************************************************/
119/** \name Per Context SAREA Support */
120/*@{*/
121
122/**
123 * Get per-context SAREA.
124 *
125 * \param inode device inode.
126 * \param file_priv DRM file private.
127 * \param cmd command.
128 * \param arg user argument pointing to a drm_ctx_priv_map structure.
129 * \return zero on success or a negative number on failure.
130 *
131 * Gets the map from drm_device::ctx_idr with the handle specified and
132 * returns its handle.
133 */
134int drm_getsareactx(struct drm_device *dev, void *data,
135 struct drm_file *file_priv)
136{
137 struct drm_ctx_priv_map *request = data;
138 struct drm_local_map *map;
139 struct drm_map_list *_entry;
140
141 mutex_lock(&dev->struct_mutex);
142
143 map = idr_find(&dev->ctx_idr, request->ctx_id);
144 if (!map) {
145 mutex_unlock(&dev->struct_mutex);
146 return -EINVAL;
147 }
148
149 request->handle = NULL;
150 list_for_each_entry(_entry, &dev->maplist, head) {
151 if (_entry->map == map) {
152 request->handle =
153 (void *)(unsigned long)_entry->user_token;
154 break;
155 }
156 }
157
158 mutex_unlock(&dev->struct_mutex);
159
160 if (request->handle == NULL)
161 return -EINVAL;
162
163 return 0;
164}
165
166/**
167 * Set per-context SAREA.
168 *
169 * \param inode device inode.
170 * \param file_priv DRM file private.
171 * \param cmd command.
172 * \param arg user argument pointing to a drm_ctx_priv_map structure.
173 * \return zero on success or a negative number on failure.
174 *
175 * Searches the mapping specified in \p arg and update the entry in
176 * drm_device::ctx_idr with it.
177 */
178int drm_setsareactx(struct drm_device *dev, void *data,
179 struct drm_file *file_priv)
180{
181 struct drm_ctx_priv_map *request = data;
182 struct drm_local_map *map = NULL;
183 struct drm_map_list *r_list = NULL;
184
185 mutex_lock(&dev->struct_mutex);
186 list_for_each_entry(r_list, &dev->maplist, head) {
187 if (r_list->map
188 && r_list->user_token == (unsigned long) request->handle)
189 goto found;
190 }
191 bad:
192 mutex_unlock(&dev->struct_mutex);
193 return -EINVAL;
194
195 found:
196 map = r_list->map;
197 if (!map)
198 goto bad;
199
200 if (IS_ERR(idr_replace(&dev->ctx_idr, map, request->ctx_id)))
201 goto bad;
202
203 mutex_unlock(&dev->struct_mutex);
204
205 return 0;
206}
207
208/*@}*/
209
210/******************************************************************/
211/** \name The actual DRM context handling routines */
212/*@{*/
213
214/**
215 * Switch context.
216 *
217 * \param dev DRM device.
218 * \param old old context handle.
219 * \param new new context handle.
220 * \return zero on success or a negative number on failure.
221 *
222 * Attempt to set drm_device::context_flag.
223 */
224static int drm_context_switch(struct drm_device * dev, int old, int new)
225{
226 if (test_and_set_bit(0, &dev->context_flag)) {
227 DRM_ERROR("Reentering -- FIXME\n");
228 return -EBUSY;
229 }
230
231 DRM_DEBUG("Context switch from %d to %d\n", old, new);
232
233 if (new == dev->last_context) {
234 clear_bit(0, &dev->context_flag);
235 return 0;
236 }
237
238 return 0;
239}
240
241/**
242 * Complete context switch.
243 *
244 * \param dev DRM device.
245 * \param new new context handle.
246 * \return zero on success or a negative number on failure.
247 *
248 * Updates drm_device::last_context and drm_device::last_switch. Verifies the
249 * hardware lock is held, clears the drm_device::context_flag and wakes up
250 * drm_device::context_wait.
251 */
252static int drm_context_switch_complete(struct drm_device *dev,
253 struct drm_file *file_priv, int new)
254{
255 dev->last_context = new; /* PRE/POST: This is the _only_ writer. */
256
257 spin_lock(&file_priv->master->lock.spinlock);
258 if (file_priv->master->lock.hw_lock == NULL ||
259 !_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) {
260 DRM_ERROR("Lock isn't held after context switch\n");
261 }
262 spin_unlock(&file_priv->master->lock.spinlock);
263
264 /* If a context switch is ever initiated
265 when the kernel holds the lock, release
266 that lock here. */
267 clear_bit(0, &dev->context_flag);
268
269 return 0;
270}
271
272/**
273 * Reserve contexts.
274 *
275 * \param inode device inode.
276 * \param file_priv DRM file private.
277 * \param cmd command.
278 * \param arg user argument pointing to a drm_ctx_res structure.
279 * \return zero on success or a negative number on failure.
280 */
281int drm_resctx(struct drm_device *dev, void *data,
282 struct drm_file *file_priv)
283{
284 struct drm_ctx_res *res = data;
285 struct drm_ctx ctx;
286 int i;
287
288 if (res->count >= DRM_RESERVED_CONTEXTS) {
289 memset(&ctx, 0, sizeof(ctx));
290 for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
291 ctx.handle = i;
292 if (copy_to_user(&res->contexts[i], &ctx, sizeof(ctx)))
293 return -EFAULT;
294 }
295 }
296 res->count = DRM_RESERVED_CONTEXTS;
297
298 return 0;
299}
300
301/**
302 * Add context.
303 *
304 * \param inode device inode.
305 * \param file_priv DRM file private.
306 * \param cmd command.
307 * \param arg user argument pointing to a drm_ctx structure.
308 * \return zero on success or a negative number on failure.
309 *
310 * Get a new handle for the context and copy to userspace.
311 */
312int drm_addctx(struct drm_device *dev, void *data,
313 struct drm_file *file_priv)
314{
315 struct drm_ctx_list *ctx_entry;
316 struct drm_ctx *ctx = data;
317
318 ctx->handle = drm_ctxbitmap_next(dev);
319 if (ctx->handle == DRM_KERNEL_CONTEXT) {
320 /* Skip kernel's context and get a new one. */
321 ctx->handle = drm_ctxbitmap_next(dev);
322 }
323 DRM_DEBUG("%d\n", ctx->handle);
324 if (ctx->handle == -1) {
325 DRM_DEBUG("Not enough free contexts.\n");
326 /* Should this return -EBUSY instead? */
327 return -ENOMEM;
328 }
329
330 ctx_entry = kmalloc(sizeof(*ctx_entry), GFP_KERNEL);
331 if (!ctx_entry) {
332 DRM_DEBUG("out of memory\n");
333 return -ENOMEM;
334 }
335
336 INIT_LIST_HEAD(&ctx_entry->head);
337 ctx_entry->handle = ctx->handle;
338 ctx_entry->tag = file_priv;
339
340 mutex_lock(&dev->ctxlist_mutex);
341 list_add(&ctx_entry->head, &dev->ctxlist);
342 mutex_unlock(&dev->ctxlist_mutex);
343
344 return 0;
345}
346
347/**
348 * Get context.
349 *
350 * \param inode device inode.
351 * \param file_priv DRM file private.
352 * \param cmd command.
353 * \param arg user argument pointing to a drm_ctx structure.
354 * \return zero on success or a negative number on failure.
355 */
356int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv)
357{
358 struct drm_ctx *ctx = data;
359
360 /* This is 0, because we don't handle any context flags */
361 ctx->flags = 0;
362
363 return 0;
364}
365
366/**
367 * Switch context.
368 *
369 * \param inode device inode.
370 * \param file_priv DRM file private.
371 * \param cmd command.
372 * \param arg user argument pointing to a drm_ctx structure.
373 * \return zero on success or a negative number on failure.
374 *
375 * Calls context_switch().
376 */
377int drm_switchctx(struct drm_device *dev, void *data,
378 struct drm_file *file_priv)
379{
380 struct drm_ctx *ctx = data;
381
382 DRM_DEBUG("%d\n", ctx->handle);
383 return drm_context_switch(dev, dev->last_context, ctx->handle);
384}
385
386/**
387 * New context.
388 *
389 * \param inode device inode.
390 * \param file_priv DRM file private.
391 * \param cmd command.
392 * \param arg user argument pointing to a drm_ctx structure.
393 * \return zero on success or a negative number on failure.
394 *
395 * Calls context_switch_complete().
396 */
397int drm_newctx(struct drm_device *dev, void *data,
398 struct drm_file *file_priv)
399{
400 struct drm_ctx *ctx = data;
401
402 DRM_DEBUG("%d\n", ctx->handle);
403 drm_context_switch_complete(dev, file_priv, ctx->handle);
404
405 return 0;
406}
407
408/**
409 * Remove context.
410 *
411 * \param inode device inode.
412 * \param file_priv DRM file private.
413 * \param cmd command.
414 * \param arg user argument pointing to a drm_ctx structure.
415 * \return zero on success or a negative number on failure.
416 *
417 * If not the special kernel context, calls ctxbitmap_free() to free the specified context.
418 */
419int drm_rmctx(struct drm_device *dev, void *data,
420 struct drm_file *file_priv)
421{
422 struct drm_ctx *ctx = data;
423
424 DRM_DEBUG("%d\n", ctx->handle);
425 if (ctx->handle != DRM_KERNEL_CONTEXT) {
426 if (dev->driver->context_dtor)
427 dev->driver->context_dtor(dev, ctx->handle);
428 drm_ctxbitmap_free(dev, ctx->handle);
429 }
430
431 mutex_lock(&dev->ctxlist_mutex);
432 if (!list_empty(&dev->ctxlist)) {
433 struct drm_ctx_list *pos, *n;
434
435 list_for_each_entry_safe(pos, n, &dev->ctxlist, head) {
436 if (pos->handle == ctx->handle) {
437 list_del(&pos->head);
438 kfree(pos);
439 }
440 }
441 }
442 mutex_unlock(&dev->ctxlist_mutex);
443
444 return 0;
445}
446
447/*@}*/
448