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 != 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 != 0) 155 break; 156 } 157 MPASS(!locked || ret == 0); 158 mtx_unlock(&lock->lock); 159 160 return (locked) ? 0 : -EBUSY; 161 } 162 163 void ttm_write_unlock(struct ttm_lock *lock) 164 { 165 mtx_lock(&lock->lock); 166 lock->rw = 0; 167 wakeup(lock); 168 mtx_unlock(&lock->lock); 169 } 170 171 static bool __ttm_write_lock(struct ttm_lock *lock) 172 { 173 bool locked = false; 174 175 if (unlikely(lock->kill_takers)) { 176 ttm_lock_send_sig(lock->signal); 177 return false; 178 } 179 if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) { 180 lock->rw = -1; 181 lock->flags &= ~TTM_WRITE_LOCK_PENDING; 182 locked = true; 183 } else { 184 lock->flags |= TTM_WRITE_LOCK_PENDING; 185 } 186 return locked; 187 } 188 189 int 190 ttm_write_lock(struct ttm_lock *lock, bool interruptible) 191 { 192 const char *wmsg; 193 int flags, ret; 194 195 ret = 0; 196 if (interruptible) { 197 flags = PCATCH; 198 wmsg = "ttmwi"; 199 } else { 200 flags = 0; 201 wmsg = "ttmw"; 202 } 203 mtx_lock(&lock->lock); 204 /* XXXKIB: linux uses __ttm_read_lock for uninterruptible sleeps */ 205 while (!__ttm_write_lock(lock)) { 206 ret = msleep(lock, &lock->lock, flags, wmsg, 0); 207 if (interruptible && ret != 0) { 208 lock->flags &= ~TTM_WRITE_LOCK_PENDING; 209 wakeup(lock); 210 break; 211 } 212 } 213 mtx_unlock(&lock->lock); 214 215 return (-ret); 216 } 217 218 void ttm_write_lock_downgrade(struct ttm_lock *lock) 219 { 220 mtx_lock(&lock->lock); 221 lock->rw = 1; 222 wakeup(lock); 223 mtx_unlock(&lock->lock); 224 } 225 226 static int __ttm_vt_unlock(struct ttm_lock *lock) 227 { 228 int ret = 0; 229 230 mtx_lock(&lock->lock); 231 if (unlikely(!(lock->flags & TTM_VT_LOCK))) 232 ret = -EINVAL; 233 lock->flags &= ~TTM_VT_LOCK; 234 wakeup(lock); 235 mtx_unlock(&lock->lock); 236 237 return ret; 238 } 239 240 static void ttm_vt_lock_remove(struct ttm_base_object **p_base) 241 { 242 struct ttm_base_object *base = *p_base; 243 struct ttm_lock *lock = container_of(base, struct ttm_lock, base); 244 int ret; 245 246 *p_base = NULL; 247 ret = __ttm_vt_unlock(lock); 248 MPASS(ret == 0); 249 } 250 251 static bool __ttm_vt_lock(struct ttm_lock *lock) 252 { 253 bool locked = false; 254 255 if (lock->rw == 0) { 256 lock->flags &= ~TTM_VT_LOCK_PENDING; 257 lock->flags |= TTM_VT_LOCK; 258 locked = true; 259 } else { 260 lock->flags |= TTM_VT_LOCK_PENDING; 261 } 262 return locked; 263 } 264 265 int ttm_vt_lock(struct ttm_lock *lock, 266 bool interruptible, 267 struct ttm_object_file *tfile) 268 { 269 const char *wmsg; 270 int flags, ret; 271 272 ret = 0; 273 if (interruptible) { 274 flags = PCATCH; 275 wmsg = "ttmwi"; 276 } else { 277 flags = 0; 278 wmsg = "ttmw"; 279 } 280 mtx_lock(&lock->lock); 281 while (!__ttm_vt_lock(lock)) { 282 ret = msleep(lock, &lock->lock, flags, wmsg, 0); 283 if (interruptible && ret != 0) { 284 lock->flags &= ~TTM_VT_LOCK_PENDING; 285 wakeup(lock); 286 break; 287 } 288 } 289 290 /* 291 * Add a base-object, the destructor of which will 292 * make sure the lock is released if the client dies 293 * while holding it. 294 */ 295 296 ret = ttm_base_object_init(tfile, &lock->base, false, 297 ttm_lock_type, &ttm_vt_lock_remove, NULL); 298 if (ret) 299 (void)__ttm_vt_unlock(lock); 300 else 301 lock->vt_holder = tfile; 302 303 return (-ret); 304 } 305 306 int ttm_vt_unlock(struct ttm_lock *lock) 307 { 308 return ttm_ref_object_base_unref(lock->vt_holder, 309 lock->base.hash.key, TTM_REF_USAGE); 310 } 311 312 void ttm_suspend_unlock(struct ttm_lock *lock) 313 { 314 mtx_lock(&lock->lock); 315 lock->flags &= ~TTM_SUSPEND_LOCK; 316 wakeup(lock); 317 mtx_unlock(&lock->lock); 318 } 319 320 static bool __ttm_suspend_lock(struct ttm_lock *lock) 321 { 322 bool locked = false; 323 324 if (lock->rw == 0) { 325 lock->flags &= ~TTM_SUSPEND_LOCK_PENDING; 326 lock->flags |= TTM_SUSPEND_LOCK; 327 locked = true; 328 } else { 329 lock->flags |= TTM_SUSPEND_LOCK_PENDING; 330 } 331 return locked; 332 } 333 334 void ttm_suspend_lock(struct ttm_lock *lock) 335 { 336 mtx_lock(&lock->lock); 337 while (!__ttm_suspend_lock(lock)) 338 msleep(lock, &lock->lock, 0, "ttms", 0); 339 mtx_unlock(&lock->lock); 340 } 341