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 <drm/drmP.h> 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_ctxbitmap_free(struct drm_device * dev, int ctx_handle) 60 { 61 mutex_lock(&dev->struct_mutex); 62 idr_remove(&dev->ctx_idr, ctx_handle); 63 mutex_unlock(&dev->struct_mutex); 64 } 65 66 /** 67 * Context bitmap allocation. 68 * 69 * \param dev DRM device. 70 * \return (non-negative) context handle on success or a negative number on failure. 71 * 72 * Allocate a new idr from drm_device::ctx_idr while holding the 73 * drm_device::struct_mutex lock. 74 */ 75 static int drm_ctxbitmap_next(struct drm_device * dev) 76 { 77 int ret; 78 79 mutex_lock(&dev->struct_mutex); 80 ret = idr_alloc(&dev->ctx_idr, NULL, DRM_RESERVED_CONTEXTS, 0, 81 GFP_KERNEL); 82 mutex_unlock(&dev->struct_mutex); 83 return ret; 84 } 85 86 /** 87 * Context bitmap initialization. 88 * 89 * \param dev DRM device. 90 * 91 * Initialise the drm_device::ctx_idr 92 */ 93 int drm_ctxbitmap_init(struct drm_device * dev) 94 { 95 idr_init(&dev->ctx_idr); 96 return 0; 97 } 98 99 /** 100 * Context bitmap cleanup. 101 * 102 * \param dev DRM device. 103 * 104 * Free all idr members using drm_ctx_sarea_free helper function 105 * while holding the drm_device::struct_mutex lock. 106 */ 107 void drm_ctxbitmap_cleanup(struct drm_device * dev) 108 { 109 mutex_lock(&dev->struct_mutex); 110 idr_destroy(&dev->ctx_idr); 111 mutex_unlock(&dev->struct_mutex); 112 } 113 114 /*@}*/ 115 116 /******************************************************************/ 117 /** \name Per Context SAREA Support */ 118 /*@{*/ 119 120 /** 121 * Get per-context SAREA. 122 * 123 * \param inode device inode. 124 * \param file_priv DRM file private. 125 * \param cmd command. 126 * \param arg user argument pointing to a drm_ctx_priv_map structure. 127 * \return zero on success or a negative number on failure. 128 * 129 * Gets the map from drm_device::ctx_idr with the handle specified and 130 * returns its handle. 131 */ 132 int drm_getsareactx(struct drm_device *dev, void *data, 133 struct drm_file *file_priv) 134 { 135 struct drm_ctx_priv_map *request = data; 136 struct drm_local_map *map; 137 struct drm_map_list *_entry; 138 139 mutex_lock(&dev->struct_mutex); 140 141 map = idr_find(&dev->ctx_idr, request->ctx_id); 142 if (!map) { 143 mutex_unlock(&dev->struct_mutex); 144 return -EINVAL; 145 } 146 147 request->handle = NULL; 148 list_for_each_entry(_entry, &dev->maplist, head) { 149 if (_entry->map == map) { 150 request->handle = 151 (void *)(unsigned long)_entry->user_token; 152 break; 153 } 154 } 155 156 mutex_unlock(&dev->struct_mutex); 157 158 if (request->handle == NULL) 159 return -EINVAL; 160 161 return 0; 162 } 163 164 /** 165 * Set per-context SAREA. 166 * 167 * \param inode device inode. 168 * \param file_priv DRM file private. 169 * \param cmd command. 170 * \param arg user argument pointing to a drm_ctx_priv_map structure. 171 * \return zero on success or a negative number on failure. 172 * 173 * Searches the mapping specified in \p arg and update the entry in 174 * drm_device::ctx_idr with it. 175 */ 176 int drm_setsareactx(struct drm_device *dev, void *data, 177 struct drm_file *file_priv) 178 { 179 struct drm_ctx_priv_map *request = data; 180 struct drm_local_map *map = NULL; 181 struct drm_map_list *r_list = NULL; 182 183 mutex_lock(&dev->struct_mutex); 184 list_for_each_entry(r_list, &dev->maplist, head) { 185 if (r_list->map 186 && r_list->user_token == (unsigned long) request->handle) 187 goto found; 188 } 189 bad: 190 mutex_unlock(&dev->struct_mutex); 191 return -EINVAL; 192 193 found: 194 map = r_list->map; 195 if (!map) 196 goto bad; 197 198 if (IS_ERR(idr_replace(&dev->ctx_idr, map, request->ctx_id))) 199 goto bad; 200 201 mutex_unlock(&dev->struct_mutex); 202 203 return 0; 204 } 205 206 /*@}*/ 207 208 /******************************************************************/ 209 /** \name The actual DRM context handling routines */ 210 /*@{*/ 211 212 /** 213 * Switch context. 214 * 215 * \param dev DRM device. 216 * \param old old context handle. 217 * \param new new context handle. 218 * \return zero on success or a negative number on failure. 219 * 220 * Attempt to set drm_device::context_flag. 221 */ 222 static int drm_context_switch(struct drm_device * dev, int old, int new) 223 { 224 if (test_and_set_bit(0, &dev->context_flag)) { 225 DRM_ERROR("Reentering -- FIXME\n"); 226 return -EBUSY; 227 } 228 229 DRM_DEBUG("Context switch from %d to %d\n", old, new); 230 231 if (new == dev->last_context) { 232 clear_bit(0, &dev->context_flag); 233 return 0; 234 } 235 236 return 0; 237 } 238 239 /** 240 * Complete context switch. 241 * 242 * \param dev DRM device. 243 * \param new new context handle. 244 * \return zero on success or a negative number on failure. 245 * 246 * Updates drm_device::last_context and drm_device::last_switch. Verifies the 247 * hardware lock is held, clears the drm_device::context_flag and wakes up 248 * drm_device::context_wait. 249 */ 250 static int drm_context_switch_complete(struct drm_device *dev, 251 struct drm_file *file_priv, int new) 252 { 253 dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ 254 255 if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) { 256 DRM_ERROR("Lock isn't held after context switch\n"); 257 } 258 259 /* If a context switch is ever initiated 260 when the kernel holds the lock, release 261 that lock here. */ 262 clear_bit(0, &dev->context_flag); 263 264 return 0; 265 } 266 267 /** 268 * Reserve contexts. 269 * 270 * \param inode device inode. 271 * \param file_priv DRM file private. 272 * \param cmd command. 273 * \param arg user argument pointing to a drm_ctx_res structure. 274 * \return zero on success or a negative number on failure. 275 */ 276 int drm_resctx(struct drm_device *dev, void *data, 277 struct drm_file *file_priv) 278 { 279 struct drm_ctx_res *res = data; 280 struct drm_ctx ctx; 281 int i; 282 283 if (res->count >= DRM_RESERVED_CONTEXTS) { 284 memset(&ctx, 0, sizeof(ctx)); 285 for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { 286 ctx.handle = i; 287 if (copy_to_user(&res->contexts[i], &ctx, sizeof(ctx))) 288 return -EFAULT; 289 } 290 } 291 res->count = DRM_RESERVED_CONTEXTS; 292 293 return 0; 294 } 295 296 /** 297 * Add context. 298 * 299 * \param inode device inode. 300 * \param file_priv DRM file private. 301 * \param cmd command. 302 * \param arg user argument pointing to a drm_ctx structure. 303 * \return zero on success or a negative number on failure. 304 * 305 * Get a new handle for the context and copy to userspace. 306 */ 307 int drm_addctx(struct drm_device *dev, void *data, 308 struct drm_file *file_priv) 309 { 310 struct drm_ctx_list *ctx_entry; 311 struct drm_ctx *ctx = data; 312 313 ctx->handle = drm_ctxbitmap_next(dev); 314 if (ctx->handle == DRM_KERNEL_CONTEXT) { 315 /* Skip kernel's context and get a new one. */ 316 ctx->handle = drm_ctxbitmap_next(dev); 317 } 318 DRM_DEBUG("%d\n", ctx->handle); 319 if (ctx->handle == -1) { 320 DRM_DEBUG("Not enough free contexts.\n"); 321 /* Should this return -EBUSY instead? */ 322 return -ENOMEM; 323 } 324 325 ctx_entry = kmalloc(sizeof(*ctx_entry), GFP_KERNEL); 326 if (!ctx_entry) { 327 DRM_DEBUG("out of memory\n"); 328 return -ENOMEM; 329 } 330 331 INIT_LIST_HEAD(&ctx_entry->head); 332 ctx_entry->handle = ctx->handle; 333 ctx_entry->tag = file_priv; 334 335 mutex_lock(&dev->ctxlist_mutex); 336 list_add(&ctx_entry->head, &dev->ctxlist); 337 mutex_unlock(&dev->ctxlist_mutex); 338 339 return 0; 340 } 341 342 /** 343 * Get context. 344 * 345 * \param inode device inode. 346 * \param file_priv DRM file private. 347 * \param cmd command. 348 * \param arg user argument pointing to a drm_ctx structure. 349 * \return zero on success or a negative number on failure. 350 */ 351 int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv) 352 { 353 struct drm_ctx *ctx = data; 354 355 /* This is 0, because we don't handle any context flags */ 356 ctx->flags = 0; 357 358 return 0; 359 } 360 361 /** 362 * Switch context. 363 * 364 * \param inode device inode. 365 * \param file_priv DRM file private. 366 * \param cmd command. 367 * \param arg user argument pointing to a drm_ctx structure. 368 * \return zero on success or a negative number on failure. 369 * 370 * Calls context_switch(). 371 */ 372 int drm_switchctx(struct drm_device *dev, void *data, 373 struct drm_file *file_priv) 374 { 375 struct drm_ctx *ctx = data; 376 377 DRM_DEBUG("%d\n", ctx->handle); 378 return drm_context_switch(dev, dev->last_context, ctx->handle); 379 } 380 381 /** 382 * New context. 383 * 384 * \param inode device inode. 385 * \param file_priv DRM file private. 386 * \param cmd command. 387 * \param arg user argument pointing to a drm_ctx structure. 388 * \return zero on success or a negative number on failure. 389 * 390 * Calls context_switch_complete(). 391 */ 392 int drm_newctx(struct drm_device *dev, void *data, 393 struct drm_file *file_priv) 394 { 395 struct drm_ctx *ctx = data; 396 397 DRM_DEBUG("%d\n", ctx->handle); 398 drm_context_switch_complete(dev, file_priv, ctx->handle); 399 400 return 0; 401 } 402 403 /** 404 * Remove context. 405 * 406 * \param inode device inode. 407 * \param file_priv DRM file private. 408 * \param cmd command. 409 * \param arg user argument pointing to a drm_ctx structure. 410 * \return zero on success or a negative number on failure. 411 * 412 * If not the special kernel context, calls ctxbitmap_free() to free the specified context. 413 */ 414 int drm_rmctx(struct drm_device *dev, void *data, 415 struct drm_file *file_priv) 416 { 417 struct drm_ctx *ctx = data; 418 419 DRM_DEBUG("%d\n", ctx->handle); 420 if (ctx->handle != DRM_KERNEL_CONTEXT) { 421 if (dev->driver->context_dtor) 422 dev->driver->context_dtor(dev, ctx->handle); 423 drm_ctxbitmap_free(dev, ctx->handle); 424 } 425 426 mutex_lock(&dev->ctxlist_mutex); 427 if (!list_empty(&dev->ctxlist)) { 428 struct drm_ctx_list *pos, *n; 429 430 list_for_each_entry_safe(pos, n, &dev->ctxlist, head) { 431 if (pos->handle == ctx->handle) { 432 list_del(&pos->head); 433 kfree(pos); 434 } 435 } 436 } 437 mutex_unlock(&dev->ctxlist_mutex); 438 439 return 0; 440 } 441 442 /*@}*/ 443