diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2022-12-12 17:52:38 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2022-12-12 17:52:38 +0000 |
commit | e30ad8795f13dd082a2c42011ddabfc484dd56c4 (patch) | |
tree | c89c176dfd1a036846668c8ec5c38408426abb88 /sys/src/9 | |
parent | 31ed5d7d2ffd891b88e2d97c0706851fab94a06c (diff) |
devsrv: fix recusrion deadlock on remove
Need to close the channel outside of srvlk,
otherwise risk deadlock when recursing.
Diffstat (limited to 'sys/src/9')
-rw-r--r-- | sys/src/9/port/devsrv.c | 55 |
1 files changed, 31 insertions, 24 deletions
diff --git a/sys/src/9/port/devsrv.c b/sys/src/9/port/devsrv.c index a7b35e292..c2bea2142 100644 --- a/sys/src/9/port/devsrv.c +++ b/sys/src/9/port/devsrv.c @@ -95,27 +95,13 @@ remove(Link **l, char *name, ulong qidpath) } static void -boardclunk(Board *b, int close) +boardclunk(Board *b) { - Srv *sp, *prv; Board *ch; if(b == &root) return; - if(close){ - assert(b->closed == 0); - b->closed++; - for(sp = b->srv; sp != nil; sp = prv){ - prv = sp->link; - free(sp->owner); - free(sp->name); - if(sp->chan != nil) - cclose(sp->chan); - free(sp); - } - b->srv = nil; - } if(decref(b) != 0) return; @@ -126,6 +112,8 @@ boardclunk(Board *b, int close) * have to walk up the tree to clear now empty parents. */ while(b->closed && b->child == nil){ + assert(b->srv == nil); + //Root should never be closed assert(b->parent != nil); ch = remove((Link**)&b->parent->child, b->name, ~0UL); @@ -328,7 +316,7 @@ srvopen(Chan *c, int omode) c->aux = ch; c->qid.path = SRVQID(ch->path, Qlease); c->mode = openmode(omode); - boardclunk(b, 0); + boardclunk(b); wunlock(&srvlk); poperror(); return c; @@ -436,7 +424,7 @@ srvremove(Chan *c) b = c->aux; wlock(&srvlk); if(waserror()){ - boardclunk(b, 0); + boardclunk(b); wunlock(&srvlk); nexterror(); } @@ -457,7 +445,7 @@ srvremove(Chan *c) remove((Link**)&b->srv, nil, c->qid.path); - boardclunk(b, 0); + boardclunk(b); wunlock(&srvlk); poperror(); @@ -546,13 +534,10 @@ srvwstat(Chan *c, uchar *dp, int n) static void srvclose(Chan *c) { + Srv *sp, *link; Board *b; - int expired; - expired = 0; - if(SRVTYPE(c->qid.path) == Qlease) - expired++; - else if(c->flag & CRCLOSE){ + if(c->flag & CRCLOSE && SRVTYPE(c->qid.path) != Qlease){ /* * in theory we need to override any changes in removability * since open, but since all that's checked is the owner, @@ -566,9 +551,31 @@ srvclose(Chan *c) } b = c->aux; + if(b == &root) + return; + wlock(&srvlk); - boardclunk(b, expired); + if(SRVTYPE(c->qid.path) != Qlease){ + boardclunk(b); + wunlock(&srvlk); + return; + } + + /* free later after releasing srvlk */ + sp = b->srv; + b->srv = nil; + b->closed++; + boardclunk(b); wunlock(&srvlk); + + for(; sp != nil; sp = link){ + link = sp->link; + if(sp->chan != nil) + ccloseq(sp->chan); + free(sp->owner); + free(sp->name); + free(sp); + } } static long |