|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.1 (the "License"). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License. ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: ! 25: /* Copyright (c) 1995, 1997 Apple Computer, Inc. All Rights Reserved */ ! 26: /* ! 27: * Copyright (c) 1995 ! 28: * The Regents of the University of California. All rights reserved. ! 29: * ! 30: * This code contains ideas from software contributed to Berkeley by ! 31: * Avadis Tevanian, Jr., Michael Wayne Young, and the Mach Operating ! 32: * System project at Carnegie-Mellon University. ! 33: * ! 34: * Redistribution and use in source and binary forms, with or without ! 35: * modification, are permitted provided that the following conditions ! 36: * are met: ! 37: * 1. Redistributions of source code must retain the above copyright ! 38: * notice, this list of conditions and the following disclaimer. ! 39: * 2. Redistributions in binary form must reproduce the above copyright ! 40: * notice, this list of conditions and the following disclaimer in the ! 41: * documentation and/or other materials provided with the distribution. ! 42: * 3. All advertising materials mentioning features or use of this software ! 43: * must display the following acknowledgement: ! 44: * This product includes software developed by the University of ! 45: * California, Berkeley and its contributors. ! 46: * 4. Neither the name of the University nor the names of its contributors ! 47: * may be used to endorse or promote products derived from this software ! 48: * without specific prior written permission. ! 49: * ! 50: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ! 51: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ! 52: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ! 53: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ! 54: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ! 55: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ! 56: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 57: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ! 58: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ! 59: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ! 60: * SUCH DAMAGE. ! 61: * ! 62: * @(#)kern_lock.c 8.18 (Berkeley) 5/21/95 ! 63: */ ! 64: ! 65: #include <sys/param.h> ! 66: #include <sys/proc.h> ! 67: #include <sys/lock.h> ! 68: #include <machine/cpu.h> ! 69: ! 70: #include <mach/mach_types.h> ! 71: ! 72: /* ! 73: * Locking primitives implementation. ! 74: * Locks provide shared/exclusive sychronization. ! 75: */ ! 76: ! 77: #if 0 ! 78: #define COUNT(p, x) if (p) (p)->p_locks += (x) ! 79: #else ! 80: #define COUNT(p, x) ! 81: #endif ! 82: ! 83: #if NCPUS > 1 ! 84: ! 85: /* ! 86: * For multiprocessor system, try spin lock first. ! 87: * ! 88: * This should be inline expanded below, but we cannot have #if ! 89: * inside a multiline define. ! 90: */ ! 91: int lock_wait_time = 100; ! 92: #define PAUSE(lkp, wanted) \ ! 93: if (lock_wait_time > 0) { \ ! 94: int i; \ ! 95: \ ! 96: simple_unlock(&lkp->lk_interlock); \ ! 97: for (i = lock_wait_time; i > 0; i--) \ ! 98: if (!(wanted)) \ ! 99: break; \ ! 100: simple_lock(&lkp->lk_interlock); \ ! 101: } \ ! 102: if (!(wanted)) \ ! 103: break; ! 104: ! 105: #else /* NCPUS == 1 */ ! 106: ! 107: /* ! 108: * It is an error to spin on a uniprocessor as nothing will ever cause ! 109: * the simple lock to clear while we are executing. ! 110: */ ! 111: #define PAUSE(lkp, wanted) ! 112: ! 113: #endif /* NCPUS == 1 */ ! 114: ! 115: /* ! 116: * Acquire a resource. ! 117: */ ! 118: #define ACQUIRE(lkp, error, extflags, wanted) \ ! 119: PAUSE(lkp, wanted); \ ! 120: for (error = 0; wanted; ) { \ ! 121: (lkp)->lk_waitcount++; \ ! 122: simple_unlock(&(lkp)->lk_interlock); \ ! 123: error = tsleep((void *)lkp, (lkp)->lk_prio, \ ! 124: (lkp)->lk_wmesg, (lkp)->lk_timo); \ ! 125: simple_lock(&(lkp)->lk_interlock); \ ! 126: (lkp)->lk_waitcount--; \ ! 127: if (error) \ ! 128: break; \ ! 129: if ((extflags) & LK_SLEEPFAIL) { \ ! 130: error = ENOLCK; \ ! 131: break; \ ! 132: } \ ! 133: } ! 134: ! 135: /* ! 136: * Initialize a lock; required before use. ! 137: */ ! 138: void ! 139: lockinit(lkp, prio, wmesg, timo, flags) ! 140: struct lock__bsd__ *lkp; ! 141: int prio; ! 142: char *wmesg; ! 143: int timo; ! 144: int flags; ! 145: { ! 146: ! 147: bzero(lkp, sizeof(struct lock__bsd__)); ! 148: simple_lock_init(&lkp->lk_interlock); ! 149: lkp->lk_flags = flags & LK_EXTFLG_MASK; ! 150: lkp->lk_prio = prio; ! 151: lkp->lk_timo = timo; ! 152: lkp->lk_wmesg = wmesg; ! 153: lkp->lk_lockholder = LK_NOPROC; ! 154: lkp->lk_lockthread = 0; ! 155: } ! 156: ! 157: /* ! 158: * Determine the status of a lock. ! 159: */ ! 160: int ! 161: lockstatus(lkp) ! 162: struct lock__bsd__ *lkp; ! 163: { ! 164: int lock_type = 0; ! 165: ! 166: simple_lock(&lkp->lk_interlock); ! 167: if (lkp->lk_exclusivecount != 0) ! 168: lock_type = LK_EXCLUSIVE; ! 169: else if (lkp->lk_sharecount != 0) ! 170: lock_type = LK_SHARED; ! 171: simple_unlock(&lkp->lk_interlock); ! 172: return (lock_type); ! 173: } ! 174: ! 175: /* ! 176: * Set, change, or release a lock. ! 177: * ! 178: * Shared requests increment the shared count. Exclusive requests set the ! 179: * LK_WANT_EXCL flag (preventing further shared locks), and wait for already ! 180: * accepted shared locks and shared-to-exclusive upgrades to go away. ! 181: */ ! 182: int ! 183: lockmgr(lkp, flags, interlkp, p) ! 184: struct lock__bsd__ *lkp; ! 185: u_int flags; ! 186: simple_lock_t interlkp; ! 187: struct proc *p; ! 188: { ! 189: int error; ! 190: pid_t pid; ! 191: int extflags; ! 192: void *self; ! 193: ! 194: error = 0; self = current_thread(); ! 195: if (p) ! 196: pid = p->p_pid; ! 197: else ! 198: pid = LK_KERNPROC; ! 199: simple_lock(&lkp->lk_interlock); ! 200: if (flags & LK_INTERLOCK) ! 201: simple_unlock(interlkp); ! 202: extflags = (flags | lkp->lk_flags) & LK_EXTFLG_MASK; ! 203: #if 0 ! 204: /* ! 205: * Once a lock has drained, the LK_DRAINING flag is set and an ! 206: * exclusive lock is returned. The only valid operation thereafter ! 207: * is a single release of that exclusive lock. This final release ! 208: * clears the LK_DRAINING flag and sets the LK_DRAINED flag. Any ! 209: * further requests of any sort will result in a panic. The bits ! 210: * selected for these two flags are chosen so that they will be set ! 211: * in memory that is freed (freed memory is filled with 0xdeadbeef). ! 212: * The final release is permitted to give a new lease on life to ! 213: * the lock by specifying LK_REENABLE. ! 214: */ ! 215: if (lkp->lk_flags & (LK_DRAINING|LK_DRAINED)) { ! 216: if (lkp->lk_flags & LK_DRAINED) ! 217: panic("lockmgr: using decommissioned lock"); ! 218: if ((flags & LK_TYPE_MASK) != LK_RELEASE || ! 219: (lkp->lk_lockholder != pid && lkp->lk_lockthread != self) ! 220: panic("lockmgr: non-release on draining lock: %d\n", ! 221: flags & LK_TYPE_MASK); ! 222: lkp->lk_flags &= ~LK_DRAINING; ! 223: if ((flags & LK_REENABLE) == 0) ! 224: lkp->lk_flags |= LK_DRAINED; ! 225: } ! 226: #endif ! 227: ! 228: switch (flags & LK_TYPE_MASK) { ! 229: ! 230: case LK_SHARED: ! 231: if (lkp->lk_lockholder != pid || lkp->lk_lockthread != self) { ! 232: /* ! 233: * If just polling, check to see if we will block. ! 234: */ ! 235: if ((extflags & LK_NOWAIT) && (lkp->lk_flags & ! 236: (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE))) { ! 237: error = EBUSY; ! 238: break; ! 239: } ! 240: /* ! 241: * Wait for exclusive locks and upgrades to clear. ! 242: */ ! 243: ACQUIRE(lkp, error, extflags, lkp->lk_flags & ! 244: (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)); ! 245: if (error) ! 246: break; ! 247: lkp->lk_sharecount++; ! 248: COUNT(p, 1); ! 249: break; ! 250: } ! 251: /* ! 252: * We hold an exclusive lock, so downgrade it to shared. ! 253: * An alternative would be to fail with EDEADLK. ! 254: */ ! 255: lkp->lk_sharecount++; ! 256: COUNT(p, 1); ! 257: /* fall into downgrade */ ! 258: ! 259: case LK_DOWNGRADE: ! 260: if (lkp->lk_lockholder != pid || ! 261: lkp->lk_lockthread != self || ! 262: lkp->lk_exclusivecount == 0) ! 263: panic("lockmgr: not holding exclusive lock"); ! 264: lkp->lk_sharecount += lkp->lk_exclusivecount; ! 265: lkp->lk_exclusivecount = 0; ! 266: lkp->lk_flags &= ~LK_HAVE_EXCL; ! 267: lkp->lk_lockholder = LK_NOPROC; ! 268: lkp->lk_lockthread = 0; ! 269: if (lkp->lk_waitcount) ! 270: wakeup((void *)lkp); ! 271: break; ! 272: ! 273: case LK_EXCLUPGRADE: ! 274: /* ! 275: * If another process is ahead of us to get an upgrade, ! 276: * then we want to fail rather than have an intervening ! 277: * exclusive access. ! 278: */ ! 279: if (lkp->lk_flags & LK_WANT_UPGRADE) { ! 280: lkp->lk_sharecount--; ! 281: COUNT(p, -1); ! 282: error = EBUSY; ! 283: break; ! 284: } ! 285: /* fall into normal upgrade */ ! 286: ! 287: case LK_UPGRADE: ! 288: /* ! 289: * Upgrade a shared lock to an exclusive one. If another ! 290: * shared lock has already requested an upgrade to an ! 291: * exclusive lock, our shared lock is released and an ! 292: * exclusive lock is requested (which will be granted ! 293: * after the upgrade). If we return an error, the file ! 294: * will always be unlocked. ! 295: */ ! 296: if ((lkp->lk_lockholder == pid && ! 297: lkp->lk_lockthread == self) || ! 298: lkp->lk_sharecount <= 0) ! 299: panic("lockmgr: upgrade exclusive lock"); ! 300: lkp->lk_sharecount--; ! 301: COUNT(p, -1); ! 302: /* ! 303: * If we are just polling, check to see if we will block. ! 304: */ ! 305: if ((extflags & LK_NOWAIT) && ! 306: ((lkp->lk_flags & LK_WANT_UPGRADE) || ! 307: lkp->lk_sharecount > 1)) { ! 308: error = EBUSY; ! 309: break; ! 310: } ! 311: if ((lkp->lk_flags & LK_WANT_UPGRADE) == 0) { ! 312: /* ! 313: * We are first shared lock to request an upgrade, so ! 314: * request upgrade and wait for the shared count to ! 315: * drop to zero, then take exclusive lock. ! 316: */ ! 317: lkp->lk_flags |= LK_WANT_UPGRADE; ! 318: ACQUIRE(lkp, error, extflags, lkp->lk_sharecount); ! 319: lkp->lk_flags &= ~LK_WANT_UPGRADE; ! 320: if (error) ! 321: break; ! 322: lkp->lk_flags |= LK_HAVE_EXCL; ! 323: lkp->lk_lockholder = pid; ! 324: lkp->lk_lockthread = self; ! 325: if (lkp->lk_exclusivecount != 0) ! 326: panic("lockmgr: non-zero exclusive count"); ! 327: lkp->lk_exclusivecount = 1; ! 328: COUNT(p, 1); ! 329: break; ! 330: } ! 331: /* ! 332: * Someone else has requested upgrade. Release our shared ! 333: * lock, awaken upgrade requestor if we are the last shared ! 334: * lock, then request an exclusive lock. ! 335: */ ! 336: if (lkp->lk_sharecount == 0 && lkp->lk_waitcount) ! 337: wakeup((void *)lkp); ! 338: /* fall into exclusive request */ ! 339: ! 340: case LK_EXCLUSIVE: ! 341: if (lkp->lk_lockholder == pid && lkp->lk_lockthread == self) { ! 342: /* ! 343: * Recursive lock. ! 344: */ ! 345: if ((extflags & LK_CANRECURSE) == 0) ! 346: panic("lockmgr: locking against myself"); ! 347: lkp->lk_exclusivecount++; ! 348: COUNT(p, 1); ! 349: break; ! 350: } ! 351: /* ! 352: * If we are just polling, check to see if we will sleep. ! 353: */ ! 354: if ((extflags & LK_NOWAIT) && ((lkp->lk_flags & ! 355: (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)) || ! 356: lkp->lk_sharecount != 0)) { ! 357: error = EBUSY; ! 358: break; ! 359: } ! 360: /* ! 361: * Try to acquire the want_exclusive flag. ! 362: */ ! 363: ACQUIRE(lkp, error, extflags, lkp->lk_flags & ! 364: (LK_HAVE_EXCL | LK_WANT_EXCL)); ! 365: if (error) ! 366: break; ! 367: lkp->lk_flags |= LK_WANT_EXCL; ! 368: /* ! 369: * Wait for shared locks and upgrades to finish. ! 370: */ ! 371: ACQUIRE(lkp, error, extflags, lkp->lk_sharecount != 0 || ! 372: (lkp->lk_flags & LK_WANT_UPGRADE)); ! 373: lkp->lk_flags &= ~LK_WANT_EXCL; ! 374: if (error) ! 375: break; ! 376: lkp->lk_flags |= LK_HAVE_EXCL; ! 377: lkp->lk_lockholder = pid; ! 378: lkp->lk_lockthread = self; ! 379: if (lkp->lk_exclusivecount != 0) ! 380: panic("lockmgr: non-zero exclusive count"); ! 381: lkp->lk_exclusivecount = 1; ! 382: COUNT(p, 1); ! 383: break; ! 384: ! 385: case LK_RELEASE: ! 386: if (lkp->lk_exclusivecount != 0) { ! 387: if (pid != lkp->lk_lockholder || ! 388: lkp->lk_lockthread != self) ! 389: panic("lockmgr: pid %d, not %s %d unlocking", ! 390: pid, "exclusive lock holder", ! 391: lkp->lk_lockholder); ! 392: lkp->lk_exclusivecount--; ! 393: COUNT(p, -1); ! 394: if (lkp->lk_exclusivecount == 0) { ! 395: lkp->lk_flags &= ~LK_HAVE_EXCL; ! 396: lkp->lk_lockholder = LK_NOPROC; ! 397: lkp->lk_lockthread = 0; ! 398: } ! 399: } else if (lkp->lk_sharecount != 0) { ! 400: lkp->lk_sharecount--; ! 401: COUNT(p, -1); ! 402: } ! 403: if (lkp->lk_waitcount) ! 404: wakeup((void *)lkp); ! 405: break; ! 406: ! 407: case LK_DRAIN: ! 408: /* ! 409: * Check that we do not already hold the lock, as it can ! 410: * never drain if we do. Unfortunately, we have no way to ! 411: * check for holding a shared lock, but at least we can ! 412: * check for an exclusive one. ! 413: */ ! 414: if (lkp->lk_lockholder == pid && lkp->lk_lockthread == self) ! 415: panic("lockmgr: draining against myself"); ! 416: /* ! 417: * If we are just polling, check to see if we will sleep. ! 418: */ ! 419: if ((extflags & LK_NOWAIT) && ((lkp->lk_flags & ! 420: (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)) || ! 421: lkp->lk_sharecount != 0 || lkp->lk_waitcount != 0)) { ! 422: error = EBUSY; ! 423: break; ! 424: } ! 425: PAUSE(lkp, ((lkp->lk_flags & ! 426: (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)) || ! 427: lkp->lk_sharecount != 0 || lkp->lk_waitcount != 0)); ! 428: for (error = 0; ((lkp->lk_flags & ! 429: (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)) || ! 430: lkp->lk_sharecount != 0 || lkp->lk_waitcount != 0); ) { ! 431: lkp->lk_flags |= LK_WAITDRAIN; ! 432: simple_unlock(&lkp->lk_interlock); ! 433: if (error = tsleep((void *)&lkp->lk_flags, lkp->lk_prio, ! 434: lkp->lk_wmesg, lkp->lk_timo)) ! 435: return (error); ! 436: if ((extflags) & LK_SLEEPFAIL) ! 437: return (ENOLCK); ! 438: simple_lock(&lkp->lk_interlock); ! 439: } ! 440: lkp->lk_flags |= LK_DRAINING | LK_HAVE_EXCL; ! 441: lkp->lk_lockholder = pid; ! 442: lkp->lk_lockthread = self; ! 443: lkp->lk_exclusivecount = 1; ! 444: COUNT(p, 1); ! 445: break; ! 446: ! 447: default: ! 448: simple_unlock(&lkp->lk_interlock); ! 449: panic("lockmgr: unknown locktype request %d", ! 450: flags & LK_TYPE_MASK); ! 451: /* NOTREACHED */ ! 452: } ! 453: if ((lkp->lk_flags & LK_WAITDRAIN) && ((lkp->lk_flags & ! 454: (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)) == 0 && ! 455: lkp->lk_sharecount == 0 && lkp->lk_waitcount == 0)) { ! 456: lkp->lk_flags &= ~LK_WAITDRAIN; ! 457: wakeup((void *)&lkp->lk_flags); ! 458: } ! 459: simple_unlock(&lkp->lk_interlock); ! 460: return (error); ! 461: } ! 462: ! 463: /* ! 464: * Print out information about state of a lock. Used by VOP_PRINT ! 465: * routines to display ststus about contained locks. ! 466: */ ! 467: lockmgr_printinfo(lkp) ! 468: struct lock__bsd__ *lkp; ! 469: { ! 470: ! 471: if (lkp->lk_sharecount) ! 472: printf(" lock type %s: SHARED (count %d)", lkp->lk_wmesg, ! 473: lkp->lk_sharecount); ! 474: else if (lkp->lk_flags & LK_HAVE_EXCL) ! 475: printf(" lock type %s: EXCL (count %d) by pid %d", ! 476: lkp->lk_wmesg, lkp->lk_exclusivecount, lkp->lk_lockholder); ! 477: if (lkp->lk_waitcount > 0) ! 478: printf(" with %d pending", lkp->lk_waitcount); ! 479: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.