linux-bk/drivers/char/drm/drm_context.c
<<
>>
Prefs
   1/**
   2 * \file drm_context.h 
   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 "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::context_sareas, while holding the drm_device::struct_sem
  57 * lock.
  58 */
  59void drm_ctxbitmap_free( drm_device_t *dev, int ctx_handle )
  60{
  61        if ( ctx_handle < 0 ) goto failed;
  62        if ( !dev->ctx_bitmap ) goto failed;
  63
  64        if ( ctx_handle < DRM_MAX_CTXBITMAP ) {
  65                down(&dev->struct_sem);
  66                clear_bit( ctx_handle, dev->ctx_bitmap );
  67                dev->context_sareas[ctx_handle] = NULL;
  68                up(&dev->struct_sem);
  69                return;
  70        }
  71failed:
  72        DRM_ERROR( "Attempt to free invalid context handle: %d\n",
  73                   ctx_handle );
  74        return;
  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 * Find the first zero bit in drm_device::ctx_bitmap and (re)allocates
  84 * drm_device::context_sareas to accommodate the new entry while holding the
  85 * drm_device::struct_sem lock.
  86 */
  87int drm_ctxbitmap_next( drm_device_t *dev )
  88{
  89        int bit;
  90
  91        if(!dev->ctx_bitmap) return -1;
  92
  93        down(&dev->struct_sem);
  94        bit = find_first_zero_bit( dev->ctx_bitmap, DRM_MAX_CTXBITMAP );
  95        if ( bit < DRM_MAX_CTXBITMAP ) {
  96                set_bit( bit, dev->ctx_bitmap );
  97                DRM_DEBUG( "drm_ctxbitmap_next bit : %d\n", bit );
  98                if((bit+1) > dev->max_context) {
  99                        dev->max_context = (bit+1);
 100                        if(dev->context_sareas) {
 101                                drm_map_t **ctx_sareas;
 102
 103                                ctx_sareas = drm_realloc(dev->context_sareas,
 104                                                (dev->max_context - 1) * 
 105                                                sizeof(*dev->context_sareas),
 106                                                dev->max_context * 
 107                                                sizeof(*dev->context_sareas),
 108                                                DRM_MEM_MAPS);
 109                                if(!ctx_sareas) {
 110                                        clear_bit(bit, dev->ctx_bitmap);
 111                                        up(&dev->struct_sem);
 112                                        return -1;
 113                                }
 114                                dev->context_sareas = ctx_sareas;
 115                                dev->context_sareas[bit] = NULL;
 116                        } else {
 117                                /* max_context == 1 at this point */
 118                                dev->context_sareas = drm_alloc(
 119                                                dev->max_context * 
 120                                                sizeof(*dev->context_sareas),
 121                                                DRM_MEM_MAPS);
 122                                if(!dev->context_sareas) {
 123                                        clear_bit(bit, dev->ctx_bitmap);
 124                                        up(&dev->struct_sem);
 125                                        return -1;
 126                                }
 127                                dev->context_sareas[bit] = NULL;
 128                        }
 129                }
 130                up(&dev->struct_sem);
 131                return bit;
 132        }
 133        up(&dev->struct_sem);
 134        return -1;
 135}
 136
 137/**
 138 * Context bitmap initialization.
 139 *
 140 * \param dev DRM device.
 141 *
 142 * Allocates and initialize drm_device::ctx_bitmap and drm_device::context_sareas, while holding
 143 * the drm_device::struct_sem lock.
 144 */
 145int drm_ctxbitmap_init( drm_device_t *dev )
 146{
 147        int i;
 148        int temp;
 149
 150        down(&dev->struct_sem);
 151        dev->ctx_bitmap = (unsigned long *) drm_alloc( PAGE_SIZE,
 152                                                        DRM_MEM_CTXBITMAP );
 153        if ( dev->ctx_bitmap == NULL ) {
 154                up(&dev->struct_sem);
 155                return -ENOMEM;
 156        }
 157        memset( (void *)dev->ctx_bitmap, 0, PAGE_SIZE );
 158        dev->context_sareas = NULL;
 159        dev->max_context = -1;
 160        up(&dev->struct_sem);
 161
 162        for ( i = 0 ; i < DRM_RESERVED_CONTEXTS ; i++ ) {
 163                temp = drm_ctxbitmap_next( dev );
 164                DRM_DEBUG( "drm_ctxbitmap_init : %d\n", temp );
 165        }
 166
 167        return 0;
 168}
 169
 170/**
 171 * Context bitmap cleanup.
 172 *
 173 * \param dev DRM device.
 174 *
 175 * Frees drm_device::ctx_bitmap and drm_device::context_sareas, while holding
 176 * the drm_device::struct_sem lock.
 177 */
 178void drm_ctxbitmap_cleanup( drm_device_t *dev )
 179{
 180        down(&dev->struct_sem);
 181        if( dev->context_sareas ) drm_free( dev->context_sareas,
 182                                             sizeof(*dev->context_sareas) * 
 183                                             dev->max_context,
 184                                             DRM_MEM_MAPS );
 185        drm_free( (void *)dev->ctx_bitmap, PAGE_SIZE, DRM_MEM_CTXBITMAP );
 186        up(&dev->struct_sem);
 187}
 188
 189/*@}*/
 190
 191/******************************************************************/
 192/** \name Per Context SAREA Support */
 193/*@{*/
 194
 195/**
 196 * Get per-context SAREA.
 197 * 
 198 * \param inode device inode.
 199 * \param filp file pointer.
 200 * \param cmd command.
 201 * \param arg user argument pointing to a drm_ctx_priv_map structure.
 202 * \return zero on success or a negative number on failure.
 203 *
 204 * Gets the map from drm_device::context_sareas with the handle specified and
 205 * returns its handle.
 206 */
 207int drm_getsareactx(struct inode *inode, struct file *filp,
 208                     unsigned int cmd, unsigned long arg)
 209{
 210        drm_file_t      *priv   = filp->private_data;
 211        drm_device_t    *dev    = priv->dev;
 212        drm_ctx_priv_map_t __user *argp = (void __user *)arg;
 213        drm_ctx_priv_map_t request;
 214        drm_map_t *map;
 215
 216        if (copy_from_user(&request, argp, sizeof(request)))
 217                return -EFAULT;
 218
 219        down(&dev->struct_sem);
 220        if (dev->max_context < 0 || request.ctx_id >= (unsigned) dev->max_context) {
 221                up(&dev->struct_sem);
 222                return -EINVAL;
 223        }
 224
 225        map = dev->context_sareas[request.ctx_id];
 226        up(&dev->struct_sem);
 227
 228        request.handle = map->handle;
 229        if (copy_to_user(argp, &request, sizeof(request)))
 230                return -EFAULT;
 231        return 0;
 232}
 233
 234/**
 235 * Set per-context SAREA.
 236 * 
 237 * \param inode device inode.
 238 * \param filp file pointer.
 239 * \param cmd command.
 240 * \param arg user argument pointing to a drm_ctx_priv_map structure.
 241 * \return zero on success or a negative number on failure.
 242 *
 243 * Searches the mapping specified in \p arg and update the entry in
 244 * drm_device::context_sareas with it.
 245 */
 246int drm_setsareactx(struct inode *inode, struct file *filp,
 247                     unsigned int cmd, unsigned long arg)
 248{
 249        drm_file_t      *priv   = filp->private_data;
 250        drm_device_t    *dev    = priv->dev;
 251        drm_ctx_priv_map_t request;
 252        drm_map_t *map = NULL;
 253        drm_map_list_t *r_list = NULL;
 254        struct list_head *list;
 255
 256        if (copy_from_user(&request,
 257                           (drm_ctx_priv_map_t __user *)arg,
 258                           sizeof(request)))
 259                return -EFAULT;
 260
 261        down(&dev->struct_sem);
 262        list_for_each(list, &dev->maplist->head) {
 263                r_list = list_entry(list, drm_map_list_t, head);
 264                if(r_list->map &&
 265                   r_list->map->handle == request.handle)
 266                        goto found;
 267        }
 268bad:
 269        up(&dev->struct_sem);
 270        return -EINVAL;
 271
 272found:
 273        map = r_list->map;
 274        if (!map) goto bad;
 275        if (dev->max_context < 0)
 276                goto bad;
 277        if (request.ctx_id >= (unsigned) dev->max_context)
 278                goto bad;
 279        dev->context_sareas[request.ctx_id] = map;
 280        up(&dev->struct_sem);
 281        return 0;
 282}
 283
 284/*@}*/
 285
 286/******************************************************************/
 287/** \name The actual DRM context handling routines */
 288/*@{*/
 289
 290/**
 291 * Switch context.
 292 *
 293 * \param dev DRM device.
 294 * \param old old context handle.
 295 * \param new new context handle.
 296 * \return zero on success or a negative number on failure.
 297 *
 298 * Attempt to set drm_device::context_flag.
 299 */
 300int drm_context_switch( drm_device_t *dev, int old, int new )
 301{
 302        if ( test_and_set_bit( 0, &dev->context_flag ) ) {
 303                DRM_ERROR( "Reentering -- FIXME\n" );
 304                return -EBUSY;
 305        }
 306
 307
 308        DRM_DEBUG( "Context switch from %d to %d\n", old, new );
 309
 310        if ( new == dev->last_context ) {
 311                clear_bit( 0, &dev->context_flag );
 312                return 0;
 313        }
 314
 315        return 0;
 316}
 317
 318/**
 319 * Complete context switch.
 320 *
 321 * \param dev DRM device.
 322 * \param new new context handle.
 323 * \return zero on success or a negative number on failure.
 324 *
 325 * Updates drm_device::last_context and drm_device::last_switch. Verifies the
 326 * hardware lock is held, clears the drm_device::context_flag and wakes up
 327 * drm_device::context_wait.
 328 */
 329int drm_context_switch_complete( drm_device_t *dev, int new )
 330{
 331        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
 332        dev->last_switch  = jiffies;
 333
 334        if ( !_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ) {
 335                DRM_ERROR( "Lock isn't held after context switch\n" );
 336        }
 337
 338                                /* If a context switch is ever initiated
 339                                   when the kernel holds the lock, release
 340                                   that lock here. */
 341        clear_bit( 0, &dev->context_flag );
 342        wake_up( &dev->context_wait );
 343
 344        return 0;
 345}
 346
 347/**
 348 * Reserve contexts.
 349 *
 350 * \param inode device inode.
 351 * \param filp file pointer.
 352 * \param cmd command.
 353 * \param arg user argument pointing to a drm_ctx_res structure.
 354 * \return zero on success or a negative number on failure.
 355 */
 356int drm_resctx( struct inode *inode, struct file *filp,
 357                 unsigned int cmd, unsigned long arg )
 358{
 359        drm_ctx_res_t res;
 360        drm_ctx_t __user *argp = (void __user *)arg;
 361        drm_ctx_t ctx;
 362        int i;
 363
 364        if ( copy_from_user( &res, argp, sizeof(res) ) )
 365                return -EFAULT;
 366
 367        if ( res.count >= DRM_RESERVED_CONTEXTS ) {
 368                memset( &ctx, 0, sizeof(ctx) );
 369                for ( i = 0 ; i < DRM_RESERVED_CONTEXTS ; i++ ) {
 370                        ctx.handle = i;
 371                        if ( copy_to_user( &res.contexts[i],
 372                                           &i, sizeof(i) ) )
 373                                return -EFAULT;
 374                }
 375        }
 376        res.count = DRM_RESERVED_CONTEXTS;
 377
 378        if ( copy_to_user( argp, &res, sizeof(res) ) )
 379                return -EFAULT;
 380        return 0;
 381}
 382
 383/**
 384 * Add context.
 385 *
 386 * \param inode device inode.
 387 * \param filp file pointer.
 388 * \param cmd command.
 389 * \param arg user argument pointing to a drm_ctx structure.
 390 * \return zero on success or a negative number on failure.
 391 *
 392 * Get a new handle for the context and copy to userspace.
 393 */
 394int drm_addctx( struct inode *inode, struct file *filp,
 395                 unsigned int cmd, unsigned long arg )
 396{
 397        drm_file_t *priv = filp->private_data;
 398        drm_device_t *dev = priv->dev;
 399        drm_ctx_list_t * ctx_entry;
 400        drm_ctx_t __user *argp = (void __user *)arg;
 401        drm_ctx_t ctx;
 402
 403        if ( copy_from_user( &ctx, argp, sizeof(ctx) ) )
 404                return -EFAULT;
 405
 406        ctx.handle = drm_ctxbitmap_next( dev );
 407        if ( ctx.handle == DRM_KERNEL_CONTEXT ) {
 408                                /* Skip kernel's context and get a new one. */
 409                ctx.handle = drm_ctxbitmap_next( dev );
 410        }
 411        DRM_DEBUG( "%d\n", ctx.handle );
 412        if ( ctx.handle == -1 ) {
 413                DRM_DEBUG( "Not enough free contexts.\n" );
 414                                /* Should this return -EBUSY instead? */
 415                return -ENOMEM;
 416        }
 417
 418        if ( ctx.handle != DRM_KERNEL_CONTEXT )
 419        {
 420                if (dev->driver->context_ctor)
 421                        dev->driver->context_ctor(dev, ctx.handle);
 422        }
 423
 424        ctx_entry = drm_alloc( sizeof(*ctx_entry), DRM_MEM_CTXLIST );
 425        if ( !ctx_entry ) {
 426                DRM_DEBUG("out of memory\n");
 427                return -ENOMEM;
 428        }
 429
 430        INIT_LIST_HEAD( &ctx_entry->head );
 431        ctx_entry->handle = ctx.handle;
 432        ctx_entry->tag = priv;
 433
 434        down( &dev->ctxlist_sem );
 435        list_add( &ctx_entry->head, &dev->ctxlist->head );
 436        ++dev->ctx_count;
 437        up( &dev->ctxlist_sem );
 438
 439        if ( copy_to_user( argp, &ctx, sizeof(ctx) ) )
 440                return -EFAULT;
 441        return 0;
 442}
 443
 444int drm_modctx( struct inode *inode, struct file *filp,
 445                 unsigned int cmd, unsigned long arg )
 446{
 447        /* This does nothing */
 448        return 0;
 449}
 450
 451/**
 452 * Get context.
 453 *
 454 * \param inode device inode.
 455 * \param filp file pointer.
 456 * \param cmd command.
 457 * \param arg user argument pointing to a drm_ctx structure.
 458 * \return zero on success or a negative number on failure.
 459 */
 460int drm_getctx( struct inode *inode, struct file *filp,
 461                 unsigned int cmd, unsigned long arg )
 462{
 463        drm_ctx_t __user *argp = (void __user *)arg;
 464        drm_ctx_t ctx;
 465
 466        if ( copy_from_user( &ctx, argp, sizeof(ctx) ) )
 467                return -EFAULT;
 468
 469        /* This is 0, because we don't handle any context flags */
 470        ctx.flags = 0;
 471
 472        if ( copy_to_user( argp, &ctx, sizeof(ctx) ) )
 473                return -EFAULT;
 474        return 0;
 475}
 476
 477/**
 478 * Switch context.
 479 *
 480 * \param inode device inode.
 481 * \param filp file pointer.
 482 * \param cmd command.
 483 * \param arg user argument pointing to a drm_ctx structure.
 484 * \return zero on success or a negative number on failure.
 485 *
 486 * Calls context_switch().
 487 */
 488int drm_switchctx( struct inode *inode, struct file *filp,
 489                    unsigned int cmd, unsigned long arg )
 490{
 491        drm_file_t *priv = filp->private_data;
 492        drm_device_t *dev = priv->dev;
 493        drm_ctx_t ctx;
 494
 495        if ( copy_from_user( &ctx, (drm_ctx_t __user *)arg, sizeof(ctx) ) )
 496                return -EFAULT;
 497
 498        DRM_DEBUG( "%d\n", ctx.handle );
 499        return drm_context_switch( dev, dev->last_context, ctx.handle );
 500}
 501
 502/**
 503 * New context.
 504 *
 505 * \param inode device inode.
 506 * \param filp file pointer.
 507 * \param cmd command.
 508 * \param arg user argument pointing to a drm_ctx structure.
 509 * \return zero on success or a negative number on failure.
 510 *
 511 * Calls context_switch_complete().
 512 */
 513int drm_newctx( struct inode *inode, struct file *filp,
 514                 unsigned int cmd, unsigned long arg )
 515{
 516        drm_file_t *priv = filp->private_data;
 517        drm_device_t *dev = priv->dev;
 518        drm_ctx_t ctx;
 519
 520        if ( copy_from_user( &ctx, (drm_ctx_t __user *)arg, sizeof(ctx) ) )
 521                return -EFAULT;
 522
 523        DRM_DEBUG( "%d\n", ctx.handle );
 524        drm_context_switch_complete( dev, ctx.handle );
 525
 526        return 0;
 527}
 528
 529/**
 530 * Remove context.
 531 *
 532 * \param inode device inode.
 533 * \param filp file pointer.
 534 * \param cmd command.
 535 * \param arg user argument pointing to a drm_ctx structure.
 536 * \return zero on success or a negative number on failure.
 537 *
 538 * If not the special kernel context, calls ctxbitmap_free() to free the specified context.
 539 */
 540int drm_rmctx( struct inode *inode, struct file *filp,
 541                unsigned int cmd, unsigned long arg )
 542{
 543        drm_file_t *priv = filp->private_data;
 544        drm_device_t *dev = priv->dev;
 545        drm_ctx_t ctx;
 546
 547        if ( copy_from_user( &ctx, (drm_ctx_t __user *)arg, sizeof(ctx) ) )
 548                return -EFAULT;
 549
 550        DRM_DEBUG( "%d\n", ctx.handle );
 551        if ( ctx.handle == DRM_KERNEL_CONTEXT + 1 ) {
 552                priv->remove_auth_on_close = 1;
 553        }
 554        if ( ctx.handle != DRM_KERNEL_CONTEXT ) {
 555                if (dev->driver->context_dtor)
 556                        dev->driver->context_dtor(dev, ctx.handle);
 557                drm_ctxbitmap_free( dev, ctx.handle );
 558        }
 559
 560        down( &dev->ctxlist_sem );
 561        if ( !list_empty( &dev->ctxlist->head ) ) {
 562                drm_ctx_list_t *pos, *n;
 563
 564                list_for_each_entry_safe( pos, n, &dev->ctxlist->head, head ) {
 565                        if ( pos->handle == ctx.handle ) {
 566                                list_del( &pos->head );
 567                                drm_free( pos, sizeof(*pos), DRM_MEM_CTXLIST );
 568                                --dev->ctx_count;
 569                        }
 570                }
 571        }
 572        up( &dev->ctxlist_sem );
 573
 574        return 0;
 575}
 576
 577/*@}*/
 578
 579
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.