123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #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,
- Qfree,
- /* 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;
- typedef struct Freemsg Freemsg;
- struct Freemsg
- {
- Freemsg *next;
- };
- struct Globalseg
- {
- Ref r;
- Segment *s;
- char *name;
- char *uid;
- int64_t length;
- long perm;
- Freemsg *free;
- /* kproc to do reading and writing */
- QLock ql; /* 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 *heapseg;
- Segment* (*_globalsegattach)(Proc*, char*);
- static Segment* globalsegattach(Proc*, char*);
- static int cmddone(void*);
- static void segmentkproc(void*);
- static void docmd(Globalseg*, int);
- /*
- * 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->r);
- unlock(&globalseglock);
- if(g == nil)
- error("global segment disappeared");
- return g;
- }
- static void
- putgseg(Globalseg *g)
- {
- if(decref(&g->r) > 0)
- return;
- if(g->s == heapseg)
- heapseg = nil;
- 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* d, Dirtab* dir, int i, int s, Dir *dp)
- {
- Proc *up = externup();
- Qid q;
- Globalseg *g;
- uint32_t 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:
- case Qfree:
- g = getgseg(c);
- if(waserror()){
- putgseg(g);
- nexterror();
- }
- q.vers = 0;
- q.type = QTFILE;
- switch(s){
- case 0:
- q.path = PATH(SEG(c), Qctl);
- devdir(c, q, "ctl", 0, g->uid, g->perm, dp);
- break;
- case 1:
- q.path = PATH(SEG(c), Qdata);
- if(g->s != nil)
- size = g->s->top - g->s->base;
- else
- size = 0;
- devdir(c, q, "data", size, g->uid, g->perm, dp);
- break;
- case 2:
- q.path = PATH(SEG(c), Qfree);
- devdir(c, q, "free", 0, g->uid, g->perm&0444, dp);
- break;
- default:
- poperror();
- putgseg(g);
- return -1;
- }
- poperror();
- putgseg(g);
- 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 int32_t
- segmentstat(Chan *c, uint8_t *db, int32_t 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)
- {
- Proc *up = externup();
- Globalseg *g;
- switch(TYPE(c)){
- case Qtopdir:
- case Qsegdir:
- if(omode != 0)
- error(Eisdir);
- break;
- case Qctl:
- case Qfree:
- 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->ql);
- if(waserror()){
- qunlock(&g->ql);
- nexterror();
- }
- if(g->kproc == nil){
- g->cmd = Cnone;
- kproc(g->name, segmentkproc, g);
- docmd(g, Cstart);
- }
- poperror();
- qunlock(&g->ql);
- }
- 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, int perm)
- {
- Proc *up = externup();
- int x, xfree;
- Globalseg *g;
- char *ep;
- if(TYPE(c) != Qtopdir)
- error(Eperm);
- if(isphysseg(name))
- error(Eexist);
- if((perm & DMDIR) == 0)
- error("must create directory");
- if(waserror()){
- unlock(&globalseglock);
- nexterror();
- }
- xfree = -1;
- if(name[0] == '#' && name[1] >= '0' && name[1] <= '9'){
- /* hack for cnk: if #n, treat it as index n */
- xfree = strtoul(name+1, &ep, 0);
- if(*ep)
- xfree = -1;
- else if(xfree < 0 || xfree >= nelem(globalseg))
- error("invalid global segment index");
- }
- lock(&globalseglock);
- if(xfree < 0){
- 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");
- }else{
- g = globalseg[xfree];
- if(g != nil)
- error(Eexist);
- }
- g = smalloc(sizeof(Globalseg));
- g->r.ref = 1;
- kstrdup(&g->name, name);
- kstrdup(&g->uid, up->user);
- g->perm = 0660;
- globalseg[xfree] = g;
- unlock(&globalseglock);
- poperror();
- c->qid.path = PATH(xfree, Qsegdir);
- c->qid.type = QTDIR;
- c->qid.vers = 0;
- c->mode = openmode(omode);
- c->mode = OWRITE;
- DBG("segmentcreate(%s, %#o %#ux)\n", name, omode, perm);
- }
- enum{PTRSIZE = 19}; /* "0x1234567812345678 " */
- static int
- readptr(char *buf, int32_t n, uintptr_t val)
- {
- if(n < PTRSIZE)
- return 0;
- snprint(buf, sizeof buf, "%*#ullx", PTRSIZE-1, val);
- buf[PTRSIZE-1] = ' ';
- return PTRSIZE;
- }
- static int
- znotempty(void *x)
- {
- Zseg *zs;
- zs = x;
- return zs->end != 0;
- }
- static int32_t
- segmentread(Chan *c, void *a, int32_t n, int64_t voff)
- {
- Proc *up = externup();
- Globalseg *g;
- Zseg *zs;
- uintptr_t va;
- char *p, *s;
- int32_t tot;
- char buf[64];
- if(c->qid.type == QTDIR)
- return devdirread(c, a, n, (Dirtab *)0, 0L, segmentgen);
- g = c->aux;
- switch(TYPE(c)){
- case Qfree:
- if(g->s == nil)
- error("segment not yet allocated");
- if(n < PTRSIZE)
- error("read buffer too small");
- zs = &g->s->zseg;
- qlock(&g->s->lk);
- if(waserror()){
- qunlock(&g->s->lk);
- nexterror();
- }
- while((va = zgetaddr(g->s)) == 0ULL){
- qunlock(&g->s->lk);
- sleep(&zs->rr, znotempty, zs);
- qlock(&g->s->lk);
- }
- p = a;
- for(tot = 0; n-tot > PTRSIZE; tot += PTRSIZE){
- p += readptr(p, n, va);
- if((va = zgetaddr(g->s)) == 0ULL)
- break;
- }
- poperror();
- qunlock(&g->s->lk);
- return tot;
- case Qctl:
- if(g->s == nil)
- error("segment not yet allocated");
- if(g->s->type&SG_KZIO)
- s = "kmsg";
- else if(g->s->type&SG_ZIO)
- s = "umsg";
- else
- s = "addr";
- snprint(buf, sizeof(buf), "%s %#p %#p\n",
- s, g->s->base, (uintptr_t)(g->s->top-g->s->base));
- return readstr(voff, a, n, buf);
- case Qdata:
- if(voff < 0)
- error(Enegoff);
- if(voff + n > g->s->top - g->s->base){
- n = g->s->top - voff;
- if(n <= 0)
- break;
- }
- qlock(&g->ql);
- if(waserror()){
- qunlock(&g->ql);
- nexterror();
- }
- g->off = voff + g->s->base;
- g->data = smalloc(n);
- if(waserror()){
- free(g->data);
- nexterror();
- }
- g->dlen = n;
- docmd(g, Cread);
- memmove(a, g->data, g->dlen);
- poperror();
- free(g->data);
- poperror();
- qunlock(&g->ql);
- return g->dlen;
- default:
- panic("segmentread");
- }
- return 0; /* not reached */
- }
- /*
- * BUG: we allocate virtual addresses but never give them
- * back when the segment is destroyed.
- * BUG: what if we overlap other segments attached by the user?
- */
- static uintptr_t
- placeseg(uintptr_t len)
- {
- static Lock lck;
- static uintptr_t va = HEAPTOP;
- uintptr_t v;
- len += BIGPGSZ; /* so we fault upon overflows */
- lock(&lck);
- len = BIGPGROUND(len);
- va -= len;
- v = va;
- unlock(&lck);
- return v;
- }
- static int32_t
- segmentwrite(Chan *c, void *a, int32_t n, int64_t voff)
- {
- Proc *up = externup();
- Cmdbuf *cb;
- Globalseg *g;
- uintptr_t va, len, top;
- int i;
- struct{
- char *name;
- int type;
- }segs[] = {
- {"kmsg", SG_SHARED|SG_ZIO|SG_KZIO},
- {"umsg", SG_SHARED|SG_ZIO},
- {"addr", SG_SHARED},
- };
- if(c->qid.type == QTDIR)
- error(Eperm);
- switch(TYPE(c)){
- case Qfree:
- error(Eperm);
- break;
- case Qctl:
- g = c->aux;
- cb = parsecmd(a, n);
- for(i = 0; i < nelem(segs); i++)
- if(strcmp(cb->f[0], segs[i].name) == 0)
- break;
- if(i < nelem(segs)){
- if(g->s != nil)
- error("already has a virtual address");
- if(cb->nf < 3)
- cmderror(cb, Ebadarg);
- va = strtoul(cb->f[1], 0, 0);
- len = strtoul(cb->f[2], 0, 0);
- if(va == 0)
- va = placeseg(len);
- top = BIGPGROUND(va + len);
- va = va&~(BIGPGSZ-1);
- len = (top - va) / BIGPGSZ;
- if(len == 0)
- cmderror(cb, "empty segment");
- g->s = newseg(segs[i].type, va, len);
- if(i == 0)
- newzmap(g->s);
- else if(i == 1)
- zgrow(g->s);
- DBG("newseg %s base %#ullx len %#ullx\n",
- cb->f[0], va, len*BIGPGSZ);
- if(i == 0 || i == 1)
- dumpzseg(g->s);
- }else if(strcmp(cb->f[0], "heap") == 0){
- if(g == nil)
- error("no globalseg");
- if(g->s == nil)
- error("no segment");
- if(heapseg)
- error("heap already set");
- else
- heapseg = g->s;
- }else
- error(Ebadctl);
- break;
- case Qdata:
- g = c->aux;
- if(voff < 0)
- error(Enegoff);
- if(voff + n > g->s->top - g->s->base){
- n = g->s->top - voff;
- if(n <= 0)
- break;
- }
- qlock(&g->ql);
- if(waserror()){
- qunlock(&g->ql);
- nexterror();
- }
- g->off = voff + g->s->base;
- g->data = smalloc(n);
- if(waserror()){
- free(g->data);
- nexterror();
- }
- g->dlen = n;
- memmove(g->data, a, g->dlen);
- docmd(g, Cwrite);
- poperror();
- free(g->data);
- poperror();
- qunlock(&g->ql);
- break;
- default:
- panic("segmentwrite");
- }
- return n;
- }
- static int32_t
- segmentwstat(Chan *c, uint8_t *dp, int32_t n)
- {
- Proc *up = externup();
- 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)!=0 && !iseve())
- error(Eperm);
- d = smalloc(sizeof(Dir)+n);
- if(waserror()){
- free(d);
- nexterror();
- }
- n = convM2D(dp, n, &d[0], (char*)&d[1]);
- if(!emptystr(d->uid) && strcmp(d->uid, g->uid) != 0)
- kstrdup(&g->uid, d->uid);
- if(d->mode != (uint32_t)~0UL)
- g->perm = d->mode & 0777;
- poperror();
- free(d);
- poperror();
- putgseg(g);
- 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)
- {
- Proc *up = externup();
- 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->r);
- unlock(&globalseglock);
- poperror();
- return s;
- }
- static void
- docmd(Globalseg *g, int cmd)
- {
- Proc *up = externup();
- g->err[0] = 0;
- g->cmd = cmd;
- wakeup(&g->cmdwait);
- while(waserror())
- {} /* no interrupts */
- sleep(&g->replywait, cmddone, g);
- poperror();
- if(g->err[0])
- error(g->err);
- }
- static int
- cmdready(void *arg)
- {
- Globalseg *g = arg;
- return g->cmd != Cnone;
- }
- /*
- * TO DO: better approach is to send segment with command,
- * temporarily add it to segment array at SEG1, do the operation, then putseg.
- * otherwise there are as many kprocs as segments.
- */
- static void
- segmentkproc(void *arg)
- {
- Proc *up = externup();
- Globalseg *g = arg;
- int done;
- int sno;
- qlock(&up->seglock);
- for(sno = 0; sno < NSEG; sno++)
- if(up->seg[sno] == nil)
- break;
- if(sno == NSEG)
- panic("segmentkproc");
- g->kproc = up;
- incref(&g->s->r);
- up->seg[sno] = g->s;
- qunlock(&up->seglock);
- 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,
- };
|