1 /************************************************************************** 2 * 3 * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24 * USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 /* 28 * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> 29 */ 30 /* 31 * Copyright (c) 2013 The FreeBSD Foundation 32 * All rights reserved. 33 * 34 * Portions of this software were developed by Konstantin Belousov 35 * <kib@FreeBSD.org> under sponsorship from the FreeBSD Foundation. 36 */ 37 38 #include <drm/ttm/ttm_lock.h> 39 #include <drm/ttm/ttm_module.h> 40 #include <linux/atomic.h> 41 #include <linux/errno.h> 42 #include <linux/wait.h> 43 #include <linux/export.h> 44 45 #define TTM_WRITE_LOCK_PENDING (1 << 0) 46 #define TTM_VT_LOCK_PENDING (1 << 1) 47 #define TTM_SUSPEND_LOCK_PENDING (1 << 2) 48 #define TTM_VT_LOCK (1 << 3) 49 #define TTM_SUSPEND_LOCK (1 << 4) 50 51 void ttm_lock_init(struct ttm_lock *lock) 52 { 53 lockinit(&lock->lock, "ttmlk", 0, LK_CANRECURSE); 54 lock->rw = 0; 55 lock->flags = 0; 56 lock->kill_takers = false; 57 lock->signal = SIGKILL; 58 } 59 EXPORT_SYMBOL(ttm_lock_init); 60 61 static void 62 ttm_lock_send_sig(int signo) 63 { 64 struct proc *p; 65 66 p = curproc; /* XXXKIB curthread ? */ 67 ksignal(p, signo); 68 } 69 70 void ttm_read_unlock(struct ttm_lock *lock) 71 { 72 lockmgr(&lock->lock, LK_EXCLUSIVE); 73 if (--lock->rw == 0) 74 wakeup(lock); 75 lockmgr(&lock->lock, LK_RELEASE); 76 } 77 EXPORT_SYMBOL(ttm_read_unlock); 78 79 static bool __ttm_read_lock(struct ttm_lock *lock) 80 { 81 bool locked = false; 82 83 lockmgr(&lock->lock, LK_EXCLUSIVE); 84 if (unlikely(lock->kill_takers)) { 85 ttm_lock_send_sig(lock->signal); 86 lockmgr(&lock->lock, LK_RELEASE); 87 return false; 88 } 89 if (lock->rw >= 0 && lock->flags == 0) { 90 ++lock->rw; 91 locked = true; 92 } 93 lockmgr(&lock->lock, LK_RELEASE); 94 return locked; 95 } 96 97 int ttm_read_lock(struct ttm_lock *lock, bool interruptible) 98 { 99 const char *wmsg; 100 int flags, ret; 101 102 ret = 0; 103 if (interruptible) { 104 flags = PCATCH; 105 wmsg = "ttmri"; 106 } else { 107 flags = 0; 108 wmsg = "ttmr"; 109 } 110 lockmgr(&lock->lock, LK_EXCLUSIVE); 111 while (!__ttm_read_lock(lock)) { 112 ret = lksleep(lock, &lock->lock, 0, wmsg, 0); 113 if (ret != 0) 114 break; 115 } 116 return (-ret); 117 } 118 EXPORT_SYMBOL(ttm_read_lock); 119 120 static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked) 121 { 122 bool block = true; 123 124 *locked = false; 125 126 lockmgr(&lock->lock, LK_EXCLUSIVE); 127 if (unlikely(lock->kill_takers)) { 128 ttm_lock_send_sig(lock->signal); 129 lockmgr(&lock->lock, LK_RELEASE); 130 return false; 131 } 132 if (lock->rw >= 0 && lock->flags == 0) { 133 ++lock->rw; 134 block = false; 135 *locked = true; 136 } else if (lock->flags == 0) { 137 block = false; 138 } 139 lockmgr(&lock->lock, LK_RELEASE); 140 141 return !block; 142 } 143 144 int ttm_read_trylock(struct ttm_lock *lock, bool interruptible) 145 { 146 const char *wmsg; 147 int flags, ret; 148 bool locked; 149 150 ret = 0; 151 if (interruptible) { 152 flags = PCATCH; 153 wmsg = "ttmrti"; 154 } else { 155 flags = 0; 156 wmsg = "ttmrt"; 157 } 158 lockmgr(&lock->lock, LK_EXCLUSIVE); 159 while (!__ttm_read_trylock(lock, &locked)) { 160 ret = lksleep(lock, &lock->lock, 0, wmsg, 0); 161 if (ret != 0) 162 break; 163 } 164 KKASSERT(!locked || ret == 0); 165 lockmgr(&lock->lock, LK_RELEASE); 166 167 return (locked) ? 0 : -EBUSY; 168 } 169 170 void ttm_write_unlock(struct ttm_lock *lock) 171 { 172 lockmgr(&lock->lock, LK_EXCLUSIVE); 173 lock->rw = 0; 174 wakeup(lock); 175 lockmgr(&lock->lock, LK_RELEASE); 176 } 177 EXPORT_SYMBOL(ttm_write_unlock); 178 179 static bool __ttm_write_lock(struct ttm_lock *lock) 180 { 181 bool locked = false; 182 183 lockmgr(&lock->lock, LK_EXCLUSIVE); 184 if (unlikely(lock->kill_takers)) { 185 ttm_lock_send_sig(lock->signal); 186 lockmgr(&lock->lock, LK_RELEASE); 187 return false; 188 } 189 if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) { 190 lock->rw = -1; 191 lock->flags &= ~TTM_WRITE_LOCK_PENDING; 192 locked = true; 193 } else { 194 lock->flags |= TTM_WRITE_LOCK_PENDING; 195 } 196 lockmgr(&lock->lock, LK_RELEASE); 197 return locked; 198 } 199 200 int ttm_write_lock(struct ttm_lock *lock, bool interruptible) 201 { 202 const char *wmsg; 203 int flags, ret; 204 205 ret = 0; 206 if (interruptible) { 207 flags = PCATCH; 208 wmsg = "ttmwi"; 209 } else { 210 flags = 0; 211 wmsg = "ttmw"; 212 } 213 lockmgr(&lock->lock, LK_EXCLUSIVE); 214 /* XXXKIB: linux uses __ttm_read_lock for uninterruptible sleeps */ 215 while (!__ttm_write_lock(lock)) { 216 ret = lksleep(lock, &lock->lock, 0, wmsg, 0); 217 if (interruptible && ret != 0) { 218 lock->flags &= ~TTM_WRITE_LOCK_PENDING; 219 wakeup(lock); 220 break; 221 } 222 } 223 lockmgr(&lock->lock, LK_RELEASE); 224 225 return (-ret); 226 } 227 EXPORT_SYMBOL(ttm_write_lock); 228 229 static int __ttm_vt_unlock(struct ttm_lock *lock) 230 { 231 int ret = 0; 232 233 lockmgr(&lock->lock, LK_EXCLUSIVE); 234 if (unlikely(!(lock->flags & TTM_VT_LOCK))) 235 ret = -EINVAL; 236 lock->flags &= ~TTM_VT_LOCK; 237 wakeup(lock); 238 lockmgr(&lock->lock, LK_RELEASE); 239 240 return ret; 241 } 242 243 static void ttm_vt_lock_remove(struct ttm_base_object **p_base) 244 { 245 struct ttm_base_object *base = *p_base; 246 struct ttm_lock *lock = container_of(base, struct ttm_lock, base); 247 int ret; 248 249 *p_base = NULL; 250 ret = __ttm_vt_unlock(lock); 251 BUG_ON(ret != 0); 252 } 253 254 static bool __ttm_vt_lock(struct ttm_lock *lock) 255 { 256 bool locked = false; 257 258 lockmgr(&lock->lock, LK_EXCLUSIVE); 259 if (lock->rw == 0) { 260 lock->flags &= ~TTM_VT_LOCK_PENDING; 261 lock->flags |= TTM_VT_LOCK; 262 locked = true; 263 } else { 264 lock->flags |= TTM_VT_LOCK_PENDING; 265 } 266 lockmgr(&lock->lock, LK_RELEASE); 267 return locked; 268 } 269 270 int ttm_vt_lock(struct ttm_lock *lock, 271 bool interruptible, 272 struct ttm_object_file *tfile) 273 { 274 const char *wmsg; 275 int flags, ret; 276 277 ret = 0; 278 if (interruptible) { 279 flags = PCATCH; 280 wmsg = "ttmwi"; 281 } else { 282 flags = 0; 283 wmsg = "ttmw"; 284 } 285 lockmgr(&lock->lock, LK_EXCLUSIVE); 286 while (!__ttm_vt_lock(lock)) { 287 ret = lksleep(lock, &lock->lock, 0, wmsg, 0); 288 if (interruptible && ret != 0) { 289 lock->flags &= ~TTM_VT_LOCK_PENDING; 290 wakeup(lock); 291 break; 292 } 293 } 294 295 /* 296 * Add a base-object, the destructor of which will 297 * make sure the lock is released if the client dies 298 * while holding it. 299 */ 300 301 ret = ttm_base_object_init(tfile, &lock->base, false, 302 ttm_lock_type, &ttm_vt_lock_remove, NULL); 303 if (ret) 304 (void)__ttm_vt_unlock(lock); 305 else 306 lock->vt_holder = tfile; 307 308 return (-ret); 309 } 310 EXPORT_SYMBOL(ttm_vt_lock); 311 312 int ttm_vt_unlock(struct ttm_lock *lock) 313 { 314 return ttm_ref_object_base_unref(lock->vt_holder, 315 lock->base.hash.key, TTM_REF_USAGE); 316 } 317 EXPORT_SYMBOL(ttm_vt_unlock); 318 319 void ttm_suspend_unlock(struct ttm_lock *lock) 320 { 321 lockmgr(&lock->lock, LK_EXCLUSIVE); 322 lock->flags &= ~TTM_SUSPEND_LOCK; 323 wakeup(lock); 324 lockmgr(&lock->lock, LK_RELEASE); 325 } 326 EXPORT_SYMBOL(ttm_suspend_unlock); 327 328 static bool __ttm_suspend_lock(struct ttm_lock *lock) 329 { 330 bool locked = false; 331 332 lockmgr(&lock->lock, LK_EXCLUSIVE); 333 if (lock->rw == 0) { 334 lock->flags &= ~TTM_SUSPEND_LOCK_PENDING; 335 lock->flags |= TTM_SUSPEND_LOCK; 336 locked = true; 337 } else { 338 lock->flags |= TTM_SUSPEND_LOCK_PENDING; 339 } 340 lockmgr(&lock->lock, LK_RELEASE); 341 return locked; 342 } 343 344 void ttm_suspend_lock(struct ttm_lock *lock) 345 { 346 lockmgr(&lock->lock, LK_EXCLUSIVE); 347 while (!__ttm_suspend_lock(lock)) 348 lksleep(lock, &lock->lock, 0, "ttms", 0); 349 lockmgr(&lock->lock, LK_RELEASE); 350 } 351 EXPORT_SYMBOL(ttm_suspend_lock); 352