diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2015-04-16 00:45:25 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2015-04-16 00:45:25 +0200 |
commit | 46070c3122227f5fc04c9b7a29d0652600fa7074 (patch) | |
tree | 82d1d3e022e7fe96baa7834f8a7718c818f39353 /sys/src/9/port/segment.c | |
parent | 88d7d8428a6cb10b421d5ff900617dad3c290fd1 (diff) |
kernel: add segio() function for reading/writing segments
devproc's procctlmemio() did not handle physical segment
types correctly, as it assumed it can just kmap() the page
in question and write to it. physical segments however
need to be mapped uncached but kmap() will always map
cached as it assumes normal memory. on some machines with
aliasing memory with different cache attributes
leads to undefined behaviour!
we borrow the code from devsegment and provide a generic
segio() function to read and write user segments which
handles all the cases without using kmap by just spawning
a kproc that attaches the segment that needs to be read
from or written to. fault() will setup the right mmu
attributes for us. it will also properly flush pages for
segments that maintain instruction cache when written.
however, tlb's have to be flushed separately.
segio() is used for devsegment and devproc now, which
also allows for simplification of fixfault() as there is no
special error handling case anymore as fixfault() is now
called from faulting process *only*.
reads from /proc/$pid/mem can now span multiple pages.
Diffstat (limited to 'sys/src/9/port/segment.c')
-rw-r--r-- | sys/src/9/port/segment.c | 182 |
1 files changed, 175 insertions, 7 deletions
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; +} |