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