168762Smckusick /* 268762Smckusick * Copyright (c) 1995 368762Smckusick * The Regents of the University of California. All rights reserved. 468762Smckusick * 568775Smckusick * This code contains ideas from software contributed to Berkeley by 668762Smckusick * Avadis Tevanian, Jr., Michael Wayne Young, and the Mach Operating 768762Smckusick * System project at Carnegie-Mellon University. 868762Smckusick * 968762Smckusick * %sccs.include.redist.c% 1068762Smckusick * 11*68800Smckusick * @(#)kern_lock.c 8.6 (Berkeley) 04/13/95 1268762Smckusick */ 1368762Smckusick 1468762Smckusick #include <sys/param.h> 1568762Smckusick #include <sys/proc.h> 1668762Smckusick #include <sys/lock.h> 1768762Smckusick 1868762Smckusick /* 1968762Smckusick * Locking primitives implementation. 2068762Smckusick * Locks provide shared/exclusive sychronization. 2168762Smckusick */ 2268762Smckusick 2368762Smckusick #if NCPUS > 1 2468762Smckusick 2568762Smckusick /* 2668762Smckusick * For multiprocessor system, try spin lock first. 2768762Smckusick * 2868762Smckusick * This should be inline expanded below, but we cannot have #if 2968762Smckusick * inside a multiline define. 3068762Smckusick */ 3168762Smckusick int lock_wait_time = 100; 3268762Smckusick #define PAUSE(lkp, wanted) \ 3368762Smckusick if (lock_wait_time > 0) { \ 3468762Smckusick int i; \ 3568762Smckusick \ 3668762Smckusick atomic_unlock(&lkp->lk_interlock); \ 3768762Smckusick for (i = lock_wait_time; i > 0; i--) \ 3868762Smckusick if (!(wanted)) \ 3968762Smckusick break; \ 4068762Smckusick atomic_lock(&lkp->lk_interlock); \ 4168762Smckusick } \ 4268762Smckusick if (!(wanted)) \ 4368762Smckusick break; 4468762Smckusick 4568762Smckusick #else /* NCPUS == 1 */ 4668762Smckusick 4768762Smckusick /* 4868762Smckusick * It is an error to spin on a uniprocessor as nothing will ever cause 4968762Smckusick * the atomic lock to clear while we are executing. 5068762Smckusick */ 5168762Smckusick #define PAUSE(lkp, wanted) 5268762Smckusick 5368762Smckusick #endif /* NCPUS == 1 */ 5468762Smckusick 5568762Smckusick /* 5668762Smckusick * Acquire a resource. 5768762Smckusick */ 5868762Smckusick #define ACQUIRE(lkp, error, extflags, wanted) \ 5968762Smckusick PAUSE(lkp, wanted); \ 6068762Smckusick for (error = 0; wanted; ) { \ 61*68800Smckusick (lkp)->lk_waitcount++; \ 6268762Smckusick atomic_unlock(&(lkp)->lk_interlock); \ 6368779Smckusick error = tsleep((void *)lkp, (lkp)->lk_prio, \ 6468779Smckusick (lkp)->lk_wmesg, (lkp)->lk_timo); \ 6568762Smckusick atomic_lock(&(lkp)->lk_interlock); \ 66*68800Smckusick (lkp)->lk_waitcount--; \ 6768762Smckusick if (error) \ 6868762Smckusick break; \ 6968762Smckusick if ((extflags) & LK_SLEEPFAIL) { \ 7068762Smckusick error = ENOLCK; \ 7168762Smckusick break; \ 7268762Smckusick } \ 7368762Smckusick } 7468762Smckusick 7568762Smckusick /* 7668762Smckusick * Initialize a lock; required before use. 7768762Smckusick */ 7868782Smckusick void 7968782Smckusick lock_init(lkp, prio, wmesg, timo, flags) 8068762Smckusick struct lock *lkp; 8168762Smckusick int prio; 8268762Smckusick char *wmesg; 8368762Smckusick int timo; 8468762Smckusick int flags; 8568762Smckusick { 8668762Smckusick bzero(lkp, sizeof(struct lock)); 8768762Smckusick atomic_lock_init(&lkp->lk_interlock); 8868762Smckusick lkp->lk_flags = flags & LK_EXTFLG_MASK; 8968762Smckusick lkp->lk_prio = prio; 9068762Smckusick lkp->lk_timo = timo; 9168762Smckusick lkp->lk_wmesg = wmesg; 9268762Smckusick lkp->lk_lockholder = LK_NOPROC; 9368762Smckusick } 9468762Smckusick 9568762Smckusick /* 9668780Smckusick * Determine the status of a lock. 9768780Smckusick */ 9868780Smckusick int 9968780Smckusick lockstatus(lkp) 10068780Smckusick struct lock *lkp; 10168780Smckusick { 10268780Smckusick int lock_type = 0; 10368780Smckusick 10468780Smckusick atomic_lock(&lkp->lk_interlock); 10568780Smckusick if (lkp->lk_exclusivecount != 0) 10668780Smckusick lock_type = LK_EXCLUSIVE; 10768780Smckusick else if (lkp->lk_sharecount != 0) 10868780Smckusick lock_type = LK_SHARED; 10968780Smckusick atomic_unlock(&lkp->lk_interlock); 11068780Smckusick return (lock_type); 11168780Smckusick } 11268780Smckusick 11368780Smckusick /* 11468762Smckusick * Set, change, or release a lock. 11568762Smckusick * 11668762Smckusick * Shared requests increment the shared count. Exclusive requests set the 11768762Smckusick * LK_WANT_EXCL flag (preventing further shared locks), and wait for already 11868762Smckusick * accepted shared locks and shared-to-exclusive upgrades to go away. 11968762Smckusick */ 12068782Smckusick int 121*68800Smckusick lockmgr(lkp, flags, p) 12268779Smckusick volatile struct lock *lkp; 123*68800Smckusick u_int flags; 12468762Smckusick struct proc *p; 12568762Smckusick { 12668779Smckusick int error; 12768762Smckusick pid_t pid; 12868779Smckusick volatile int extflags; 12968762Smckusick 130*68800Smckusick error = 0; 13168762Smckusick pid = p->p_pid; 13268775Smckusick atomic_lock(&lkp->lk_interlock); 13368762Smckusick extflags = (flags | lkp->lk_flags) & LK_EXTFLG_MASK; 134*68800Smckusick if (lkp->lk_flags & LK_DRAINED) 135*68800Smckusick panic("lockmgr: using decommissioned lock"); 13668762Smckusick 13768762Smckusick switch (flags & LK_TYPE_MASK) { 13868762Smckusick 13968762Smckusick case LK_SHARED: 14068762Smckusick if (lkp->lk_lockholder != pid) { 14168762Smckusick /* 14268762Smckusick * If just polling, check to see if we will block. 14368762Smckusick */ 14468762Smckusick if ((extflags & LK_NOWAIT) && (lkp->lk_flags & 14568762Smckusick (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE))) { 146*68800Smckusick error = EBUSY; 147*68800Smckusick break; 14868762Smckusick } 14968762Smckusick /* 15068762Smckusick * Wait for exclusive locks and upgrades to clear. 15168762Smckusick */ 15268762Smckusick ACQUIRE(lkp, error, extflags, lkp->lk_flags & 15368762Smckusick (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)); 154*68800Smckusick if (error) 155*68800Smckusick break; 15668762Smckusick lkp->lk_sharecount++; 157*68800Smckusick break; 15868762Smckusick } 15968762Smckusick /* 16068762Smckusick * We hold an exclusive lock, so downgrade it to shared. 16168762Smckusick * An alternative would be to fail with EDEADLK. 16268762Smckusick */ 16368762Smckusick lkp->lk_sharecount++; 16468762Smckusick /* fall into downgrade */ 16568762Smckusick 16668762Smckusick case LK_DOWNGRADE: 16768762Smckusick if (lkp->lk_lockholder != pid || lkp->lk_exclusivecount == 0) 16868762Smckusick panic("lockmgr: not holding exclusive lock"); 16968762Smckusick lkp->lk_sharecount += lkp->lk_exclusivecount; 17068762Smckusick lkp->lk_exclusivecount = 0; 17168762Smckusick lkp->lk_flags &= ~LK_HAVE_EXCL; 17268762Smckusick lkp->lk_lockholder = LK_NOPROC; 173*68800Smckusick if (lkp->lk_waitcount) 17468779Smckusick wakeup((void *)lkp); 175*68800Smckusick break; 17668762Smckusick 17768779Smckusick case LK_EXCLUPGRADE: 17868779Smckusick /* 17968779Smckusick * If another process is ahead of us to get an upgrade, 18068779Smckusick * then we want to fail rather than have an intervening 18168779Smckusick * exclusive access. 18268779Smckusick */ 18368779Smckusick if (lkp->lk_flags & LK_WANT_UPGRADE) { 18468779Smckusick lkp->lk_sharecount--; 185*68800Smckusick error = EBUSY; 186*68800Smckusick break; 18768779Smckusick } 18868779Smckusick /* fall into normal upgrade */ 18968779Smckusick 19068762Smckusick case LK_UPGRADE: 19168762Smckusick /* 19268762Smckusick * Upgrade a shared lock to an exclusive one. If another 19368762Smckusick * shared lock has already requested an upgrade to an 19468762Smckusick * exclusive lock, our shared lock is released and an 19568762Smckusick * exclusive lock is requested (which will be granted 19668775Smckusick * after the upgrade). If we return an error, the file 19768775Smckusick * will always be unlocked. 19868762Smckusick */ 19968762Smckusick if (lkp->lk_lockholder == pid || lkp->lk_sharecount <= 0) 20068762Smckusick panic("lockmgr: upgrade exclusive lock"); 20168775Smckusick lkp->lk_sharecount--; 20268762Smckusick /* 20368762Smckusick * If we are just polling, check to see if we will block. 20468762Smckusick */ 20568762Smckusick if ((extflags & LK_NOWAIT) && 20668762Smckusick ((lkp->lk_flags & LK_WANT_UPGRADE) || 20768762Smckusick lkp->lk_sharecount > 1)) { 208*68800Smckusick error = EBUSY; 209*68800Smckusick break; 21068762Smckusick } 21168762Smckusick if ((lkp->lk_flags & LK_WANT_UPGRADE) == 0) { 21268762Smckusick /* 21368762Smckusick * We are first shared lock to request an upgrade, so 21468762Smckusick * request upgrade and wait for the shared count to 21568762Smckusick * drop to zero, then take exclusive lock. 21668762Smckusick */ 21768762Smckusick lkp->lk_flags |= LK_WANT_UPGRADE; 21868762Smckusick ACQUIRE(lkp, error, extflags, lkp->lk_sharecount); 21968762Smckusick lkp->lk_flags &= ~LK_WANT_UPGRADE; 220*68800Smckusick if (error) 221*68800Smckusick break; 22268762Smckusick lkp->lk_flags |= LK_HAVE_EXCL; 22368762Smckusick lkp->lk_lockholder = pid; 22468762Smckusick if (lkp->lk_exclusivecount != 0) 22568762Smckusick panic("lockmgr: non-zero exclusive count"); 22668762Smckusick lkp->lk_exclusivecount = 1; 227*68800Smckusick break; 22868762Smckusick } 22968762Smckusick /* 23068762Smckusick * Someone else has requested upgrade. Release our shared 23168762Smckusick * lock, awaken upgrade requestor if we are the last shared 23268762Smckusick * lock, then request an exclusive lock. 23368762Smckusick */ 234*68800Smckusick if (lkp->lk_sharecount == 0 && lkp->lk_waitcount) 23568779Smckusick wakeup((void *)lkp); 23668762Smckusick /* fall into exclusive request */ 23768762Smckusick 23868762Smckusick case LK_EXCLUSIVE: 23968762Smckusick if (lkp->lk_lockholder == pid) { 24068762Smckusick /* 24168762Smckusick * Recursive lock. 24268762Smckusick */ 24368762Smckusick if ((extflags & LK_CANRECURSE) == 0) 24468762Smckusick panic("lockmgr: locking against myself"); 24568762Smckusick lkp->lk_exclusivecount++; 246*68800Smckusick break; 24768762Smckusick } 24868762Smckusick /* 24968762Smckusick * If we are just polling, check to see if we will sleep. 25068762Smckusick */ 25168762Smckusick if ((extflags & LK_NOWAIT) && ((lkp->lk_flags & 25268762Smckusick (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)) || 25368762Smckusick lkp->lk_sharecount != 0)) { 254*68800Smckusick error = EBUSY; 255*68800Smckusick break; 25668762Smckusick } 25768762Smckusick /* 25868762Smckusick * Try to acquire the want_exclusive flag. 25968762Smckusick */ 26068762Smckusick ACQUIRE(lkp, error, extflags, lkp->lk_flags & 26168762Smckusick (LK_HAVE_EXCL | LK_WANT_EXCL)); 262*68800Smckusick if (error) 263*68800Smckusick break; 26468762Smckusick lkp->lk_flags |= LK_WANT_EXCL; 26568762Smckusick /* 26668762Smckusick * Wait for shared locks and upgrades to finish. 26768762Smckusick */ 26868762Smckusick ACQUIRE(lkp, error, extflags, lkp->lk_sharecount != 0 || 26968762Smckusick (lkp->lk_flags & LK_WANT_UPGRADE)); 27068762Smckusick lkp->lk_flags &= ~LK_WANT_EXCL; 271*68800Smckusick if (error) 272*68800Smckusick break; 27368762Smckusick lkp->lk_flags |= LK_HAVE_EXCL; 27468762Smckusick lkp->lk_lockholder = pid; 27568762Smckusick if (lkp->lk_exclusivecount != 0) 27668762Smckusick panic("lockmgr: non-zero exclusive count"); 27768762Smckusick lkp->lk_exclusivecount = 1; 278*68800Smckusick break; 27968762Smckusick 28068762Smckusick case LK_RELEASE: 28168762Smckusick if (lkp->lk_exclusivecount != 0) { 282*68800Smckusick if (pid != lkp->lk_lockholder) 283*68800Smckusick panic("lockmgr: pid %d, not %s %d unlocking", 284*68800Smckusick pid, "exclusive lock holder", 285*68800Smckusick lkp->lk_lockholder); 28668762Smckusick lkp->lk_exclusivecount--; 28768762Smckusick if (lkp->lk_exclusivecount == 0) { 28868762Smckusick lkp->lk_flags &= ~LK_HAVE_EXCL; 28968762Smckusick lkp->lk_lockholder = LK_NOPROC; 29068762Smckusick } 29168762Smckusick } else if (lkp->lk_sharecount != 0) 29268762Smckusick lkp->lk_sharecount--; 293*68800Smckusick if (lkp->lk_waitcount) 29468779Smckusick wakeup((void *)lkp); 295*68800Smckusick break; 296*68800Smckusick 297*68800Smckusick case LK_DRAIN: 298*68800Smckusick /* 299*68800Smckusick * If we are just polling, check to see if we will sleep. 300*68800Smckusick */ 301*68800Smckusick if ((extflags & LK_NOWAIT) && ((lkp->lk_flags & 302*68800Smckusick (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)) || 303*68800Smckusick lkp->lk_sharecount != 0 || lkp->lk_waitcount != 0)) { 304*68800Smckusick error = EBUSY; 305*68800Smckusick break; 30668762Smckusick } 307*68800Smckusick PAUSE(lkp, ((lkp->lk_flags & 308*68800Smckusick (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)) || 309*68800Smckusick lkp->lk_sharecount != 0 || lkp->lk_waitcount != 0)); 310*68800Smckusick for (error = 0; ((lkp->lk_flags & 311*68800Smckusick (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)) || 312*68800Smckusick lkp->lk_sharecount != 0 || lkp->lk_waitcount != 0); ) { 313*68800Smckusick lkp->lk_flags |= LK_WAITDRAIN; 314*68800Smckusick atomic_unlock(&lkp->lk_interlock); 315*68800Smckusick if (error = tsleep((void *)&lkp->lk_flags, lkp->lk_prio, 316*68800Smckusick lkp->lk_wmesg, lkp->lk_timo)) 317*68800Smckusick return (error); 318*68800Smckusick if ((extflags) & LK_SLEEPFAIL) 319*68800Smckusick return (ENOLCK); 320*68800Smckusick atomic_lock(&lkp->lk_interlock); 321*68800Smckusick } 322*68800Smckusick lkp->lk_flags |= LK_DRAINED; 323*68800Smckusick break; 32468762Smckusick 32568762Smckusick default: 32668775Smckusick atomic_unlock(&lkp->lk_interlock); 32768762Smckusick panic("lockmgr: unknown locktype request %d", 32868762Smckusick flags & LK_TYPE_MASK); 32968775Smckusick /* NOTREACHED */ 33068762Smckusick } 331*68800Smckusick if ((lkp->lk_flags & LK_WAITDRAIN) && ((lkp->lk_flags & 332*68800Smckusick (LK_HAVE_EXCL | LK_WANT_EXCL | LK_WANT_UPGRADE)) == 0 && 333*68800Smckusick lkp->lk_sharecount == 0 && lkp->lk_waitcount == 0)) { 334*68800Smckusick lkp->lk_flags &= ~LK_WAITDRAIN; 335*68800Smckusick wakeup((void *)&lkp->lk_flags); 336*68800Smckusick } 337*68800Smckusick atomic_unlock(&lkp->lk_interlock); 338*68800Smckusick return (error); 33968762Smckusick } 340