// Sleeping locks #include "core/types.h" #include "core/defs.h" #include "core/param.h" #include "arch/x86.h" #include "arch/memlayout.h" #include "arch/mmu.h" #include "core/proc.h" #include "core/spinlock.h" #include "core/sleeplock.h" void initsleeplock(struct sleeplock *lk, char *name) { initlock(&lk->lk, "sleep lock"); lk->name = name; lk->locked = 0; lk->pid = 0; } void acquiresleep(struct sleeplock *lk) { acquire(&lk->lk); while (lk->locked) { sleep(lk, &lk->lk); } lk->locked = 1; if (myproc()) { lk->pid = myproc()->pid; } else { lk->pid = 0; } release(&lk->lk); } void releasesleep(struct sleeplock *lk) { acquire(&lk->lk); lk->locked = 0; lk->pid = 0; wakeup(lk); release(&lk->lk); } // Does *this process* hold lk? // // The pid comparison is not decoration. Without it this answers "is anyone // holding this", and every caller is an assertion of the form // // if (!holdingsleep(&b->lock)) panic("brelse"); // // whose entire purpose is to catch a process releasing a lock it never took. // Answering the weaker question disarms all seven of them: iunlock() on an // inode locked by another process, and brelse() on somebody else's buffer, // both sailed through and unlocked it underneath the real holder. Two // processes then believe they hold the same inode, and the corruption surfaces // somewhere else entirely -- which is exactly the shape of bug these panics // exist to prevent. // // myproc() is null on the boot path before the scheduler runs, and // acquiresleep() stores 0 for that case, so compare the same way rather than // dereferencing null here. int holdingsleep(struct sleeplock *lk) { struct proc *p = myproc(); acquire(&lk->lk); int r = lk->locked && lk->pid == (p ? p->pid : 0); release(&lk->lk); return r; }