shithub: riscv

Download patch

ref: 46070c3122227f5fc04c9b7a29d0652600fa7074
parent: 88d7d8428a6cb10b421d5ff900617dad3c290fd1
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Wed Apr 15 20:45:25 EDT 2015

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.

--- a/sys/src/9/port/devproc.c
+++ b/sys/src/9/port/devproc.c
@@ -152,7 +152,7 @@
 #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,7 +511,13 @@
 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--;
@@ -518,6 +524,15 @@
 		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 @@
 	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 @@
 	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 @@
 		unlock(i);
 		nexterror();
 	}
-
-	if(i->s != s)
-		error(Eprocdied);
 		
 	tc = i->c;
 	if(tc == nil)
@@ -1476,127 +1488,58 @@
 	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)
+	sio = c->aux;
+	if(sio == nil){
+		sio = smalloc(sizeof(Segio));
+		c->aux = sio;
+	}
+	for(i = 0; i < NSEG; i++) {
+		if(p->seg[i] == s)
 			break;
-		putseg(s);
-		poperror();
 	}
-
-	/*
-	 * Only access the page while segment is locked
-	 * as the proc could segfree or relocate the pte
-	 * concurrently.
-	 */ 
+	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();
-	putseg(s);
+	qunlock(&p->seglock);
 	poperror();
 
-	if(read)
-		memmove(va, a, n);	/* can fault */
-
-	free(a);
+	if(waserror()) {
+		putseg(s);
+		nexterror();
+	}
+	n = segio(sio, s, a, n, offset, read);
+	putseg(s);
 	poperror();
+
+	if(!read)
+		p->newtlb = 1;
 
 	return n;
 }
--- a/sys/src/9/port/devsegment.c
+++ b/sys/src/9/port/devsegment.c
@@ -11,13 +11,6 @@
 	Qsegdir,
 	Qctl,
 	Qdata,
-
-	/* commands to kproc */
-	Cnone=0,
-	Cread,
-	Cwrite,
-	Cstart,
-	Cdie,
 };
 
 #define TYPE(x) 	(int)( (c)->qid.path & 0x7 )
@@ -28,7 +21,6 @@
 struct Globalseg
 {
 	Ref;
-	Segment	*s;
 
 	char	*name;
 	char	*uid;
@@ -35,16 +27,7 @@
 	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 @@
 
 	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 @@
 		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 @@
 	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 @@
 		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;
@@ -316,57 +274,6 @@
 }
 
 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)
 {
 	Globalseg *g;
@@ -387,7 +294,7 @@
 			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 @@
 			error(Ebadctl);
 		break;
 	case Qdata:
-		return segmentio(g, a, n, voff, 1);
+		return segio(g, g->s, a, n, voff, 0);
 	default:
 		panic("segmentwrite");
 	}
@@ -518,72 +425,6 @@
 	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;
-
-	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);
 }
 
 /*
--- a/sys/src/9/port/fault.c
+++ b/sys/src/9/port/fault.c
@@ -40,7 +40,7 @@
 			return -1;
 		}
 
-		if(fixfault(s, addr, read, 1) == 0)
+		if(fixfault(s, addr, read) == 0)
 			break;
 
 		splhi();
@@ -58,7 +58,7 @@
 }
 
 static void
-faulterror(char *s, Chan *c, int isfatal)
+faulterror(char *s, Chan *c)
 {
 	char buf[ERRMAX];
 
@@ -67,8 +67,7 @@
 		s = buf;
 	}
 	if(up->nerrlab) {
-		if(isfatal)
-			postnote(up, 1, s, NDebug);
+		postnote(up, 1, s, NDebug);
 		error(s);
 	}
 	pexit(s, 1);
@@ -75,7 +74,7 @@
 }
 
 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 @@
 	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 @@
 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 @@
 
 	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 @@
 	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 @@
 	}
 	qunlock(s);
 
-	if(doputmmu)
-		putmmu(addr, mmuphys, *pg);
+	putmmu(addr, mmuphys, *pg);
 
 	return 0;
 }
--- a/sys/src/9/port/portdat.h
+++ b/sys/src/9/port/portdat.h
@@ -39,6 +39,7 @@
 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;
@@ -390,6 +391,22 @@
 	Pte	*ssegmap[SSEGMAPSIZE];
 	Sema	sema;
 	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
--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -107,7 +107,7 @@
 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 @@
 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*);
--- a/sys/src/9/port/segment.c
+++ b/sys/src/9/port/segment.c
@@ -670,18 +670,16 @@
 	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 @@
 
 		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;
 }
@@ -766,4 +775,163 @@
 	ps->flen = s->flen;
 	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;
 }