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