Req* allocreq(Reqpool *pool, ulong tag) { Req *r; r = emalloc9p(sizeof *r); r->tag = tag; r->pool = pool; increqref(r); increqref(r); if(caninsertkey(pool->map, tag, r) == 0) { closereq(r); closereq(r); return nil; } return r; }
void needkeyflush(Req *r) { Req **l; for(l=&needwait; *l; l=&(*l)->aux){ if(*l == r){ *l = r->aux; if(r->aux == nil) needlast = l; closereq(r); break; } } logbufflush(&needkeybuf, r); }
void confirmflush(Req *r) { Req **l; for(l=&cusewait; *l; l=&(*l)->aux){ if(*l == r){ *l = r->aux; if(r->aux == nil) cuselast = l; closereq(r); break; } } logbufflush(&confbuf, r); }
static int rflush(Req *r, char *error) { Req *or; assert(error == nil); or = r->oldreq; if(or){ qlock(&or->lk); if(or->responded == 0){ or->flush = erealloc9p(or->flush, (or->nflush+1)*sizeof(or->flush[0])); or->flush[or->nflush++] = r; qunlock(&or->lk); return -1; /* delay response until or is responded */ } qunlock(&or->lk); closereq(or); } r->oldreq = nil; return 0; }
void closereq(Req *r) { int i; if(r == nil) return; if(chatty9p > 1) fprint(2, "closereq %p %d\n", r, r->ref.ref); if(decref(&r->ref) == 0){ if(r->fid) closefid(r->fid); if(r->newfid) closefid(r->newfid); if(r->afid) closefid(r->afid); if(r->oldreq) closereq(r->oldreq); for(i=0; i<r->nflush; i++) respond(r->flush[i], nil); free(r->flush); switch(r->ifcall.type){ case Tstat: free(r->ofcall.stat); free(r->d.name); free(r->d.uid); free(r->d.gid); free(r->d.muid); break; } if(r->pool->destroy) r->pool->destroy(r); free(r->buf); free(r->rbuf); free(r); } }
void closereq(Req *r) { if(r == nil) return; if(chatty9p > 1) fprint(2, "closereq %p %ld\n", r, r->ref.ref); if(decref(&r->ref) == 0) { if(r->fid) closefid(r->fid); if(r->newfid) closefid(r->newfid); if(r->afid) closefid(r->afid); if(r->oldreq) closereq(r->oldreq); if(r->nflush) fprint(2, "closereq: flushes remaining\n"); free(r->flush); switch(r->ifcall.type) { case Tstat: free(r->ofcall.stat); free(r->d.name); free(r->d.uid); free(r->d.gid); free(r->d.muid); break; } if(r->pool->destroy) r->pool->destroy(r); free(r->buf); free(r->rbuf); free(r); } }
void respond(Req *r, char *error) { int i, m, n; char errbuf[ERRMAX]; Srv *srv; srv = r->srv; assert(srv != nil); if(r->responded){ assert(r->pool); goto free; } assert(r->responded == 0); r->error = error; switch(r->ifcall.type){ default: assert(0); /* * Flush is special. If the handler says so, we return * without further processing. Respond will be called * again once it is safe. */ case Tflush: if(rflush(r, error)<0) return; break; case Tversion: rversion(r, error); break; case Tauth: rauth(r, error); break; case Tattach: rattach(r, error); break; case Twalk: rwalk(r, error); break; case Topen: ropen(r, error); break; case Tcreate: rcreate(r, error); break; case Tread: rread(r, error); break; case Twrite: rwrite(r, error); break; case Tclunk: rclunk(r, error); break; case Tremove: rremove(r, error, errbuf); break; case Tstat: rstat(r, error); break; case Twstat: rwstat(r, error); break; } r->ofcall.tag = r->ifcall.tag; r->ofcall.type = r->ifcall.type+1; if(r->error) setfcallerror(&r->ofcall, r->error); if(chatty9p) fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall); qlock(&srv->wlock); n = convS2M(&r->ofcall, srv->wbuf, srv->msize); if(n <= 0){ fprint(2, "n = %d %F\n", n, &r->ofcall); abort(); } assert(n > 2); /* * There is a race here - we must remove the entry before * the write, so that if the client is very fast and reuses the * tag, the read loop won't think it is still in use. * * By removing the entry before the write, we open up a * race with incoming Tflush messages. Specifically, an * incoming Tflush might not see r even though it has not * yet been responded to. It would then send an Rflush * immediately, potentially before we do the write. This can't * happen because we already old srv->wlock, so nothing * is going out on the wire before this write. */ if(r->pool) /* not a fake */ closereq(removereq(r->pool, r->ifcall.tag)); qlock(&r->lk); r->responded = 1; if(r->pool) if(r->ref.ref == 1+r->nflush) if(r->fid){ /* * There are no references other than in our r->flush array, * so no one else should be accessing r concurrently. * Close the fid now, before responding to the message. * * If the client is behaving (there are no outstanding T-messages * that reference r->fid) and the message is a Tclunk or Tremove, * then this closefid will call destroyfid. * * This means destroyfid can't piddle around * indefinitely (we're holding srv->wlock!), but it provides * for tighter semantics as to when destroyfid is called. * * LANL has observed cases where waiting until after the write * can delay a closefid on a Twrite for many 9P transactions, * so that a handful of transactions can happen including a Tclunk * and a Topen, and the original fid will still not be destroyed. */ closefid(r->fid); r->fid = nil; } qunlock(&r->lk); m = write(srv->outfd, srv->wbuf, n); if(m != n) sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd); qunlock(&srv->wlock); free: qlock(&r->lk); /* no one will add flushes now */ for(i=0; i<r->nflush; i++){ r->flush[i]->oldreq = nil; /* so it doesn't try to lock us! */ respond(r->flush[i], nil); } free(r->flush); r->flush = nil; r->nflush = 0; qunlock(&r->lk); if(r->pool) closereq(r); else free(r); }
void respond(Req *r, char *error) { int i, m, n; char errbuf[ERRMAX]; Srv *srv; srv = r->srv; assert(srv != nil); assert(r->responded == 0); r->error = error; switch(r->ifcall.type){ default: assert(0); /* * Flush is special. If the handler says so, we return * without further processing. Respond will be called * again once it is safe. */ case Tflush: if(rflush(r, error)<0) return; break; case Tversion: rversion(r, error); break; case Tauth: rauth(r, error); break; case Tattach: rattach(r, error); break; case Twalk: rwalk(r, error); break; case Topen: ropen(r, error); break; case Tcreate: rcreate(r, error); break; case Tread: rread(r, error); break; case Twrite: rwrite(r, error); break; case Tclunk: rclunk(r, error); break; case Tremove: rremove(r, error, errbuf); break; case Tstat: rstat(r, error); break; case Twstat: rwstat(r, error); break; } r->ofcall.tag = r->ifcall.tag; r->ofcall.type = r->ifcall.type+1; if(r->error) setfcallerror(&r->ofcall, r->error); if(chatty9p) fprint(2, "-%d-> %F\n", srv->outfd, &r->ofcall); qlock(&srv->wlock); n = convS2M(&r->ofcall, srv->wbuf, srv->msize); if(n <= 0){ fprint(2, "n = %d %F\n", n, &r->ofcall); abort(); } assert(n > 2); if(r->pool) /* not a fake */ closereq(removereq(r->pool, r->ifcall.tag)); m = write(srv->outfd, srv->wbuf, n); if(m != n) sysfatal("lib9p srv: write %d returned %d on fd %d: %r", n, m, srv->outfd); qunlock(&srv->wlock); qlock(&r->lk); /* no one will add flushes now */ r->responded = 1; qunlock(&r->lk); for(i=0; i<r->nflush; i++) respond(r->flush[i], nil); free(r->flush); r->flush = nil; r->nflush = 0; if(r->pool) closereq(r); else free(r); }