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 * $FreeBSD: head/sys/dev/drm2/ttm/ttm_lock.c 247835 2013-03-05 09:49:34Z kib $ 38 */ 39 40 #include <drm/ttm/ttm_lock.h> 41 #include <drm/ttm/ttm_module.h> 42 #include <linux/atomic.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 PROC_LOCK(p); 68 ksignal(p, signo); 69 PROC_UNLOCK(p); 70 } 71 72 void ttm_read_unlock(struct ttm_lock *lock) 73 { 74 lockmgr(&lock->lock, LK_EXCLUSIVE); 75 if (--lock->rw == 0) 76 wakeup(lock); 77 lockmgr(&lock->lock, LK_RELEASE); 78 } 79 EXPORT_SYMBOL(ttm_read_unlock); 80 81 static bool __ttm_read_lock(struct ttm_lock *lock) 82 { 83 bool locked = false; 84 85 lockmgr(&lock->lock, LK_EXCLUSIVE); 86 if (unlikely(lock->kill_takers)) { 87 ttm_lock_send_sig(lock->signal); 88 lockmgr(&lock->lock, LK_RELEASE); 89 return false; 90 } 91 if (lock->rw >= 0 && lock->flags == 0) { 92 ++lock->rw; 93 locked = true; 94 } 95 lockmgr(&lock->lock, LK_RELEASE); 96 return locked; 97 } 98 99 int ttm_read_lock(struct ttm_lock *lock, bool interruptible) 100 { 101 const char *wmsg; 102 int flags, ret; 103 104 ret = 0; 105 if (interruptible) { 106 flags = PCATCH; 107 wmsg = "ttmri"; 108 } else { 109 flags = 0; 110 wmsg = "ttmr"; 111 } 112 lockmgr(&lock->lock, LK_EXCLUSIVE); 113 while (!__ttm_read_lock(lock)) { 114 ret = lksleep(lock, &lock->lock, 0, wmsg, 0); 115 if (ret != 0) 116 break; 117 } 118 return (-ret); 119 } 120 EXPORT_SYMBOL(ttm_read_lock); 121 122 static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked) 123 { 124 bool block = true; 125 126 *locked = false; 127 128 lockmgr(&lock->lock, LK_EXCLUSIVE); 129 if (unlikely(lock->kill_takers)) { 130 ttm_lock_send_sig(lock->signal); 131 lockmgr(&lock->lock, LK_RELEASE); 132 return false; 133 } 134 if (lock->rw >= 0 && lock->flags == 0) { 135 ++lock->rw; 136 block = false; 137 *locked = true; 138 } else if (lock->flags == 0) { 139 block = false; 140 } 141 lockmgr(&lock->lock, LK_RELEASE); 142 143 return !block; 144 } 145 146 int ttm_read_trylock(struct ttm_lock *lock, bool interruptible) 147 { 148 const char *wmsg; 149 int flags, ret; 150 bool locked; 151 152 ret = 0; 153 if (interruptible) { 154 flags = PCATCH; 155 wmsg = "ttmrti"; 156 } else { 157 flags = 0; 158 wmsg = "ttmrt"; 159 } 160 lockmgr(&lock->lock, LK_EXCLUSIVE); 161 while (!__ttm_read_trylock(lock, &locked)) { 162 ret = lksleep(lock, &lock->lock, 0, wmsg, 0); 163 if (ret != 0) 164 break; 165 } 166 KKASSERT(!locked || ret == 0); 167 lockmgr(&lock->lock, LK_RELEASE); 168 169 return (locked) ? 0 : -EBUSY; 170 } 171 172 void ttm_write_unlock(struct ttm_lock *lock) 173 { 174 lockmgr(&lock->lock, LK_EXCLUSIVE); 175 lock->rw = 0; 176 wakeup(lock); 177 lockmgr(&lock->lock, LK_RELEASE); 178 } 179 EXPORT_SYMBOL(ttm_write_unlock); 180 181 static bool __ttm_write_lock(struct ttm_lock *lock) 182 { 183 bool locked = false; 184 185 lockmgr(&lock->lock, LK_EXCLUSIVE); 186 if (unlikely(lock->kill_takers)) { 187 ttm_lock_send_sig(lock->signal); 188 lockmgr(&lock->lock, LK_RELEASE); 189 return false; 190 } 191 if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) { 192 lock->rw = -1; 193 lock->flags &= ~TTM_WRITE_LOCK_PENDING; 194 locked = true; 195 } else { 196 lock->flags |= TTM_WRITE_LOCK_PENDING; 197 } 198 lockmgr(&lock->lock, LK_RELEASE); 199 return locked; 200 } 201 202 int ttm_write_lock(struct ttm_lock *lock, bool interruptible) 203 { 204 const char *wmsg; 205 int flags, ret; 206 207 ret = 0; 208 if (interruptible) { 209 flags = PCATCH; 210 wmsg = "ttmwi"; 211 } else { 212 flags = 0; 213 wmsg = "ttmw"; 214 } 215 lockmgr(&lock->lock, LK_EXCLUSIVE); 216 /* XXXKIB: linux uses __ttm_read_lock for uninterruptible sleeps */ 217 while (!__ttm_write_lock(lock)) { 218 ret = lksleep(lock, &lock->lock, 0, wmsg, 0); 219 if (interruptible && ret != 0) { 220 lock->flags &= ~TTM_WRITE_LOCK_PENDING; 221 wakeup(lock); 222 break; 223 } 224 } 225 lockmgr(&lock->lock, LK_RELEASE); 226 227 return (-ret); 228 } 229 EXPORT_SYMBOL(ttm_write_lock); 230 231 void ttm_write_lock_downgrade(struct ttm_lock *lock) 232 { 233 lockmgr(&lock->lock, LK_EXCLUSIVE); 234 lock->rw = 1; 235 wakeup(lock); 236 lockmgr(&lock->lock, LK_RELEASE); 237 } 238 239 static int __ttm_vt_unlock(struct ttm_lock *lock) 240 { 241 int ret = 0; 242 243 lockmgr(&lock->lock, LK_EXCLUSIVE); 244 if (unlikely(!(lock->flags & TTM_VT_LOCK))) 245 ret = -EINVAL; 246 lock->flags &= ~TTM_VT_LOCK; 247 wakeup(lock); 248 lockmgr(&lock->lock, LK_RELEASE); 249 250 return ret; 251 } 252 253 static void ttm_vt_lock_remove(struct ttm_base_object **p_base) 254 { 255 struct ttm_base_object *base = *p_base; 256 struct ttm_lock *lock = container_of(base, struct ttm_lock, base); 257 int ret; 258 259 *p_base = NULL; 260 ret = __ttm_vt_unlock(lock); 261 BUG_ON(ret != 0); 262 } 263 264 static bool __ttm_vt_lock(struct ttm_lock *lock) 265 { 266 bool locked = false; 267 268 lockmgr(&lock->lock, LK_EXCLUSIVE); 269 if (lock->rw == 0) { 270 lock->flags &= ~TTM_VT_LOCK_PENDING; 271 lock->flags |= TTM_VT_LOCK; 272 locked = true; 273 } else { 274 lock->flags |= TTM_VT_LOCK_PENDING; 275 } 276 lockmgr(&lock->lock, LK_RELEASE); 277 return locked; 278 } 279 280 int ttm_vt_lock(struct ttm_lock *lock, 281 bool interruptible, 282 struct ttm_object_file *tfile) 283 { 284 const char *wmsg; 285 int flags, ret; 286 287 ret = 0; 288 if (interruptible) { 289 flags = PCATCH; 290 wmsg = "ttmwi"; 291 } else { 292 flags = 0; 293 wmsg = "ttmw"; 294 } 295 lockmgr(&lock->lock, LK_EXCLUSIVE); 296 while (!__ttm_vt_lock(lock)) { 297 ret = lksleep(lock, &lock->lock, 0, wmsg, 0); 298 if (interruptible && ret != 0) { 299 lock->flags &= ~TTM_VT_LOCK_PENDING; 300 wakeup(lock); 301 break; 302 } 303 } 304 305 /* 306 * Add a base-object, the destructor of which will 307 * make sure the lock is released if the client dies 308 * while holding it. 309 */ 310 311 ret = ttm_base_object_init(tfile, &lock->base, false, 312 ttm_lock_type, &ttm_vt_lock_remove, NULL); 313 if (ret) 314 (void)__ttm_vt_unlock(lock); 315 else 316 lock->vt_holder = tfile; 317 318 return (-ret); 319 } 320 EXPORT_SYMBOL(ttm_vt_lock); 321 322 int ttm_vt_unlock(struct ttm_lock *lock) 323 { 324 return ttm_ref_object_base_unref(lock->vt_holder, 325 lock->base.hash.key, TTM_REF_USAGE); 326 } 327 EXPORT_SYMBOL(ttm_vt_unlock); 328 329 void ttm_suspend_unlock(struct ttm_lock *lock) 330 { 331 lockmgr(&lock->lock, LK_EXCLUSIVE); 332 lock->flags &= ~TTM_SUSPEND_LOCK; 333 wakeup(lock); 334 lockmgr(&lock->lock, LK_RELEASE); 335 } 336 EXPORT_SYMBOL(ttm_suspend_unlock); 337 338 static bool __ttm_suspend_lock(struct ttm_lock *lock) 339 { 340 bool locked = false; 341 342 lockmgr(&lock->lock, LK_EXCLUSIVE); 343 if (lock->rw == 0) { 344 lock->flags &= ~TTM_SUSPEND_LOCK_PENDING; 345 lock->flags |= TTM_SUSPEND_LOCK; 346 locked = true; 347 } else { 348 lock->flags |= TTM_SUSPEND_LOCK_PENDING; 349 } 350 lockmgr(&lock->lock, LK_RELEASE); 351 return locked; 352 } 353 354 void ttm_suspend_lock(struct ttm_lock *lock) 355 { 356 lockmgr(&lock->lock, LK_EXCLUSIVE); 357 while (!__ttm_suspend_lock(lock)) 358 lksleep(lock, &lock->lock, 0, "ttms", 0); 359 lockmgr(&lock->lock, LK_RELEASE); 360 } 361 EXPORT_SYMBOL(ttm_suspend_lock); 362