linux/drivers/gpu/drm/vmwgfx/ttm_lock.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 OR MIT */
   2/**************************************************************************
   3 *
   4 * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
   5 * All Rights Reserved.
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a
   8 * copy of this software and associated documentation files (the
   9 * "Software"), to deal in the Software without restriction, including
  10 * without limitation the rights to use, copy, modify, merge, publish,
  11 * distribute, sub license, and/or sell copies of the Software, and to
  12 * permit persons to whom the Software is furnished to do so, subject to
  13 * the following conditions:
  14 *
  15 * The above copyright notice and this permission notice (including the
  16 * next paragraph) shall be included in all copies or substantial portions
  17 * of the Software.
  18 *
  19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
  23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
  26 *
  27 **************************************************************************/
  28/*
  29 * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
  30 */
  31
  32#include <linux/atomic.h>
  33#include <linux/errno.h>
  34#include <linux/wait.h>
  35#include <linux/sched/signal.h>
  36#include "ttm_lock.h"
  37#include "ttm_object.h"
  38
  39#define TTM_WRITE_LOCK_PENDING    (1 << 0)
  40#define TTM_VT_LOCK_PENDING       (1 << 1)
  41#define TTM_SUSPEND_LOCK_PENDING  (1 << 2)
  42#define TTM_VT_LOCK               (1 << 3)
  43#define TTM_SUSPEND_LOCK          (1 << 4)
  44
  45void ttm_lock_init(struct ttm_lock *lock)
  46{
  47        spin_lock_init(&lock->lock);
  48        init_waitqueue_head(&lock->queue);
  49        lock->rw = 0;
  50        lock->flags = 0;
  51}
  52
  53void ttm_read_unlock(struct ttm_lock *lock)
  54{
  55        spin_lock(&lock->lock);
  56        if (--lock->rw == 0)
  57                wake_up_all(&lock->queue);
  58        spin_unlock(&lock->lock);
  59}
  60
  61static bool __ttm_read_lock(struct ttm_lock *lock)
  62{
  63        bool locked = false;
  64
  65        spin_lock(&lock->lock);
  66        if (lock->rw >= 0 && lock->flags == 0) {
  67                ++lock->rw;
  68                locked = true;
  69        }
  70        spin_unlock(&lock->lock);
  71        return locked;
  72}
  73
  74int ttm_read_lock(struct ttm_lock *lock, bool interruptible)
  75{
  76        int ret = 0;
  77
  78        if (interruptible)
  79                ret = wait_event_interruptible(lock->queue,
  80                                               __ttm_read_lock(lock));
  81        else
  82                wait_event(lock->queue, __ttm_read_lock(lock));
  83        return ret;
  84}
  85
  86static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked)
  87{
  88        bool block = true;
  89
  90        *locked = false;
  91
  92        spin_lock(&lock->lock);
  93        if (lock->rw >= 0 && lock->flags == 0) {
  94                ++lock->rw;
  95                block = false;
  96                *locked = true;
  97        } else if (lock->flags == 0) {
  98                block = false;
  99        }
 100        spin_unlock(&lock->lock);
 101
 102        return !block;
 103}
 104
 105int ttm_read_trylock(struct ttm_lock *lock, bool interruptible)
 106{
 107        int ret = 0;
 108        bool locked;
 109
 110        if (interruptible)
 111                ret = wait_event_interruptible
 112                        (lock->queue, __ttm_read_trylock(lock, &locked));
 113        else
 114                wait_event(lock->queue, __ttm_read_trylock(lock, &locked));
 115
 116        if (unlikely(ret != 0)) {
 117                BUG_ON(locked);
 118                return ret;
 119        }
 120
 121        return (locked) ? 0 : -EBUSY;
 122}
 123
 124void ttm_write_unlock(struct ttm_lock *lock)
 125{
 126        spin_lock(&lock->lock);
 127        lock->rw = 0;
 128        wake_up_all(&lock->queue);
 129        spin_unlock(&lock->lock);
 130}
 131
 132static bool __ttm_write_lock(struct ttm_lock *lock)
 133{
 134        bool locked = false;
 135
 136        spin_lock(&lock->lock);
 137        if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) {
 138                lock->rw = -1;
 139                lock->flags &= ~TTM_WRITE_LOCK_PENDING;
 140                locked = true;
 141        } else {
 142                lock->flags |= TTM_WRITE_LOCK_PENDING;
 143        }
 144        spin_unlock(&lock->lock);
 145        return locked;
 146}
 147
 148int ttm_write_lock(struct ttm_lock *lock, bool interruptible)
 149{
 150        int ret = 0;
 151
 152        if (interruptible) {
 153                ret = wait_event_interruptible(lock->queue,
 154                                               __ttm_write_lock(lock));
 155                if (unlikely(ret != 0)) {
 156                        spin_lock(&lock->lock);
 157                        lock->flags &= ~TTM_WRITE_LOCK_PENDING;
 158                        wake_up_all(&lock->queue);
 159                        spin_unlock(&lock->lock);
 160                }
 161        } else
 162                wait_event(lock->queue, __ttm_write_lock(lock));
 163
 164        return ret;
 165}
 166
 167void ttm_suspend_unlock(struct ttm_lock *lock)
 168{
 169        spin_lock(&lock->lock);
 170        lock->flags &= ~TTM_SUSPEND_LOCK;
 171        wake_up_all(&lock->queue);
 172        spin_unlock(&lock->lock);
 173}
 174
 175static bool __ttm_suspend_lock(struct ttm_lock *lock)
 176{
 177        bool locked = false;
 178
 179        spin_lock(&lock->lock);
 180        if (lock->rw == 0) {
 181                lock->flags &= ~TTM_SUSPEND_LOCK_PENDING;
 182                lock->flags |= TTM_SUSPEND_LOCK;
 183                locked = true;
 184        } else {
 185                lock->flags |= TTM_SUSPEND_LOCK_PENDING;
 186        }
 187        spin_unlock(&lock->lock);
 188        return locked;
 189}
 190
 191void ttm_suspend_lock(struct ttm_lock *lock)
 192{
 193        wait_event(lock->queue, __ttm_suspend_lock(lock));
 194}
 195