#include <u.h>#include <libc.h>#include <fcall.h>#include "compat.h"#include "error.h" typedef struct Fid Fid;typedef struct Export Export;typedef struct Exq Exq;typedef struct Exwork Exwork; enum{ Nfidhash = 32, Maxfdata = 8192, Maxrpc = IOHDRSZ + Maxfdata,}; struct Export{ Ref r; Exq* work; Lock fidlock; Fid* fid[Nfidhash]; int io; /* fd to read/write */ int iounit; int nroots; Chan **roots;}; struct Fid{ Fid* next; Fid** last; Chan* chan; long offset; int fid; int ref; /* fcalls using the fid; locked by Export.Lock */ int attached; /* fid attached or cloned but not clunked */}; struct Exq{ Lock lk; int responding; /* writing out reply message */ int noresponse; /* don't respond to this one */ Exq* next; int shut; /* has been noted for shutdown */ Export* export; void* slave; Fcall rpc; uchar buf[Maxrpc];}; struct Exwork{ Lock l; int ref; int nwaiters; /* queue of slaves waiting for work */ QLock qwait; Rendez rwait; Exq *head; /* work waiting for a slave */ Exq *tail;}; Exwork exq; static void exshutdown(Export*);static void exflush(Export*, int, int);static void exslave(void*);static void exfree(Export*);static void exportproc(Export*); static char* Exattach(Export*, Fcall*, uchar*);static char* Exauth(Export*, Fcall*, uchar*);static char* Exclunk(Export*, Fcall*, uchar*);static char* Excreate(Export*, Fcall*, uchar*);static char* Exversion(Export*, Fcall*, uchar*);static char* Exopen(Export*, Fcall*, uchar*);static char* Exread(Export*, Fcall*, uchar*);static char* Exremove(Export*, Fcall*, uchar*);static char* Exsession(Export*, Fcall*, uchar*);static char* Exstat(Export*, Fcall*, uchar*);static char* Exwalk(Export*, Fcall*, uchar*);static char* Exwrite(Export*, Fcall*, uchar*);static char* Exwstat(Export*, Fcall*, uchar*); static char *(*fcalls[Tmax])(Export*, Fcall*, uchar*); static char Enofid[] = "no such fid";static char Eseekdir[] = "can't seek on a directory";static char Ereaddir[] = "unaligned read of a directory";static int exdebug = 0; intsysexport(int fd, Chan **roots, int nroots){ Export *fs; fs = smalloc(sizeof(Export)); fs->r.ref = 1; fs->io = fd; fs->roots = roots; fs->nroots = nroots; exportproc(fs); return 0;} static voidexportinit(void){ lock(&exq.l); exq.ref++; if(fcalls[Tversion] != nil){ unlock(&exq.l); return; } fmtinstall('F', fcallfmt); fcalls[Tversion] = Exversion; fcalls[Tauth] = Exauth; fcalls[Tattach] = Exattach; fcalls[Twalk] = Exwalk; fcalls[Topen] = Exopen; fcalls[Tcreate] = Excreate; fcalls[Tread] = Exread; fcalls[Twrite] = Exwrite; fcalls[Tclunk] = Exclunk; fcalls[Tremove] = Exremove; fcalls[Tstat] = Exstat; fcalls[Twstat] = Exwstat; unlock(&exq.l);} static voidexportproc(Export *fs){ Exq *q; int n, ed; exportinit(); ed = errdepth(-1); for(;;){ errdepth(ed); q = smalloc(sizeof(Exq)); n = read9pmsg(fs->io, q->buf, Maxrpc); if(n <= 0 || convM2S(q->buf, n, &q->rpc) != n) goto bad; if(exdebug) print("export %d <- %F\n", getpid(), &q->rpc); if(q->rpc.type == Tflush){ exflush(fs, q->rpc.tag, q->rpc.oldtag); free(q); continue; } q->export = fs; incref(&fs->r); lock(&exq.l); if(exq.head == nil) exq.head = q; else exq.tail->next = q; q->next = nil; exq.tail = q; n = exq.nwaiters; if(n) exq.nwaiters = n - 1; unlock(&exq.l); if(!n) kproc("exportfs", exslave, nil); rendwakeup(&exq.rwait); }bad: free(q); if(exdebug) fprint(2, "export proc shutting down: %r\n"); exshutdown(fs); exfree(fs);} static voidexflush(Export *fs, int flushtag, int tag){ Exq *q, **last; Fcall fc; uchar buf[Maxrpc]; int n; /* hasn't been started? */ lock(&exq.l); last = &exq.head; for(q = exq.head; q != nil; q = q->next){ if(q->export == fs && q->rpc.tag == tag){ *last = q->next; unlock(&exq.l); exfree(fs); free(q); goto Respond; } last = &q->next; } unlock(&exq.l); /* in progress? */ lock(&fs->r); for(q = fs->work; q != nil; q = q->next){ if(q->rpc.tag == tag){ lock(&q->lk); q->noresponse = 1; if(!q->responding) rendintr(q->slave); unlock(&q->lk); break; } } unlock(&fs->r); Respond: fc.type = Rflush; fc.tag = flushtag; n = convS2M(&fc, buf, Maxrpc); if(n == 0) panic("convS2M error on write"); if(write(fs->io, buf, n) != n) panic("mount write");} static voidexshutdown(Export *fs){ Exq *q, **last; lock(&exq.l); last = &exq.head; for(q = exq.head; q != nil; q = *last){ if(q->export == fs){ *last = q->next; exfree(fs); free(q); continue; } last = &q->next; } /* * cleanly shut down the slaves if this is the last fs around */ exq.ref--; if(!exq.ref) rendwakeup(&exq.rwait); unlock(&exq.l); /* * kick any sleepers */ lock(&fs->r); for(q = fs->work; q != nil; q = q->next){ lock(&q->lk); q->noresponse = 1; if(!q->responding) rendintr(q->slave); unlock(&q->lk); } unlock(&fs->r);} static voidexfree(Export *fs){ Fid *f, *n; int i; if(decref(&fs->r) != 0) return; for(i = 0; i < Nfidhash; i++){ for(f = fs->fid[i]; f != nil; f = n){ if(f->chan != nil) cclose(f->chan); n = f->next; free(f); } } free(fs);} static intexwork(void *){ int work; lock(&exq.l); work = exq.head != nil || !exq.ref; unlock(&exq.l); return work;} static voidexslave(void *){ Export *fs; Exq *q, *t, **last; char *volatile err; int n, ed; while(waserror()) fprint(2, "exslave %d errored out of loop -- heading back in!\n", getpid()); ed = errdepth(-1); for(;;){ errdepth(ed); qlock(&exq.qwait); if(waserror()){ qunlock(&exq.qwait); nexterror(); } rendsleep(&exq.rwait, exwork, nil); lock(&exq.l); if(!exq.ref){ unlock(&exq.l); poperror(); qunlock(&exq.qwait); break; } q = exq.head; if(q == nil){ unlock(&exq.l); poperror(); qunlock(&exq.qwait); continue; } exq.head = q->next; if(exq.head == nil) exq.tail = nil; poperror(); qunlock(&exq.qwait); /* * put the job on the work queue before it's * visible as off of the head queue, so it's always * findable for flushes and shutdown */ q->slave = up; q->noresponse = 0; q->responding = 0; rendclearintr(); fs = q->export; lock(&fs->r); q->next = fs->work; fs->work = q; unlock(&fs->r); unlock(&exq.l); if(exdebug > 1) print("exslave dispatch %d %F\n", getpid(), &q->rpc); if(waserror()){ print("exslave err %r\n"); err = up->error; }else{ if(q->rpc.type >= Tmax || !fcalls[q->rpc.type]) err = "bad fcall type"; else err = (*fcalls[q->rpc.type])(fs, &q->rpc, &q->buf[IOHDRSZ]); poperror(); } q->rpc.type++; if(err){ q->rpc.type = Rerror; q->rpc.ename = err; } n = convS2M(&q->rpc, q->buf, Maxrpc); if(exdebug) print("exslave %d -> %F\n", getpid(), &q->rpc); lock(&q->lk); if(!q->noresponse){ q->responding = 1; unlock(&q->lk); write(fs->io, q->buf, n); }else unlock(&q->lk); /* * exflush might set noresponse at this point, but * setting noresponse means don't send a response now; * it's okay that we sent a response already. */ if(exdebug > 1) print("exslave %d written %d\n", getpid(), q->rpc.tag); lock(&fs->r); last = &fs->work; for(t = fs->work; t != nil; t = t->next){ if(t == q){ *last = q->next; break; } last = &t->next; } unlock(&fs->r); exfree(q->export); free(q); rendclearintr(); lock(&exq.l); exq.nwaiters++; unlock(&exq.l); } if(exdebug) fprint(2, "export slaveshutting down\n"); kexit();} Fid*Exmkfid(Export *fs, int fid){ ulong h; Fid *f, *nf; nf = mallocz(sizeof(Fid), 1); if(nf == nil) return nil; lock(&fs->fidlock); h = fid % Nfidhash; for(f = fs->fid[h]; f != nil; f = f->next){ if(f->fid == fid){ unlock(&fs->fidlock); free(nf); return nil; } } nf->next = fs->fid[h]; if(nf->next != nil) nf->next->last = &nf->next; nf->last = &fs->fid[h]; fs->fid[h] = nf; nf->fid = fid; nf->ref = 1; nf->attached = 1; nf->offset = 0; nf->chan = nil; unlock(&fs->fidlock); return nf;} Fid*Exgetfid(Export *fs, int fid){ Fid *f; ulong h; lock(&fs->fidlock); h = fid % Nfidhash; for(f = fs->fid[h]; f; f = f->next){ if(f->fid == fid){ if(f->attached == 0) break; f->ref++; unlock(&fs->fidlock); return f; } } unlock(&fs->fidlock); return nil;} voidExputfid(Export *fs, Fid *f){ lock(&fs->fidlock); f->ref--; if(f->ref == 0 && f->attached == 0){ if(f->chan != nil) cclose(f->chan); f->chan = nil; *f->last = f->next; if(f->next != nil) f->next->last = f->last; unlock(&fs->fidlock); free(f); return; } unlock(&fs->fidlock);Lines 1–500 of 803. Load more
#include <u.h> #include <libc.h> #include <fcall.h> #include "compat.h" #include "error.h" typedef struct Fid Fid; typedef struct Export Export; typedef struct Exq Exq; typedef struct Exwork Exwork; enum { Nfidhash = 32, Maxfdata = 8192, Maxrpc = IOHDRSZ + Maxfdata, }; struct Export { Ref r; Exq* work; Lock fidlock; Fid* fid[Nfidhash]; int io; /* fd to read/write */ int iounit; int nroots; Chan **roots; }; struct Fid { Fid* next; Fid** last; Chan* chan; long offset; int fid; int ref; /* fcalls using the fid; locked by Export.Lock */ int attached; /* fid attached or cloned but not clunked */ }; struct Exq { Lock lk; int responding; /* writing out reply message */ int noresponse; /* don't respond to this one */ Exq* next; int shut; /* has been noted for shutdown */ Export* export; void* slave; Fcall rpc; uchar buf[Maxrpc]; }; struct Exwork { Lock l; int ref; int nwaiters; /* queue of slaves waiting for work */ QLock qwait; Rendez rwait; Exq *head; /* work waiting for a slave */ Exq *tail; }; Exwork exq; static void exshutdown(Export*); static void exflush(Export*, int, int); static void exslave(void*); static void exfree(Export*); static void exportproc(Export*); static char* Exattach(Export*, Fcall*, uchar*); static char* Exauth(Export*, Fcall*, uchar*); static char* Exclunk(Export*, Fcall*, uchar*); static char* Excreate(Export*, Fcall*, uchar*); static char* Exversion(Export*, Fcall*, uchar*); static char* Exopen(Export*, Fcall*, uchar*); static char* Exread(Export*, Fcall*, uchar*); static char* Exremove(Export*, Fcall*, uchar*); static char* Exsession(Export*, Fcall*, uchar*); static char* Exstat(Export*, Fcall*, uchar*); static char* Exwalk(Export*, Fcall*, uchar*); static char* Exwrite(Export*, Fcall*, uchar*); static char* Exwstat(Export*, Fcall*, uchar*); static char *(*fcalls[Tmax])(Export*, Fcall*, uchar*); static char Enofid[] = "no such fid"; static char Eseekdir[] = "can't seek on a directory"; static char Ereaddir[] = "unaligned read of a directory"; static int exdebug = 0; int sysexport(int fd, Chan **roots, int nroots) { Export *fs; fs = smalloc(sizeof(Export)); fs->r.ref = 1; fs->io = fd; fs->roots = roots; fs->nroots = nroots; exportproc(fs); return 0; } static void exportinit(void) { lock(&exq.l); exq.ref++; if(fcalls[Tversion] != nil){ unlock(&exq.l); return; } fmtinstall('F', fcallfmt); fcalls[Tversion] = Exversion; fcalls[Tauth] = Exauth; fcalls[Tattach] = Exattach; fcalls[Twalk] = Exwalk; fcalls[Topen] = Exopen; fcalls[Tcreate] = Excreate; fcalls[Tread] = Exread; fcalls[Twrite] = Exwrite; fcalls[Tclunk] = Exclunk; fcalls[Tremove] = Exremove; fcalls[Tstat] = Exstat; fcalls[Twstat] = Exwstat; unlock(&exq.l); } static void exportproc(Export *fs) { Exq *q; int n, ed; exportinit(); ed = errdepth(-1); for(;;){ errdepth(ed); q = smalloc(sizeof(Exq)); n = read9pmsg(fs->io, q->buf, Maxrpc); if(n <= 0 || convM2S(q->buf, n, &q->rpc) != n) goto bad; if(exdebug) print("export %d <- %F\n", getpid(), &q->rpc); if(q->rpc.type == Tflush){ exflush(fs, q->rpc.tag, q->rpc.oldtag); free(q); continue; } q->export = fs; incref(&fs->r); lock(&exq.l); if(exq.head == nil) exq.head = q; else exq.tail->next = q; q->next = nil; exq.tail = q; n = exq.nwaiters; if(n) exq.nwaiters = n - 1; unlock(&exq.l); if(!n) kproc("exportfs", exslave, nil); rendwakeup(&exq.rwait); } bad: free(q); if(exdebug) fprint(2, "export proc shutting down: %r\n"); exshutdown(fs); exfree(fs); } static void exflush(Export *fs, int flushtag, int tag) { Exq *q, **last; Fcall fc; uchar buf[Maxrpc]; int n; /* hasn't been started? */ lock(&exq.l); last = &exq.head; for(q = exq.head; q != nil; q = q->next){ if(q->export == fs && q->rpc.tag == tag){ *last = q->next; unlock(&exq.l); exfree(fs); free(q); goto Respond; } last = &q->next; } unlock(&exq.l); /* in progress? */ lock(&fs->r); for(q = fs->work; q != nil; q = q->next){ if(q->rpc.tag == tag){ lock(&q->lk); q->noresponse = 1; if(!q->responding) rendintr(q->slave); unlock(&q->lk); break; } } unlock(&fs->r); Respond: fc.type = Rflush; fc.tag = flushtag; n = convS2M(&fc, buf, Maxrpc); if(n == 0) panic("convS2M error on write"); if(write(fs->io, buf, n) != n) panic("mount write"); } static void exshutdown(Export *fs) { Exq *q, **last; lock(&exq.l); last = &exq.head; for(q = exq.head; q != nil; q = *last){ if(q->export == fs){ *last = q->next; exfree(fs); free(q); continue; } last = &q->next; } /* * cleanly shut down the slaves if this is the last fs around */ exq.ref--; if(!exq.ref) rendwakeup(&exq.rwait); unlock(&exq.l); /* * kick any sleepers */ lock(&fs->r); for(q = fs->work; q != nil; q = q->next){ lock(&q->lk); q->noresponse = 1; if(!q->responding) rendintr(q->slave); unlock(&q->lk); } unlock(&fs->r); } static void exfree(Export *fs) { Fid *f, *n; int i; if(decref(&fs->r) != 0) return; for(i = 0; i < Nfidhash; i++){ for(f = fs->fid[i]; f != nil; f = n){ if(f->chan != nil) cclose(f->chan); n = f->next; free(f); } } free(fs); } static int exwork(void *) { int work; lock(&exq.l); work = exq.head != nil || !exq.ref; unlock(&exq.l); return work; } static void exslave(void *) { Export *fs; Exq *q, *t, **last; char *volatile err; int n, ed; while(waserror()) fprint(2, "exslave %d errored out of loop -- heading back in!\n", getpid()); ed = errdepth(-1); for(;;){ errdepth(ed); qlock(&exq.qwait); if(waserror()){ qunlock(&exq.qwait); nexterror(); } rendsleep(&exq.rwait, exwork, nil); lock(&exq.l); if(!exq.ref){ unlock(&exq.l); poperror(); qunlock(&exq.qwait); break; } q = exq.head; if(q == nil){ unlock(&exq.l); poperror(); qunlock(&exq.qwait); continue; } exq.head = q->next; if(exq.head == nil) exq.tail = nil; poperror(); qunlock(&exq.qwait); /* * put the job on the work queue before it's * visible as off of the head queue, so it's always * findable for flushes and shutdown */ q->slave = up; q->noresponse = 0; q->responding = 0; rendclearintr(); fs = q->export; lock(&fs->r); q->next = fs->work; fs->work = q; unlock(&fs->r); unlock(&exq.l); if(exdebug > 1) print("exslave dispatch %d %F\n", getpid(), &q->rpc); if(waserror()){ print("exslave err %r\n"); err = up->error; }else{ if(q->rpc.type >= Tmax || !fcalls[q->rpc.type]) err = "bad fcall type"; else err = (*fcalls[q->rpc.type])(fs, &q->rpc, &q->buf[IOHDRSZ]); poperror(); } q->rpc.type++; if(err){ q->rpc.type = Rerror; q->rpc.ename = err; } n = convS2M(&q->rpc, q->buf, Maxrpc); if(exdebug) print("exslave %d -> %F\n", getpid(), &q->rpc); lock(&q->lk); if(!q->noresponse){ q->responding = 1; unlock(&q->lk); write(fs->io, q->buf, n); }else unlock(&q->lk); /* * exflush might set noresponse at this point, but * setting noresponse means don't send a response now; * it's okay that we sent a response already. */ if(exdebug > 1) print("exslave %d written %d\n", getpid(), q->rpc.tag); lock(&fs->r); last = &fs->work; for(t = fs->work; t != nil; t = t->next){ if(t == q){ *last = q->next; break; } last = &t->next; } unlock(&fs->r); exfree(q->export); free(q); rendclearintr(); lock(&exq.l); exq.nwaiters++; unlock(&exq.l); } if(exdebug) fprint(2, "export slaveshutting down\n"); kexit(); } Fid* Exmkfid(Export *fs, int fid) { ulong h; Fid *f, *nf; nf = mallocz(sizeof(Fid), 1); if(nf == nil) return nil; lock(&fs->fidlock); h = fid % Nfidhash; for(f = fs->fid[h]; f != nil; f = f->next){ if(f->fid == fid){ unlock(&fs->fidlock); free(nf); return nil; } } nf->next = fs->fid[h]; if(nf->next != nil) nf->next->last = &nf->next; nf->last = &fs->fid[h]; fs->fid[h] = nf; nf->fid = fid; nf->ref = 1; nf->attached = 1; nf->offset = 0; nf->chan = nil; unlock(&fs->fidlock); return nf; } Fid* Exgetfid(Export *fs, int fid) { Fid *f; ulong h; lock(&fs->fidlock); h = fid % Nfidhash; for(f = fs->fid[h]; f; f = f->next){ if(f->fid == fid){ if(f->attached == 0) break; f->ref++; unlock(&fs->fidlock); return f; } } unlock(&fs->fidlock); return nil; } void Exputfid(Export *fs, Fid *f) { lock(&fs->fidlock); f->ref--; if(f->ref == 0 && f->attached == 0){ if(f->chan != nil) cclose(f->chan); f->chan = nil; *f->last = f->next; if(f->next != nil) f->next->last = f->last; unlock(&fs->fidlock); free(f); return; } unlock(&fs->fidlock);