From 325456efbb396a7f81a58cdfd6aaf360a63b202d Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 18 Oct 1991 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1991-10-18 --- port/devenv.c | 618 +++++++++++++++++++------------------------------ port/pgrp.c | 65 ++---- port/portdat.h | 15 +- port/portfns.h | 3 +- port/sysfile.c | 3 +- 5 files changed, 263 insertions(+), 441 deletions(-) diff --git a/port/devenv.c b/port/devenv.c index 314ef0972ffa469e584ee037a3944d6904779259..b7dc325ead031a36d32201e3caa21c640734267d 100644 --- a/port/devenv.c +++ b/port/devenv.c @@ -7,60 +7,41 @@ #include "devtab.h" -/* - * An environment value is kept in some number of contiguous - * Envvals, with the Env's val pointing at the first. - * Envvals are allocated from the end of a fixed arena, which - * is compacted when the arena end is reached. - * A `piece' (number of contiguous Envvals) is free to be - * reclaimed if its e pointer is 0. - * - * Locking: an env's val can change by compaction, so lock - * an env before using its value. A pgrp env[] slot can go - * to 0 and the corresponding env freed (by envremove()), so - * lock the pgrp around the use of a value retrieved from a slot. - * Lock in order: pgrp, envalloc, env (but ok to skip envalloc - * lock if there is no possibility of blocking). - */ - struct Envval { - ulong n; /* number of Envval's (including this) in this piece */ - ulong len; /* how much of dat[] is valid */ - Env *e; /* the Env whose val points here */ - char dat[4]; /* possibly extends into further envvals after this */ + Envval *next; /* for hashing & easy deletion from hash list */ + Envval *prev; + ulong len; /* length of val that is valid */ + int ref; + char *val; }; -/* number of contiguous Envvals needed to hold n characters */ -#define EVNEEDED(n) ((n)<4? 1 : 1+((n)+(sizeof(Envval))-1-4)/(sizeof(Envval))) +enum{ + MAXENV = (BY2PG - sizeof(Envval)), + EVHASH = 64, + EVFREE = 16, + ALIGN = 16, +}; struct { - Lock; - Envval *arena; - Envval *vfree; - Envval *end; - Env *efree; - Env *earena; + Envval *free[EVFREE+1]; + char *block; /* the free page we are allocating from */ + char *lim; /* end of block */ }envalloc; -void compactenv(Env *, ulong); +QLock evlock; +Envval evhash[EVHASH]; +char *evscratch; /* for constructing the contents of a file */ + +Envval *newev(char*, ulong); +Envval *evalloc(ulong); +void evfree(Envval*); void envreset(void) { - int i, n; - - n = EVNEEDED(conf.nenvchar); - envalloc.arena = ialloc(n*sizeof(Envval), 0); - envalloc.vfree = envalloc.arena; - envalloc.end = envalloc.arena+n; - - envalloc.earena = ialloc(conf.nenv*sizeof(Env), 0); - envalloc.efree = envalloc.earena; - for(i=0; ival points at a value big enough to hold nchars chars. - * The caller should fix e->val->len. - * envalloc and e should be locked - */ -void -growenval(Env *e, ulong nchars) -{ - Envval *p; - ulong n, nfree; - - n = EVNEEDED(nchars); - if(p = e->val){ /* assign = */ - if(p->n < n){ - if(p+p->n == envalloc.vfree){ - compactenv(e, n - p->n); - p = e->val; - envalloc.vfree += n - p->n; - }else{ - compactenv(e, n); - p = envalloc.vfree; - envalloc.vfree += n; - memmove(p, e->val, e->val->n*sizeof(Envval)); - p->e = e; - e->val->e = 0; - e->val = p; - } - p->n = n; - } - }else{ - compactenv(e, n); - p = envalloc.vfree; - envalloc.vfree += n; - p->n = n; - p->e = e; - e->val = p; - } -} - -/* - * Make sure there is room for n Envval's at the end of envalloc.vfree. - * Call this with envalloc and e locked. - */ -void -compactenv(Env *e, ulong n) -{ - Envval *p1, *p2; - Env *p2e; - - if(envalloc.end-envalloc.vfree >= n) - return; - p1 = envalloc.arena; /* dest */ - p2 = envalloc.arena; /* source */ - while(p2 < envalloc.vfree){ - p2e = p2->e; - if(p2e == 0){ - Free: - p2 += p2->n; - continue; - } - if(p2e=envalloc.earena+conf.nenv){ - print("%lux not an env\n", p2e); - panic("compactenv"); - } - if(p1 != p2){ - if(p2e != e) - lock(p2e); - if(p2->e != p2e){ /* freed very recently */ - print("compactenv p2e moved\n"); - if(p2->e) - panic("compactenv p2->e %lux\n", p2->e); - unlock(p2e); - goto Free; - } - if(p2+p2->n > envalloc.end) - panic("compactenv copying too much"); - memmove(p1, p2, p2->n*sizeof(Envval)); - p2e->val = p1; - if(p2e != e) - unlock(p2e); - } - p2 += p1->n; - p1 += p1->n; - } - envalloc.vfree = p1; - if(envalloc.end-envalloc.vfree < n){ - print("env compact failed\n"); - error(Enoenv); - } -} - -/* - * Return an env with a copy of e's value. - * envalloc and e should be locked, - * and the value returned will be locked too. - */ -Env * -copyenv(Env *e, int trunc) -{ - Env *ne; - int n; - - ne = envalloc.efree; - if(!ne){ - print("out of envs\n"); - error(Enoenv); - } - envalloc.efree = ne->next; - lock(ne); - if(waserror()){ - unlock(ne); - nexterror(); - } - ne->next = 0; - ne->pgref = 1; - strncpy(ne->name, e->name, NAMELEN); - if(e->val && !trunc){ - n = e->val->len; - /* - * growenval can't hold the lock on another env - * because compactenv assumes only one is held - */ - unlock(e); - growenval(ne, n); - lock(e); - if(n != e->val->len){ - print("e changed in copyenv\n"); - if(n > ne->val->len) - n = ne->val->len; - } - if((char*)(ne->val+ne->val->n) < ne->val->dat+n) - panic("copyenv corrupt"); - memmove(ne->val->dat, e->val->dat, n); - ne->val->len = n; - } - poperror(); - return ne; -} - int envgen(Chan *c, Dirtab *tab, int ntab, int s, Dir *dp) { - Env *e; Egrp *eg; + Env *e; int ans; eg = u->p->egrp; - lock(eg); + qlock(&eg->ev); if(s >= eg->nenv) ans = -1; else{ - e = eg->etab[s].env; - if(e == 0) + e = &eg->etab[s]; + if(!e->name) ans = 0; else{ - lock(e); - devdir(c, (Qid){s+1,0}, e->name, e->val? e->val->len : 0, 0666, dp); - unlock(e); + devdir(c, (Qid){s+1, (ulong)e->val}, e->name->val, e->val? e->val->len : 0, 0666, dp); ans = 1; } } - unlock(eg); + qunlock(&eg->ev); return ans; } @@ -242,32 +82,14 @@ envattach(char *spec) Chan* envclone(Chan *c, Chan *nc) { - Egrp *eg; - - if(!(c->qid.path&CHDIR)){ - eg = u->p->egrp; - lock(eg); - eg->etab[c->qid.path-1].chref++; - unlock(eg); - } return devclone(c, nc); } int envwalk(Chan *c, char *name) { - Egrp *eg; - if(devwalk(c, name, 0, 0, envgen)){ - if(!(c->qid.path&CHDIR)){ - eg = u->p->egrp; - lock(eg); - eg->etab[c->qid.path-1].chref++; - unlock(eg); - return 1; - } - } - return 0; + return devwalk(c, name, 0, 0, envgen); } void @@ -279,48 +101,31 @@ envstat(Chan *c, char *db) Chan * envopen(Chan *c, int omode) { - Env *e, *ne; - Envp *ep; Egrp *eg; + Env *e; + int mode; - if(omode & (OWRITE|OTRUNC)){ - if(c->qid.path & CHDIR) + mode = openmode(omode); + if(c->qid.path & CHDIR){ + if(omode != OREAD) error(Eperm); + }else{ eg = u->p->egrp; - lock(eg); - ep = &eg->etab[c->qid.path-1]; - e = ep->env; - if(!e){ - unlock(eg); - error(Egreg); + qlock(&eg->ev); + e = &eg->etab[c->qid.path-1]; + if(!e->name){ + qunlock(&eg->ev); + error(Enonexist); } - lock(&envalloc); - lock(e); - if(waserror()){ - unlock(e); - unlock(&envalloc); - unlock(eg); - nexterror(); - } - if(e->pgref == 0) - panic("envopen"); - if(e->pgref == 1){ - if((omode&OTRUNC) && e->val){ - e->val->e = 0; - e->val = 0; - } - }else{ - ne = copyenv(e, omode&OTRUNC); - e->pgref--; /* it will still be positive */ - ep->env = ne; - unlock(ne); + if(omode == (OWRITE|OTRUNC) && e->val){ + qlock(&evlock); + evfree(e->val); + qunlock(&evlock); + e->val = 0; } - poperror(); - unlock(e); - unlock(&envalloc); - unlock(eg); + qunlock(&eg->ev); } - c->mode = openmode(omode); + c->mode = mode; c->flag |= COPEN; c->offset = 0; return c; @@ -329,219 +134,270 @@ envopen(Chan *c, int omode) void envcreate(Chan *c, char *name, int omode, ulong perm) { - Env *e; Egrp *eg; + Env *e, *ne; int i; if(c->qid.path != CHDIR) error(Eperm); + omode = openmode(omode); eg = u->p->egrp; - lock(eg); - lock(&envalloc); - if(waserror()){ - unlock(&envalloc); - unlock(eg); - nexterror(); - } - e = envalloc.efree; - if(e == 0){ - print("out of envs\n"); + qlock(&eg->ev); + e = eg->etab; + ne = 0; + for(i = 0; i < eg->nenv; i++, e++) + if(e->name == 0) + ne = e; + else if(strcmp(e->name->val, name) == 0){ + qunlock(&eg->ev); + error(Einuse); + } + if(ne) + e = ne; + else if(eg->nenv == conf.npgenv){ + print("out of egroup envs\n"); + qunlock(&eg->ev); error(Enoenv); } - envalloc.efree = e->next; - e->next = 0; - e->pgref = 1; - strncpy(e->name, name, NAMELEN); - if(eg->nenv == conf.npgenv){ - for(i = 0; inenv; i++) - if(eg->etab[i].chref == 0) - break; - if(i == eg->nenv){ - print("out of egroup envs\n"); - error(Enoenv); - } - }else - i = eg->nenv++; - c->qid.path = i+1; - eg->etab[i].env = e; - eg->etab[i].chref = 1; - unlock(&envalloc); - unlock(eg); + i = e - eg->etab + 1; + e->val = 0; + qlock(&evlock); + e->name = newev(name, strlen(name)+1); + qunlock(&evlock); + if(i > eg->nenv) + eg->nenv = i; + qunlock(&eg->ev); + c->qid = (Qid){i, 0}; c->offset = 0; - c->mode = openmode(omode); - poperror(); + c->mode = omode; c->flag |= COPEN; } void envremove(Chan *c) { - Env *e; - Envp *ep; Egrp *eg; + Env *e; if(c->qid.path & CHDIR) error(Eperm); eg = u->p->egrp; - lock(eg); - ep = &eg->etab[c->qid.path-1]; - e = ep->env; - if(!e){ - unlock(eg); + qlock(&eg->ev); + e = &eg->etab[c->qid.path-1]; + if(!e->name){ + qunlock(&eg->ev); error(Enonexist); } - ep->env = 0; - ep->chref--; envpgclose(e); - unlock(eg); + qunlock(&eg->ev); } void envwstat(Chan *c, char *db) -{ int dumpenv(void); - dumpenv(); /*DEBUG*/ - print("envwstat\n"); - error(Egreg); +{ + error(Eperm); } void envclose(Chan * c) { - Egrp *eg; +} - if(c->qid.path & CHDIR) - return; - eg = u->p->egrp; - lock(eg); - eg->etab[c->qid.path-1].chref--; - unlock(eg); +void +envpgcopy(Env *t, Env *f) +{ + qlock(&evlock); + if(t->name = f->name) + t->name->ref++; + if(t->val = f->val) + t->val->ref++; + qunlock(&evlock); } void envpgclose(Env *e) { - lock(&envalloc); - lock(e); - if(--e->pgref <= 0){ - if(e->val){ - e->val->e = 0; - e->val = 0; - } - e->next = envalloc.efree; - envalloc.efree = e; - } - unlock(e); - unlock(&envalloc); + qlock(&evlock); + if(e->name) + evfree(e->name); + if(e->val) + evfree(e->val); + e->name = e->val = 0; + qunlock(&evlock); } long -envread(Chan *c, void *va, long n, ulong offset) +envread(Chan *c, void *a, long n, ulong offset) { + Egrp *eg; Env *e; Envval *ev; - char *p; long vn; - Egrp *eg; - char *a = va; if(c->qid.path & CHDIR) return devdirread(c, a, n, 0, 0, envgen); eg = u->p->egrp; - lock(eg); - e = eg->etab[c->qid.path-1].env; - if(!e){ - unlock(eg); - error(Eio); + qlock(&eg->ev); + e = &eg->etab[c->qid.path-1]; + if(!e->name){ + qunlock(&eg->ev); + error(Enonexist); } - lock(e); ev = e->val; - vn = ev? e->val->len : 0; - if(offset+n > vn) + vn = ev ? ev->len : 0; + if(offset + n > vn) n = vn - offset; if(n <= 0) n = 0; else - memmove(a, ev->dat+offset, n); - unlock(e); - unlock(eg); + memmove(a, ev->val + offset, n); + qunlock(&eg->ev); return n; } long -envwrite(Chan *c, void *va, long n, ulong offset) +envwrite(Chan *c, void *a, long n, ulong offset) { + Egrp *eg; Env *e; - char *p; Envval *ev; - long vn; - Egrp *eg; - char *a = va; + ulong olen; if(n <= 0) return 0; + if(offset + n > MAXENV) + error(Etoobig); eg = u->p->egrp; - lock(eg); - e = eg->etab[c->qid.path-1].env; /* caller checks for CHDIR */ - if(!e){ - unlock(eg); - error(Eio); - } - lock(&envalloc); - lock(e); - if(waserror()){ - unlock(e); - unlock(&envalloc); - unlock(eg); - nexterror(); + qlock(&eg->ev); + e = &eg->etab[c->qid.path-1]; + if(!e->name){ + qunlock(&eg->ev); + error(Enonexist); } - if(e->pgref>1) - panic("envwrite to non-duped env"); - growenval(e, offset+n); ev = e->val; - vn = ev? ev->len : 0; - if(offset > vn) - error(Egreg); /* perhaps should zero fill */ - memmove(ev->dat+offset, a, n); - e->val->len = offset+n; - poperror(); - unlock(e); - unlock(&envalloc); - unlock(eg); + olen = ev ? ev->len : 0; + qlock(&evlock); + if(offset == 0 && n >= olen) + e->val = newev(a, n); + else{ + if(olen > offset) + olen = offset; + if(ev) + memmove(evscratch, ev->val, olen); + if(olen < offset) + memset(evscratch + olen, '\0', offset - olen); + memmove(evscratch + offset, a, n); + e->val = newev(evscratch, offset + n); + } + if(ev) + evfree(ev); + qunlock(&evlock); + qunlock(&eg->ev); return n; } -void -dumpenv(void) +/* + * called with evlock qlocked + */ +Envval * +newev(char *s, ulong n) { - Env *e; - Envp *ep; Envval *ev; - Egrp *eg; - int i; - char hold; + uchar *t; + int h; + + h = 0; + for(t = (uchar*)s; t - (uchar*)s < n; t++) + h = (h << 1) ^ *t; + h &= EVHASH - 1; + for(ev = evhash[h].next; ev; ev = ev->next) + if(ev->len == n && memcmp(ev->val, s, n) == 0){ + ev->ref++; + return ev; + } + ev = evalloc(n); + ev->len = n; + memmove(ev->val, s, n); + if(ev->next = evhash[h].next) + ev->next->prev = ev; + evhash[h].next = ev; + ev->prev = &evhash[h]; + return ev; +} - eg = u->p->egrp; - for(ep=eg->etab, i=0; inenv; i++, ep++) - print("P%d(%lux %d)",i, ep->env, ep->chref); - for(e=envalloc.earena; e<&envalloc.earena[conf.nenv]; e++) - if(e->pgref){ - print("E{%lux %d '%s'}[", e, e->pgref, e->name); - if(e->val){ - hold = e->val->dat[e->val->len]; - e->val->dat[e->val->len] = 0; - print("%s", e->val->dat); - e->val->dat[e->val->len] = hold; - } - print("]"); - }else if(e->val) - print("whoops, free env %lux has val=%lux\n",e,e->val); - for(i=0, e=envalloc.efree; e; e=e->next) - i++; - print("\n%d free envs", i); - for(i=0, ev=envalloc.arena; evn) - if(!ev->e) - i += ev->n*sizeof(Envval); - print(" %d free enval chars\n", i+((char *)envalloc.end-(char*)envalloc.vfree)); +/* + * called only from newev + */ +Envval * +evalloc(ulong n) +{ + Envval *ev, **p; + char *b, *lim; + ulong size; + + n = (n - 1) / ALIGN; + size = (n + 1) * ALIGN; + p = &envalloc.free[n < EVFREE ? n : EVFREE]; + for(ev = *p; ev; ev = *p){ + if(ev->len == size){ + *p = ev->next; + ev->ref = 1; + return ev; + } + p = &ev->next; + } + + /* + * make sure we have enough space to allocate the buffer. + * if not, use the remaining space for the smallest buffers + */ + if(size > MAXENV) + panic("evalloc"); + b = envalloc.block; + lim = envalloc.lim; + if(!b || lim < b + size + sizeof *ev){ + p = &envalloc.free[0]; + while(lim >= b + ALIGN + sizeof *ev){ + ev = (Envval*)b; + ev->len = ALIGN; + ev->val = b + sizeof *ev; + ev->next = *p; + *p = ev; + b += ALIGN + sizeof *ev; + } + b = (char*)VA(kmap(newpage(0, 0, 0))); + envalloc.lim = b + BY2PG; + } + + ev = (Envval*)b; + ev->val = b + sizeof *ev; + ev->ref = 1; + envalloc.block = b + size + sizeof *ev; + return ev; +} + +/* + * called with evlock qlocked + */ +void +evfree(Envval *ev) +{ + int n; + + if(--ev->ref > 0) + return; + + if(ev->prev) + ev->prev->next = ev->next; + if(ev->next) + ev->next->prev = ev->prev; + n = (ev->len + ALIGN - 1) & ~(ALIGN - 1); + ev->len = n; + n = (n - 1) / ALIGN; + if(n > EVFREE) + n = EVFREE; + ev->next = envalloc.free[n]; + envalloc.free[n] = ev; } /* diff --git a/port/pgrp.c b/port/pgrp.c index 89bafd177e349824606a47931998ff238f9647a5..5560cfafa6dfdef37377426bae999312f76e88f6 100644 --- a/port/pgrp.c +++ b/port/pgrp.c @@ -56,7 +56,7 @@ grpinit(void) ee = &egrpalloc.free[conf.npgrp]; for(e = egrpalloc.free; e < ee; e++) { e->next = e+1; - e->etab = ialloc(conf.npgenv*sizeof(Envp*), 0); + e->etab = ialloc(conf.npgenv*sizeof(Env), 0); } e[-1].next = 0; @@ -222,19 +222,18 @@ closepgrp(Pgrp *p) } void -closeegrp(Egrp *e) +closeegrp(Egrp *eg) { - Envp *ep; + Env *e; int i; - if(decref(e) == 0) { - ep = e->etab; - for(i=0; inenv; i++,ep++) - if(ep->env) - envpgclose(ep->env); + if(decref(eg) == 0) { + e = eg->etab; + for(i=0; inenv; i++, e++) + envpgclose(e); lock(&egrpalloc); - e->next = egrpalloc.free; - egrpalloc.free = e; + eg->next = egrpalloc.free; + egrpalloc.free = eg; unlock(&egrpalloc); } } @@ -292,41 +291,17 @@ newmount(Mhead *mh, Chan *to) void envcpy(Egrp *to, Egrp *from) { - Envp *ep; - Env *e; - int i; - - lock(from); - to->nenv = from->nenv; - ep = to->etab; - for(i=0; inenv; i++,ep++){ - ep->chref = 0; - e = ep->env = from->etab[i].env; - if(e){ - lock(e); - if(waserror()){ - unlock(e); - unlock(from); - ep->env = 0; - nexterror(); - } - /* - * If pgrp being forked has an open channel - * on this env, it may write it after the fork - * so make a copy now. - * Don't worry about other pgrps, because they - * will copy if they are about to write. - */ - if(from->etab[i].chref){ - ep->env = copyenv(e, 0); - unlock(ep->env); - }else - e->pgref++; - poperror(); - unlock(e); - } - } - unlock(from); + Env *te, *fe; + int i, nenv; + + qlock(&from->ev); + nenv = from->nenv; + to->nenv = nenv; + te = to->etab; + fe = from->etab; + for(i=0; i < nenv; i++, te++, fe++) + envpgcopy(te, fe); + qunlock(&from->ev); } void diff --git a/port/portdat.h b/port/portdat.h index 721dea138380ea74d43793c211ef06eced32ff0e..48f7045d8155e06fbf0a32f592c8f2bd93e3c01b 100644 --- a/port/portdat.h +++ b/port/portdat.h @@ -7,7 +7,6 @@ typedef struct Dev Dev; typedef struct Dirtab Dirtab; typedef struct Egrp Egrp; typedef struct Env Env; -typedef struct Envp Envp; typedef struct Envval Envval; typedef struct Etherpkt Etherpkt; typedef struct Fgrp Fgrp; @@ -187,17 +186,8 @@ struct Dirtab struct Env { - Lock; + Envval *name; Envval *val; - char name[NAMELEN]; - Env *next; /* in chain of Envs for a egrp */ - int pgref; /* # pgrps pointing here */ -}; - -struct Envp -{ - Env *env; - int chref; /* # chans from pgrp pointing here */ }; /* @@ -392,7 +382,8 @@ struct Egrp Ref; Egrp *next; int nenv; /* highest active env table entry, +1 */ - Envp *etab; + QLock ev; /* for all of etab */ + Env *etab; }; #define NFD 100 diff --git a/port/portfns.h b/port/portfns.h index 56f59eca2b8ef1b9727b8317898573457fabe646..10bb2269bf21ccfeb350b52a95066f9dd1b1afdc 100644 --- a/port/portfns.h +++ b/port/portfns.h @@ -56,7 +56,8 @@ void duppage(Page*); Segment* dupseg(Segment*); void dupswap(Page*); void envcpy(Egrp*, Egrp*); -void envpgclose(Env *); +void envpgclose(Env*); +void envpgcopy(Env*, Env*); int eqchan(Chan*, Chan*, long); int eqqid(Qid, Qid); void error(int); diff --git a/port/sysfile.c b/port/sysfile.c index d9eae2551498b6b83d7dc3df2014532b13909eb8..6a06f17cc43a865ac73160788b361691d2b5e5f6 100644 --- a/port/sysfile.c +++ b/port/sysfile.c @@ -52,11 +52,10 @@ int openmode(ulong o) { if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)) - Err: error(Ebadarg); o &= ~(OTRUNC|OCEXEC|ORCLOSE); if(o > OEXEC) - goto Err; + error(Ebadarg); if(o == OEXEC) return OREAD; return o;