diff --git a/sys/src/9/port/devproc.c b/sys/src/9/port/devproc.c index 97a2dba5f..5e1beddbc 100644 --- a/sys/src/9/port/devproc.c +++ b/sys/src/9/port/devproc.c @@ -152,7 +152,7 @@ static char *sname[]={ "Text", "Data", "Bss", "Stack", "Shared", "Phys", "Fixed" #define NOTEID(q) ((q).vers) void procctlreq(Proc*, char*, int); -int procctlmemio(Proc*, uintptr, int, void*, int); +long procctlmemio(Chan*, Proc*, uintptr, void*, long, int); Chan* proctext(Chan*, Proc*); int procstopped(void*); ulong procpagecount(Proc *); @@ -511,13 +511,28 @@ procwstat(Chan *c, uchar *db, int n) static void procclose(Chan *c) { - if(QID(c->qid) == Qtrace && (c->flag & COPEN) != 0){ + Segio *sio; + + if((c->flag & COPEN) == 0) + return; + + switch(QID(c->qid)){ + case Qtrace: lock(&tlock); if(topens > 0) topens--; if(topens == 0) proctrace = nil; unlock(&tlock); + return; + case Qmem: + sio = c->aux; + if(sio != nil){ + c->aux = nil; + segio(sio, nil, nil, 0, 0, 0); + free(sio); + } + return; } } @@ -774,7 +789,7 @@ procread(Chan *c, void *va, long n, vlong off) case Qmem: addr = off2addr(off); if(addr < KZERO) - return procctlmemio(p, addr, n, va, 1); + return procctlmemio(c, p, addr, va, n, 1); if(!iseve()) error(Eperm); @@ -1051,7 +1066,7 @@ procwrite(Chan *c, void *va, long n, vlong off) case Qmem: if(p->state != Stopped) error(Ebadctl); - n = procctlmemio(p, off2addr(off), n, va, 0); + n = procctlmemio(c, p, off2addr(off), va, n, 0); break; case Qregs: @@ -1163,9 +1178,6 @@ proctext(Chan *c, Proc *p) unlock(i); nexterror(); } - - if(i->s != s) - error(Eprocdied); tc = i->c; if(tc == nil) @@ -1476,127 +1488,58 @@ procstopped(void *a) return ((Proc*)a)->state == Stopped; } -int -procctlmemio(Proc *p, uintptr offset, int n, void *va, int read) +long +procctlmemio(Chan *c, Proc *p, uintptr offset, void *a, long n, int read) { - KMap *k; - Pte *pte; - Page *pg; + Segio *sio; Segment *s; - uintptr soff; - char *a, *b; - int i, l; + int i; - /* Only one page at a time */ - l = BY2PG - (offset&(BY2PG-1)); - if(n > l) - n = l; - - /* - * Make temporary copy to avoid fault while we have - * segment locked as we would deadlock when trying - * to read the calling procs memory. - */ - a = malloc(n); - if(a == nil) - error(Enomem); + s = seg(p, offset, 0); + if(s == nil) + error(Ebadarg); + eqlock(&p->seglock); if(waserror()) { - free(a); + qunlock(&p->seglock); nexterror(); } - - if(!read) - memmove(a, va, n); /* can fault */ - - for(;;) { - s = seg(p, offset, 0); - if(s == nil) - error(Ebadarg); - - eqlock(&p->seglock); - if(waserror()) { - qunlock(&p->seglock); - nexterror(); - } - - for(i = 0; i < NSEG; i++) { - if(p->seg[i] == s) - break; - } - if(i == NSEG) - error(Egreg); /* segment gone */ - - eqlock(s); - if(waserror()){ - qunlock(s); - nexterror(); - } - if(!read && (s->type&SG_TYPE) == SG_TEXT) { - s = txt2data(s); - p->seg[i] = s; - } - incref(s); - qunlock(&p->seglock); - poperror(); - poperror(); - /* segment s still locked, fixfault() unlocks */ - if(waserror()){ - putseg(s); - nexterror(); - } - if(fixfault(s, offset, read, 0) == 0) - break; - putseg(s); - poperror(); + sio = c->aux; + if(sio == nil){ + sio = smalloc(sizeof(Segio)); + c->aux = sio; } - - /* - * Only access the page while segment is locked - * as the proc could segfree or relocate the pte - * concurrently. - */ + for(i = 0; i < NSEG; i++) { + if(p->seg[i] == s) + break; + } + if(i == NSEG) + error(Egreg); /* segment gone */ eqlock(s); if(waserror()){ qunlock(s); nexterror(); } - if(offset+n >= s->top) - n = s->top-offset; - soff = offset-s->base; - pte = s->map[soff/PTEMAPMEM]; - if(pte == nil) - error(Egreg); /* page gone, should retry? */ - pg = pte->pages[(soff&(PTEMAPMEM-1))/BY2PG]; - if(pagedout(pg)) - error(Egreg); /* page gone, should retry? */ - - /* Map and copy the page */ - k = kmap(pg); - b = (char*)VA(k); - b += offset&(BY2PG-1); - if(read) - memmove(a, b, n); - else - memmove(b, a, n); - kunmap(k); - - if(!read){ - /* Ensure the process sees text page changes */ - if(s->flushme) - pg->txtflush = ~0; - p->newtlb = 1; + if(!read && (s->type&SG_TYPE) == SG_TEXT) { + s = txt2data(s); + p->seg[i] = s; } - + offset -= s->base; + incref(s); /* for us while we copy */ qunlock(s); poperror(); + qunlock(&p->seglock); + poperror(); + + if(waserror()) { + putseg(s); + nexterror(); + } + n = segio(sio, s, a, n, offset, read); putseg(s); poperror(); - if(read) - memmove(va, a, n); /* can fault */ - - free(a); - poperror(); + if(!read) + p->newtlb = 1; return n; } diff --git a/sys/src/9/port/devsegment.c b/sys/src/9/port/devsegment.c index 2acaa0d76..07b0fca9c 100644 --- a/sys/src/9/port/devsegment.c +++ b/sys/src/9/port/devsegment.c @@ -11,13 +11,6 @@ enum Qsegdir, Qctl, Qdata, - - /* commands to kproc */ - Cnone=0, - Cread, - Cwrite, - Cstart, - Cdie, }; #define TYPE(x) (int)( (c)->qid.path & 0x7 ) @@ -28,23 +21,13 @@ 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; - char *addr; - int dlen; - int cmd; - char err[64]; + Segio; }; static Globalseg *globalseg[100]; @@ -53,9 +36,7 @@ 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); + static Segment* fixedseg(uintptr va, ulong len); /* @@ -87,8 +68,7 @@ putgseg(Globalseg *g) return; if(g->s != nil) putseg(g->s); - if(g->kproc) - docmd(g, Cdie); + segio(g, nil, nil, 0, 0, 0); free(g->name); free(g->uid); free(g); @@ -191,14 +171,6 @@ 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) { @@ -230,20 +202,6 @@ segmentopen(Chan *c, int omode) devpermcheck(g->uid, g->perm, omode); if(g->s == nil) error("segment not yet allocated"); - if(g->kproc == nil){ - eqlock(&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; @@ -315,57 +273,6 @@ segmentcreate(Chan *c, char *name, int omode, ulong perm) return c; } -static long -segmentio(Globalseg *g, void *a, long n, vlong off, int wr) -{ - uintptr m; - void *b; - - m = g->s->top - g->s->base; - if(off < 0 || off >= m){ - if(wr) - error(Ebadarg); - return 0; - } - if(off+n > m){ - if(wr) - error(Ebadarg); - n = m - off; - } - - if((uintptr)a > KZERO) - b = a; - else { - b = smalloc(n); - if(waserror()){ - free(b); - nexterror(); - } - if(wr) - memmove(b, a, n); - } - - eqlock(&g->l); - if(waserror()){ - qunlock(&g->l); - nexterror(); - } - g->addr = (char*)g->s->base + off; - g->data = b; - g->dlen = n; - docmd(g, wr ? Cwrite : Cread); - qunlock(&g->l); - poperror(); - - if(a != b){ - if(!wr) - memmove(a, b, n); - free(b); - poperror(); - } - return n; -} - static long segmentread(Chan *c, void *a, long n, vlong voff) { @@ -387,7 +294,7 @@ segmentread(Chan *c, void *a, long n, vlong voff) snprint(buf, sizeof(buf), "va %#p %#p\n", g->s->base, g->s->top-g->s->base); return readstr(voff, a, n, buf); case Qdata: - return segmentio(g, a, n, voff, 0); + return segio(g, g->s, a, n, voff, 1); default: panic("segmentread"); } @@ -430,7 +337,7 @@ segmentwrite(Chan *c, void *a, long n, vlong voff) error(Ebadctl); break; case Qdata: - return segmentio(g, a, n, voff, 1); + return segio(g, g->s, a, n, voff, 0); default: panic("segmentwrite"); } @@ -520,72 +427,6 @@ globalsegattach(Proc *p, char *name) 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; - - while(waserror()) - ; - for(done = 0; !done;){ - sleep(&g->cmdwait, cmdready, g); - if(waserror()){ - strncpy(g->err, up->errstr, sizeof(g->err)-1); - g->err[sizeof(g->err)-1] = 0; - } else { - switch(g->cmd){ - case Cstart: - break; - case Cdie: - done = 1; - break; - case Cread: - memmove(g->data, g->addr, g->dlen); - break; - case Cwrite: - memmove(g->addr, g->data, g->dlen); - break; - } - poperror(); - } - g->cmd = Cnone; - wakeup(&g->replywait); - } - - pexit("done", 1); -} - /* * allocate a fixed segment with sequential run of of adjacent * user memory pages. diff --git a/sys/src/9/port/fault.c b/sys/src/9/port/fault.c index 966c2992d..123ae4e80 100644 --- a/sys/src/9/port/fault.c +++ b/sys/src/9/port/fault.c @@ -40,7 +40,7 @@ fault(uintptr addr, int read) return -1; } - if(fixfault(s, addr, read, 1) == 0) + if(fixfault(s, addr, read) == 0) break; splhi(); @@ -58,7 +58,7 @@ fault(uintptr addr, int read) } static void -faulterror(char *s, Chan *c, int isfatal) +faulterror(char *s, Chan *c) { char buf[ERRMAX]; @@ -67,15 +67,14 @@ faulterror(char *s, Chan *c, int isfatal) s = buf; } if(up->nerrlab) { - if(isfatal) - postnote(up, 1, s, NDebug); + postnote(up, 1, s, NDebug); error(s); } pexit(s, 1); } static void -pio(Segment *s, uintptr addr, uintptr soff, Page **p, int isfatal) +pio(Segment *s, uintptr addr, uintptr soff, Page **p) { Page *new; KMap *k; @@ -120,11 +119,11 @@ retry: k = kmap(new); kaddr = (char*)VA(k); while(waserror()) { - if(isfatal && strcmp(up->errstr, Eintr) == 0) + if(strcmp(up->errstr, Eintr) == 0) continue; kunmap(k); putpage(new); - faulterror(Eioload, c, isfatal); + faulterror(Eioload, c); } n = devtab[c->type]->read(c, kaddr, ask, daddr); if(n != ask) @@ -194,7 +193,7 @@ void (*checkaddr)(uintptr, Segment *, Page *); uintptr addr2check; int -fixfault(Segment *s, uintptr addr, int read, int doputmmu) +fixfault(Segment *s, uintptr addr, int read) { int type; Pte **pte, *etp; @@ -222,7 +221,7 @@ fixfault(Segment *s, uintptr addr, int read, int doputmmu) case SG_TEXT: /* Demand load */ if(pagedout(*pg)) - pio(s, addr, soff, pg, doputmmu); + pio(s, addr, soff, pg); mmuphys = PPN((*pg)->pa) | PTERONLY|PTEVALID; (*pg)->modref = PG_REF; @@ -242,7 +241,7 @@ fixfault(Segment *s, uintptr addr, int read, int doputmmu) case SG_DATA: common: /* Demand load/pagein/copy on write */ if(pagedout(*pg)) - pio(s, addr, soff, pg, doputmmu); + pio(s, addr, soff, pg); /* * It's only possible to copy on write if @@ -288,8 +287,7 @@ fixfault(Segment *s, uintptr addr, int read, int doputmmu) } qunlock(s); - if(doputmmu) - putmmu(addr, mmuphys, *pg); + putmmu(addr, mmuphys, *pg); return 0; } diff --git a/sys/src/9/port/portdat.h b/sys/src/9/port/portdat.h index 495baac93..f2d0a1223 100644 --- a/sys/src/9/port/portdat.h +++ b/sys/src/9/port/portdat.h @@ -39,6 +39,7 @@ typedef struct RWlock RWlock; typedef struct Sargs Sargs; typedef struct Schedq Schedq; typedef struct Segment Segment; +typedef struct Segio Segio; typedef struct Sema Sema; typedef struct Timer Timer; typedef struct Timers Timers; @@ -392,6 +393,22 @@ struct Segment ulong mark; /* portcountrefs */ }; +struct Segio +{ + QLock; + Rendez cmdwait; + Rendez replywait; + + Proc *p; /* segmentio kproc */ + Segment *s; + + char *data; + char *addr; + int dlen; + int cmd; + char err[64]; +}; + enum { RENDLOG = 5, diff --git a/sys/src/9/port/portfns.h b/sys/src/9/port/portfns.h index 8b1675816..12032e174 100644 --- a/sys/src/9/port/portfns.h +++ b/sys/src/9/port/portfns.h @@ -107,7 +107,7 @@ int fault(uintptr, int); void fdclose(int, int); Chan* fdtochan(int, int, int, int); int findmount(Chan**, Mhead**, int, int, Qid); -int fixfault(Segment*, uintptr, int, int); +int fixfault(Segment*, uintptr, int); void flushmmu(void); void forceclosefgrp(void); void forkchild(Proc*, Ureg*); @@ -304,6 +304,7 @@ void (*screenputs)(char*, int); long seconds(void); uintptr segattach(Proc*, ulong, char *, uintptr, uintptr); void segclock(uintptr); +long segio(Segio*, Segment*, void*, long, vlong, int); void segpage(Segment*, Page*); int setcolor(ulong, ulong, ulong, ulong); void setkernur(Ureg*, Proc*); diff --git a/sys/src/9/port/segment.c b/sys/src/9/port/segment.c index 7adc005ea..ad3cc0a92 100644 --- a/sys/src/9/port/segment.c +++ b/sys/src/9/port/segment.c @@ -670,18 +670,16 @@ found: return va; } -uintptr -syssegflush(va_list list) +static void +segflush(void *va, uintptr len) { - uintptr from, to, off, len; + uintptr from, to, off; Segment *s; Pte *pte; Page **pg, **pe; - from = va_arg(list, uintptr); - to = va_arg(list, ulong); - to += from; - + from = (uintptr)va; + to = from + len; to = PGROUND(to); from &= ~(BY2PG-1); if(to < from) @@ -717,6 +715,17 @@ syssegflush(va_list list) qunlock(s); } +} + +uintptr +syssegflush(va_list list) +{ + void *va; + ulong len; + + va = va_arg(list, void*); + len = va_arg(list, ulong); + segflush(va, len); flushmmu(); return 0; } @@ -767,3 +776,162 @@ data2txt(Segment *s) ps->flushme = 1; return ps; } + + +enum { + /* commands to segmentioproc */ + Cnone=0, + Cread, + Cwrite, + Cdie, +}; + +static int +cmddone(void *arg) +{ + Segio *sio = arg; + + return sio->cmd == Cnone; +} + +static void +docmd(Segio *sio, int cmd) +{ + sio->err[0] = 0; + sio->cmd = cmd; + wakeup(&sio->cmdwait); + sleep(&sio->replywait, cmddone, sio); + if(sio->err[0]) + error(sio->err); +} + +static int +cmdready(void *arg) +{ + Segio *sio = arg; + + return sio->cmd != Cnone; +} + +static void +segmentioproc(void *arg) +{ + Segio *sio = arg; + int done; + int sno; + + for(sno = 0; sno < NSEG; sno++) + if(up->seg[sno] == nil && sno != ESEG) + break; + if(sno == NSEG) + panic("segmentkproc"); + + sio->p = up; + incref(sio->s); + up->seg[sno] = sio->s; + + cclose(up->dot); + up->dot = up->slash; + incref(up->dot); + + while(waserror()) + ; + for(done = 0; !done;){ + sleep(&sio->cmdwait, cmdready, sio); + if(waserror()){ + strncpy(sio->err, up->errstr, sizeof(sio->err)-1); + sio->err[sizeof(sio->err)-1] = 0; + } else { + if(sio->s != nil && up->seg[sno] != sio->s){ + putseg(up->seg[sno]); + incref(sio->s); + up->seg[sno] = sio->s; + flushmmu(); + } + switch(sio->cmd){ + case Cread: + memmove(sio->data, sio->addr, sio->dlen); + break; + case Cwrite: + memmove(sio->addr, sio->data, sio->dlen); + if(sio->s->flushme) + segflush(sio->addr, sio->dlen); + break; + case Cdie: + done = 1; + break; + } + poperror(); + } + sio->cmd = Cnone; + wakeup(&sio->replywait); + } + + pexit("done", 1); +} + +long +segio(Segio *sio, Segment *s, void *a, long n, vlong off, int read) +{ + uintptr m; + void *b; + + b = a; + if(s != nil){ + m = s->top - s->base; + if(off < 0 || off >= m){ + if(!read) + error(Ebadarg); + return 0; + } + if(off+n > m){ + if(!read) + error(Ebadarg); + n = m - off; + } + + if((uintptr)a < KZERO) { + b = smalloc(n); + if(waserror()){ + free(b); + nexterror(); + } + if(!read) + memmove(b, a, n); + } + } + + eqlock(sio); + if(waserror()){ + qunlock(sio); + nexterror(); + } + sio->s = s; + if(s == nil){ + if(sio->p != nil){ + docmd(sio, Cdie); + sio->p = nil; + } + qunlock(sio); + poperror(); + return 0; + } + if(sio->p == nil){ + sio->cmd = Cnone; + kproc("segmentio", segmentioproc, sio); + } + sio->addr = (char*)s->base + off; + sio->data = b; + sio->dlen = n; + docmd(sio, read ? Cread : Cwrite); + qunlock(sio); + poperror(); + + if(a != b){ + if(read) + memmove(a, b, n); + free(b); + poperror(); + } + return n; +}