linux/drivers/gpu/drm/drm_lock.c
<<
>>
Prefs
   1/**
   2 * \file drm_lock.c
   3 * IOCTLs for locking
   4 *
   5 * \author Rickard E. (Rik) Faith <faith@valinux.com>
   6 * \author Gareth Hughes <gareth@valinux.com>
   7 */
   8
   9/*
  10 * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
  11 *
  12 * Copyright 1999 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#include "drmP.h"
  37
  38static int drm_notifier(void *priv);
  39
  40/**
  41 * Lock ioctl.
  42 *
  43 * \param inode device inode.
  44 * \param file_priv DRM file private.
  45 * \param cmd command.
  46 * \param arg user argument, pointing to a drm_lock structure.
  47 * \return zero on success or negative number on failure.
  48 *
  49 * Add the current task to the lock wait queue, and attempt to take to lock.
  50 */
  51int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv)
  52{
  53        DECLARE_WAITQUEUE(entry, current);
  54        struct drm_lock *lock = data;
  55        struct drm_master *master = file_priv->master;
  56        int ret = 0;
  57
  58        ++file_priv->lock_count;
  59
  60        if (lock->context == DRM_KERNEL_CONTEXT) {
  61                DRM_ERROR("Process %d using kernel context %d\n",
  62                          task_pid_nr(current), lock->context);
  63                return -EINVAL;
  64        }
  65
  66        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
  67                  lock->context, task_pid_nr(current),
  68                  master->lock.hw_lock->lock, lock->flags);
  69
  70        if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE))
  71                if (lock->context < 0)
  72                        return -EINVAL;
  73
  74        add_wait_queue(&master->lock.lock_queue, &entry);
  75        spin_lock_bh(&master->lock.spinlock);
  76        master->lock.user_waiters++;
  77        spin_unlock_bh(&master->lock.spinlock);
  78
  79        for (;;) {
  80                __set_current_state(TASK_INTERRUPTIBLE);
  81                if (!master->lock.hw_lock) {
  82                        /* Device has been unregistered */
  83                        send_sig(SIGTERM, current, 0);
  84                        ret = -EINTR;
  85                        break;
  86                }
  87                if (drm_lock_take(&master->lock, lock->context)) {
  88                        master->lock.file_priv = file_priv;
  89                        master->lock.lock_time = jiffies;
  90                        atomic_inc(&dev->counts[_DRM_STAT_LOCKS]);
  91                        break;  /* Got lock */
  92                }
  93
  94                /* Contention */
  95                schedule();
  96                if (signal_pending(current)) {
  97                        ret = -EINTR;
  98                        break;
  99                }
 100        }
 101        spin_lock_bh(&master->lock.spinlock);
 102        master->lock.user_waiters--;
 103        spin_unlock_bh(&master->lock.spinlock);
 104        __set_current_state(TASK_RUNNING);
 105        remove_wait_queue(&master->lock.lock_queue, &entry);
 106
 107        DRM_DEBUG("%d %s\n", lock->context,
 108                  ret ? "interrupted" : "has lock");
 109        if (ret) return ret;
 110
 111        /* don't set the block all signals on the master process for now 
 112         * really probably not the correct answer but lets us debug xkb
 113         * xserver for now */
 114        if (!file_priv->is_master) {
 115                sigemptyset(&dev->sigmask);
 116                sigaddset(&dev->sigmask, SIGSTOP);
 117                sigaddset(&dev->sigmask, SIGTSTP);
 118                sigaddset(&dev->sigmask, SIGTTIN);
 119                sigaddset(&dev->sigmask, SIGTTOU);
 120                dev->sigdata.context = lock->context;
 121                dev->sigdata.lock = master->lock.hw_lock;
 122                block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
 123        }
 124
 125        if (dev->driver->dma_ready && (lock->flags & _DRM_LOCK_READY))
 126                dev->driver->dma_ready(dev);
 127
 128        if (dev->driver->dma_quiescent && (lock->flags & _DRM_LOCK_QUIESCENT))
 129        {
 130                if (dev->driver->dma_quiescent(dev)) {
 131                        DRM_DEBUG("%d waiting for DMA quiescent\n",
 132                                  lock->context);
 133                        return -EBUSY;
 134                }
 135        }
 136
 137        if (dev->driver->kernel_context_switch &&
 138            dev->last_context != lock->context) {
 139                dev->driver->kernel_context_switch(dev, dev->last_context,
 140                                                   lock->context);
 141        }
 142
 143        return 0;
 144}
 145
 146/**
 147 * Unlock ioctl.
 148 *
 149 * \param inode device inode.
 150 * \param file_priv DRM file private.
 151 * \param cmd command.
 152 * \param arg user argument, pointing to a drm_lock structure.
 153 * \return zero on success or negative number on failure.
 154 *
 155 * Transfer and free the lock.
 156 */
 157int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv)
 158{
 159        struct drm_lock *lock = data;
 160        struct drm_master *master = file_priv->master;
 161
 162        if (lock->context == DRM_KERNEL_CONTEXT) {
 163                DRM_ERROR("Process %d using kernel context %d\n",
 164                          task_pid_nr(current), lock->context);
 165                return -EINVAL;
 166        }
 167
 168        atomic_inc(&dev->counts[_DRM_STAT_UNLOCKS]);
 169
 170        /* kernel_context_switch isn't used by any of the x86 drm
 171         * modules but is required by the Sparc driver.
 172         */
 173        if (dev->driver->kernel_context_switch_unlock)
 174                dev->driver->kernel_context_switch_unlock(dev);
 175        else {
 176                if (drm_lock_free(&master->lock, lock->context)) {
 177                        /* FIXME: Should really bail out here. */
 178                }
 179        }
 180
 181        unblock_all_signals();
 182        return 0;
 183}
 184
 185/**
 186 * Take the heavyweight lock.
 187 *
 188 * \param lock lock pointer.
 189 * \param context locking context.
 190 * \return one if the lock is held, or zero otherwise.
 191 *
 192 * Attempt to mark the lock as held by the given context, via the \p cmpxchg instruction.
 193 */
 194int drm_lock_take(struct drm_lock_data *lock_data,
 195                  unsigned int context)
 196{
 197        unsigned int old, new, prev;
 198        volatile unsigned int *lock = &lock_data->hw_lock->lock;
 199
 200        spin_lock_bh(&lock_data->spinlock);
 201        do {
 202                old = *lock;
 203                if (old & _DRM_LOCK_HELD)
 204                        new = old | _DRM_LOCK_CONT;
 205                else {
 206                        new = context | _DRM_LOCK_HELD |
 207                                ((lock_data->user_waiters + lock_data->kernel_waiters > 1) ?
 208                                 _DRM_LOCK_CONT : 0);
 209                }
 210                prev = cmpxchg(lock, old, new);
 211        } while (prev != old);
 212        spin_unlock_bh(&lock_data->spinlock);
 213
 214        if (_DRM_LOCKING_CONTEXT(old) == context) {
 215                if (old & _DRM_LOCK_HELD) {
 216                        if (context != DRM_KERNEL_CONTEXT) {
 217                                DRM_ERROR("%d holds heavyweight lock\n",
 218                                          context);
 219                        }
 220                        return 0;
 221                }
 222        }
 223
 224        if ((_DRM_LOCKING_CONTEXT(new)) == context && (new & _DRM_LOCK_HELD)) {
 225                /* Have lock */
 226                return 1;
 227        }
 228        return 0;
 229}
 230EXPORT_SYMBOL(drm_lock_take);
 231
 232/**
 233 * This takes a lock forcibly and hands it to context.  Should ONLY be used
 234 * inside *_unlock to give lock to kernel before calling *_dma_schedule.
 235 *
 236 * \param dev DRM device.
 237 * \param lock lock pointer.
 238 * \param context locking context.
 239 * \return always one.
 240 *
 241 * Resets the lock file pointer.
 242 * Marks the lock as held by the given context, via the \p cmpxchg instruction.
 243 */
 244static int drm_lock_transfer(struct drm_lock_data *lock_data,
 245                             unsigned int context)
 246{
 247        unsigned int old, new, prev;
 248        volatile unsigned int *lock = &lock_data->hw_lock->lock;
 249
 250        lock_data->file_priv = NULL;
 251        do {
 252                old = *lock;
 253                new = context | _DRM_LOCK_HELD;
 254                prev = cmpxchg(lock, old, new);
 255        } while (prev != old);
 256        return 1;
 257}
 258
 259/**
 260 * Free lock.
 261 *
 262 * \param dev DRM device.
 263 * \param lock lock.
 264 * \param context context.
 265 *
 266 * Resets the lock file pointer.
 267 * Marks the lock as not held, via the \p cmpxchg instruction. Wakes any task
 268 * waiting on the lock queue.
 269 */
 270int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context)
 271{
 272        unsigned int old, new, prev;
 273        volatile unsigned int *lock = &lock_data->hw_lock->lock;
 274
 275        spin_lock_bh(&lock_data->spinlock);
 276        if (lock_data->kernel_waiters != 0) {
 277                drm_lock_transfer(lock_data, 0);
 278                lock_data->idle_has_lock = 1;
 279                spin_unlock_bh(&lock_data->spinlock);
 280                return 1;
 281        }
 282        spin_unlock_bh(&lock_data->spinlock);
 283
 284        do {
 285                old = *lock;
 286                new = _DRM_LOCKING_CONTEXT(old);
 287                prev = cmpxchg(lock, old, new);
 288        } while (prev != old);
 289
 290        if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
 291                DRM_ERROR("%d freed heavyweight lock held by %d\n",
 292                          context, _DRM_LOCKING_CONTEXT(old));
 293                return 1;
 294        }
 295        wake_up_interruptible(&lock_data->lock_queue);
 296        return 0;
 297}
 298EXPORT_SYMBOL(drm_lock_free);
 299
 300/**
 301 * If we get here, it means that the process has called DRM_IOCTL_LOCK
 302 * without calling DRM_IOCTL_UNLOCK.
 303 *
 304 * If the lock is not held, then let the signal proceed as usual.  If the lock
 305 * is held, then set the contended flag and keep the signal blocked.
 306 *
 307 * \param priv pointer to a drm_sigdata structure.
 308 * \return one if the signal should be delivered normally, or zero if the
 309 * signal should be blocked.
 310 */
 311static int drm_notifier(void *priv)
 312{
 313        struct drm_sigdata *s = (struct drm_sigdata *) priv;
 314        unsigned int old, new, prev;
 315
 316        /* Allow signal delivery if lock isn't held */
 317        if (!s->lock || !_DRM_LOCK_IS_HELD(s->lock->lock)
 318            || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context)
 319                return 1;
 320
 321        /* Otherwise, set flag to force call to
 322           drmUnlock */
 323        do {
 324                old = s->lock->lock;
 325                new = old | _DRM_LOCK_CONT;
 326                prev = cmpxchg(&s->lock->lock, old, new);
 327        } while (prev != old);
 328        return 0;
 329}
 330
 331/**
 332 * This function returns immediately and takes the hw lock
 333 * with the kernel context if it is free, otherwise it gets the highest priority when and if
 334 * it is eventually released.
 335 *
 336 * This guarantees that the kernel will _eventually_ have the lock _unless_ it is held
 337 * by a blocked process. (In the latter case an explicit wait for the hardware lock would cause
 338 * a deadlock, which is why the "idlelock" was invented).
 339 *
 340 * This should be sufficient to wait for GPU idle without
 341 * having to worry about starvation.
 342 */
 343
 344void drm_idlelock_take(struct drm_lock_data *lock_data)
 345{
 346        int ret = 0;
 347
 348        spin_lock_bh(&lock_data->spinlock);
 349        lock_data->kernel_waiters++;
 350        if (!lock_data->idle_has_lock) {
 351
 352                spin_unlock_bh(&lock_data->spinlock);
 353                ret = drm_lock_take(lock_data, DRM_KERNEL_CONTEXT);
 354                spin_lock_bh(&lock_data->spinlock);
 355
 356                if (ret == 1)
 357                        lock_data->idle_has_lock = 1;
 358        }
 359        spin_unlock_bh(&lock_data->spinlock);
 360}
 361EXPORT_SYMBOL(drm_idlelock_take);
 362
 363void drm_idlelock_release(struct drm_lock_data *lock_data)
 364{
 365        unsigned int old, prev;
 366        volatile unsigned int *lock = &lock_data->hw_lock->lock;
 367
 368        spin_lock_bh(&lock_data->spinlock);
 369        if (--lock_data->kernel_waiters == 0) {
 370                if (lock_data->idle_has_lock) {
 371                        do {
 372                                old = *lock;
 373                                prev = cmpxchg(lock, old, DRM_KERNEL_CONTEXT);
 374                        } while (prev != old);
 375                        wake_up_interruptible(&lock_data->lock_queue);
 376                        lock_data->idle_has_lock = 0;
 377                }
 378        }
 379        spin_unlock_bh(&lock_data->spinlock);
 380}
 381EXPORT_SYMBOL(drm_idlelock_release);
 382
 383
 384int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv)
 385{
 386        struct drm_master *master = file_priv->master;
 387        return (file_priv->lock_count && master->lock.hw_lock &&
 388                _DRM_LOCK_IS_HELD(master->lock.hw_lock->lock) &&
 389                master->lock.file_priv == file_priv);
 390}
 391
 392EXPORT_SYMBOL(drm_i_have_hw_lock);
 393
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.