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.
This commit is contained in:
parent
88d7d8428a
commit
46070c3122
6 changed files with 262 additions and 294 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue