1 /*- 2 * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice(s), this list of conditions and the following disclaimer as 9 * the first lines of this file unmodified other than the possible 10 * addition of one or more copyright notices. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice(s), this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 25 * DAMAGE. 26 */ 27 28 /* 29 * Shared/exclusive locks. This implementation assures deterministic lock 30 * granting behavior, so that slocks and xlocks are interleaved. 31 * 32 * Priority propagation will not generally raise the priority of lock holders, 33 * so should not be relied upon in combination with sx locks. 34 */ 35 36 #include <sys/cdefs.h> 37 __FBSDID("$FreeBSD$"); 38 39 #include "opt_ddb.h" 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/ktr.h> 44 #include <sys/linker_set.h> 45 #include <sys/condvar.h> 46 #include <sys/lock.h> 47 #include <sys/mutex.h> 48 #include <sys/proc.h> 49 #include <sys/sx.h> 50 #include <sys/lock_profile.h> 51 52 #ifdef DDB 53 #include <ddb/ddb.h> 54 55 static void db_show_sx(struct lock_object *lock); 56 #endif 57 58 struct lock_class lock_class_sx = { 59 "sx", 60 LC_SLEEPLOCK | LC_SLEEPABLE | LC_RECURSABLE | LC_UPGRADABLE, 61 #ifdef DDB 62 db_show_sx 63 #endif 64 }; 65 66 #ifndef INVARIANTS 67 #define _sx_assert(sx, what, file, line) 68 #endif 69 70 void 71 sx_sysinit(void *arg) 72 { 73 struct sx_args *sargs = arg; 74 75 sx_init(sargs->sa_sx, sargs->sa_desc); 76 } 77 78 void 79 sx_init(struct sx *sx, const char *description) 80 { 81 82 sx->sx_lock = mtx_pool_find(mtxpool_lockbuilder, sx); 83 sx->sx_cnt = 0; 84 cv_init(&sx->sx_shrd_cv, description); 85 sx->sx_shrd_wcnt = 0; 86 cv_init(&sx->sx_excl_cv, description); 87 sx->sx_excl_wcnt = 0; 88 sx->sx_xholder = NULL; 89 lock_profile_object_init(&sx->sx_object, &lock_class_sx, description); 90 lock_init(&sx->sx_object, &lock_class_sx, description, NULL, 91 LO_WITNESS | LO_RECURSABLE | LO_SLEEPABLE | LO_UPGRADABLE); 92 } 93 94 void 95 sx_destroy(struct sx *sx) 96 { 97 98 KASSERT((sx->sx_cnt == 0 && sx->sx_shrd_wcnt == 0 && sx->sx_excl_wcnt == 99 0), ("%s (%s): holders or waiters\n", __func__, 100 sx->sx_object.lo_name)); 101 102 sx->sx_lock = NULL; 103 cv_destroy(&sx->sx_shrd_cv); 104 cv_destroy(&sx->sx_excl_cv); 105 106 lock_profile_object_destroy(&sx->sx_object); 107 lock_destroy(&sx->sx_object); 108 } 109 110 void 111 _sx_slock(struct sx *sx, const char *file, int line) 112 { 113 uint64_t waittime = 0; 114 int contested = 0; 115 116 mtx_lock(sx->sx_lock); 117 KASSERT(sx->sx_xholder != curthread, 118 ("%s (%s): slock while xlock is held @ %s:%d\n", __func__, 119 sx->sx_object.lo_name, file, line)); 120 WITNESS_CHECKORDER(&sx->sx_object, LOP_NEWORDER, file, line); 121 122 /* 123 * Loop in case we lose the race for lock acquisition. 124 */ 125 while (sx->sx_cnt < 0) { 126 sx->sx_shrd_wcnt++; 127 lock_profile_obtain_lock_failed(&sx->sx_object, &contested, &waittime); 128 cv_wait(&sx->sx_shrd_cv, sx->sx_lock); 129 sx->sx_shrd_wcnt--; 130 } 131 132 /* Acquire a shared lock. */ 133 sx->sx_cnt++; 134 135 if (sx->sx_cnt == 1) 136 lock_profile_obtain_lock_success(&sx->sx_object, contested, waittime, file, line); 137 138 LOCK_LOG_LOCK("SLOCK", &sx->sx_object, 0, 0, file, line); 139 WITNESS_LOCK(&sx->sx_object, 0, file, line); 140 curthread->td_locks++; 141 142 mtx_unlock(sx->sx_lock); 143 } 144 145 int 146 _sx_try_slock(struct sx *sx, const char *file, int line) 147 { 148 149 mtx_lock(sx->sx_lock); 150 if (sx->sx_cnt >= 0) { 151 sx->sx_cnt++; 152 LOCK_LOG_TRY("SLOCK", &sx->sx_object, 0, 1, file, line); 153 WITNESS_LOCK(&sx->sx_object, LOP_TRYLOCK, file, line); 154 curthread->td_locks++; 155 mtx_unlock(sx->sx_lock); 156 return (1); 157 } else { 158 LOCK_LOG_TRY("SLOCK", &sx->sx_object, 0, 0, file, line); 159 mtx_unlock(sx->sx_lock); 160 return (0); 161 } 162 } 163 164 void 165 _sx_xlock(struct sx *sx, const char *file, int line) 166 { 167 int contested = 0; 168 uint64_t waittime = 0; 169 170 mtx_lock(sx->sx_lock); 171 172 /* 173 * With sx locks, we're absolutely not permitted to recurse on 174 * xlocks, as it is fatal (deadlock). Normally, recursion is handled 175 * by WITNESS, but as it is not semantically correct to hold the 176 * xlock while in here, we consider it API abuse and put it under 177 * INVARIANTS. 178 */ 179 KASSERT(sx->sx_xholder != curthread, 180 ("%s (%s): xlock already held @ %s:%d", __func__, 181 sx->sx_object.lo_name, file, line)); 182 WITNESS_CHECKORDER(&sx->sx_object, LOP_NEWORDER | LOP_EXCLUSIVE, file, 183 line); 184 185 /* Loop in case we lose the race for lock acquisition. */ 186 while (sx->sx_cnt != 0) { 187 sx->sx_excl_wcnt++; 188 lock_profile_obtain_lock_failed(&sx->sx_object, &contested, &waittime); 189 cv_wait(&sx->sx_excl_cv, sx->sx_lock); 190 sx->sx_excl_wcnt--; 191 } 192 193 MPASS(sx->sx_cnt == 0); 194 195 /* Acquire an exclusive lock. */ 196 sx->sx_cnt--; 197 sx->sx_xholder = curthread; 198 199 lock_profile_obtain_lock_success(&sx->sx_object, contested, waittime, file, line); 200 LOCK_LOG_LOCK("XLOCK", &sx->sx_object, 0, 0, file, line); 201 WITNESS_LOCK(&sx->sx_object, LOP_EXCLUSIVE, file, line); 202 curthread->td_locks++; 203 204 mtx_unlock(sx->sx_lock); 205 } 206 207 int 208 _sx_try_xlock(struct sx *sx, const char *file, int line) 209 { 210 211 mtx_lock(sx->sx_lock); 212 if (sx->sx_cnt == 0) { 213 sx->sx_cnt--; 214 sx->sx_xholder = curthread; 215 LOCK_LOG_TRY("XLOCK", &sx->sx_object, 0, 1, file, line); 216 WITNESS_LOCK(&sx->sx_object, LOP_EXCLUSIVE | LOP_TRYLOCK, file, 217 line); 218 curthread->td_locks++; 219 mtx_unlock(sx->sx_lock); 220 return (1); 221 } else { 222 LOCK_LOG_TRY("XLOCK", &sx->sx_object, 0, 0, file, line); 223 mtx_unlock(sx->sx_lock); 224 return (0); 225 } 226 } 227 228 void 229 _sx_sunlock(struct sx *sx, const char *file, int line) 230 { 231 #ifdef LOCK_PROFILING 232 struct lock_object lo; 233 int count = -1; 234 #endif 235 _sx_assert(sx, SX_SLOCKED, file, line); 236 mtx_lock(sx->sx_lock); 237 238 curthread->td_locks--; 239 WITNESS_UNLOCK(&sx->sx_object, 0, file, line); 240 241 /* Release. */ 242 sx->sx_cnt--; 243 244 #ifdef LOCK_PROFILING 245 if (sx->sx_cnt == 0) { 246 memcpy(&lo, &sx->sx_object, sizeof(lo)); 247 sx->sx_object.lo_flags &= ~LO_CONTESTED; 248 count = 0; 249 } 250 #endif 251 /* 252 * If we just released the last shared lock, wake any waiters up, giving 253 * exclusive lockers precedence. In order to make sure that exclusive 254 * lockers won't be blocked forever, don't wake shared lock waiters if 255 * there are exclusive lock waiters. 256 */ 257 if (sx->sx_excl_wcnt > 0) { 258 if (sx->sx_cnt == 0) 259 cv_signal(&sx->sx_excl_cv); 260 } else if (sx->sx_shrd_wcnt > 0) 261 cv_broadcast(&sx->sx_shrd_cv); 262 263 LOCK_LOG_LOCK("SUNLOCK", &sx->sx_object, 0, 0, file, line); 264 265 mtx_unlock(sx->sx_lock); 266 #ifdef LOCK_PROFILING 267 if (count == 0) 268 lock_profile_release_lock(&lo); 269 #endif 270 } 271 272 void 273 _sx_xunlock(struct sx *sx, const char *file, int line) 274 { 275 #ifdef LOCK_PROFILING 276 struct lock_object lo; 277 #endif 278 279 _sx_assert(sx, SX_XLOCKED, file, line); 280 mtx_lock(sx->sx_lock); 281 MPASS(sx->sx_cnt == -1); 282 283 curthread->td_locks--; 284 WITNESS_UNLOCK(&sx->sx_object, LOP_EXCLUSIVE, file, line); 285 286 /* Release. */ 287 sx->sx_cnt++; 288 sx->sx_xholder = NULL; 289 290 #ifdef LOCK_PROFILING 291 memcpy(&lo, &sx->sx_object, sizeof(lo)); 292 sx->sx_object.lo_flags &= ~LO_CONTESTED; 293 #endif 294 /* 295 * Wake up waiters if there are any. Give precedence to slock waiters. 296 */ 297 if (sx->sx_shrd_wcnt > 0) 298 cv_broadcast(&sx->sx_shrd_cv); 299 else if (sx->sx_excl_wcnt > 0) 300 cv_signal(&sx->sx_excl_cv); 301 302 LOCK_LOG_LOCK("XUNLOCK", &sx->sx_object, 0, 0, file, line); 303 304 mtx_unlock(sx->sx_lock); 305 #ifdef LOCK_PROFILING 306 lock_profile_release_lock(&lo); 307 #endif 308 } 309 310 int 311 _sx_try_upgrade(struct sx *sx, const char *file, int line) 312 { 313 314 _sx_assert(sx, SX_SLOCKED, file, line); 315 mtx_lock(sx->sx_lock); 316 317 if (sx->sx_cnt == 1) { 318 sx->sx_cnt = -1; 319 sx->sx_xholder = curthread; 320 321 LOCK_LOG_TRY("XUPGRADE", &sx->sx_object, 0, 1, file, line); 322 WITNESS_UPGRADE(&sx->sx_object, LOP_EXCLUSIVE | LOP_TRYLOCK, 323 file, line); 324 325 mtx_unlock(sx->sx_lock); 326 return (1); 327 } else { 328 LOCK_LOG_TRY("XUPGRADE", &sx->sx_object, 0, 0, file, line); 329 mtx_unlock(sx->sx_lock); 330 return (0); 331 } 332 } 333 334 void 335 _sx_downgrade(struct sx *sx, const char *file, int line) 336 { 337 338 _sx_assert(sx, SX_XLOCKED, file, line); 339 mtx_lock(sx->sx_lock); 340 MPASS(sx->sx_cnt == -1); 341 342 WITNESS_DOWNGRADE(&sx->sx_object, 0, file, line); 343 344 sx->sx_cnt = 1; 345 sx->sx_xholder = NULL; 346 if (sx->sx_shrd_wcnt > 0) 347 cv_broadcast(&sx->sx_shrd_cv); 348 349 LOCK_LOG_LOCK("XDOWNGRADE", &sx->sx_object, 0, 0, file, line); 350 351 mtx_unlock(sx->sx_lock); 352 } 353 354 #ifdef INVARIANT_SUPPORT 355 #ifndef INVARIANTS 356 #undef _sx_assert 357 #endif 358 359 /* 360 * In the non-WITNESS case, sx_assert() can only detect that at least 361 * *some* thread owns an slock, but it cannot guarantee that *this* 362 * thread owns an slock. 363 */ 364 void 365 _sx_assert(struct sx *sx, int what, const char *file, int line) 366 { 367 368 if (panicstr != NULL) 369 return; 370 switch (what) { 371 case SX_LOCKED: 372 case SX_SLOCKED: 373 #ifdef WITNESS 374 witness_assert(&sx->sx_object, what, file, line); 375 #else 376 mtx_lock(sx->sx_lock); 377 if (sx->sx_cnt <= 0 && 378 (what == SX_SLOCKED || sx->sx_xholder != curthread)) 379 panic("Lock %s not %slocked @ %s:%d\n", 380 sx->sx_object.lo_name, (what == SX_SLOCKED) ? 381 "share " : "", file, line); 382 mtx_unlock(sx->sx_lock); 383 #endif 384 break; 385 case SX_XLOCKED: 386 mtx_lock(sx->sx_lock); 387 if (sx->sx_xholder != curthread) 388 panic("Lock %s not exclusively locked @ %s:%d\n", 389 sx->sx_object.lo_name, file, line); 390 mtx_unlock(sx->sx_lock); 391 break; 392 case SX_UNLOCKED: 393 #ifdef WITNESS 394 witness_assert(&sx->sx_object, what, file, line); 395 #else 396 /* 397 * We are able to check only exclusive lock here, 398 * we cannot assert that *this* thread owns slock. 399 */ 400 mtx_lock(sx->sx_lock); 401 if (sx->sx_xholder == curthread) 402 panic("Lock %s exclusively locked @ %s:%d\n", 403 sx->sx_object.lo_name, file, line); 404 mtx_unlock(sx->sx_lock); 405 #endif 406 break; 407 default: 408 panic("Unknown sx lock assertion: %d @ %s:%d", what, file, 409 line); 410 } 411 } 412 #endif /* INVARIANT_SUPPORT */ 413 414 #ifdef DDB 415 void 416 db_show_sx(struct lock_object *lock) 417 { 418 struct thread *td; 419 struct sx *sx; 420 421 sx = (struct sx *)lock; 422 423 db_printf(" state: "); 424 if (sx->sx_cnt < 0) { 425 td = sx->sx_xholder; 426 db_printf("XLOCK: %p (tid %d, pid %d, \"%s\")\n", td, 427 td->td_tid, td->td_proc->p_pid, td->td_proc->p_comm); 428 } else if (sx->sx_cnt > 0) 429 db_printf("SLOCK: %d locks\n", sx->sx_cnt); 430 else 431 db_printf("UNLOCKED\n"); 432 db_printf(" waiters: %d shared, %d exclusive\n", sx->sx_shrd_wcnt, 433 sx->sx_excl_wcnt); 434 } 435 436 /* 437 * Check to see if a thread that is blocked on a sleep queue is actually 438 * blocked on an sx lock. If so, output some details and return true. 439 * If the lock has an exclusive owner, return that in *ownerp. 440 */ 441 int 442 sx_chain(struct thread *td, struct thread **ownerp) 443 { 444 struct sx *sx; 445 struct cv *cv; 446 447 /* 448 * First, see if it looks like td is blocked on a condition 449 * variable. 450 */ 451 cv = td->td_wchan; 452 if (cv->cv_description != td->td_wmesg) 453 return (0); 454 455 /* 456 * Ok, see if it looks like td is blocked on the exclusive 457 * condition variable. 458 */ 459 sx = (struct sx *)((char *)cv - offsetof(struct sx, sx_excl_cv)); 460 if (LOCK_CLASS(&sx->sx_object) == &lock_class_sx && 461 sx->sx_excl_wcnt > 0) 462 goto ok; 463 464 /* 465 * Second, see if it looks like td is blocked on the shared 466 * condition variable. 467 */ 468 sx = (struct sx *)((char *)cv - offsetof(struct sx, sx_shrd_cv)); 469 if (LOCK_CLASS(&sx->sx_object) == &lock_class_sx && 470 sx->sx_shrd_wcnt > 0) 471 goto ok; 472 473 /* Doesn't seem to be an sx lock. */ 474 return (0); 475 476 ok: 477 /* We think we have an sx lock, so output some details. */ 478 db_printf("blocked on sx \"%s\" ", td->td_wmesg); 479 if (sx->sx_cnt >= 0) { 480 db_printf("SLOCK (count %d)\n", sx->sx_cnt); 481 *ownerp = NULL; 482 } else { 483 db_printf("XLOCK\n"); 484 *ownerp = sx->sx_xholder; 485 } 486 return (1); 487 } 488 #endif 489