diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/devsegment.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/devsegment.c')
-rwxr-xr-x | sys/src/9/port/devsegment.c | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/sys/src/9/port/devsegment.c b/sys/src/9/port/devsegment.c new file mode 100755 index 000000000..8e5091b79 --- /dev/null +++ b/sys/src/9/port/devsegment.c @@ -0,0 +1,578 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +enum +{ + Qtopdir, + Qsegdir, + Qctl, + Qdata, + + /* commands to kproc */ + Cnone=0, + Cread, + Cwrite, + Cstart, + Cdie, +}; + +#define TYPE(x) (int)( (c)->qid.path & 0x7 ) +#define SEG(x) ( ((c)->qid.path >> 3) & 0x3f ) +#define PATH(s, t) ( ((s)<<3) | (t) ) + +typedef struct Globalseg Globalseg; +struct Globalseg +{ + Ref; + Segment *s; + + char *name; + char *uid; + vlong length; + long perm; + + /* kproc to do reading and writing */ + QLock l; /* sync kproc access */ + Rendez cmdwait; /* where kproc waits */ + Rendez replywait; /* where requestor waits */ + Proc *kproc; + char *data; + long off; + int dlen; + int cmd; + char err[64]; +}; + +static Globalseg *globalseg[100]; +static Lock globalseglock; + + + Segment* (*_globalsegattach)(Proc*, char*); +static Segment* globalsegattach(Proc *p, char *name); +static int cmddone(void*); +static void segmentkproc(void*); +static void docmd(Globalseg *g, int cmd); + +/* + * returns with globalseg incref'd + */ +static Globalseg* +getgseg(Chan *c) +{ + int x; + Globalseg *g; + + x = SEG(c); + lock(&globalseglock); + if(x >= nelem(globalseg)) + panic("getgseg"); + g = globalseg[x]; + if(g != nil) + incref(g); + unlock(&globalseglock); + if(g == nil) + error("global segment disappeared"); + return g; +} + +static void +putgseg(Globalseg *g) +{ + if(decref(g) > 0) + return; + if(g->s != nil) + putseg(g->s); + if(g->kproc) + docmd(g, Cdie); + free(g->name); + free(g->uid); + free(g); +} + +static int +segmentgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + Globalseg *g; + ulong size; + + switch(TYPE(c)) { + case Qtopdir: + if(s == DEVDOTDOT){ + q.vers = 0; + q.path = PATH(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, "#g", 0, eve, DMDIR|0777, dp); + break; + } + + if(s >= nelem(globalseg)) + return -1; + + lock(&globalseglock); + g = globalseg[s]; + if(g == nil){ + unlock(&globalseglock); + return 0; + } + q.vers = 0; + q.path = PATH(s, Qsegdir); + q.type = QTDIR; + devdir(c, q, g->name, 0, g->uid, DMDIR|0777, dp); + unlock(&globalseglock); + + break; + case Qsegdir: + if(s == DEVDOTDOT){ + q.vers = 0; + q.path = PATH(0, Qtopdir); + q.type = QTDIR; + devdir(c, q, "#g", 0, eve, DMDIR|0777, dp); + break; + } + /* fall through */ + case Qctl: + case Qdata: + switch(s){ + case 0: + g = getgseg(c); + q.vers = 0; + q.path = PATH(SEG(c), Qctl); + q.type = QTFILE; + devdir(c, q, "ctl", 0, g->uid, g->perm, dp); + putgseg(g); + break; + case 1: + g = getgseg(c); + q.vers = 0; + q.path = PATH(SEG(c), Qdata); + q.type = QTFILE; + if(g->s != nil) + size = g->s->top - g->s->base; + else + size = 0; + devdir(c, q, "data", size, g->uid, g->perm, dp); + putgseg(g); + break; + default: + return -1; + } + break; + } + return 1; +} + +static void +segmentinit(void) +{ + _globalsegattach = globalsegattach; +} + +static Chan* +segmentattach(char *spec) +{ + return devattach('g', spec); +} + +static Walkqid* +segmentwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, segmentgen); +} + +static int +segmentstat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, segmentgen); +} + +static int +cmddone(void *arg) +{ + Globalseg *g = arg; + + return g->cmd == Cnone; +} + +static Chan* +segmentopen(Chan *c, int omode) +{ + Globalseg *g; + + switch(TYPE(c)){ + case Qtopdir: + case Qsegdir: + if(omode != 0) + error(Eisdir); + break; + case Qctl: + g = getgseg(c); + if(waserror()){ + putgseg(g); + nexterror(); + } + devpermcheck(g->uid, g->perm, omode); + c->aux = g; + poperror(); + c->flag |= COPEN; + break; + case Qdata: + g = getgseg(c); + if(waserror()){ + putgseg(g); + nexterror(); + } + devpermcheck(g->uid, g->perm, omode); + if(g->s == nil) + error("segment not yet allocated"); + if(g->kproc == nil){ + qlock(&g->l); + if(waserror()){ + qunlock(&g->l); + nexterror(); + } + if(g->kproc == nil){ + g->cmd = Cnone; + kproc(g->name, segmentkproc, g); + docmd(g, Cstart); + } + qunlock(&g->l); + poperror(); + } + c->aux = g; + poperror(); + c->flag |= COPEN; + break; + default: + panic("segmentopen"); + } + c->mode = openmode(omode); + c->offset = 0; + return c; +} + +static void +segmentclose(Chan *c) +{ + if(TYPE(c) == Qtopdir) + return; + if(c->flag & COPEN) + putgseg(c->aux); +} + +static void +segmentcreate(Chan *c, char *name, int omode, ulong perm) +{ + int x, xfree; + Globalseg *g; + + if(TYPE(c) != Qtopdir) + error(Eperm); + + if(isphysseg(name)) + error(Eexist); + + if((perm & DMDIR) == 0) + error(Ebadarg); + + if(waserror()){ + unlock(&globalseglock); + nexterror(); + } + lock(&globalseglock); + xfree = -1; + for(x = 0; x < nelem(globalseg); x++){ + g = globalseg[x]; + if(g == nil){ + if(xfree < 0) + xfree = x; + } else { + if(strcmp(g->name, name) == 0) + error(Eexist); + } + } + if(xfree < 0) + error("too many global segments"); + g = smalloc(sizeof(Globalseg)); + g->ref = 1; + kstrdup(&g->name, name); + kstrdup(&g->uid, up->user); + g->perm = 0660; + globalseg[xfree] = g; + unlock(&globalseglock); + poperror(); + + c->qid.path = PATH(x, Qsegdir); + c->qid.type = QTDIR; + c->qid.vers = 0; + c->mode = openmode(omode); + c->mode = OWRITE; +} + +static long +segmentread(Chan *c, void *a, long n, vlong voff) +{ + Globalseg *g; + char buf[32]; + + if(c->qid.type == QTDIR) + return devdirread(c, a, n, (Dirtab *)0, 0L, segmentgen); + + switch(TYPE(c)){ + case Qctl: + g = c->aux; + if(g->s == nil) + error("segment not yet allocated"); + sprint(buf, "va %#lux %#lux\n", g->s->base, g->s->top-g->s->base); + return readstr(voff, a, n, buf); + case Qdata: + g = c->aux; + if(voff > g->s->top - g->s->base) + error(Ebadarg); + if(voff + n > g->s->top - g->s->base) + n = g->s->top - g->s->base - voff; + qlock(&g->l); + g->off = voff + g->s->base; + g->data = smalloc(n); + if(waserror()){ + free(g->data); + qunlock(&g->l); + nexterror(); + } + g->dlen = n; + docmd(g, Cread); + memmove(a, g->data, g->dlen); + free(g->data); + qunlock(&g->l); + poperror(); + return g->dlen; + default: + panic("segmentread"); + } + return 0; /* not reached */ +} + +static long +segmentwrite(Chan *c, void *a, long n, vlong voff) +{ + Cmdbuf *cb; + Globalseg *g; + ulong va, len, top; + + if(c->qid.type == QTDIR) + error(Eperm); + + switch(TYPE(c)){ + case Qctl: + g = c->aux; + cb = parsecmd(a, n); + if(strcmp(cb->f[0], "va") == 0){ + if(g->s != nil) + error("already has a virtual address"); + if(cb->nf < 3) + error(Ebadarg); + va = strtoul(cb->f[1], 0, 0); + len = strtoul(cb->f[2], 0, 0); + top = PGROUND(va + len); + va = va&~(BY2PG-1); + len = (top - va) / BY2PG; + if(len == 0) + error(Ebadarg); + g->s = newseg(SG_SHARED, va, len); + } else + error(Ebadctl); + break; + case Qdata: + g = c->aux; + if(voff + n > g->s->top - g->s->base) + error(Ebadarg); + qlock(&g->l); + g->off = voff + g->s->base; + g->data = smalloc(n); + if(waserror()){ + free(g->data); + qunlock(&g->l); + nexterror(); + } + g->dlen = n; + memmove(g->data, a, g->dlen); + docmd(g, Cwrite); + free(g->data); + qunlock(&g->l); + poperror(); + return g->dlen; + default: + panic("segmentwrite"); + } + return 0; /* not reached */ +} + +static int +segmentwstat(Chan *c, uchar *dp, int n) +{ + Globalseg *g; + Dir *d; + + if(c->qid.type == QTDIR) + error(Eperm); + + g = getgseg(c); + if(waserror()){ + putgseg(g); + nexterror(); + } + + if(strcmp(g->uid, up->user) && !iseve()) + error(Eperm); + d = smalloc(sizeof(Dir)+n); + n = convM2D(dp, n, &d[0], (char*)&d[1]); + g->perm = d->mode & 0777; + + putgseg(g); + poperror(); + + free(d); + return n; +} + +static void +segmentremove(Chan *c) +{ + Globalseg *g; + int x; + + if(TYPE(c) != Qsegdir) + error(Eperm); + lock(&globalseglock); + x = SEG(c); + g = globalseg[x]; + globalseg[x] = nil; + unlock(&globalseglock); + if(g != nil) + putgseg(g); +} + +/* + * called by segattach() + */ +static Segment* +globalsegattach(Proc *p, char *name) +{ + int x; + Globalseg *g; + Segment *s; + + g = nil; + if(waserror()){ + unlock(&globalseglock); + nexterror(); + } + lock(&globalseglock); + for(x = 0; x < nelem(globalseg); x++){ + g = globalseg[x]; + if(g != nil && strcmp(g->name, name) == 0) + break; + } + if(x == nelem(globalseg)){ + unlock(&globalseglock); + poperror(); + return nil; + } + devpermcheck(g->uid, g->perm, ORDWR); + s = g->s; + if(s == nil) + error("global segment not assigned a virtual address"); + if(isoverlap(p, s->base, s->top - s->base) != nil) + error("overlaps existing segment"); + incref(s); + unlock(&globalseglock); + poperror(); + return s; +} + +static void +docmd(Globalseg *g, int cmd) +{ + g->err[0] = 0; + g->cmd = cmd; + wakeup(&g->cmdwait); + sleep(&g->replywait, cmddone, g); + if(g->err[0]) + error(g->err); +} + +static int +cmdready(void *arg) +{ + Globalseg *g = arg; + + return g->cmd != Cnone; +} + +static void +segmentkproc(void *arg) +{ + Globalseg *g = arg; + int done; + int sno; + + for(sno = 0; sno < NSEG; sno++) + if(up->seg[sno] == nil && sno != ESEG) + break; + if(sno == NSEG) + panic("segmentkproc"); + g->kproc = up; + + incref(g->s); + up->seg[sno] = g->s; + + for(done = 0; !done;){ + sleep(&g->cmdwait, cmdready, g); + if(waserror()){ + strncpy(g->err, up->errstr, sizeof(g->err)); + } else { + switch(g->cmd){ + case Cstart: + break; + case Cdie: + done = 1; + break; + case Cread: + memmove(g->data, (char*)g->off, g->dlen); + break; + case Cwrite: + memmove((char*)g->off, g->data, g->dlen); + break; + } + poperror(); + } + g->cmd = Cnone; + wakeup(&g->replywait); + } +} + +Dev segmentdevtab = { + 'g', + "segment", + + devreset, + segmentinit, + devshutdown, + segmentattach, + segmentwalk, + segmentstat, + segmentopen, + segmentcreate, + segmentclose, + segmentread, + devbread, + segmentwrite, + devbwrite, + segmentremove, + segmentwstat, +}; + |