1247835Skib/************************************************************************** 2247835Skib * 3247835Skib * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA 4247835Skib * All Rights Reserved. 5247835Skib * 6247835Skib * Permission is hereby granted, free of charge, to any person obtaining a 7247835Skib * copy of this software and associated documentation files (the 8247835Skib * "Software"), to deal in the Software without restriction, including 9247835Skib * without limitation the rights to use, copy, modify, merge, publish, 10247835Skib * distribute, sub license, and/or sell copies of the Software, and to 11247835Skib * permit persons to whom the Software is furnished to do so, subject to 12247835Skib * the following conditions: 13247835Skib * 14247835Skib * The above copyright notice and this permission notice (including the 15247835Skib * next paragraph) shall be included in all copies or substantial portions 16247835Skib * of the Software. 17247835Skib * 18247835Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19247835Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20247835Skib * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 21247835Skib * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 22247835Skib * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23247835Skib * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24247835Skib * USE OR OTHER DEALINGS IN THE SOFTWARE. 25247835Skib * 26247835Skib **************************************************************************/ 27247835Skib/* 28247835Skib * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> 29247835Skib */ 30247835Skib/* 31247835Skib * Copyright (c) 2013 The FreeBSD Foundation 32247835Skib * All rights reserved. 33247835Skib * 34247835Skib * Portions of this software were developed by Konstantin Belousov 35247835Skib * <kib@FreeBSD.org> under sponsorship from the FreeBSD Foundation. 36247835Skib */ 37247835Skib 38247835Skib#include <sys/cdefs.h> 39247835Skib__FBSDID("$FreeBSD$"); 40247835Skib 41247835Skib#include <dev/drm2/ttm/ttm_lock.h> 42247835Skib#include <dev/drm2/ttm/ttm_module.h> 43247835Skib 44247835Skib#define TTM_WRITE_LOCK_PENDING (1 << 0) 45247835Skib#define TTM_VT_LOCK_PENDING (1 << 1) 46247835Skib#define TTM_SUSPEND_LOCK_PENDING (1 << 2) 47247835Skib#define TTM_VT_LOCK (1 << 3) 48247835Skib#define TTM_SUSPEND_LOCK (1 << 4) 49247835Skib 50247835Skibvoid ttm_lock_init(struct ttm_lock *lock) 51247835Skib{ 52247835Skib mtx_init(&lock->lock, "ttmlk", NULL, MTX_DEF); 53247835Skib lock->rw = 0; 54247835Skib lock->flags = 0; 55247835Skib lock->kill_takers = false; 56247835Skib lock->signal = SIGKILL; 57247835Skib} 58247835Skib 59247835Skibstatic void 60247835Skibttm_lock_send_sig(int signo) 61247835Skib{ 62247835Skib struct proc *p; 63247835Skib 64247835Skib p = curproc; /* XXXKIB curthread ? */ 65247835Skib PROC_LOCK(p); 66247835Skib kern_psignal(p, signo); 67247835Skib PROC_UNLOCK(p); 68247835Skib} 69247835Skib 70247835Skibvoid ttm_read_unlock(struct ttm_lock *lock) 71247835Skib{ 72247835Skib mtx_lock(&lock->lock); 73247835Skib if (--lock->rw == 0) 74247835Skib wakeup(lock); 75247835Skib mtx_unlock(&lock->lock); 76247835Skib} 77247835Skib 78247835Skibstatic bool __ttm_read_lock(struct ttm_lock *lock) 79247835Skib{ 80247835Skib bool locked = false; 81247835Skib 82247835Skib if (unlikely(lock->kill_takers)) { 83247835Skib ttm_lock_send_sig(lock->signal); 84247835Skib return false; 85247835Skib } 86247835Skib if (lock->rw >= 0 && lock->flags == 0) { 87247835Skib ++lock->rw; 88247835Skib locked = true; 89247835Skib } 90247835Skib return locked; 91247835Skib} 92247835Skib 93247835Skibint 94247835Skibttm_read_lock(struct ttm_lock *lock, bool interruptible) 95247835Skib{ 96247835Skib const char *wmsg; 97247835Skib int flags, ret; 98247835Skib 99247835Skib ret = 0; 100247835Skib if (interruptible) { 101247835Skib flags = PCATCH; 102247835Skib wmsg = "ttmri"; 103247835Skib } else { 104247835Skib flags = 0; 105247835Skib wmsg = "ttmr"; 106247835Skib } 107247835Skib mtx_lock(&lock->lock); 108247835Skib while (!__ttm_read_lock(lock)) { 109247835Skib ret = msleep(lock, &lock->lock, flags, wmsg, 0); 110263170Sdumbbell if (ret == EINTR) 111263170Sdumbbell ret = ERESTARTSYS; 112247835Skib if (ret != 0) 113247835Skib break; 114247835Skib } 115247835Skib return (-ret); 116247835Skib} 117247835Skib 118247835Skibstatic bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked) 119247835Skib{ 120247835Skib bool block = true; 121247835Skib 122247835Skib *locked = false; 123247835Skib 124247835Skib if (unlikely(lock->kill_takers)) { 125247835Skib ttm_lock_send_sig(lock->signal); 126247835Skib return false; 127247835Skib } 128247835Skib if (lock->rw >= 0 && lock->flags == 0) { 129247835Skib ++lock->rw; 130247835Skib block = false; 131247835Skib *locked = true; 132247835Skib } else if (lock->flags == 0) { 133247835Skib block = false; 134247835Skib } 135247835Skib 136247835Skib return !block; 137247835Skib} 138247835Skib 139247835Skibint ttm_read_trylock(struct ttm_lock *lock, bool interruptible) 140247835Skib{ 141247835Skib const char *wmsg; 142247835Skib int flags, ret; 143247835Skib bool locked; 144247835Skib 145247835Skib ret = 0; 146247835Skib if (interruptible) { 147247835Skib flags = PCATCH; 148247835Skib wmsg = "ttmrti"; 149247835Skib } else { 150247835Skib flags = 0; 151247835Skib wmsg = "ttmrt"; 152247835Skib } 153247835Skib mtx_lock(&lock->lock); 154247835Skib while (!__ttm_read_trylock(lock, &locked)) { 155247835Skib ret = msleep(lock, &lock->lock, flags, wmsg, 0); 156263170Sdumbbell if (ret == EINTR) 157263170Sdumbbell ret = ERESTARTSYS; 158247835Skib if (ret != 0) 159247835Skib break; 160247835Skib } 161247835Skib MPASS(!locked || ret == 0); 162247835Skib mtx_unlock(&lock->lock); 163247835Skib 164247835Skib return (locked) ? 0 : -EBUSY; 165247835Skib} 166247835Skib 167247835Skibvoid ttm_write_unlock(struct ttm_lock *lock) 168247835Skib{ 169247835Skib mtx_lock(&lock->lock); 170247835Skib lock->rw = 0; 171247835Skib wakeup(lock); 172247835Skib mtx_unlock(&lock->lock); 173247835Skib} 174247835Skib 175247835Skibstatic bool __ttm_write_lock(struct ttm_lock *lock) 176247835Skib{ 177247835Skib bool locked = false; 178247835Skib 179247835Skib if (unlikely(lock->kill_takers)) { 180247835Skib ttm_lock_send_sig(lock->signal); 181247835Skib return false; 182247835Skib } 183247835Skib if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) { 184247835Skib lock->rw = -1; 185247835Skib lock->flags &= ~TTM_WRITE_LOCK_PENDING; 186247835Skib locked = true; 187247835Skib } else { 188247835Skib lock->flags |= TTM_WRITE_LOCK_PENDING; 189247835Skib } 190247835Skib return locked; 191247835Skib} 192247835Skib 193247835Skibint 194247835Skibttm_write_lock(struct ttm_lock *lock, bool interruptible) 195247835Skib{ 196247835Skib const char *wmsg; 197247835Skib int flags, ret; 198247835Skib 199247835Skib ret = 0; 200247835Skib if (interruptible) { 201247835Skib flags = PCATCH; 202247835Skib wmsg = "ttmwi"; 203247835Skib } else { 204247835Skib flags = 0; 205247835Skib wmsg = "ttmw"; 206247835Skib } 207247835Skib mtx_lock(&lock->lock); 208247835Skib /* XXXKIB: linux uses __ttm_read_lock for uninterruptible sleeps */ 209247835Skib while (!__ttm_write_lock(lock)) { 210247835Skib ret = msleep(lock, &lock->lock, flags, wmsg, 0); 211263170Sdumbbell if (ret == EINTR) 212263170Sdumbbell ret = ERESTARTSYS; 213247835Skib if (interruptible && ret != 0) { 214247835Skib lock->flags &= ~TTM_WRITE_LOCK_PENDING; 215247835Skib wakeup(lock); 216247835Skib break; 217247835Skib } 218247835Skib } 219247835Skib mtx_unlock(&lock->lock); 220247835Skib 221247835Skib return (-ret); 222247835Skib} 223247835Skib 224247835Skibvoid ttm_write_lock_downgrade(struct ttm_lock *lock) 225247835Skib{ 226247835Skib mtx_lock(&lock->lock); 227247835Skib lock->rw = 1; 228247835Skib wakeup(lock); 229247835Skib mtx_unlock(&lock->lock); 230247835Skib} 231247835Skib 232247835Skibstatic int __ttm_vt_unlock(struct ttm_lock *lock) 233247835Skib{ 234247835Skib int ret = 0; 235247835Skib 236247835Skib mtx_lock(&lock->lock); 237247835Skib if (unlikely(!(lock->flags & TTM_VT_LOCK))) 238247835Skib ret = -EINVAL; 239247835Skib lock->flags &= ~TTM_VT_LOCK; 240247835Skib wakeup(lock); 241247835Skib mtx_unlock(&lock->lock); 242247835Skib 243247835Skib return ret; 244247835Skib} 245247835Skib 246247835Skibstatic void ttm_vt_lock_remove(struct ttm_base_object **p_base) 247247835Skib{ 248247835Skib struct ttm_base_object *base = *p_base; 249247835Skib struct ttm_lock *lock = container_of(base, struct ttm_lock, base); 250247835Skib int ret; 251247835Skib 252247835Skib *p_base = NULL; 253247835Skib ret = __ttm_vt_unlock(lock); 254247835Skib MPASS(ret == 0); 255247835Skib} 256247835Skib 257247835Skibstatic bool __ttm_vt_lock(struct ttm_lock *lock) 258247835Skib{ 259247835Skib bool locked = false; 260247835Skib 261247835Skib if (lock->rw == 0) { 262247835Skib lock->flags &= ~TTM_VT_LOCK_PENDING; 263247835Skib lock->flags |= TTM_VT_LOCK; 264247835Skib locked = true; 265247835Skib } else { 266247835Skib lock->flags |= TTM_VT_LOCK_PENDING; 267247835Skib } 268247835Skib return locked; 269247835Skib} 270247835Skib 271247835Skibint ttm_vt_lock(struct ttm_lock *lock, 272247835Skib bool interruptible, 273247835Skib struct ttm_object_file *tfile) 274247835Skib{ 275247835Skib const char *wmsg; 276247835Skib int flags, ret; 277247835Skib 278247835Skib ret = 0; 279247835Skib if (interruptible) { 280247835Skib flags = PCATCH; 281247835Skib wmsg = "ttmwi"; 282247835Skib } else { 283247835Skib flags = 0; 284247835Skib wmsg = "ttmw"; 285247835Skib } 286247835Skib mtx_lock(&lock->lock); 287247835Skib while (!__ttm_vt_lock(lock)) { 288247835Skib ret = msleep(lock, &lock->lock, flags, wmsg, 0); 289263170Sdumbbell if (ret == EINTR) 290263170Sdumbbell ret = ERESTARTSYS; 291247835Skib if (interruptible && ret != 0) { 292247835Skib lock->flags &= ~TTM_VT_LOCK_PENDING; 293247835Skib wakeup(lock); 294247835Skib break; 295247835Skib } 296247835Skib } 297247835Skib 298247835Skib /* 299247835Skib * Add a base-object, the destructor of which will 300247835Skib * make sure the lock is released if the client dies 301247835Skib * while holding it. 302247835Skib */ 303247835Skib 304247835Skib ret = ttm_base_object_init(tfile, &lock->base, false, 305247835Skib ttm_lock_type, &ttm_vt_lock_remove, NULL); 306247835Skib if (ret) 307247835Skib (void)__ttm_vt_unlock(lock); 308247835Skib else 309247835Skib lock->vt_holder = tfile; 310247835Skib 311247835Skib return (-ret); 312247835Skib} 313247835Skib 314247835Skibint ttm_vt_unlock(struct ttm_lock *lock) 315247835Skib{ 316247835Skib return ttm_ref_object_base_unref(lock->vt_holder, 317247835Skib lock->base.hash.key, TTM_REF_USAGE); 318247835Skib} 319247835Skib 320247835Skibvoid ttm_suspend_unlock(struct ttm_lock *lock) 321247835Skib{ 322247835Skib mtx_lock(&lock->lock); 323247835Skib lock->flags &= ~TTM_SUSPEND_LOCK; 324247835Skib wakeup(lock); 325247835Skib mtx_unlock(&lock->lock); 326247835Skib} 327247835Skib 328247835Skibstatic bool __ttm_suspend_lock(struct ttm_lock *lock) 329247835Skib{ 330247835Skib bool locked = false; 331247835Skib 332247835Skib if (lock->rw == 0) { 333247835Skib lock->flags &= ~TTM_SUSPEND_LOCK_PENDING; 334247835Skib lock->flags |= TTM_SUSPEND_LOCK; 335247835Skib locked = true; 336247835Skib } else { 337247835Skib lock->flags |= TTM_SUSPEND_LOCK_PENDING; 338247835Skib } 339247835Skib return locked; 340247835Skib} 341247835Skib 342247835Skibvoid ttm_suspend_lock(struct ttm_lock *lock) 343247835Skib{ 344247835Skib mtx_lock(&lock->lock); 345247835Skib while (!__ttm_suspend_lock(lock)) 346247835Skib msleep(lock, &lock->lock, 0, "ttms", 0); 347247835Skib mtx_unlock(&lock->lock); 348247835Skib} 349