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