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