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