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