ref: edafd783588d269cd0234e775b464be728bdcd92
parent: de5fee8aa45bf9af85583b87009c23a219efd155
author: 9ferno <[email protected]>
date: Fri Nov 5 09:39:15 EDT 2021
migrating 9front proc, queue and chan code
--- a/os/ip/plan9.c
+++ b/os/ip/plan9.c
@@ -27,10 +27,3 @@
{
return up->env->errstr;
}
-
-int
-postnote(Proc *p, int, char *, int)
-{
- swiproc(p, 0);
- return 0;
-}
--- a/os/pc64/fpu.c
+++ b/os/pc64/fpu.c
@@ -86,11 +86,6 @@
"precision loss",
};
-enum
-{
- NDebug, /* print debug message */
-};
-
static void
mathnote(ulong status, uintptr pc)
{
--- a/os/pc64/inferno.main.c
+++ b/os/pc64/inferno.main.c
@@ -179,8 +179,8 @@
*/
o = up->env;
o->pgrp->slash = namec("#/", Atodir, 0, 0);
- cnameclose(o->pgrp->slash->name);
- o->pgrp->slash->name = newcname("/");
+ cnameclose(o->pgrp->slash->path);
+ o->pgrp->slash->path = newcname("/");
o->pgrp->dot = cclone(o->pgrp->slash);
chandevinit();
--- a/os/pc64/main.c
+++ b/os/pc64/main.c
@@ -293,8 +293,8 @@
*/
o = up->env;
o->pgrp->slash = namec("#/", Atodir, 0, 0);
- cnameclose(o->pgrp->slash->name);
- o->pgrp->slash->name = newcname("/");
+ pathclose(o->pgrp->slash->path);
+ o->pgrp->slash->path = newpath("/");
o->pgrp->dot = cclone(o->pgrp->slash);
chandevinit();
--- a/os/pc64/trap.c
+++ b/os/pc64/trap.c
@@ -406,7 +406,7 @@
linkproc(void)
{
spllo();
- up->kpfun(up->arg);
+ up->kpfun(up->kparg);
pexit("kproc dying", 0);
}
@@ -422,7 +422,7 @@
p->sched.sp = (uintptr)p->kstack+KSTACK-BY2WD;
p->kpfun = func;
- p->arg = arg;
+ p->kparg = arg;
}
--- a/os/port/chan.c
+++ b/os/port/chan.c
@@ -5,21 +5,10 @@
#include "fns.h"
#include "../port/error.h"
-char*
-channame(Chan *c) /* DEBUGGING */
-{
- if(c == nil)
- return "<nil chan>";
- if(c->name == nil)
- return "<nil name>";
- if(c->name->s == nil)
- return "<nil name.s>";
- return c->name->s;
-}
-
enum
{
- CNAMESLOP = 20
+ PATHSLOP = 20,
+ PATHMSLOP = 20,
};
struct
@@ -34,13 +23,28 @@
struct Elemlist
{
+ char *aname; /* original name */
char *name; /* copy of name, so '/' can be overwritten */
int nelems;
char **elems;
int *off;
int mustbedir;
+ int nerror;
+ int prefix;
};
+char*
+chanpath(Chan *c)
+{
+ if(c == nil)
+ return "<nil chan>";
+ if(c->path == nil)
+ return "<nil path>";
+ if(c->path->s == nil)
+ return "<nil path.s>";
+ return c->path->s;
+}
+
#define SEP(c) ((c) == 0 || (c) == '/')
void cleancname(Cname*);
@@ -53,26 +57,27 @@
int
incref(Ref *r)
{
- int x;
+ long old, new;
- lock(&r->l);
- x = ++r->ref;
- unlock(&r->l);
- return x;
+ do {
+ old = r->ref;
+ new = old+1;
+ } while(!cmpswap(&r->ref, old, new));
+ return new;
}
int
decref(Ref *r)
{
- int x;
+ long old, new;
- lock(&r->l);
- x = --r->ref;
- unlock(&r->l);
- if(x < 0)
- panic("decref, pc=0x%zux", getcallerpc(&r));
-
- return x;
+ do {
+ old = r->ref;
+ if(old <= 0)
+ panic("decref pc=%#p", getcallerpc(&r));
+ new = old-1;
+ } while(!cmpswap(&r->ref, old, new));
+ return new;
}
/*
@@ -87,20 +92,19 @@
int nt;
nt = strlen(t);
- if(nt+1 <= ns){
- memmove(s, t, nt+1);
+ if(nt < ns){
+ memmove(s, t, nt);
+ s[nt] = '\0';
return;
}
- /* too long */
- if(ns < 4){
- /* but very short! */
- strncpy(s, t, ns);
- return;
- }
- /* truncate with ... at character boundary (very rare case) */
- memmove(s, t, ns-4);
+ /* too long, truncate */
+ nt = ns-1;
+ memmove(s, t, nt);
+ s[nt] = '\0';
+ /* append ... if there is space */
ns -= 4;
- s[ns] = '\0';
+ if(ns < 0)
+ return;
/* look for first byte of UTF-8 sequence by skipping continuation bytes */
while(ns>0 && (s[--ns]&0xC0)==0x80)
;
@@ -126,17 +130,18 @@
int n;
char *t, *prev;
- n = strlen(s)+1;
+ n = strlen(s);
/* if it's a user, we can wait for memory; if not, something's very wrong */
- if(up){
- t = smalloc(n);
- setmalloctag(t, getcallerpc(&p));
- }else{
- t = malloc(n);
+ if(up != nil)
+ t = smalloc(n+1);
+ else{
+ t = malloc(n+1);
if(t == nil)
panic("kstrdup: no memory");
}
+ setmalloctag(t, getcallerpc(&p));
memmove(t, s, n);
+ t[n] = '\0';
prev = *p;
*p = t;
free(prev);
@@ -147,10 +152,17 @@
{
int i;
+ todinit(); /* avoid later reentry causing infinite recursion */
for(i=0; devtab[i] != nil; i++)
devtab[i]->reset();
}
+/*
+ * closeproc() kproc is used by 9front not inferno
+ * TODO not sure if closeproc() is needed for 9ferno
+ */
+static void closeproc(void*);
+
void
chandevinit(void)
{
@@ -158,6 +170,7 @@
for(i=0; devtab[i] != nil; i++)
devtab[i]->init();
+ kproc("closeproc", closeproc, nil, 0);
}
void
@@ -164,7 +177,7 @@
chandevshutdown(void)
{
int i;
-
+
/* shutdown in reverse order */
for(i=0; devtab[i] != nil; i++)
;
@@ -210,74 +223,166 @@
c->mqid.path = 0;
c->mqid.vers = 0;
c->mqid.type = 0;
- c->name = 0;
+ c->path = nil;
return c;
}
-static Ref ncname;
-
-Cname*
-newcname(char *s)
+Path*
+newpath(char *s)
{
- Cname *n;
int i;
+ Path *p;
- n = smalloc(sizeof(Cname));
+ p = smalloc(sizeof(Path));
i = strlen(s);
- n->len = i;
- n->alen = i+CNAMESLOP;
- n->s = smalloc(n->alen);
- memmove(n->s, s, i+1);
- n->ref = 1;
- incref(&ncname);
- return n;
+ p->len = i;
+ p->alen = i+PATHSLOP;
+ p->s = smalloc(p->alen);
+ memmove(p->s, s, i+1);
+ p->ref = 1;
+
+ /*
+ * Cannot use newpath for arbitrary names because the mtpt
+ * array will not be populated correctly. The names #/ and / are
+ * allowed, but other names with / in them draw warnings.
+ */
+ if(strchr(s, '/') != nil && strcmp(s, "#/") != 0 && strcmp(s, "/") != 0)
+ print("newpath: %s from %#p\n", s, getcallerpc(&s));
+
+ p->mlen = 1;
+ p->malen = PATHMSLOP;
+ p->mtpt = smalloc(p->malen*sizeof p->mtpt[0]);
+ return p;
}
+static Path*
+copypath(Path *p)
+{
+ int i;
+ Path *pp;
+
+ pp = smalloc(sizeof(Path));
+ pp->ref = 1;
+
+ pp->len = p->len;
+ pp->alen = p->alen;
+ pp->s = smalloc(p->alen);
+ memmove(pp->s, p->s, p->len+1);
+
+ pp->mlen = p->mlen;
+ pp->malen = p->malen;
+ pp->mtpt = smalloc(p->malen*sizeof pp->mtpt[0]);
+ for(i=0; i<pp->mlen; i++){
+ pp->mtpt[i] = p->mtpt[i];
+ if(pp->mtpt[i] != nil)
+ incref(pp->mtpt[i]);
+ }
+
+ return pp;
+}
+
void
-cnameclose(Cname *n)
+pathclose(Path *p)
{
- if(n == nil)
+ int i;
+
+ if(p == nil || decref(p))
return;
- if(decref(n))
- return;
- decref(&ncname);
- free(n->s);
- free(n);
+ for(i=0; i<p->mlen; i++)
+ if(p->mtpt[i] != nil)
+ cclose(p->mtpt[i]);
+ free(p->mtpt);
+ free(p->s);
+ free(p);
}
-Cname*
-addelem(Cname *n, char *s)
+/*
+ * In place, rewrite name to compress multiple /, eliminate ., and process ..
+ * (Really only called to remove a trailing .. that has been added.
+ * Otherwise would need to update n->mtpt as well.)
+ */
+static void
+fixdotdotname(Path *p)
{
- int i, a;
- char *t;
- Cname *new;
+ char *r;
- if(s[0]=='.' && s[1]=='\0')
- return n;
+ if(p->s[0] == '#'){
+ r = strchr(p->s, '/');
+ if(r == nil)
+ return;
+ cleanname(r);
- if(n->ref > 1){
+ /*
+ * The correct name is #i rather than #i/,
+ * but the correct name of #/ is #/.
+ */
+ if(strcmp(r, "/")==0 && p->s[1] != '/')
+ *r = '\0';
+ }else
+ cleanname(p->s);
+ p->len = strlen(p->s);
+}
+
+static Path*
+uniquepath(Path *p)
+{
+ Path *new;
+
+ if(p->ref > 1){
/* copy on write */
- new = newcname(n->s);
- cnameclose(n);
- n = new;
+ new = copypath(p);
+ pathclose(p);
+ p = new;
}
+ return p;
+}
+Path*
+addelem(Path *p, char *s, Chan *from)
+{
+ char *t;
+ int a, i;
+ Chan *c, **tt;
+
+ if(s[0]=='.' && s[1]=='\0')
+ return p;
+
+ p = uniquepath(p);
+
i = strlen(s);
- if(n->len+1+i+1 > n->alen){
- a = n->len+1+i+1 + CNAMESLOP;
+ a = p->len+1+i+1;
+ if(a > p->alen){
+ a += PATHSLOP;
t = smalloc(a);
- memmove(t, n->s, n->len+1);
- free(n->s);
- n->s = t;
- n->alen = a;
+ memmove(t, p->s, p->len+1);
+ free(p->s);
+ p->s = t;
+ p->alen = a;
}
- if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/') /* don't insert extra slash if one is present */
- n->s[n->len++] = '/';
- memmove(n->s+n->len, s, i+1);
- n->len += i;
- if(isdotdot(s))
- cleancname(n);
- return n;
+ /* don't insert extra slash if one is present */
+ if(p->len>0 && p->s[p->len-1]!='/' && s[0]!='/')
+ p->s[p->len++] = '/';
+ memmove(p->s+p->len, s, i+1);
+ p->len += i;
+ if(isdotdot(s)){
+ fixdotdotname(p);
+ if(p->mlen > 1 && (c = p->mtpt[--p->mlen]) != nil){
+ p->mtpt[p->mlen] = nil;
+ cclose(c);
+ }
+ }else{
+ if(p->mlen >= p->malen){
+ p->malen = p->mlen+1+PATHMSLOP;
+ tt = smalloc(p->malen*sizeof tt[0]);
+ memmove(tt, p->mtpt, p->mlen*sizeof tt[0]);
+ free(p->mtpt);
+ p->mtpt = tt;
+ }
+ p->mtpt[p->mlen++] = from;
+ if(from != nil)
+ incref(from);
+ }
+ return p;
}
void
@@ -285,6 +390,12 @@
{
c->flag = CFREE;
+ if(c->dirrock != nil){
+ free(c->dirrock);
+ c->dirrock = nil;
+ c->nrock = 0;
+ c->mrock = 0;
+ }
if(c->umh != nil){
putmhead(c->umh);
c->umh = nil;
@@ -302,7 +413,8 @@
c->mchan = nil;
}
- cnameclose(c->name);
+ pathclose(c->path);
+ c->path = nil;
lock(&chanalloc);
c->next = chanalloc.free;
@@ -310,18 +422,110 @@
unlock(&chanalloc);
}
+/*
+ * Queue a chan to be closed by one of the clunk procs.
+ */
+struct {
+ Chan *head;
+ Chan *tail;
+ ulong nqueued;
+ ulong nclosed;
+ Lock l;
+ QLock q;
+ Rendez r;
+} clunkq;
+
+static int
+clunkwork(void*)
+{
+ return clunkq.head != nil;
+}
+
+static void
+closechanq(Chan *c)
+{
+ lock(&clunkq.l);
+ clunkq.nqueued++;
+ c->next = nil;
+ if(clunkq.head != nil)
+ clunkq.tail->next = c;
+ else
+ clunkq.head = c;
+ clunkq.tail = c;
+ unlock(&clunkq.l);
+ wakeup(&clunkq.r);
+}
+
+static Chan*
+closechandeq(void)
+{
+ Chan *c;
+
+ lock(&clunkq.l);
+ c = clunkq.head;
+ if(c != nil) {
+ clunkq.head = c->next;
+ clunkq.nclosed++;
+ }
+ unlock(&clunkq.l);
+ return c;
+}
+
+static void
+closeproc(void *)
+{
+ Chan *c;
+
+ for(;;){
+ c = closechandeq();
+ if(c == nil) {
+ qlock(&clunkq.q);
+ if(!waserror()) {
+ tsleep(&clunkq.r, clunkwork, nil, 500);
+ poperror();
+ }
+ c = closechandeq();
+ if(c == nil) {
+ if(clunkq.q.head != nil) {
+ qunlock(&clunkq.q);
+ pexit("no work", 1);
+ }
+ qunlock(&clunkq.q);
+ continue;
+ }
+ if(clunkq.q.head == nil) {
+ if(!waserror()) {
+ kproc("closeproc", closeproc, nil, 0);
+ poperror();
+ }
+ }
+ qunlock(&clunkq.q);
+ }
+ if(!waserror()){
+ devtab[c->type]->close(c);
+ poperror();
+ }
+ chanfree(c);
+ }
+}
+
void
cclose(Chan *c)
{
- if(c == 0)
- return;
+ if(c == nil || c->ref < 1 || c->flag&CFREE)
+ panic("cclose %#p", getcallerpc(&c));
- if(c->flag&CFREE)
- panic("cclose %zux", getcallerpc(&c));
-
if(decref(c))
return;
+ if(devtab[c->type]->dc == L'M')
+ if((c->flag&COPEN) == 0 || (c->flag&(CRCLOSE|CCACHE)) == CCACHE)
+ if((c->qid.type&(QTEXCL|QTMOUNT|QTAUTH)) == 0)
+ if((clunkq.nqueued - clunkq.nclosed) < 64){
+ closechanq(c);
+ return;
+ }
+
if(!waserror()){
devtab[c->type]->close(c);
poperror();
@@ -329,6 +533,16 @@
chanfree(c);
}
+void
+ccloseq(Chan *c)
+{
+ if(c == nil || c->ref < 1 || c->flag&CFREE)
+ panic("ccloseq %#p", getcallerpc(&c));
+
+ if(decref(c) == 0)
+ closechanq(c);
+}
+
/*
* Make sure we have the only copy of c. (Copy on write.)
*/
@@ -337,12 +551,18 @@
{
Chan *nc;
- if(c->ref != 1) {
+ if(c->ref != 1){
nc = cclone(c);
cclose(c);
c = nc;
}
+ if(c->umh != nil){ //BUG
+ print("cunique umh != nil from %#p\n", getcallerpc(&c));
+ putmhead(c->umh);
+ c->umh = nil;
+ }
+
return c;
}
@@ -353,11 +573,11 @@
}
int
-eqchan(Chan *a, Chan *b, int pathonly)
+eqchan(Chan *a, Chan *b, int skipvers)
{
if(a->qid.path != b->qid.path)
return 0;
- if(!pathonly && a->qid.vers!=b->qid.vers)
+ if(!skipvers && a->qid.vers!=b->qid.vers)
return 0;
if(a->type != b->type)
return 0;
@@ -367,11 +587,11 @@
}
int
-eqchantdqid(Chan *a, int type, int dev, Qid qid, int pathonly)
+eqchantdqid(Chan *a, int type, int dev, Qid qid, int skipvers)
{
if(a->qid.path != qid.path)
return 0;
- if(!pathonly && a->qid.vers!=qid.vers)
+ if(!skipvers && a->qid.vers!=qid.vers)
return 0;
if(a->type != type)
return 0;
@@ -389,130 +609,150 @@
mh->ref = 1;
mh->from = from;
incref(from);
+ setmalloctag(mh, getcallerpc(&from));
+ return mh;
+}
/*
- n = from->name->len;
- if(n >= sizeof(mh->fromname))
- n = sizeof(mh->fromname)-1;
- memmove(mh->fromname, from->name->s, n);
- mh->fromname[n] = 0;
-*/
- return mh;
+ * This is necessary because there are many
+ * pointers to the top of a given mount list:
+ *
+ * - the mhead in the namespace hash table
+ * - the mhead in chans returned from findmount:
+ * used in namec and then by unionread.
+ * - the mhead in chans returned from createdir:
+ * used in the open/create race protect, which is gone.
+ *
+ * The RWlock in the Mhead protects the mount list it contains.
+ * The mount list is deleted in cunmount() and closepgrp().
+ * The RWlock ensures that nothing is using the mount list at that time.
+ *
+ * It is okay to replace c->mh with whatever you want as
+ * long as you are sure you have a unique reference to it.
+ *
+ * This comment might belong somewhere else.
+ */
+void
+putmhead(Mhead *m)
+{
+ if(m == nil)
+ return;
+ if(decref(m))
+ return;
+ assert(m->mount == nil);
+ cclose(m->from);
+ free(m);
}
int
cmount(Chan *new, Chan *old, int flag, char *spec)
{
- Pgrp *pg;
- int order, flg;
+ int order;
Mhead *m, **l, *mh;
- Mount *nm, *f, *um, **h;
+ Mount *nm, *f, *um;
+ Pgrp *pg;
+ if(old->umh != nil)
+ print("cmount: unexpected umh, caller %#p\n", getcallerpc(&new));
+
if(QTDIR & (old->qid.type^new->qid.type))
error(Emount);
-if(old->umh)
- print("cmount old extra umh\n");
-
order = flag&MORDER;
- if((old->qid.type&QTDIR)==0 && order != MREPL)
+ if((old->qid.type&QTDIR) == 0 && order != MREPL)
error(Emount);
+ nm = newmount(new, flag, spec);
mh = new->umh;
+ if(mh != nil) {
+ rlock(&mh->lock);
+ if(waserror()) {
+ runlock(&mh->lock);
+ mountfree(nm);
+ nexterror();
+ }
+ um = mh->mount;
+ if(um != nil){
+ /*
+ * Not allowed to bind when the old directory is itself a union.
+ * (Maybe it should be allowed, but I don't see what the semantics
+ * would be.)
+ *
+ * We need to check mh->mount->next to tell unions apart from
+ * simple mount points, so that things like
+ * mount -c fd /root
+ * bind -c /root /
+ * work.
+ *
+ * The check of mount->mflag allows things like
+ * mount fd /root
+ * bind -c /root /
+ *
+ * This is far more complicated than it should be, but I don't
+ * see an easier way at the moment.
+ */
+ if((flag&MCREATE) != 0 && (um->next != nil || (um->mflag&MCREATE) == 0))
+ error(Emount);
- /*
- * Not allowed to bind when the old directory
- * is itself a union. (Maybe it should be allowed, but I don't see
- * what the semantics would be.)
- *
- * We need to check mh->mount->next to tell unions apart from
- * simple mount points, so that things like
- * mount -c fd /root
- * bind -c /root /
- * work. The check of mount->mflag catches things like
- * mount fd /root
- * bind -c /root /
- *
- * This is far more complicated than it should be, but I don't
- * see an easier way at the moment. -rsc
- */
- if((flag&MCREATE) && mh && mh->mount
- && (mh->mount->next || !(mh->mount->mflag&MCREATE)))
- error(Emount);
+ /*
+ * copy a union when binding it onto a directory
+ */
+ f = nm;
+ for(um = um->next; um != nil; um = um->next){
+ f->next = newmount(um->to, order==MREPL? MAFTER: order, um->spec);
+ f = f->next;
+ }
+ }
+ runlock(&mh->lock);
+ poperror();
+ }
pg = up->env->pgrp;
wlock(&pg->ns);
-
l = &MOUNTH(pg, old->qid);
- for(m = *l; m; m = m->hash) {
+ for(m = *l; m != nil; m = m->hash){
if(eqchan(m->from, old, 1))
break;
l = &m->hash;
}
-
- if(m == nil) {
+ if(m == nil){
/*
* nothing mounted here yet. create a mount
* head and add to the hash table.
*/
m = newmhead(old);
- *l = m;
-
/*
* if this is a union mount, add the old
* node to the mount chain.
*/
if(order != MREPL)
- m->mount = newmount(m, old, 0, 0);
+ m->mount = newmount(old, 0, nil);
+ *l = m;
}
wlock(&m->lock);
- if(waserror()){
- wunlock(&m->lock);
- nexterror();
- }
- wunlock(&pg->ns);
-
- nm = newmount(m, new, flag, spec);
- if(mh != nil && mh->mount != nil) {
- /*
- * copy a union when binding it onto a directory
- */
- flg = order;
- if(order == MREPL)
- flg = MAFTER;
- h = &nm->next;
- um = mh->mount;
- for(um = um->next; um; um = um->next) {
- f = newmount(m, um->to, flg, um->spec);
- *h = f;
- h = &f->next;
- }
- }
-
- if(m->mount && order == MREPL) {
- mountfree(m->mount);
- m->mount = 0;
- }
-
- if(flag & MCREATE)
- nm->mflag |= MCREATE;
-
- if(m->mount && order == MAFTER) {
- for(f = m->mount; f->next; f = f->next)
+ um = m->mount;
+ if(um != nil && order == MAFTER){
+ for(f = um; f->next != nil; f = f->next)
;
f->next = nm;
- }
- else {
- for(f = nm; f->next; f = f->next)
- ;
- f->next = m->mount;
+ um = nil;
+ } else {
+ if(order != MREPL){
+ for(f = nm; f->next != nil; f = f->next)
+ ;
+ f->next = um;
+ um = nil;
+ }
m->mount = nm;
}
-
+ order = nm->mountid;
wunlock(&m->lock);
- poperror();
- return nm->mountid;
+ wunlock(&pg->ns);
+
+ mountfree(um);
+
+ return order;
}
void
@@ -522,11 +762,11 @@
Mhead *m, **l;
Mount *f, **p;
- if(mnt->umh) /* should not happen */
+ if(mnt->umh != nil) /* should not happen */
print("cunmount newp extra umh %p has %p\n", mnt, mnt->umh);
/*
- * It _can_ happen that mounted->umh is non-nil,
+ * It _can_ happen that mounted->umh is non-nil,
* because mounted is the result of namec(Aopen)
* (see sysfile.c:/^sysunmount).
* If we open a union directory, it will have a umh.
@@ -538,47 +778,44 @@
wlock(&pg->ns);
l = &MOUNTH(pg, mnt->qid);
- for(m = *l; m; m = m->hash) {
+ for(m = *l; m != nil; m = m->hash){
if(eqchan(m->from, mnt, 1))
break;
l = &m->hash;
}
- if(m == 0) {
+ if(m == nil){
wunlock(&pg->ns);
error(Eunmount);
}
wlock(&m->lock);
- if(mounted == 0) {
+ f = m->mount;
+ if(mounted == nil){
*l = m->hash;
- wunlock(&pg->ns);
- mountfree(m->mount);
m->mount = nil;
- cclose(m->from);
wunlock(&m->lock);
+ wunlock(&pg->ns);
+ mountfree(f);
putmhead(m);
return;
}
-
- p = &m->mount;
- for(f = *p; f; f = f->next) {
- /* BUG: Needs to be 2 pass */
+ for(p = &m->mount; f != nil; f = f->next){
if(eqchan(f->to, mounted, 1) ||
- (f->to->mchan && eqchan(f->to->mchan, mounted, 1))) {
+ (f->to->mchan != nil && eqchan(f->to->mchan, mounted, 1))){
*p = f->next;
- f->next = 0;
- mountfree(f);
- if(m->mount == nil) {
+ f->next = nil;
+ if(m->mount == nil){
*l = m->hash;
- cclose(m->from);
wunlock(&m->lock);
wunlock(&pg->ns);
+ mountfree(f);
putmhead(m);
return;
}
wunlock(&m->lock);
wunlock(&pg->ns);
+ mountfree(f);
return;
}
p = &f->next;
@@ -594,105 +831,119 @@
Chan *nc;
Walkqid *wq;
+ if(c == nil || c->ref < 1 || c->flag&CFREE)
+ panic("cclone: %#p", getcallerpc(&c));
wq = devtab[c->type]->walk(c, nil, nil, 0);
if(wq == nil)
error("clone failed");
nc = wq->clone;
free(wq);
- nc->name = c->name;
- if(c->name)
- incref(c->name);
+ if((nc->path = c->path) != nil)
+ incref(c->path);
return nc;
}
+/* also used by sysfile.c:/^mountfix */
int
findmount(Chan **cp, Mhead **mp, int type, int dev, Qid qid)
{
+ Chan *to;
Pgrp *pg;
Mhead *m;
pg = up->env->pgrp;
rlock(&pg->ns);
- for(m = MOUNTH(pg, qid); m; m = m->hash){
- rlock(&m->lock);
-if(m->from == nil){
- print("m %p m->from 0\n", m);
- runlock(&m->lock);
- continue;
-}
- if(eqchantdqid(m->from, type, dev, qid, 1)) {
+ for(m = MOUNTH(pg, qid); m != nil; m = m->hash){
+ if(eqchantdqid(m->from, type, dev, qid, 1)){
+ if(mp != nil)
+ incref(m);
+ rlock(&m->lock);
+ to = m->mount->to;
+ incref(to);
+ runlock(&m->lock);
runlock(&pg->ns);
if(mp != nil){
- incref(m);
- if(*mp != nil)
- putmhead(*mp);
+ putmhead(*mp);
*mp = m;
}
if(*cp != nil)
cclose(*cp);
- incref(m->mount->to);
- *cp = m->mount->to;
- runlock(&m->lock);
+ *cp = to;
return 1;
}
- runlock(&m->lock);
}
-
runlock(&pg->ns);
return 0;
}
-int
-domount(Chan **cp, Mhead **mp)
+/*
+ * Calls findmount but also updates path.
+ */
+static int
+domount(Chan **cp, Mhead **mp, Path **path)
{
- return findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid);
+ Chan **lc, *from;
+ Path *p;
+
+ if(findmount(cp, mp, (*cp)->type, (*cp)->dev, (*cp)->qid) == 0)
+ return 0;
+
+ if(path != nil){
+ p = *path;
+ p = uniquepath(p);
+ if(p->mlen <= 0)
+ print("domount: path %s has mlen==%d\n", p->s, p->mlen);
+ else{
+ from = (*mp)->from;
+ incref(from);
+ lc = &p->mtpt[p->mlen-1];
+ if(*lc != nil)
+ cclose(*lc);
+ *lc = from;
+ }
+ *path = p;
+ }
+ return 1;
}
-Chan*
-undomount(Chan *c, Cname *name)
+/*
+ * If c is the right-hand-side of a mount point, returns the left hand side.
+ * Changes name to reflect the fact that we've uncrossed the mountpoint,
+ * so name had better be ours to change!
+ */
+static Chan*
+undomount(Chan *c, Path *path)
{
Chan *nc;
- Pgrp *pg;
- Mount *t;
- Mhead **h, **he, *f;
- pg = up->env->pgrp;
- rlock(&pg->ns);
- if(waserror()) {
- runlock(&pg->ns);
- nexterror();
- }
+ if(path->ref != 1 || path->mlen == 0)
+ print("undomount: path %s ref %d mlen %d caller %#p\n",
+ path->s, path->ref, path->mlen, getcallerpc(&c));
- he = &pg->mnthash[MNTHASH];
- for(h = pg->mnthash; h < he; h++) {
- for(f = *h; f; f = f->hash) {
- if(strcmp(f->from->name->s, name->s) != 0)
- continue;
- for(t = f->mount; t; t = t->next) {
- if(eqchan(c, t->to, 1)) {
- /*
- * We want to come out on the left hand side of the mount
- * point using the element of the union that we entered on.
- * To do this, find the element that has a from name of
- * c->name->s.
- */
- if(strcmp(t->head->from->name->s, name->s) != 0)
- continue;
- nc = t->head->from;
- incref(nc);
- cclose(c);
- c = nc;
- break;
- }
- }
- }
+ if(path->mlen > 0 && (nc = path->mtpt[path->mlen-1]) != nil){
+ cclose(c);
+ path->mtpt[path->mlen-1] = nil;
+ c = nc;
}
- poperror();
- runlock(&pg->ns);
return c;
}
/*
+ * Call dev walk but catch errors.
+ */
+static Walkqid*
+ewalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ Walkqid *wq;
+
+ if(waserror())
+ return nil;
+ wq = devtab[c->type]->walk(c, nc, name, nname);
+ poperror();
+ return wq;
+}
+
+/*
* Either walks all the way or not at all. No partial results in *cp.
* *nerror is the number of names to display in an error message.
*/
@@ -700,17 +951,17 @@
int
walk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
{
- int dev, dotdot, i, n, nhave, ntry, type;
- Chan *c, *nc;
- Cname *cname;
- Mount *f;
+ int dev, didmount, dotdot, i, n, nhave, ntry, type;
+ Chan *c, *nc, *mtpt;
+ Path *path;
Mhead *mh, *nmh;
+ Mount *f;
Walkqid *wq;
c = *cp;
incref(c);
- cname = c->name;
- incref(cname);
+ path = c->path;
+ incref(path);
mh = nil;
/*
@@ -720,18 +971,21 @@
* 3. move to the first mountpoint along the way.
* 4. repeat.
*
- * An invariant is that each time through the loop, c is on the undomount
- * side of the mount point, and c's name is cname.
+ * Each time through the loop:
+ *
+ * If didmount==0, c is on the undomount side of the mount point.
+ * If didmount==1, c is on the domount side of the mount point.
+ * Either way, c's full path is path.
*/
+ didmount = 0;
for(nhave=0; nhave<nnames; nhave+=n){
- if((c->qid.type&QTDIR)==0){
+ if((c->qid.type&QTDIR) == 0){
if(nerror)
*nerror = nhave;
- cnameclose(cname);
+ pathclose(path);
cclose(c);
- strcpy(up->env->errstr, Enotdir);
- if(mh != nil)
- putmhead(mh);
+ kstrcpy(up->env->errstr, Enotdir, ERRMAX);
+ putmhead(mh);
return -1;
}
ntry = nnames - nhave;
@@ -740,83 +994,89 @@
dotdot = 0;
for(i=0; i<ntry; i++){
if(isdotdot(names[nhave+i])){
- if(i==0) {
+ if(i==0){
dotdot = 1;
ntry = 1;
- } else
+ }else
ntry = i;
break;
}
}
- if(!dotdot && !nomount)
- domount(&c, &mh);
+ if(!dotdot && !nomount && !didmount)
+ domount(&c, &mh, &path);
type = c->type;
dev = c->dev;
- if((wq = devtab[type]->walk(c, nil, names+nhave, ntry)) == nil){
+ if((wq = ewalk(c, nil, names+nhave, ntry)) == nil){
/* try a union mount, if any */
- if(mh && !nomount){
+ if(mh != nil && !nomount){
/*
- * mh->mount == c, so start at mh->mount->next
+ * mh->mount->to == c, so start at mh->mount->next
*/
rlock(&mh->lock);
- for(f = mh->mount->next; f; f = f->next)
- if((wq = devtab[f->to->type]->walk(f->to, nil, names+nhave, ntry)) != nil)
+ if((f = mh->mount) != nil)
+ f = f->next;
+ for(; f != nil; f = f->next)
+ if((wq = ewalk(f->to, nil, names+nhave, ntry)) != nil){
+ type = f->to->type;
+ dev = f->to->dev;
break;
+ }
runlock(&mh->lock);
- if(f != nil){
- type = f->to->type;
- dev = f->to->dev;
- }
}
if(wq == nil){
cclose(c);
- cnameclose(cname);
+ pathclose(path);
if(nerror)
*nerror = nhave+1;
- if(mh != nil)
- putmhead(mh);
+ putmhead(mh);
return -1;
}
}
- nmh = nil;
- if(dotdot) {
+ didmount = 0;
+ if(dotdot){
assert(wq->nqid == 1);
assert(wq->clone != nil);
- cname = addelem(cname, "..");
- nc = undomount(wq->clone, cname);
+ path = addelem(path, "..", nil);
+ nc = undomount(wq->clone, path);
+ nmh = nil;
n = 1;
- } else {
+ }else{
nc = nil;
- if(!nomount)
- for(i=0; i<wq->nqid && i<ntry-1; i++)
- if(findmount(&nc, &nmh, type, dev, wq->qid[i]))
+ nmh = nil;
+ if(!nomount){
+ for(i=0; i<wq->nqid && i<ntry-1; i++){
+ if(findmount(&nc, &nmh, type, dev, wq->qid[i])){
+ didmount = 1;
break;
+ }
+ }
+ }
if(nc == nil){ /* no mount points along path */
if(wq->clone == nil){
cclose(c);
- cnameclose(cname);
- if(wq->nqid==0 || (wq->qid[wq->nqid-1].type&QTDIR)){
+ pathclose(path);
+ if(wq->nqid == 0 || (wq->qid[wq->nqid-1].type&QTDIR) != 0){
if(nerror)
*nerror = nhave+wq->nqid+1;
- strcpy(up->env->errstr, Edoesnotexist);
+ kstrcpy(up->env->errstr, Edoesnotexist, ERRMAX);
}else{
if(nerror)
*nerror = nhave+wq->nqid;
- strcpy(up->env->errstr, Enotdir);
+ kstrcpy(up->env->errstr, Enotdir, ERRMAX);
}
free(wq);
- if(mh != nil)
- putmhead(mh);
+ putmhead(mh);
return -1;
}
n = wq->nqid;
nc = wq->clone;
}else{ /* stopped early, at a mount point */
+ assert(didmount);
if(wq->clone != nil){
cclose(wq->clone);
wq->clone = nil;
@@ -823,8 +1083,12 @@
}
n = i+1;
}
- for(i=0; i<n; i++)
- cname = addelem(cname, names[nhave+i]);
+ for(i=0; i<n; i++){
+ mtpt = nil;
+ if(i==n-1 && nmh!=nil)
+ mtpt = nmh->from;
+ path = addelem(path, names[nhave+i], mtpt);
+ }
}
cclose(c);
c = nc;
@@ -832,24 +1096,16 @@
mh = nmh;
free(wq);
}
-
putmhead(mh);
-
c = cunique(c);
- if(c->umh != nil){ //BUG
- print("walk umh\n");
- putmhead(c->umh);
- c->umh = nil;
- }
+ pathclose(c->path);
+ c->path = path;
- cnameclose(c->name);
- c->name = cname;
-
cclose(*cp);
*cp = c;
if(nerror)
- *nerror = 0;
+ *nerror = nhave;
return 0;
}
@@ -885,31 +1141,6 @@
{
}
-/*
- * In place, rewrite name to compress multiple /, eliminate ., and process ..
- */
-void
-cleancname(Cname *n)
-{
- char *p;
-
- if(n->s[0] == '#'){
- p = strchr(n->s, '/');
- if(p == nil)
- return;
- cleanname(p);
-
- /*
- * The correct name is #i rather than #i/,
- * but the correct name of #/ is #/.
- */
- if(strcmp(p, "/")==0 && n->s[1] != '/')
- *p = '\0';
- }else
- cleanname(n->s);
- n->len = strlen(n->s);
-}
-
static void
growparse(Elemlist *e)
{
@@ -939,11 +1170,11 @@
* rather than a directory.
*/
static void
-parsename(char *name, Elemlist *e)
+parsename(char *aname, Elemlist *e)
{
- char *slash;
+ char *name, *slash;
- kstrdup(&e->name, name);
+ kstrdup(&e->name, aname);
name = e->name;
e->nelems = 0;
e->elems = nil;
@@ -951,12 +1182,12 @@
e->off[0] = skipslash(name) - name;
for(;;){
name = skipslash(name);
- if(*name=='\0'){
+ if(*name == '\0'){
+ e->off[e->nelems] = name+strlen(name) - e->name;
e->mustbedir = 1;
break;
}
growparse(e);
-
e->elems[e->nelems++] = name;
slash = utfrune(name, '/');
if(slash == nil){
@@ -970,6 +1201,59 @@
}
}
+static void
+namelenerror(char *aname, int len, char *err)
+{
+ char *ename, *name, *next;
+ int i, errlen;
+
+ /*
+ * If the name is short enough, just use the whole thing.
+ */
+ errlen = strlen(err);
+ if(len < ERRMAX/3 || len+errlen < 2*ERRMAX/3)
+ snprint(up->genbuf, sizeof up->genbuf, "%.*s",
+ utfnlen(aname, len), aname);
+ else{
+ /*
+ * Print a suffix of the name, but try to get a little info.
+ */
+ ename = aname+len;
+ next = ename;
+ do{
+ name = next;
+ if(next == aname)
+ break;
+ while(next > aname)
+ if(*--next == '/')
+ break;
+ len = ename-next;
+ }while(len < ERRMAX/3 || len + errlen < 2*ERRMAX/3);
+
+ /*
+ * If the name is ridiculously long, chop it.
+ */
+ if(name == ename){
+ name = ename-ERRMAX/4;
+ if(name <= aname)
+ panic("bad math in namelenerror");
+ /* walk out of current UTF sequence */
+ for(i=0; (*name&0xC0)==0x80 && i<UTFmax; i++)
+ name++;
+ }
+ snprint(up->genbuf, sizeof up->genbuf, "...%.*s",
+ utfnlen(name, ename-name), name);
+ }
+ snprint(up->env->errstr, ERRMAX, "%#q %s", up->genbuf, err);
+ nexterror();
+}
+
+void
+nameerror(char *name, char *err)
+{
+ namelenerror(name, strlen(name), err);
+}
+
void*
memrchr(void *va, int c, long n)
{
@@ -1001,19 +1285,24 @@
Chan*
namec(char *aname, int amode, int omode, ulong perm)
{
- int n, prefix, len, t, nomount, npath;
- Chan *c, *cnew;
- Cname *cname;
+ int len, n, t, nomount;
+ Chan *c;
+ Chan *volatile cnew;
+ Path *volatile path;
Elemlist e;
Rune r;
Mhead *m;
- char *createerr, tmperrbuf[ERRMAX];
+ char *err;
char *name;
- name = aname;
- if(name[0] == '\0')
+ if(aname[0] == '\0')
error("empty file name");
- validname(name, 1);
+ aname = validnamedup(aname, 1);
+ if(waserror()){
+ free(aname);
+ nexterror();
+ }
+ name = aname;
/*
* Find the starting off point (the current slash, the root of
@@ -1026,30 +1315,31 @@
c = up->env->pgrp->slash;
incref(c);
break;
-
+
case '#':
nomount = 1;
up->genbuf[0] = '\0';
n = 0;
- while(*name!='\0' && (*name != '/' || n < 2)){
+ while(*name != '\0' && (*name != '/' || n < 2)){
if(n >= sizeof(up->genbuf)-1)
error(Efilename);
up->genbuf[n++] = *name++;
}
up->genbuf[n] = '\0';
- n = chartorune(&r, up->genbuf+1)+1;
- if(r == 'M')
- error(Enoattach);
/*
- * the nodevs exceptions are
+ * noattach is sandboxing.
+ *
+ * the OK exceptions are:
* | it only gives access to pipes you create
+ * d this process's file descriptors
* e this process's environment
- * s private file2chan creation space
- * D private secure sockets name space
- * a private TLS name space
+ * the iffy exceptions are:
+ * c time and pid, but also cons and consctl
+ * p control of your own processes (and unfortunately
+ * any others left unprotected)
*/
- if(up->env->pgrp->nodevs &&
- (utfrune("|esDa", r) == nil || r == 's' && up->genbuf[n]!='\0'))
+ n = chartorune(&r, up->genbuf+1)+1;
+ if(up->env->pgrp->noattach && utfrune("|decp", r)==nil)
error(Enoattach);
t = devno(r, 1);
if(t == -1)
@@ -1062,23 +1352,36 @@
incref(c);
break;
}
- prefix = name - aname;
+ e.aname = aname;
+ e.prefix = name - aname;
e.name = nil;
e.elems = nil;
e.off = nil;
e.nelems = 0;
+ e.nerror = 0;
if(waserror()){
cclose(c);
free(e.name);
free(e.elems);
+ /*
+ * Prepare nice error, showing first e.nerror elements of name.
+ */
+ if(e.nerror == 0)
+ nexterror();
+ if(e.off[e.nerror]==0)
+ print("nerror=%d but off=%d\n",
+ e.nerror, e.off[e.nerror]);
+ len = e.prefix+e.off[e.nerror];
free(e.off);
-//dumpmount();
- nexterror();
+ err = up->env->errstr;
+ up->env->errstr = up->env->syserrstr;
+ up->env->syserrstr = err;
+ namelenerror(aname, len, err);
}
/*
- * Build a list of elements in the path.
+ * Build a list of elements in the name.
*/
parsename(name, &e);
@@ -1088,9 +1391,8 @@
if(amode == Acreate){
/* perm must have DMDIR if last element is / or /. */
if(e.mustbedir && !(perm&DMDIR)){
- npath = e.nelems;
- strcpy(tmperrbuf, "create without DMDIR");
- goto NameError;
+ e.nerror = e.nelems;
+ error("create without DMDIR");
}
/* don't try to walk the last path element just yet. */
@@ -1099,66 +1401,67 @@
e.nelems--;
}
- if(walk(&c, e.elems, e.nelems, nomount, &npath) < 0){
- if(npath < 0 || npath > e.nelems){
- print("namec %s walk error npath=%d\n", aname, npath);
- nexterror();
+ if(walk(&c, e.elems, e.nelems, nomount, &e.nerror) < 0){
+ if(e.nerror < 0 || e.nerror > e.nelems){
+ print("namec %s walk error nerror=%d\n", aname, e.nerror);
+ e.nerror = 0;
}
- strcpy(tmperrbuf, up->env->errstr);
- NameError:
- len = prefix+e.off[npath];
- if(len < ERRMAX/3 || (name=memrchr(aname, '/', len))==nil || name==aname)
- snprint(up->genbuf, sizeof up->genbuf, "%.*s", len, aname);
- else
- snprint(up->genbuf, sizeof up->genbuf, "...%.*s", (int)(len-(name-aname)), name);
- snprint(up->env->errstr, ERRMAX, "%#q %s", up->genbuf, tmperrbuf);
nexterror();
}
- if(e.mustbedir && !(c->qid.type&QTDIR)){
- npath = e.nelems;
- strcpy(tmperrbuf, "not a directory");
- goto NameError;
- }
+ if(e.mustbedir && (c->qid.type&QTDIR) == 0)
+ error("not a directory");
- if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR)){
- npath = e.nelems;
+ if(amode == Aopen && (omode&3) == OEXEC && (c->qid.type&QTDIR) != 0)
error("cannot exec directory");
- }
switch(amode){
- case Aaccess:
- if(!nomount)
- domount(&c, nil);
- break;
-
case Abind:
+ /* no need to maintain path - cannot dotdot an Abind */
m = nil;
if(!nomount)
- domount(&c, &m);
- if(c->umh != nil)
- putmhead(c->umh);
+ domount(&c, &m, nil);
+ if(waserror()){
+ putmhead(m);
+ nexterror();
+ }
+ c = cunique(c);
c->umh = m;
+ poperror();
break;
+ case Aaccess:
case Aremove:
case Aopen:
Open:
- /* save the name; domount might change c */
- cname = c->name;
- incref(cname);
+ /* save&update the name; domount might change c */
+ path = c->path;
+ incref(path);
+ if(waserror()){
+ pathclose(path);
+ nexterror();
+ }
m = nil;
if(!nomount)
- domount(&c, &m);
-
+ domount(&c, &m, &path);
+ if(waserror()){
+ putmhead(m);
+ nexterror();
+ }
/* our own copy to open or remove */
c = cunique(c);
+ poperror();
/* now it's our copy anyway, we can put the name back */
- cnameclose(c->name);
- c->name = cname;
+ pathclose(c->path);
+ c->path = path;
+ poperror();
+ /* record whether c is on a mount point */
+ c->ismtpt = m!=nil;
+
switch(amode){
+ case Aaccess:
case Aremove:
putmhead(m);
break;
@@ -1165,28 +1468,22 @@
case Aopen:
case Acreate:
-if(c->umh != nil){
- print("cunique umh\n");
- putmhead(c->umh);
- c->umh = nil;
-}
-
/* only save the mount head if it's a multiple element union */
- if(m && m->mount && m->mount->next)
- c->umh = m;
- else
- putmhead(m);
+ if(m != nil) {
+ rlock(&m->lock);
+ if(m->mount != nil && m->mount->next != nil) {
+ c->umh = m;
+ runlock(&m->lock);
+ } else {
+ runlock(&m->lock);
+ putmhead(m);
+ }
+ }
/* save registers else error() in open has wrong value of c saved */
saveregisters();
- if(omode == OEXEC)
- c->flag &= ~CCACHE;
-
c = devtab[c->type]->open(c, omode&~OCEXEC);
-
- if(omode & OCEXEC)
- c->flag |= CCEXEC;
if(omode & ORCLOSE)
c->flag |= CRCLOSE;
break;
@@ -1198,7 +1495,7 @@
* Directories (e.g. for cd) are left before the mount point,
* so one may mount on / or . and see the effect.
*/
- if(!(c->qid.type & QTDIR))
+ if((c->qid.type&QTDIR) == 0)
error(Enotdir);
break;
@@ -1217,6 +1514,7 @@
* If omode&OEXCL is set, just give up.
*/
e.nelems++;
+ e.nerror++;
if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) == 0){
if(omode&OEXCL)
error(Eexist);
@@ -1228,11 +1526,11 @@
* The semantics of the create(2) system call are that if the
* file exists and can be written, it is to be opened with truncation.
* On the other hand, the create(5) message fails if the file exists.
- * If we get two create(2) calls happening simultaneously,
- * they might both get here and send create(5) messages, but only
+ * If we get two create(2) calls happening simultaneously,
+ * they might both get here and send create(5) messages, but only
* one of the messages will succeed. To provide the expected create(2)
* semantics, the call with the failed message needs to try the above
- * walk again, opening for truncation. This correctly solves the
+ * walk again, opening for truncation. This correctly solves the
* create/create race, in the sense that any observable outcome can
* be explained as one happening before the other.
* The create/create race is quite common. For example, it happens
@@ -1242,7 +1540,7 @@
* The implementation still admits a create/create/remove race:
* (A) walk to file, fails
* (B) walk to file, fails
- * (A) create file, succeeds, returns
+ * (A) create file, succeeds, returns
* (B) create file, fails
* (A) remove file, succeeds, returns
* (B) walk to file, return failure.
@@ -1281,48 +1579,44 @@
* if findmount gave us a new Chan.
*/
cnew = cunique(cnew);
- cnameclose(cnew->name);
- cnew->name = c->name;
- incref(cnew->name);
+ pathclose(cnew->path);
+ cnew->path = c->path;
+ incref(cnew->path);
devtab[cnew->type]->create(cnew, e.elems[e.nelems-1], omode&~(OEXCL|OCEXEC), perm);
- poperror();
- if(omode & OCEXEC)
- cnew->flag |= CCEXEC;
if(omode & ORCLOSE)
cnew->flag |= CRCLOSE;
- if(m)
- putmhead(m);
+ poperror();
+ putmhead(m);
cclose(c);
c = cnew;
- c->name = addelem(c->name, e.elems[e.nelems-1]);
+ c->path = addelem(c->path, e.elems[e.nelems-1], nil);
break;
}
/* create failed */
cclose(cnew);
- if(m)
- putmhead(m);
+ putmhead(m);
if(omode & OEXCL)
nexterror();
/* save error */
- createerr = up->env->errstr;
- up->env->errstr = tmperrbuf;
+ err = up->env->errstr;
+ up->env->errstr = up->env->syserrstr;
+ up->env->syserrstr = err;
/* note: we depend that walk does not error */
- if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0){
- up->env->errstr = createerr;
- error(createerr); /* report true error */
- }
- up->env->errstr = createerr;
+ if(walk(&c, e.elems+e.nelems-1, 1, nomount, nil) < 0)
+ error(err); /* report true error */
+ /* restore error */
+ err = up->env->syserrstr;
+ up->env->syserrstr = up->env->errstr;
+ up->env->errstr = err;
omode |= OTRUNC;
goto Open;
default:
- panic("unknown namec access %d\n", amode);
+ panic("unknown namec access %d", amode);
}
- poperror();
-
/* place final element in genbuf for e.g. exec */
if(e.nelems > 0)
kstrcpy(up->genbuf, e.elems[e.nelems-1], sizeof up->genbuf);
@@ -1331,6 +1625,9 @@
free(e.name);
free(e.elems);
free(e.off);
+ poperror(); /* e c */
+ free(aname);
+ poperror(); /* aname */
return c;
}
@@ -1364,12 +1661,17 @@
* routine works for kernel and user memory both.
* The parameter slashok flags whether a slash character is an error
* or a valid character.
+ *
+ * The parameter dup flags whether the string should be copied
+ * out of user space before being scanned the second time.
+ * (Otherwise a malicious thread could remove the NUL, causing us
+ * to access unchecked addresses.)
*/
-void
-validname(char *aname, int slashok)
+static char*
+validname0(char *aname, int slashok, int dup, uintptr pc)
{
- char *ename, *name;
- int c;
+ char *ename, *name, *s;
+ int c, n;
Rune r;
name = aname;
@@ -1376,8 +1678,19 @@
ename = memchr(name, 0, (1<<16));
if(ename==nil || ename-name>=(1<<16))
- error("name too long");
+ error(Etoolong);
+ s = nil;
+ if(dup){
+ n = ename-name;
+ s = smalloc(n+1);
+ memmove(s, name, n);
+ s[n] = 0;
+ aname = s;
+ name = s;
+ setmalloctag(s, pc);
+ }
+
while(*name){
/* all characters above '~' are ok */
c = *(uchar*)name;
@@ -1385,47 +1698,33 @@
name += chartorune(&r, name);
else{
if(isfrog[c])
- if(!slashok || c!='/'){
- snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname);
- error(up->genbuf);
+ if(!slashok || c!='/'){
+ snprint(up->genbuf, sizeof(up->genbuf), "%s: %q", Ebadchar, aname);
+ free(s);
+ error(up->genbuf);
}
name++;
}
}
+ return s;
}
void
-isdir(Chan *c)
+validname(char *aname, int slashok)
{
- if(c->qid.type & QTDIR)
- return;
- error(Enotdir);
+ validname0(aname, slashok, 0, getcallerpc(&aname));
}
-/*
- * This is necessary because there are many
- * pointers to the top of a given mount list:
- *
- * - the mhead in the namespace hash table
- * - the mhead in chans returned from findmount:
- * used in namec and then by unionread.
- * - the mhead in chans returned from createdir:
- * used in the open/create race protect, which is gone.
- *
- * The RWlock in the Mhead protects the mount list it contains.
- * The mount list is deleted when we cunmount.
- * The RWlock ensures that nothing is using the mount list at that time.
- *
- * It is okay to replace c->mh with whatever you want as
- * long as you are sure you have a unique reference to it.
- *
- * This comment might belong somewhere else.
- */
+char*
+validnamedup(char *aname, int slashok)
+{
+ return validname0(aname, slashok, 1, getcallerpc(&aname));
+}
+
void
-putmhead(Mhead *m)
+isdir(Chan *c)
{
- if(m && decref(m) == 0){
- m->mount = (Mount*)0xCafeBeef;
- free(m);
- }
+ if(c->qid.type & QTDIR)
+ return;
+ error(Enotdir);
}
--- a/os/port/dev.c
+++ b/os/port/dev.c
@@ -135,7 +135,7 @@
spec = "";
buf = smalloc(4+strlen(spec)+1);
sprint(buf, "#%C%s", tc, spec);
- c->name = newcname(buf);
+ c->path = newpath(buf);
free(buf);
return c;
}
@@ -157,7 +157,6 @@
nc->qid = c->qid;
nc->offset = c->offset;
nc->umh = nil;
- nc->mountid = c->mountid;
nc->aux = c->aux;
nc->mqid = c->mqid;
nc->mcp = c->mcp;
@@ -269,12 +268,12 @@
switch((*gen)(c, nil, tab, ntab, i, &dir)){
case -1:
if(c->qid.type & QTDIR){
- if(c->name == nil)
+ if(c->path == nil)
elem = "???";
- else if(strcmp(c->name->s, "/") == 0)
+ else if(strcmp(c->path->s, "/") == 0)
elem = "/";
else
- for(elem=p=c->name->s; *p; p++)
+ for(elem=p=c->path->s; *p; p++)
if(*p == '/')
elem = p+1;
devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir);
--- a/os/port/devcons.c
+++ b/os/port/devcons.c
@@ -498,7 +498,7 @@
for(i = 0; i <= o->fgrp->maxfd; i++) {
if((c = o->fgrp->fd[i]) == nil)
continue;
- print("%d: %s\n", i, c->name == nil? "???": c->name->s);
+ print("%d: %s\n", i, c->path == nil? "???": c->path->s);
}
}
--- a/os/port/devmnt.c
+++ b/os/port/devmnt.c
@@ -740,7 +740,6 @@
void
mountrpc(Mnt *m, Mntrpc *r)
{
- char *sn, *cn;
int t;
r->reply.tag = 0;
@@ -757,14 +756,8 @@
default:
if(t == r->request.type+1)
break;
- sn = "?";
- if(m->c->name != nil)
- sn = m->c->name->s;
- cn = "?";
- if(r->c != nil && r->c->name != nil)
- cn = r->c->name->s;
- print("mnt: proc %s %ud: mismatch from %s %s rep 0x%lux tag %d fid %ld T%d R%d rp %d\n",
- up->text, up->pid, sn, cn,
+ print("mnt: proc %s %lud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
+ up->text, up->pid, chanpath(m->c), chanpath(r->c),
r, r->request.tag, r->request.fid, r->request.type,
r->reply.type, r->reply.tag);
error(Emountrpc);
@@ -1142,12 +1135,12 @@
/* This routine is mostly vestiges of prior lives; now it's just sanity checking */
if(c->mchan == nil)
- panic("mntchk 1: nil mchan c %s\n", channame(c));
+ panic("mntchk 1: nil mchan c %s\n", chanpath(c));
m = c->mchan->mux;
if(m == nil)
- print("mntchk 2: nil mux c %s c->mchan %s \n", channame(c), channame(c->mchan));
+ print("mntchk 2: nil mux c %s c->mchan %s \n", chanpath(c), chanpath(c->mchan));
/*
* Was it closed and reused (was error(Eshutdown); now, it can't happen)
--- a/os/port/devprog.c
+++ b/os/port/devprog.c
@@ -487,7 +487,7 @@
&"r w rw"[(c->mode&3)<<1],
devtab[c->type]->dc, c->dev,
c->qid.path, w, c->qid.vers, c->qid.type,
- c->iounit, c->offset, c->name->s);
+ c->iounit, c->offset, c->path->s);
return n;
}
@@ -499,7 +499,7 @@
int n, i, w, ww;
f = o->fgrp; /* f is not locked because we've acquired */
- n = readstr(0, va, count, o->pgrp->dot->name->s);
+ n = readstr(0, va, count, o->pgrp->dot->path->s);
n += snprint(va+n, count-n, "\n");
offset = progoffset(offset, va, &n);
/* compute width of qid.path */
@@ -887,19 +887,19 @@
mntscan(mw, o->pgrp);
if(mw->mh == 0) {
mw->cddone = 1;
- i = snprint(a, n, "cd %s\n", o->pgrp->dot->name->s);
+ i = snprint(a, n, "cd %s\n", o->pgrp->dot->path->s);
poperror();
release();
return i;
}
int2flag(mw->cm->mflag, flag);
- if(strcmp(mw->cm->to->name->s, "#M") == 0){
+ if(strcmp(mw->cm->to->path->s, "#M") == 0){
i = snprint(a, n, "mount %s %s %s %s\n", flag,
- mw->cm->to->mchan->name->s,
- mw->mh->from->name->s, mw->cm->spec? mw->cm->spec : "");
+ mw->cm->to->mchan->path->s,
+ mw->mh->from->path->s, mw->cm->spec? mw->cm->spec : "");
}else
i = snprint(a, n, "bind %s %s %s\n", flag,
- mw->cm->to->name->s, mw->mh->from->name->s);
+ mw->cm->to->path->s, mw->mh->from->path->s);
poperror();
release();
return i;
--- a/os/port/dis.c
+++ b/os/port/dis.c
@@ -314,11 +314,11 @@
q = proctab(0);
for(eq = q+conf.nproc; q < eq; q++) {
if(q->iprog == p) {
- swiproc(q, 1);
+ postnote(q, 1, "interrupted", NUser);
return;
}
}
- /*print("didn't find\n");*/
+ print("swigrog: didn't find proc\n");
}
static Prog*
--- a/os/port/exportfs.c
+++ b/os/port/exportfs.c
@@ -406,7 +406,8 @@
q->flusht = fq;
if(q->busy){
pid = q->slave->pid;
- swiproc(q->slave, 0);
+ /* swiproc(q->slave, 0); */
+ postnote(q->slave, 1, "exflushed", NExit);
}
unlock(q);
unlock(fs);
@@ -455,7 +456,8 @@
fs->work = q->next;
lock(q);
q->shut = 1;
- swiproc(q->slave, 0); /* whether busy or not */
+ /* swiproc(q->slave, 0);*/ /* whether busy or not */
+ postnote(q->slave, 1, "exshutdown", NExit);
unlock(q);
}
unlock(fs);
@@ -761,7 +763,7 @@
exmount(Chan *c, Mhead **mp, int doname)
{
Chan *nc;
- Cname *oname;
+ Path *oname;
nc = nil;
if((c->flag & COPEN) == 0 && findmount(&nc, mp, c->type, c->dev, c->qid)){
@@ -772,10 +774,10 @@
nc = cunique(nc);
poperror();
if(doname){
- oname = c->name;
+ oname = c->path;
incref(oname);
- cnameclose(nc->name);
- nc->name = oname;
+ pathclose(nc->path);
+ nc->path = oname;
}
return nc;
}
@@ -981,7 +983,7 @@
{
Fid *f;
volatile struct {Chan *c;} c, dc;
- Cname *oname;
+ Path *oname;
Uqid *qid;
Mhead *m;
@@ -1009,10 +1011,10 @@
nexterror();
}
if(m != nil){
- oname = c.c->name;
+ oname = c.c->path;
incref(oname);
if(waserror()){
- cnameclose(oname);
+ pathclose(oname);
nexterror();
}
dc.c = createdir(c.c, m);
@@ -1022,12 +1024,12 @@
}
c.c = cunique(dc.c);
poperror();
- cnameclose(c.c->name);
+ pathclose(c.c->path);
poperror();
- c.c->name = oname;
+ c.c->path = oname;
}
devtab[c.c->type]->create(c.c, t->name, t->mode, t->perm);
- c.c->name = addelem(c.c->name, t->name);
+ c.c->path = addelem(c.c->path, t->name, c.c);
if(t->mode & ORCLOSE)
c.c->flag |= CRCLOSE;
qid = uqidalloc(fs, c.c);
--- a/os/port/inferno.c
+++ b/os/port/inferno.c
@@ -869,8 +869,8 @@
dot = o->pgrp->dot;
np.np->dot = cclone(dot);
np.np->slash = cclone(dot);
- cnameclose(np.np->slash->name);
- np.np->slash->name = newcname("/");
+ pathclose(np.np->slash->path);
+ np.np->slash->path = newpath("/");
np.np->nodevs = o->pgrp->nodevs;
opg = o->pgrp;
o->pgrp = np.np;
--- a/os/port/lib.h
+++ b/os/port/lib.h
@@ -154,7 +154,7 @@
#define ORDWR 2 /* read and write */
#define OEXEC 3 /* execute, == read but check execute permission */
#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */
-#define OCEXEC 32 /* or'ed in, close on exec */
+#define OCEXEC 32 /* or'ed in (per file descriptor), close on exec */
#define ORCLOSE 64 /* or'ed in, remove on close */
#define OEXCL 0x1000 /* or'ed in, exclusive create */
--- a/os/port/pgrp.c
+++ b/os/port/pgrp.c
@@ -8,6 +8,14 @@
static Ref pgrpid;
static Ref mountid;
+enum {
+ Whinesecs = 10, /* frequency of out-of-resources printing */
+};
+
+/* TODO code here is different from 9front. Need to understand why. */
+
+static Ref mountid;
+
Pgrp*
newpgrp(void)
{
@@ -20,7 +28,24 @@
return p;
}
+Rgrp*
+newrgrp(void)
+{
+ Rgrp *r;
+
+ r = smalloc(sizeof(Rgrp));
+ r->ref = 1;
+ return r;
+}
+
void
+closergrp(Rgrp *r)
+{
+ if(decref(r) == 0)
+ free(r);
+}
+
+void
closepgrp(Pgrp *p)
{
Mhead **h, **e, *f, *next;
@@ -78,40 +103,31 @@
{
int i;
Mount *n, *m, **link, *order;
- Mhead *f, **tom, **l, *mh;
+ Mhead *f, **l, *mh;
- wlock(&from->ns);
- if(waserror()){
- wunlock(&from->ns);
- nexterror();
- }
- order = 0;
- tom = to->mnthash;
+ wlock(&to->ns);
+ rlock(&from->ns);
+ order = nil;
for(i = 0; i < MNTHASH; i++) {
- l = tom++;
- for(f = from->mnthash[i]; f; f = f->hash) {
+ l = &to->mnthash[i];
+ for(f = from->mnthash[i]; f != nil; f = f->hash) {
rlock(&f->lock);
- if(waserror()){
- runlock(&f->lock);
- nexterror();
- }
- mh = malloc(sizeof(Mhead));
- if(mh == nil)
- error(Enomem);
- mh->from = f->from;
- mh->ref = 1;
- incref(mh->from);
+ mh = newmhead(f->from);
*l = mh;
l = &mh->hash;
link = &mh->mount;
- for(m = f->mount; m; m = m->next) {
- n = newmount(mh, m->to, m->mflag, m->spec);
- m->copy = n;
- pgrpinsert(&order, m);
+ for(m = f->mount; m != nil; m = m->next) {
+ n = smalloc(sizeof(Mount));
+ n->mountid = m->mountid;
+ n->mflag = m->mflag;
+ n->to = m->to;
+ incref(n->to);
+ if(m->spec != nil)
+ kstrdup(&n->spec, m->spec);
+ pgrpinsert(&order, n);
*link = n;
link = &n->next;
}
- poperror();
runlock(&f->lock);
}
}
@@ -118,10 +134,8 @@
/*
* Allocate mount ids in the same sequence as the parent group
*/
- lock(&mountid.l);
- for(m = order; m; m = m->order)
- m->copy->mountid = mountid.ref++;
- unlock(&mountid.l);
+ for(m = order; m != nil; m = m->order)
+ m->mountid = incref(&mountid);
to->progmode = from->progmode;
to->slash = cclone(from->slash);
@@ -128,10 +142,11 @@
to->dot = cclone(from->dot);
to->nodevs = from->nodevs;
- poperror();
- wunlock(&from->ns);
+ runlock(&from->ns);
+ wunlock(&to->ns);
}
+/* not used by 9front. why? */
Fgrp*
newfgrp(Fgrp *old)
{
@@ -156,30 +171,46 @@
Fgrp*
dupfgrp(Fgrp *f)
{
- int i;
- Chan *c;
Fgrp *new;
- int n;
+ Chan *c;
+ int i;
new = smalloc(sizeof(Fgrp));
- new->ref = 1;
+ if(f == nil){
+ new->flag = smalloc(DELTAFD*sizeof(new->flag[0]));
+ new->fd = smalloc(DELTAFD*sizeof(new->fd[0]));
+ new->nfd = DELTAFD;
+ new->ref = 1;
+ return new;
+ }
+
lock(f);
- n = DELTAFD;
- if(f->maxfd >= n)
- n = (f->maxfd+1 + DELTAFD-1)/DELTAFD * DELTAFD;
- new->nfd = n;
- new->fd = malloc(n*sizeof(Chan*));
+ /* Make new fd list shorter if possible, preserving quantization */
+ new->nfd = f->maxfd+1;
+ i = new->nfd%DELTAFD;
+ if(i != 0)
+ new->nfd += DELTAFD - i;
+ new->fd = malloc(new->nfd*sizeof(new->fd[0]));
if(new->fd == nil){
unlock(f);
free(new);
- error(Enomem);
+ error("no memory for fgrp");
}
+ new->flag = malloc(new->nfd*sizeof(new->flag[0]));
+ if(new->flag == nil){
+ unlock(f);
+ free(new->fd);
+ free(new);
+ error("no memory for fgrp");
+ }
+ new->ref = 1;
+
new->maxfd = f->maxfd;
- new->minfd = f->minfd;
for(i = 0; i <= f->maxfd; i++) {
- if(c = f->fd[i]){
+ if((c = f->fd[i]) != nil){
incref(c);
new->fd[i] = c;
+ new->flag[i] = f->flag[i];
}
}
unlock(f);
@@ -193,46 +224,81 @@
int i;
Chan *c;
- if(f == nil || decref(f) != 0)
+ if(f == nil || decref(f))
return;
+ /*
+ * If we get into trouble, forceclosefgrp
+ * will bail us out.
+ */
+ up->env->closingfgrp = f;
for(i = 0; i <= f->maxfd; i++)
- if(c = f->fd[i])
+ if((c = f->fd[i]) != nil){
+ f->fd[i] = nil;
cclose(c);
+ }
+ up->env->closingfgrp = nil;
free(f->fd);
+ free(f->flag);
free(f);
}
+/*
+ * Called from interrupted() because up is in the middle
+ * of closefgrp and just got a kill ctl message.
+ * This usually means that up has wedged because
+ * of some kind of deadly embrace with mntclose
+ * trying to talk to itself. To break free, hand the
+ * unclosed channels to the close queue. Once they
+ * are finished, the blocked cclose that we've
+ * interrupted will finish by itself.
+ */
+void
+forceclosefgrp(void)
+{
+ int i;
+ Chan *c;
+ Fgrp *f;
+
+ if(up->procctl != Proc_exitme || up->env->closingfgrp == nil){
+ print("bad forceclosefgrp call");
+ return;
+ }
+
+ f = up->env->closingfgrp;
+ for(i = 0; i <= f->maxfd; i++)
+ if((c = f->fd[i]) != nil){
+ f->fd[i] = nil;
+ ccloseq(c);
+ }
+}
+
Mount*
-newmount(Mhead *mh, Chan *to, int flag, char *spec)
+newmount(Chan *to, int flag, char *spec)
{
Mount *m;
m = smalloc(sizeof(Mount));
m->to = to;
- m->head = mh;
incref(to);
m->mountid = incref(&mountid);
m->mflag = flag;
- if(spec != 0)
+ if(spec != nil)
kstrdup(&m->spec, spec);
-
+ setmalloctag(m, getcallerpc(&to));
return m;
}
-
void
mountfree(Mount *m)
{
Mount *f;
- while(m) {
- f = m->next;
- cclose(m->to);
- m->mountid = 0;
- free(m->spec);
- free(m);
- m = f;
+ while((f = m) != nil) {
+ m = m->next;
+ cclose(f->to);
+ free(f->spec);
+ free(f);
}
}
@@ -239,19 +305,32 @@
void
resrcwait(char *reason)
{
+ static ulong lastwhine;
+ ulong now;
char *p;
- if(up == 0)
- panic("resrcwait");
+ if(up == nil)
+ panic("resrcwait: %s", reason);
p = up->psstate;
- if(reason) {
+ if(reason != nil) {
+ if(waserror()){
+ up->psstate = p;
+ nexterror();
+ }
up->psstate = reason;
- print("%s\n", reason);
+ now = seconds();
+ /* don't tie up the console with complaints */
+ if(now - lastwhine > Whinesecs) {
+ lastwhine = now;
+ print("%s\n", reason);
+ }
}
-
- tsleep(&up->sleep, return0, 0, 300);
- up->psstate = p;
+ tsleep(&up->sleep, return0, 0, 100+nrand(200));
+ if(reason != nil) {
+ up->psstate = p;
+ poperror();
+ }
}
void
--- a/os/port/portdat.h
+++ b/os/port/portdat.h
@@ -24,7 +24,9 @@
typedef struct Mntwalk Mntwalk;
typedef struct Mnt Mnt;
typedef struct Mhead Mhead;
+typedef struct Note Note;
typedef struct Osenv Osenv;
+typedef struct Path Path;
typedef struct Perf Perf;
typedef struct Pgrp Pgrp;
typedef struct Proc Proc;
@@ -35,6 +37,7 @@
typedef struct Rendez Rendez;
typedef struct Rept Rept;
typedef struct Rootdata Rootdata;
+typedef struct Rgrp Rgrp;
typedef struct RWlock RWlock;
typedef struct Schedq Schedq;
typedef struct Signerkey Signerkey;
@@ -100,6 +103,10 @@
s32 gid; /* Numeric group id for system */
char* user; /* Inferno user name */
int fpuostate;
+
+ /* from 9front */
+ Rgrp *rgrp; /* Rendez group */
+ Fgrp *closingfgrp; /* used during teardown */
};
enum
@@ -117,10 +124,13 @@
struct RWlock
{
- Lock; /* Lock modify lock */
- QLock x; /* Mutual exclusion lock */
- QLock k; /* Lock for waiting writers */
- s32 readers; /* Count of readers in lock */
+ Lock use;
+ Proc *head; /* list of waiting processes */
+ Proc *tail;
+ uintptr wpc; /* pc of writer */
+ Proc *wproc; /* writing proc */
+ int readers; /* number of readers */
+ int writer; /* number of writers */
};
struct Talarm
@@ -191,11 +201,12 @@
struct Chan
{
- Lock;
Ref;
+ Lock;
Chan* next; /* allocation */
Chan* link;
- s64 offset; /* in file */
+ s64 offset; /* in fd */
+ s64 devoffset; /* in underlying device; see read */
u16 type;
u32 dev;
u16 mode; /* read/write */
@@ -202,30 +213,37 @@
u16 flag;
Qid qid;
s32 fid; /* for devmnt */
- u32 iounit; /* chunk size for i/o; 0==default */
+ u32 iounit; /* chunk size for i/o; 0==default */
Mhead* umh; /* mount point that derived Chan; used in unionread */
Chan* umc; /* channel in union; held for union read */
QLock umqlock; /* serialize unionreads */
s32 uri; /* union read index */
s32 dri; /* devdirread index */
- u32 mountid;
- Mntcache *mcp; /* Mount cache pointer */
- Mnt *mux; /* Mnt for clients using me for messages */
+ uchar* dirrock; /* directory entry rock for translations */
+ int nrock;
+ int mrock;
+ QLock rockqlock;
+ int ismtpt;
+ Mntcache*mcp; /* Mount cache pointer */
+ Mnt* mux; /* Mnt for clients using me for messages */
union {
void* aux;
- char tag[4]; /* for iproute */
+ u32 mid; /* for ns in devproc */
};
Chan* mchan; /* channel to mounted server */
Qid mqid; /* qid of root of mount point */
- Cname *name;
+ Path* path;
};
-struct Cname
+struct Path
{
Ref;
- s32 alen; /* allocated length */
- s32 len; /* strlen(s) */
char *s;
+ Chan **mtpt; /* mtpt history */
+ int len; /* strlen(s) */
+ int alen; /* allocated length of s */
+ int mlen; /* number of path elements */
+ int malen; /* allocated length of mtpt */
};
struct Dev
@@ -286,8 +304,6 @@
{
u32 mountid;
Mount* next;
- Mhead* head;
- Mount* copy;
Mount* order;
Chan* to; /* channel replacing channel */
s32 mflag;
@@ -320,6 +336,19 @@
enum
{
+ NUser, /* note provided externally */
+ NExit, /* deliver note quietly */
+ NDebug, /* print debug message */
+};
+
+struct Note
+{
+ char msg[ERRMAX];
+ int flag; /* whether system posted it */
+};
+
+enum
+{
RENDLOG = 5,
RENDHASH = 1<<RENDLOG, /* Hash to lookup rendezvous tags */
MNTLOG = 5,
@@ -328,6 +357,7 @@
MAXNFD = 4000, /* max per process file descriptors */
MAXKEY = 8, /* keys for signed modules */
};
+#define REND(p,s) ((p)->rendhash[(s)&((1<<RENDLOG)-1)])
#define MOUNTH(p,qid) ((p)->mnthash[(qid).path&((1<<MNTLOG)-1)])
struct Mntparam {
@@ -340,11 +370,12 @@
struct Pgrp
{
Ref; /* also used as a lock when mounting */
+ RWlock ns; /* Namespace n read/one write lock */
+ int noattach;
+ Mhead* mnthash[MNTHASH];
u32 pgrpid;
QLock debug; /* single access via devproc.c */
- RWlock ns; /* Namespace n read/one write lock */
QLock nsh;
- Mhead* mnthash[MNTHASH];
s32 progmode;
Chan* dot;
Chan* slash;
@@ -356,6 +387,7 @@
{
Ref;
Lock;
+ Proc *rendhash[RENDHASH]; /* Rendezvous tag hash */
Chan **fd;
uchar *flag; /* per file-descriptor flags (CCEXEC) */
s32 nfd; /* number allocated */
@@ -364,6 +396,13 @@
s32 exceed; /* debugging */
};
+struct Rgrp
+{
+ Ref;
+ Lock;
+ Proc *rendhash[RENDHASH]; /* Rendezvous tag hash */
+};
+
struct Evalue
{
union {
@@ -448,6 +487,8 @@
Scheding,
Running,
Queueing,
+ QueueingR,
+ QueueingW,
Wakeme,
Broken,
Stopped,
@@ -459,7 +500,15 @@
Proc_traceme,
Proc_exitbig,
+ TUser = 0, /* Proc.time */
+ TSys,
+ TReal,
+ TCUser,
+ TCSys,
+ TCReal,
+
NERR = 30,
+ NNOTE = 5,
Unknown = 0,
IdleGC,
@@ -488,64 +537,61 @@
int n;
};
+/* inferno uses Osenv for environment information. It is cheaper to create
+ * new processes with a default environment
+ */
struct Proc
{
- Label sched; /* known to l.s */
- char* kstack; /* known to l.s */
- Mach* mach; /* machine running this proc */
- char text[KNAMELEN];
- Proc* rnext; /* next process in run queue */
- Proc* qnext; /* next process on queue for a QLock */
- QLock* qlock; /* addrof qlock being queued for DEBUG */
+ Label sched; /* known to l.s */
+ char *kstack; /* known to l.s */
+ Mach *mach; /* machine running this proc */
+ char text[KNAMELEN];
+ /* char *user; 9front only */
+
+ /*
+ * below 3 args fields are not used by 9ferno
+ * leaving them alone to stay compatible with 9front
+ */
+/* char *args;*/
+/* int nargs; *//* number of bytes of args */
+/* int setargs; *//* process changed its args */
+
+ Proc *rnext; /* next process in run queue */
+ Proc *qnext; /* next process on queue for a QLock */
+ /* check notes in proc.c for how these 2 fields are used */
+ void *blockinglock; /* address of QLock or RWLock being queued for, DEBUG */
+ /* not in 9front as we can reason that info from qpc */
+ uintptr qpc; /* last call that blocked in QLock or RWLock */
+
+ char *psstate; /* What /proc/#/status reports */
s32 state;
- s32 type;
- void* prog; /* Dummy Prog for interp release */
- void* iprog;
- Osenv* env;
- Osenv defenv;
- s32 swipend; /* software interrupt pending for Prog TODO replace with notepending? */
- Lock sysio; /* note handler lock */
- char* psstate; /* What /proc/#/status reports */
- s32 pid;
- s32 procctl; /* Control for /proc debugging */
- uintptr pc; /* DEBUG only */
- Lock rlock; /* sync between sleep/swiproc for r */
- Rendez* r; /* rendezvous point slept on */
- Rendez sleep; /* place for syssleep/debug */
- s32 killed; /* by swiproc */
- s32 kp; /* true if a kernel process */
- Proc *palarm; /* Next alarm time */
- u32 alarm; /* Time of call */
- s32 priority; /* scheduler priority */
- u32 twhen;
- Timer; /* For tsleep and real-time */
- Rendez* trend;
- Proc* tlink;
- s32 (*tfn)(void*);
- void (*kpfun)(void*);
- void* arg;
- PFPU; /* machine specific fpu state */
- s32 scallnr;
- s32 nerrlab;
- Label errlab[NERR];
- char genbuf[128]; /* buffer used e.g. for last name element from namec */
+ u32 pid;
+ u32 noteid; /* Equivalent of note group */
+/* u32 parentpid; *//* no single parent in inferno, send it to the process group */
- Lock *lockwait;
- Lock *lastlock; /* debugging */
- Lock *lastilock; /* debugging */
+/* Proc *parent; *//* Process to send wait record on exit */
+/* Lock exl; *//* Lock count and waitq */
+/* Waitq *waitq; *//* Exited processes wait children */
+/* int nchild; *//* Number of living children */
+/* int nwait; *//* Number of uncollected wait records */
+/* QLock qwaitr;*/
+/* Rendez waitr; *//* Place to hang out in wait */
- Mach* mp; /* machine this process last ran on */
- Mach* wired;
- int nlocks; /* number of locks held by proc */
- /* obsoleted u32 movetime; */ /* next time process should switch processors */
- u32 delaysched;
- s32 preempted; /* process yielding in interrupt */
- uintptr qpc; /* last call that blocked in qlock */
- void* dbgreg; /* User registers for devproc */
- s32 dbgstop; /* don't run this kproc */
- Edf* edf; /* if non-null, real-time proc, edf contains scheduling params */
+/* QLock seglock; *//* locked whenever seg[] changes */
+/* Segment *seg[NSEG]; */
+/* Pgrp *pgrp; */ /* Process group for namespace */
+/* Egrp *egrp; */ /* Environment group */
+/* Fgrp *fgrp; */ /* File descriptor group */
+/* Rgrp *rgrp; */ /* Rendez group */
+
+/* Fgrp *closingfgrp;*/ /* used during teardown */
+
+/* int insyscall;*/
+ u32 time[6]; /* User, Sys, Real; child U, S, R */
+
+/* uvlong kentry; */ /* Kernel entry time stamp (for profiling) */
/*
* pcycles: cycles spent in this process (updated on procswitch)
* when this is the current proc and we're in the kernel
@@ -554,19 +600,108 @@
* when this is not the current process or we're in user mode
* (procrestores and procsaves balance), it is pcycles.
*/
- s64 pcycles;
+ vlong pcycles;
- PMMU; /* TODO obsolete? machine specific mmu state */
-
- /* TODO 9front fields that need to incorporated into 9ferno */
- /* TODO replace swiproc() with postnote() */
- u32 noteid; /* Equivalent of note group */
QLock debug; /* to access debugging elements of User */
- int trace; /* process being traced? */
- ulong procmode; /* proc device default file mode */
+ Proc *pdbg; /* the debugging process */
+ /*
+ * Pgrp.progmode is used by inferno and procmode by 9front
+ * Leaving them different, for now
+ */
+ u32 procmode; /* proc device default file mode */
int privatemem; /* proc does not let anyone read mem */
int noswap; /* process is not swappable */
int hang; /* hang at next exec for debug */
+ int procctl; /* Control for /proc debugging */
+ uintptr pc; /* DEBUG only */
+
+ Lock rlock; /* sync sleep/wakeup with postnote */
+ Rendez *r; /* rendezvous point slept on */
+ Rendez sleep; /* place for syssleep/debug */
+ int notepending; /* note issued but not acted on */
+ int kp; /* true if a kernel process */
+ Proc *palarm; /* Next alarm time */
+ ulong alarm; /* Time of call */
+/* int newtlb; */ /* Pager has changed my pte's, I must flush */
+
+ uintptr rendtag; /* Tag for rendezvous */
+ uintptr rendval; /* Value for rendezvous */
+ Proc *rendhash; /* Hash list for tag values */
+
+ Timer; /* For tsleep and real-time, has twhen of inferno */
+ Rendez *trend;
+ int (*tfn)(void*);
+ void (*kpfun)(void*);
+ void *kparg;
+
+/* Sargs s; *//* syscall arguments */
+/* int scallnr; *//* sys call number */
+ int nerrlab;
+ Label errlab[NERR];
+ /* below fields are in Osenv */
+/* char *syserrstr; *//* last error from a system call, errbuf0 or 1 */
+/* char *errstr; *//* reason we're unwinding the error stack, errbuf1 or 0 */
+/* char errbuf0[ERRMAX];*/
+/* char errbuf1[ERRMAX];*/
+ char genbuf[128]; /* buffer used e.g. for last name element from namec */
+/* Chan *slash; part of Pgrp in inferno */
+/* Chan *dot; part of Pgrp in inferno */
+
+ Note note[NNOTE];
+ short nnote;
+ short notified; /* sysnoted is due */
+ Note lastnote;
+ int (*notify)(void*, char*);
+
+ Lock *lockwait;
+ Lock *lastlock; /* debugging */
+ Lock *lastilock; /* debugging */
+
+ Mach *wired;
+ Mach *mp; /* machine this process last ran on */
+ int nlocks; /* number of locks held by proc */
+ ulong delaysched;
+ ulong priority; /* priority level */
+/* ulong basepri; *//* base priority level */
+/* uchar fixedpri; *//* priority level deson't change */
+/* ulong cpu; *//* cpu average */
+/* ulong lastupdate;*/
+ uchar yield; /* non-zero if the process just did a sleep(0) */
+ ulong readytime; /* time process came ready */
+ int preempted; /* true if this process hasn't finished the interrupt
+ * that last preempted it
+ */
+ Edf *edf; /* if non-null, real-time proc, edf contains scheduling params */
+ int trace; /* process being traced? */
+
+ QLock *eql; /* interruptable eqlock */
+
+ void *ureg; /* User registers for notes */
+ void *dbgreg; /* User registers for devproc */
+
+ PFPU; /* machine specific fpu state */
+ PMMU; /* machine specific mmu state, obsolete on 9ferno amd64? */
+
+/* char *syscalltrace; *//* syscall trace */
+
+ Watchpt *watchpt; /* watchpoints */
+ int nwatchpt;
+
+ /* inferno specific fields */
+ s32 type;
+ void* prog; /* Dummy Prog for interp release */
+ void* iprog;
+ Osenv* env;
+ Osenv defenv;
+ s32 swipend; /* software interrupt pending for Prog TODO replace with notepending? */
+ Lock sysio; /* note handler lock */
+
+ /* inferno specific fields that are obsolete? */
+ int fpstate;
+ int killed; /* by swiproc */
+ Proc* tlink;
+ ulong movetime; /* next time process should switch processors */
+ int dbgstop; /* don't run this kproc */
};
enum
--- a/os/port/portfns.h
+++ b/os/port/portfns.h
@@ -1,5 +1,5 @@
Timer* addclock0link(void (*)(void), int);
-Cname* addelem(Cname*, char*);
+Path* addelem(Path *p, char *s, Chan *from);
void addprog(Proc*);
void addrootfile(char*, uchar*, ulong);
Block* adjustblock(Block*, int);
@@ -10,7 +10,7 @@
int blocklen(Block*);
int breakhit(Ureg *ur, Proc*);
void callwithureg(void(*)(Ureg*));
-char* channame(Chan*);
+char* chanpath(Chan*);
int canlock(Lock*);
int canqlock(QLock*);
void cclose(Chan*);
@@ -26,6 +26,7 @@
void cinit(void);
Chan* cclone(Chan*);
void cclose(Chan*);
+void ccloseq(Chan*);
void closeegrp(Egrp*);
void closefgrp(Fgrp*);
void closemount(Mount*);
@@ -33,7 +34,6 @@
void closesigs(Skeyset*);
void cmderror(Cmdbuf*, char*);
int cmount(Chan*, Chan*, int, char*);
-void cnameclose(Cname*);
Block* concatblock(Block*);
void confinit(void);
void (*consdebug)(void);
@@ -75,7 +75,6 @@
int devwstat(Chan*, uchar*, int);
void disinit(void*);
void disfault(void*, char*);
-int domount(Chan**, Mhead**);
void drawactive(int);
void drawcmap(void);
void dumpaproc(Proc*);
@@ -93,8 +92,6 @@
void excinit(void);
void exhausted(char*);
void exit(int);
-void reboot(void);
-void halt(void);
int export(int, char*, int);
uvlong fastticks(uvlong*);
uvlong fastticks2ns(uvlong);
@@ -101,6 +98,7 @@
void fdclose(Fgrp*, int);
Chan* fdtochan(Fgrp*, int, int, int, int);
int findmount(Chan**, Mhead**, int, int, Qid);
+void forceclosefgrp(void);
void free(void*);
void freeb(Block*);
void freeblist(Block*);
@@ -110,6 +108,7 @@
uintptr getrealloctag(void*);
void gotolabel(Label*);
char* getconfenv(void);
+void halt(void);
void (*hwrandbuf)(void*, u32);
void hnputl(void*, u32);
void hnputs(void*, u16);
@@ -117,6 +116,7 @@
void iallocsummary(void);
void ilock(Lock*);
s32 incref(Ref*);
+void interrupted(void);
void iomapinit(u32);
s32 ioreservewin(u32, u32, u32, u32, char*);
int iprint(char*, ...);
@@ -184,12 +184,15 @@
Chan* newchan(void);
Egrp* newegrp(void);
Fgrp* newfgrp(Fgrp*);
-Mount* newmount(Mhead*, Chan*, int, char*);
+int newfd(Chan*, int);
+Mhead* newmhead(Chan*);
+Mount* newmount(Chan*, int, char*);
+Path* newpath(char*);
Pgrp* newpgrp(void);
Proc* newproc(void);
char* nextelem(char*, char*);
void nexterror(void);
-Cname* newcname(char*);
+Path* newpath(char*);
int notify(Ureg*);
void notkilled(void);
int nrand(int);
@@ -200,6 +203,7 @@
Block* padblock(Block*, int);
void panic(char*, ...);
Cmdbuf* parsecmd(char*, int);
+void pathclose(Path*);
ulong perfticks(void);
void pexit(char*, int);
void pgrpcpy(Pgrp*, Pgrp*);
@@ -264,6 +268,7 @@
int readnum_vlong(ulong, char*, ulong, vlong, int);
int readstr(ulong, char*, ulong, char*);
void ready(Proc*);
+void reboot(void);
void renameproguser(char*, char*);
void renameuser(char*, char*);
void resrcwait(char*);
@@ -322,6 +327,7 @@
void userinit(void);
ulong userpc(void);
void validname(char*, int);
+char* validnamedup(char*, int);
void validstat(uchar*, int);
void validwstatname(char*);
Proc* wakeup(Rendez*);
--- a/os/port/proc.c
+++ b/os/port/proc.c
@@ -393,6 +393,7 @@
if(pt != nil)
pt(p, SRun, 0);
*/
+/* for debugging */
/* if(p->pid != prevpid){
prevpid = p->pid;
if(p->type == Interp && p->iprog != nil){
@@ -406,19 +407,6 @@
return p;
}
-int
-setpri(int priority)
-{
- int p;
-
- /* called by up so not on run queue */
- p = up->priority;
- up->priority = priority;
- if(up->state == Running && anyhigher())
- sched();
- return p;
-}
-
/*
* not using memset 0 on the Proc structure
* to avoid leaking KSTACK
@@ -449,7 +437,7 @@
p->mach = 0;
p->qnext = 0;
p->kp = 0;
- p->killed = 0;
+ p->killed = 0; /* TODO replace these 2 with notepending */
p->swipend = 0;
p->nlocks = 0;
p->delaysched = 0;
@@ -477,7 +465,54 @@
return p;
}
+/*
+ * wire this proc to a machine
+ */
void
+procwired(Proc *p, int bm)
+{
+ Proc *pp;
+ int i;
+ char nwired[MAXMACH];
+ Mach *wm;
+
+ if(bm < 0){
+ /* pick a machine to wire to */
+ memset(nwired, 0, sizeof(nwired));
+ p->wired = nil;
+ for(i=0; i<conf.nproc; i++){
+ pp = proctab(i);
+ wm = pp->wired;
+ if(wm != nil && pp->pid)
+ nwired[wm->machno]++;
+ }
+ bm = 0;
+ for(i=0; i<conf.nmach; i++)
+ if(nwired[i] < nwired[bm])
+ bm = i;
+ } else {
+ /* use the virtual machine requested */
+ bm = bm % conf.nmach;
+ }
+
+ p->wired = MACHP(bm);
+ p->mp = p->wired;
+}
+
+int
+setpri(int priority)
+{
+ int p;
+
+ /* called by up so not on run queue */
+ p = up->priority;
+ up->priority = priority;
+ if(up->state == Running && anyhigher())
+ sched();
+ return p;
+}
+
+void
procinit(void)
{
Proc *p;
@@ -487,7 +522,8 @@
p = xalloc(conf.nproc*sizeof(Proc));
if(p == nil){
xsummary();
- panic("cannot allocate %ud procs (%udMB)", conf.nproc, conf.nproc*sizeof(Proc)/(1024*1024));
+ panic("cannot allocate %ud procs (%udMB)",
+ conf.nproc, conf.nproc*sizeof(Proc)/(1024*1024));
}
procalloc.arena = p;
procalloc.free = p;
@@ -508,13 +544,12 @@
*
* we lock both the process and the rendezvous to keep r->p
* and p->r synchronized.
- * TODO
- * 9front checks up->notepending instead of up->swipend
*/
void
sleep(Rendez *r, int (*f)(void*), void *arg)
{
int s;
+/* void (*pt)(Proc*, int, vlong);*/
if(up == nil)
panic("sleep() not in process (%zux)", getcallerpc(&r));
@@ -546,10 +581,11 @@
*/
r->p = up;
- /*
- * if killed or condition happened, never mind
- */
- if(up->killed || f(arg)){
+ if(up->notepending || f(arg)){
+ /*
+ * if condition happened or a note is pending
+ * never mind
+ */
r->p = nil;
unlock(&up->rlock);
unlock(r);
@@ -568,15 +604,22 @@
procswitch();
}
- if(up->killed || up->swipend) {
- up->killed = 0;
- up->swipend = 0;
+ if(up->notepending) {
+ up->notepending = 0;
splx(s);
- error(Eintr);
+ interrupted();
}
splx(s);
}
+void
+interrupted(void)
+{
+ if(up->procctl == Proc_exitme && up->env->closingfgrp != nil)
+ forceclosefgrp();
+ error(Eintr);
+}
+
static int
tfn(void *arg)
{
@@ -659,37 +702,129 @@
return p;
}
-/* TODO replace with postnote()
- * man 9 tsleep of inferno
- * man 9 postnote of 9front
+/*
+ * if waking a sleeping process, this routine must hold both
+ * p->rlock and r->lock. However, it can't know them in
+ * the same order as wakeup causing a possible lock ordering
+ * deadlock. We break the deadlock by giving up the p->rlock
+ * lock if we can't get the r->lock and retrying.
*/
-void
-swiproc(Proc *p, int interp)
+int
+postnote(Proc *p, int dolock, char *n, int flag)
{
- ulong s;
- Rendez *r;
+ int s, ret;
+ QLock *q;
if(p == nil)
- return;
+ return 0;
- s = splhi();
- lock(&p->rlock);
- if(!interp)
- p->killed = 1;
- r = p->r;
- if(r != nil) {
- lock(r);
- if(r->p == p){
- p->swipend = 1;
+ if(dolock)
+ qlock(&p->debug);
+
+ if(p->pid == 0){
+ if(dolock)
+ qunlock(&p->debug);
+ return 0;
+ }
+
+ if(n != nil && flag != NUser && (p->notify == nil || p->notified))
+ p->nnote = 0;
+
+ ret = 0;
+ if(p->nnote < NNOTE && n != nil) {
+ kstrcpy(p->note[p->nnote].msg, n, ERRMAX);
+ p->note[p->nnote++].flag = flag;
+ ret = 1;
+ }
+ p->notepending = 1;
+ if(dolock)
+ qunlock(&p->debug);
+
+ /* this loop is to avoid lock ordering problems. */
+ for(;;){
+ Rendez *r;
+
+ s = splhi();
+ lock(&p->rlock);
+ r = p->r;
+
+ /* waiting for a wakeup? */
+ if(r == nil)
+ break; /* no */
+
+ /* try for the second lock */
+ if(canlock(r)){
+ if(p->state != Wakeme || r->p != p)
+ panic("postnote: state %d %d %d", r->p != p, p->r != r, p->state);
+ p->r = nil;
r->p = nil;
ready(p);
+ unlock(r);
+ break;
}
- unlock(r);
+
+ /* give other process time to get out of critical section and try again */
+ unlock(&p->rlock);
+ splx(s);
+ sched();
}
unlock(&p->rlock);
splx(s);
+
+ switch(p->state){
+ case Queueing:
+ /* Try and pull out of a eqlock */
+ if((q = p->eql) != nil){
+ lock(&q->use);
+ if(p->state == Queueing && p->eql == q){
+ Proc *d, *l;
+
+ for(l = nil, d = q->head; d != nil; l = d, d = d->qnext){
+ if(d == p){
+ if(l != nil)
+ l->qnext = p->qnext;
+ else
+ q->head = p->qnext;
+ if(p->qnext == nil)
+ q->tail = l;
+ p->qnext = nil;
+ p->eql = nil; /* not taken */
+ ready(p);
+ break;
+ }
+ }
+ }
+ unlock(&q->use);
+ }
+ break;
+ case Rendezvous:
+ /* Try and pull out of a rendezvous */
+ lock(p->env->rgrp);
+ if(p->state == Rendezvous) {
+ Proc *d, **l;
+
+ l = &REND(p->env->rgrp, p->rendtag);
+ for(d = *l; d != nil; d = d->rendhash) {
+ if(d == p) {
+ *l = p->rendhash;
+ p->rendval = ~0;
+ ready(p);
+ break;
+ }
+ l = &d->rendhash;
+ }
+ }
+ unlock(p->env->rgrp);
+ break;
+ }
+ return ret;
}
+/*
+ * 9front maintains broken processes. Not bothering with them
+ * as there should not be any broken proc's in inferno
+ */
+
void
notkilled(void)
{
@@ -782,7 +917,7 @@
p->kp = 1;
p->fpsave = up->fpsave;
- p->scallnr = up->scallnr;
+/* p->scallnr = up->scallnr; */
p->nerrlab = 0;
kstrdup(&p->env->user, up->env->user);
@@ -803,10 +938,10 @@
p->env->egrp = eg;
}
-/* p->nnote = 0;
+ p->nnote = 0;
p->notify = nil;
p->notified = 0;
- p->notepending = 0;*/
+ p->notepending = 0;
p->procmode = 0640;
p->privatemem = 1;
@@ -813,14 +948,32 @@
p->noswap = 1;
p->hang = 0;
p->kp = 1;
+
+/* p->kpfun = func;
+ p->kparg = arg;
+ kprocchild(p, linkproc);*/
+/* this does all of the above 3 lines */
kprocchild(p, func, arg);
strcpy(p->text, name);
+/* if(kpgrp == nil)
+ kpgrp = newpgrp();
+ p->pgrp = kpgrp;
+ incref(kpgrp);*/
+
+ memset(p->time, 0, sizeof(p->time));
+ p->time[TReal] = MACHP(0)->ticks;
+/* cycles(&p->kentry);
+ p->pcycles = -p->kentry;*/
+
pidalloc(p);
qunlock(&p->debug);
+/* procpriority(p, PriKproc, 0);*/
+
+ p->psstate = nil;
ready(p);
}
--- a/os/port/qlock.c
+++ b/os/port/qlock.c
@@ -4,13 +4,50 @@
#include "dat.h"
#include "fns.h"
+struct {
+ ulong rlock;
+ ulong rlockq;
+ ulong wlock;
+ ulong wlockq;
+ ulong qlock;
+ ulong qlockq;
+} rwstats;
+
+/*
+ lock()
+ blockinglock = qpc = nil
+ unlock()
+ blockinglock = qpc = nil
+
+ lock()
+ blockinglock = qpc = nil
+ placed in the queue
+ blockinglock = lock address
+ qpc = pc that called lock()
+ out of the queue, ready to run
+ blockinglock = nil
+ qpc = pc that called lock()
+ unlock()
+ blockinglock = qpc = nil
+ */
+
void
eqlock(QLock *q)
{
Proc *p;
+ if(m->ilockdepth != 0)
+ print("eqlock: %#p: ilockdepth %d\n", getcallerpc(&q), m->ilockdepth);
+ if(up != nil && up->nlocks)
+ print("eqlock: %#p: nlocks %d\n", getcallerpc(&q), up->nlocks);
+ if(up != nil && up->eql != nil)
+ print("eqlock: %#p: eql %p\n", getcallerpc(&q), up->eql);
+ if(q->use.key == 0x55555555)
+ panic("eqlock: q %#p, key 5*", q);
+
lock(&q->use);
- if(!q->locked) {
+ rwstats.qlock++;
+ if(q->locked == 0) {
q->locked = 1;
unlock(&q->use);
return;
@@ -17,6 +54,12 @@
}
if(up == nil)
panic("eqlock");
+ if(up->notepending){
+ up->notepending = 0;
+ unlock(&q->use);
+ interrupted();
+ }
+ rwstats.qlockq++;
p = q->tail;
if(p == nil)
q->head = up;
@@ -24,33 +67,54 @@
p->qnext = up;
q->tail = up;
up->qnext = nil;
+ up->blockinglock = q;
+ up->eql = q;
up->qpc = getcallerpc(&q);
up->state = Queueing;
unlock(&q->use);
sched();
- /* up->eql = nil; */
+ up->blockinglock = nil;
+ if(up->eql == nil){
+ up->notepending = 0;
+ interrupted();
+ }
+ up->eql = nil;
+ up->qpc = 0;
}
void
qlock(QLock *q)
{
- Proc *p, *mp;
+ Proc *p;
+ if(m->ilockdepth != 0)
+ print("qlock: %#p: ilockdepth %d\n", getcallerpc(&q), m->ilockdepth);
+ if(up != nil && up->nlocks)
+ print("qlock: %#p: nlocks %d\n", getcallerpc(&q), up->nlocks);
+ if(up != nil && up->eql != nil)
+ print("qlock: %#p: eql %p\n", getcallerpc(&q), up->eql);
+ if(q->use.key == 0x55555555)
+ panic("qlock: q %#p, key 5*", q);
lock(&q->use);
- if(!q->locked) {
+ rwstats.qlock++;
+ if(q->locked == 0) {
q->locked = 1;
unlock(&q->use);
return;
}
+ if(up == nil)
+ panic("qlock");
+ rwstats.qlockq++;
p = q->tail;
- mp = up;
- if(p == 0)
- q->head = mp;
+ if(p == nil)
+ q->head = up;
else
- p->qnext = mp;
- q->tail = mp;
- mp->qnext = 0;
- mp->state = Queueing;
+ p->qnext = up;
+ up->qnext = nil;
+ q->tail = up;
+ up->blockinglock = q;
+ up->eql = nil;
+ up->state = Queueing;
up->qpc = getcallerpc(&q);
unlock(&q->use);
sched();
@@ -70,6 +134,7 @@
return 1;
}
+/* blockinglock should not be nil */
void
qunlock(QLock *q)
{
@@ -76,11 +141,21 @@
Proc *p;
lock(&q->use);
+ if (q->locked == 0)
+ print("qunlock called with qlock not held, from %#p\n",
+ getcallerpc(&q));
+ if (up != nil && up->blockinglock != nil)
+ print("qunlock called with blockinglock %#p, from %#p\n",
+ up->blockinglock, getcallerpc(&q));
+ if (up != nil)
+ up->qpc = 0;
p = q->head;
- if(p) {
+ if(p != nil) {
+ /* some other process is waiting for this lock */
q->head = p->qnext;
- if(q->head == 0)
- q->tail = 0;
+ if(q->head == nil)
+ q->tail = nil;
+ p->blockinglock = nil;
unlock(&q->use);
ready(p);
return;
@@ -89,50 +164,160 @@
unlock(&q->use);
}
+/* why is there no interruptible rlock()? */
void
-rlock(RWlock *l)
+rlock(RWlock *q)
{
- qlock(&l->x); /* wait here for writers and exclusion */
- lock(l);
- l->readers++;
- canqlock(&l->k); /* block writers if we are the first reader */
- unlock(l);
- qunlock(&l->x);
+ Proc *p;
+
+ lock(&q->use);
+ rwstats.rlock++;
+ if(q->writer == 0 && q->head == nil){
+ /* no writer, go for it */
+ q->readers++;
+ unlock(&q->use);
+ return;
+ }
+
+ rwstats.rlockq++;
+ p = q->tail;
+ if(up == nil)
+ panic("rlock");
+ if(p == nil)
+ q->head = up;
+ else
+ p->qnext = up;
+ q->tail = up;
+ up->qnext = nil;
+ up->blockinglock = q;
+ up->eql = nil;
+ up->state = QueueingR;
+ up->qpc = getcallerpc(&q);
+ unlock(&q->use);
+ sched();
}
/* same as rlock but punts if there are any writers waiting */
int
-canrlock(RWlock *l)
+canrlock(RWlock *q)
{
- if (!canqlock(&l->x))
- return 0;
- lock(l);
- l->readers++;
- canqlock(&l->k); /* block writers if we are the first reader */
- unlock(l);
- qunlock(&l->x);
- return 1;
+ lock(&q->use);
+ rwstats.rlock++;
+ if(q->writer == 0 && q->head == nil){
+ /* no writer, go for it */
+ q->readers++;
+ unlock(&q->use);
+ return 1;
+ }
+ unlock(&q->use);
+ return 0;
}
void
-runlock(RWlock *l)
+runlock(RWlock *q)
{
- lock(l);
- if(--l->readers == 0) /* last reader out allows writers */
- qunlock(&l->k);
- unlock(l);
+ Proc *p;
+
+ lock(&q->use);
+ if (up != nil && up->blockinglock != nil)
+ print("runlock called with blockinglock %#p, from %#p\n",
+ up->blockinglock, getcallerpc(&q));
+ if (up != nil)
+ up->qpc = 0;
+ p = q->head;
+ if(--(q->readers) > 0 || p == nil){
+ unlock(&q->use);
+ return;
+ }
+ /* last reader out allows writers */
+ /* start waiting writer */
+ if(p->state != QueueingW)
+ panic("runlock");
+ q->head = p->qnext;
+ if(q->head == nil)
+ q->tail = nil;
+ q->writer = 1;
+ p->blockinglock = nil;
+ unlock(&q->use);
+ ready(p);
}
void
-wlock(RWlock *l)
+wlock(RWlock *q)
{
- qlock(&l->x); /* wait here for writers and exclusion */
- qlock(&l->k); /* wait here for last reader */
+ Proc *p;
+
+ lock(&q->use);
+ rwstats.wlock++;
+ if(q->readers == 0 && q->writer == 0){
+ /* noone waiting, go for it */
+ q->wpc = getcallerpc(&q);
+ q->wproc = up;
+ q->writer = 1;
+ unlock(&q->use);
+ return;
+ }
+
+ /* wait */
+ rwstats.wlockq++;
+ p = q->tail;
+ if(up == nil)
+ panic("wlock");
+ if(p == nil)
+ q->head = up;
+ else
+ p->qnext = up;
+ q->tail = up;
+ up->qnext = nil;
+ up->blockinglock = q;
+ up->eql = nil;
+ up->state = QueueingW;
+ up->qpc = getcallerpc(&q);
+ unlock(&q->use);
+ sched();
}
void
-wunlock(RWlock *l)
+wunlock(RWlock *q)
{
- qunlock(&l->k);
- qunlock(&l->x);
+ Proc *p;
+
+ lock(&q->use);
+ if (up != nil && up->blockinglock != nil)
+ print("runlock called with blockinglock %#p, from %#p\n",
+ up->blockinglock, getcallerpc(&q));
+ if (up != nil)
+ up->qpc = 0;
+ p = q->head;
+ if(p == nil){
+ q->writer = 0;
+ unlock(&q->use);
+ return;
+ }
+ if(p->state == QueueingW){
+ /* start waiting writer */
+ q->head = p->qnext;
+ if(q->head == nil)
+ q->tail = nil;
+ p->blockinglock = nil;
+ unlock(&q->use);
+ ready(p);
+ return;
+ }
+
+ if(p->state != QueueingR)
+ panic("wunlock");
+
+ /* waken waiting readers */
+ while(q->head != nil && q->head->state == QueueingR){
+ p = q->head;
+ q->head = p->qnext;
+ q->readers++;
+ p->blockinglock = nil;
+ ready(p);
+ }
+ if(q->head == nil)
+ q->tail = nil;
+ q->writer = 0;
+ unlock(&q->use);
}
--- a/os/port/sysfile.c
+++ b/os/port/sysfile.c
@@ -5,53 +5,134 @@
#include "fns.h"
#include "../port/error.h"
-static int
-growfd(Fgrp *f, int fd)
+static void
+unlockfgrp(Fgrp *f)
{
- int n;
- Chan **nfd, **ofd;
+ int ex;
- if(fd < f->nfd)
+ ex = f->exceed;
+ f->exceed = 0;
+ unlock(f);
+ if(ex)
+ pprint("warning: process exceeds %d file descriptors\n", ex);
+}
+
+int
+growfd(Fgrp *f, int fd) /* fd is always >= 0 */
+{
+ Chan **newfd, **oldfd;
+ uchar *newflag, *oldflag;
+ int nfd;
+
+ nfd = f->nfd;
+ if(fd < nfd)
return 0;
- n = f->nfd+DELTAFD;
- if(n > MAXNFD)
- n = MAXNFD;
- if(fd >= n)
+ if(fd >= nfd+DELTAFD)
+ return -1; /* out of range */
+ /*
+ * Unbounded allocation is unwise; besides, there are only 16 bits
+ * of fid in 9P
+ */
+ if(nfd >= MAXNFD){
+ Exhausted:
+ print("no free file descriptors\n");
return -1;
- nfd = malloc(n*sizeof(Chan*));
- if(nfd == nil)
+ }
+ oldfd = f->fd;
+ oldflag = f->flag;
+ newfd = malloc((nfd+DELTAFD)*sizeof(newfd[0]));
+ if(newfd == nil)
+ goto Exhausted;
+ memmove(newfd, oldfd, nfd*sizeof(newfd[0]));
+ newflag = malloc((nfd+DELTAFD)*sizeof(newflag[0]));
+ if(newflag == nil){
+ free(newfd);
+ goto Exhausted;
+ }
+ memmove(newflag, oldflag, nfd*sizeof(newflag[0]));
+ f->fd = newfd;
+ f->flag = newflag;
+ f->nfd = nfd+DELTAFD;
+ if(fd > f->maxfd){
+ if(fd/100 > f->maxfd/100)
+ f->exceed = (fd/100)*100;
+ f->maxfd = fd;
+ }
+ free(oldfd);
+ free(oldflag);
+ return 1;
+}
+
+/*
+ * this assumes that the fgrp is locked
+ */
+int
+findfreefd(Fgrp *f, int start)
+{
+ int fd;
+
+ for(fd=start; fd<f->nfd; fd++)
+ if(f->fd[fd] == nil)
+ break;
+ if(fd >= f->nfd && growfd(f, fd) < 0)
return -1;
- ofd = f->fd;
- memmove(nfd, ofd, f->nfd*sizeof(Chan *));
- f->fd = nfd;
- f->nfd = n;
- free(ofd);
- return 0;
+ return fd;
}
int
-newfd(Chan *c)
+newfd(Chan *c, int mode)
{
- int i;
- Fgrp *f = up->env->fgrp;
+ int fd, flag;
+ Fgrp *f;
+ f = up->env->fgrp;
lock(f);
- for(i=f->minfd; i<f->nfd; i++)
- if(f->fd[i] == 0)
- break;
- if(i >= f->nfd && growfd(f, i) < 0){
- unlock(f);
- exhausted("file descriptors");
+ fd = findfreefd(f, 0);
+ if(fd < 0){
+ unlockfgrp(f);
return -1;
}
- f->minfd = i + 1;
- if(i > f->maxfd)
- f->maxfd = i;
- f->fd[i] = c;
- unlock(f);
- return i;
+ if(fd > f->maxfd)
+ f->maxfd = fd;
+ f->fd[fd] = c;
+
+ /* per file-descriptor flags */
+ flag = 0;
+ if(mode & OCEXEC)
+ flag |= CCEXEC;
+ f->flag[fd] = flag;
+
+ unlockfgrp(f);
+ return fd;
}
+int
+newfd2(int fd[2], Chan *c[2])
+{
+ Fgrp *f;
+
+ f = up->env->fgrp;
+ lock(f);
+ fd[0] = findfreefd(f, 0);
+ if(fd[0] < 0){
+ unlockfgrp(f);
+ return -1;
+ }
+ fd[1] = findfreefd(f, fd[0]+1);
+ if(fd[1] < 0){
+ unlockfgrp(f);
+ return -1;
+ }
+ if(fd[1] > f->maxfd)
+ f->maxfd = fd[1];
+ f->fd[fd[0]] = c[0];
+ f->fd[fd[1]] = c[1];
+ f->flag[fd[0]] = 0;
+ f->flag[fd[1]] = 0;
+ unlockfgrp(f);
+ return 0;
+}
+
Chan*
fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref)
{
@@ -205,7 +286,7 @@
cclose(c);
nexterror();
}
- fd = newfd(c);
+ fd = newfd(c, mode);
if(fd < 0)
error(Enofd);
poperror();
@@ -231,7 +312,7 @@
if(fd != -1){
lock(f);
if(fd<0 || growfd(f, fd) < 0) {
- unlock(f);
+ unlockfgrp(f);
cclose(c);
error(Ebadfd);
}
@@ -239,7 +320,8 @@
f->maxfd = fd;
oc = f->fd[fd];
f->fd[fd] = c;
- unlock(f);
+ f->flag[fd] = 0;
+ unlockfgrp(f);
if(oc)
cclose(oc);
}else{
@@ -247,7 +329,7 @@
cclose(c);
nexterror();
}
- fd = newfd(c);
+ fd = newfd(c, 0);
if(fd < 0)
error(Enofd);
poperror();
@@ -288,13 +370,13 @@
return nil;
c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
s = nil;
- if(c->name != nil){
- s = malloc(c->name->len+1);
+ if(c->path != nil){
+ s = malloc(c->path->len+1);
if(s == nil){
cclose(c);
error(Enomem);
}
- memmove(s, c->name->s, c->name->len+1);
+ memmove(s, c->path->s, c->path->len+1);
cclose(c);
}
poperror();
@@ -327,7 +409,7 @@
nexterror();
}
- fd = newfd(ac);
+ fd = newfd(ac, 0);
if(fd < 0)
error(Enofd);
poperror(); /* ac */
@@ -398,12 +480,8 @@
error(Egreg);
c[0] = d->open(c[0], ORDWR);
c[1] = d->open(c[1], ORDWR);
- fd[0] = newfd(c[0]);
- if(fd[0] < 0)
+ if(newfd2(fd, c) < 0)
error(Enofd);
- fd[1] = newfd(c[1]);
- if(fd[1] < 0)
- error(Enofd);
poperror();
return 0;
}
@@ -556,7 +634,7 @@
cclose(c);
nexterror();
}
- fd = newfd(c);
+ fd = newfd(c, mode);
if(fd < 0)
error(Enofd);
poperror();