diff options
author | Robert Morris <[email protected]> | 2019-09-23 06:50:25 -0400 |
---|---|---|
committer | Robert Morris <[email protected]> | 2019-09-23 06:50:25 -0400 |
commit | d175beadf5f009d0fb8881e32c913b1bc175c9f6 (patch) | |
tree | fcb8129eea981362de151f85bb185a4575b6e71d /kernel/proc.c | |
parent | 843ce7776568ac2d4f71886e95077709b83bc613 (diff) | |
download | xv6-labs-d175beadf5f009d0fb8881e32c913b1bc175c9f6.tar.gz xv6-labs-d175beadf5f009d0fb8881e32c913b1bc175c9f6.tar.bz2 xv6-labs-d175beadf5f009d0fb8881e32c913b1bc175c9f6.zip |
bug fix: reparent() sometimes deadlocked
bug fix: exit() sometimes released a different parent lock than it acquired
usertests
Diffstat (limited to 'kernel/proc.c')
-rw-r--r-- | kernel/proc.c | 47 |
1 files changed, 32 insertions, 15 deletions
diff --git a/kernel/proc.c b/kernel/proc.c index 0d91a59..4f6caee 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -286,11 +286,11 @@ fork(void) } // Pass p's abandoned children to init. -// Caller must hold p->lock and parent->lock. +// Caller must hold p->lock. void -reparent(struct proc *p, struct proc *parent) { +reparent(struct proc *p) +{ struct proc *pp; - int child_of_init = (p->parent == initproc); for(pp = proc; pp < &proc[NPROC]; pp++){ // this code uses pp->parent without holding pp->lock. @@ -302,13 +302,10 @@ reparent(struct proc *p, struct proc *parent) { // because only the parent changes it, and we're the parent. acquire(&pp->lock); pp->parent = initproc; - if(pp->state == ZOMBIE) { - if(!child_of_init) - acquire(&initproc->lock); - wakeup1(initproc); - if(!child_of_init) - release(&initproc->lock); - } + // we should wake up init here, but that would require + // initproc->lock, which would be a deadlock, since we hold + // the lock on one of init's children (pp). this is why + // exit() always wakes init (before acquiring any locks). release(&pp->lock); } } @@ -339,20 +336,38 @@ exit(int status) end_op(); p->cwd = 0; - acquire(&p->parent->lock); - + // we might re-parent a child to init. we can't be precise + // about waking up init, since we can't acquire its lock once + // we've acquired any other proc lock. so wake up init whether + // that's necessary or not. init may miss this wakeup, + // that that seems harmless. + acquire(&initproc->lock); + wakeup1(initproc); + release(&initproc->lock); + + // grab a copy of p->parent, to ensure that we unlock the + // same parent we locked. in case our parent gives us away + // to init while we're waiting for the parent lock. + acquire(&p->lock); + struct proc *original_parent = p->parent; + release(&p->lock); + + // we need the parent's lock in order to wake it up from wait(). + // the parent-then-child rule says we have to lock it first. + acquire(&original_parent->lock); + acquire(&p->lock); // Give any children to init. - reparent(p, p->parent); + reparent(p); // Parent might be sleeping in wait(). - wakeup1(p->parent); + wakeup1(original_parent); p->xstate = status; p->state = ZOMBIE; - release(&p->parent->lock); + release(&original_parent->lock); // Jump into the scheduler, never to return. sched(); @@ -564,6 +579,8 @@ wakeup(void *chan) static void wakeup1(struct proc *p) { + if(!holding(&p->lock)) + panic("wakeup1"); if(p->chan == p && p->state == SLEEPING) { p->state = RUNNABLE; } |