1 /*
   2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 /*
   7  * lock.c -- IOCTLs for locking -*- linux-c -*-
   8  * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
   9  */
  10 /*
  11  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  12  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
  13  * All Rights Reserved.
  14  *
  15  * Permission is hereby granted, free of charge, to any person obtaining a
  16  * copy of this software and associated documentation files (the "Software"),
  17  * to deal in the Software without restriction, including without limitation
  18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  19  * and/or sell copies of the Software, and to permit persons to whom the
  20  * Software is furnished to do so, subject to the following conditions:
  21  *
  22  * The above copyright notice and this permission notice (including the next
  23  * paragraph) shall be included in all copies or substantial portions of the
  24  * Software.
  25  *
  26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  27  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  28  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  29  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  30  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  31  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  32  * OTHER DEALINGS IN THE SOFTWARE.
  33  *
  34  * Authors:
  35  *    Rickard E. (Rik) Faith <faith@valinux.com>
  36  *    Gareth Hughes <gareth@valinux.com>
  37  *
  38  */
  39 
  40 #include "drmP.h"
  41 
  42 int
  43 drm_lock_take(drm_lock_data_t *lock_data, unsigned int context)
  44 {
  45         unsigned int old, new;
  46         volatile unsigned int *lock = &lock_data->hw_lock->lock;
  47 
  48         do {
  49                 old = *lock;
  50                 if (old & _DRM_LOCK_HELD)
  51                         new = old | _DRM_LOCK_CONT;
  52                 else
  53                         new = context | _DRM_LOCK_HELD;
  54         } while (!atomic_cmpset_int(lock, old, new));
  55 
  56         if (_DRM_LOCKING_CONTEXT(old) == context) {
  57                 if (old & _DRM_LOCK_HELD) {
  58                         if (context != DRM_KERNEL_CONTEXT) {
  59                                 DRM_ERROR("%d holds heavyweight lock\n",
  60                                     context);
  61                         }
  62                         return (0);
  63                 }
  64         }
  65         if (new == (context | _DRM_LOCK_HELD)) {
  66                 /* Have lock */
  67                 return (1);
  68         }
  69         return (0);
  70 }
  71 
  72 /*
  73  * This takes a lock forcibly and hands it to context.  Should ONLY be used
  74  * inside *_unlock to give lock to kernel before calling *_dma_schedule.
  75  */
  76 int
  77 drm_lock_transfer(drm_device_t *dev, drm_lock_data_t *lock_data,
  78                         unsigned int context)
  79 {
  80         unsigned int old, new;
  81         volatile unsigned int *lock = &lock_data->hw_lock->lock;
  82 
  83         dev->lock.filp = NULL;
  84         do {
  85                 old  = *lock;
  86                 new  = context | _DRM_LOCK_HELD;
  87         } while (!atomic_cmpset_int(lock, old, new));
  88 
  89         return (1);
  90 }
  91 
  92 int
  93 drm_lock_free(drm_device_t *dev, volatile unsigned int *lock,
  94     unsigned int context)
  95 {
  96         unsigned int old, new;
  97 
  98         mutex_enter(&(dev->lock.lock_mutex));
  99         dev->lock.filp = NULL;
 100         do {
 101                 old  = *lock;
 102                 new = 0;
 103         } while (!atomic_cmpset_int(lock, old, new));
 104 
 105         if (_DRM_LOCK_IS_HELD(old) &&
 106             (_DRM_LOCKING_CONTEXT(old) != context)) {
 107                 DRM_ERROR("%d freed heavyweight lock held by %d\n",
 108                     context, _DRM_LOCKING_CONTEXT(old));
 109                 mutex_exit(&(dev->lock.lock_mutex));
 110                 return (1);
 111         }
 112         cv_broadcast(&(dev->lock.lock_cv));
 113         mutex_exit(&(dev->lock.lock_mutex));
 114         return (0);
 115 }
 116 
 117 /*ARGSUSED*/
 118 int
 119 drm_lock(DRM_IOCTL_ARGS)
 120 {
 121         DRM_DEVICE;
 122         drm_lock_t lock;
 123         int ret = 0;
 124 
 125         DRM_COPYFROM_WITH_RETURN(&lock, (void *)data, sizeof (lock));
 126 
 127         if (lock.context == DRM_KERNEL_CONTEXT) {
 128                 DRM_ERROR("Process %d using kernel context %d\n",
 129                     DRM_CURRENTPID, lock.context);
 130                 return (EINVAL);
 131         }
 132 
 133         DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
 134             lock.context, DRM_CURRENTPID, dev->lock.hw_lock->lock,
 135             lock.flags);
 136         if (dev->driver->use_dma_queue && lock.context < 0)
 137                 return (EINVAL);
 138 
 139         mutex_enter(&(dev->lock.lock_mutex));
 140         for (;;) {
 141                 if (drm_lock_take(&dev->lock, lock.context)) {
 142                         dev->lock.filp = fpriv;
 143                         dev->lock.lock_time = ddi_get_lbolt();
 144                         break;  /* Got lock */
 145                 }
 146                 ret = cv_wait_sig(&(dev->lock.lock_cv),
 147                     &(dev->lock.lock_mutex));
 148 
 149                 if (ret == 0) {
 150                         mutex_exit(&(dev->lock.lock_mutex));
 151                         return (EINTR);
 152                 }
 153         }
 154         mutex_exit(&(dev->lock.lock_mutex));
 155         DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
 156 
 157         if (dev->driver->dma_quiescent != NULL &&
 158             (lock.flags & _DRM_LOCK_QUIESCENT))
 159                 dev->driver->dma_quiescent(dev);
 160 
 161         return (0);
 162 }
 163 
 164 /*ARGSUSED*/
 165 int
 166 drm_unlock(DRM_IOCTL_ARGS)
 167 {
 168         DRM_DEVICE;
 169         drm_lock_t lock;
 170 
 171         DRM_COPYFROM_WITH_RETURN(&lock, (void *)data, sizeof (lock));
 172 
 173         DRM_DEBUG("%d (pid %d) requests unlock (0x%08x), flags = 0x%08x\n",
 174             lock.context, DRM_CURRENTPID, dev->lock.hw_lock->lock,
 175             lock.flags);
 176 
 177         if (lock.context == DRM_KERNEL_CONTEXT) {
 178                 DRM_ERROR("Process %d using kernel context %d\n",
 179                     DRM_CURRENTPID, lock.context);
 180                 return (EINVAL);
 181         }
 182         atomic_inc_32(&dev->counts[_DRM_STAT_UNLOCKS]);
 183 
 184         DRM_LOCK();
 185         if (drm_lock_free(dev, &dev->lock.hw_lock->lock, lock.context)) {
 186                 DRM_ERROR("drm_unlock\n");
 187         }
 188         DRM_UNLOCK();
 189         return (0);
 190 }