Browse Source

Plan 9 from Bell Labs 2014-03-12

David du Colombier 10 years ago
parent
commit
90d5fe66a5

+ 16 - 4
sys/src/9/boot/boot.c

@@ -19,6 +19,7 @@ int	mflag;
 int	fflag;
 int	kflag;
 int	debugboot;
+int	nousbboot;
 
 char	*bargv[Nbarg];
 int	bargc;
@@ -62,6 +63,8 @@ debuginit(int argc, char **argv)
 
 	if(getenv("debugboot"))
 		debugboot = 1;
+	if(getenv("nousbboot"))
+		nousbboot = 1;
 #ifdef DEBUG
 	print("argc=%d\n", argc);
 	for(fd = 0; fd < argc; fd++)
@@ -111,8 +114,7 @@ pickmethod(int argc, char **argv)
 static void
 doauth(int cpuflag)
 {
-	if(debugboot)
-		fprint(2, "auth...");
+	dprint("auth...");
 	authentication(cpuflag);
 }
 
@@ -255,8 +257,10 @@ boot(int argc, char *argv[])
 	 *  set up usb keyboard & mouse, if any.
 	 *  starts partfs on first disk, if any, to permit nvram on usb.
 	 */
-	usbinit(Dontpost);
+	if (!nousbboot)
+		usbinit(Dontpost);
 
+	dprint("pickmethod...");
 	mp = pickmethod(argc, argv);
 	islocal = strcmp(mp->name, "local") == 0;
 	ishybrid = strcmp(mp->name, "hybrid") == 0;
@@ -264,13 +268,17 @@ boot(int argc, char *argv[])
 	kbmap();			/*  load keymap if it's there. */
 
 	/* don't trigger aoe until the network has been configured */
+	dprint("bind #æ...");
 	bind("#æ", "/dev", MAFTER);	/* nvram could be here */
+	dprint("bind #S...");
 	bind("#S", "/dev", MAFTER);	/* nvram could be here */
+	dprint("partinit...");
 	partinit();
 
 	doauth(cpuflag);	/* authentication usually changes hostowner */
 	rfork(RFNAMEG);		/* leave existing subprocs in own namespace */
-	usbinit(Post);		/* restart partfs under the new hostowner id */
+	if (!nousbboot)
+		usbinit(Post);	/* restart partfs under the new hostowner id */
 	fd = connectroot(mp, islocal, ishybrid);
 	afd = nsinit(fd, &rsp);
 	close(fd);
@@ -319,6 +327,7 @@ rootserver(char *arg)
 	int n;
 
 	/* look for required reply */
+	dprint("read #e/nobootprompt...");
 	readfile("#e/nobootprompt", reply, sizeof(reply));
 	if(reply[0]){
 		mp = findmethod(reply);
@@ -336,6 +345,7 @@ rootserver(char *arg)
 	sprint(prompt+n, ")");
 
 	/* create default reply */
+	dprint("read #e/bootargs...");
 	readfile("#e/bootargs", reply, sizeof(reply));
 	if(reply[0] == 0 && arg != 0)
 		strcpy(reply, arg);
@@ -349,6 +359,7 @@ rootserver(char *arg)
 
 	/* parse replies */
 	do{
+		dprint("outin...");
 		outin(prompt, reply, sizeof(reply));
 		mp = findmethod(reply);
 	}while(mp == nil);
@@ -359,6 +370,7 @@ HaveMethod:
 	cp = strchr(reply, '!');
 	if(cp)
 		strcpy(sys, cp+1);
+	dprint("pickmethod done\n");
 	return mp;
 }
 

+ 2 - 0
sys/src/9/boot/boot.h

@@ -12,6 +12,8 @@ enum
 	Nbarg=	16,
 };
 
+#define dprint(...) if(debugboot) fprint(2, __VA_ARGS__); else USED(debugboot)
+
 extern char*	bootdisk;		/* defined in ../$arch/boot$CONF.c */
 extern char*	rootdir;
 extern int	(*cfs)(int);

+ 36 - 34
sys/src/9/boot/bootip.c

@@ -5,20 +5,18 @@
 #include "boot.h"
 
 static	uchar	fsip[IPaddrlen];
-	uchar	auip[IPaddrlen];
+static	uchar	auip[IPaddrlen];
 static	char	mpoint[32];
 
 static int isvalidip(uchar*);
-static void netndb(char*, uchar*);
-static void netenv(char*, uchar*);
-
+static void getndbvar(char *name, uchar *var, char *prompt);
 
 void
 configip(int bargc, char **bargv, int needfs)
 {
 	Waitmsg *w;
 	int argc, pid;
-	char **arg, **argv, buf[32], *p;
+	char **arg, **argv, *p;
 
 	fmtinstall('I', eipfmt);
 	fmtinstall('M', eipfmt);
@@ -48,24 +46,30 @@ configip(int bargc, char **bargv, int needfs)
 		break;
 	} ARGEND;
 
-	/* bind in an ip interface */
+	/* bind in an ip interface or two */
+	dprint("bind #I...");
 	if(bind("#I", mpoint, MAFTER) < 0)
 		fatal("bind #I");
+	dprint("bind #l0...");
 	if(access("#l0", 0) == 0 && bind("#l0", mpoint, MAFTER) < 0)
 		warning("bind #l0");
+	dprint("bind #l1...");
 	if(access("#l1", 0) == 0 && bind("#l1", mpoint, MAFTER) < 0)
 		warning("bind #l1");
+	dprint("bind #l2...");
 	if(access("#l2", 0) == 0 && bind("#l2", mpoint, MAFTER) < 0)
 		warning("bind #l2");
+	dprint("bind #l3...");
 	if(access("#l3", 0) == 0 && bind("#l3", mpoint, MAFTER) < 0)
 		warning("bind #l3");
 	werrstr("");
 
-	/* let ipconfig configure the ip interface */
+	/* let ipconfig configure the first ip interface */
 	switch(pid = fork()){
 	case -1:
 		fatal("fork configuring ip: %r");
 	case 0:
+		dprint("starting ipconfig...");
 		exec("/boot/ipconfig", arg);
 		fatal("execing /boot/ipconfig: %r");
 	default:
@@ -73,8 +77,7 @@ configip(int bargc, char **bargv, int needfs)
 	}
 
 	/* wait for ipconfig to finish */
-	if(debugboot)
-		fprint(2, "waiting for dhcp...");
+	dprint("waiting for dhcp...");
 	for(;;){
 		w = wait();
 		if(w != nil && w->pid == pid){
@@ -86,31 +89,11 @@ configip(int bargc, char **bargv, int needfs)
 			fatal("configuring ip");
 		free(w);
 	}
-	if(debugboot)
-		fprint(2, "\n");
-
-	if(!needfs)
-		return;
-
-	/* if we didn't get a file and auth server, query user */
-	netndb("fs", fsip);
-	if(!isvalidip(fsip))
-		netenv("fs", fsip);
-	while(!isvalidip(fsip)){
-		buf[0] = 0;
-		outin("filesystem IP address", buf, sizeof(buf));
-		if (parseip(fsip, buf) == -1)
-			fprint(2, "configip: can't parse fs ip %s\n", buf);
-	}
+	dprint("\n");
 
-	netndb("auth", auip);
-	if(!isvalidip(auip))
-		netenv("auth", auip);
-	while(!isvalidip(auip)){
-		buf[0] = 0;
-		outin("authentication server IP address", buf, sizeof(buf));
-		if (parseip(auip, buf) == -1)
-			fprint(2, "configip: can't parse auth ip %s\n", buf);
+	if(needfs) {  /* if we didn't get a file and auth server, query user */
+		getndbvar("fs", fsip, "filesystem IP address");
+		getndbvar("auth", auip, "authentication server IP address");
 	}
 }
 
@@ -126,7 +109,9 @@ setauthaddr(char *proto, int port)
 void
 configtcp(Method*)
 {
+	dprint("configip...");
 	configip(bargc, bargv, 1);
+	dprint("setauthaddr...");
 	setauthaddr("tcp", 567);
 }
 
@@ -137,6 +122,7 @@ connecttcp(void)
 	char buf[64];
 
 	snprint(buf, sizeof buf, "tcp!%I!564", fsip);
+	dprint("dial %s...", buf);
 	fd = dial(buf, 0, 0, 0);
 	if (fd < 0)
 		werrstr("dial %s: %r", buf);
@@ -166,6 +152,7 @@ netenv(char *attr, uchar *ip)
 		return;
 
 	n = read(fd, buf, sizeof(buf)-1);
+	close(fd);
 	if(n <= 0)
 		return;
 	buf[n] = 0;
@@ -203,5 +190,20 @@ netndb(char *attr, uchar *ip)
 			return;
 		}
 	}
-	return;
+}
+
+static void
+getndbvar(char *name, uchar *var, char *prompt)
+{
+	char buf[64];
+
+	netndb(name, var);
+	if(!isvalidip(var))
+		netenv(name, var);
+	while(!isvalidip(var)){
+		buf[0] = 0;
+		outin(prompt, buf, sizeof buf);
+		if (parseip(var, buf) == -1)
+			fprint(2, "configip: can't parse %s ip %s\n", name, buf);
+	}
 }

+ 3 - 6
sys/src/9/boot/parts.c

@@ -113,8 +113,7 @@ sdaddpart(SDunit* unit, char* name, uvlong start, uvlong end)
 	if (fprint(unit->ctl, "part %s %lld %lld\n", name, start, end) < 0)
 		fprint(2, "can't update %s's devsd partition table for %s: %r\n",
 			unit->name, name);
-	if (debugboot)
-		print("part %s %lld %lld\n", name, start, end);
+	dprint("part %s %lld %lld\n", name, start, end);
 }
 
 static long
@@ -193,8 +192,7 @@ oldp9part(SDunit *unit)
 	pp->start = unit->sectors - 2;
 	pp->end = unit->sectors - 1;
 
-	if(debugboot)
-		print("oldp9part %s\n", unit->name);
+	dprint("oldp9part %s\n", unit->name);
 	if(sdreadblk(unit, pp, partbuf, 0, 0) < 0)
 		return;
 
@@ -265,8 +263,7 @@ p9part(SDunit *unit, char *name)
 	uvlong start, end;
 	int i, n;
 
-	if(debugboot)
-		print("p9part %s %s\n", unit->name, name);
+	dprint("p9part %s %s\n", unit->name, name);
 	p = sdfindpart(unit, name);
 	if(p == nil)
 		return;

+ 7 - 14
sys/src/9/boot/usb.c

@@ -13,8 +13,6 @@ enum {
 	Post,
 };
 
-int	debugboot;
-
 static char usbdisk0[] = "/dev/sdU0.0";
 static char sdxxctl[]  = "/dev/sdXX/ctl";
 
@@ -34,14 +32,12 @@ start(char *name, char **argv, char *file)
 		return -1;
 	}
 
-	if(debugboot)
-		fprint(2, "%s...", name);
+	dprint("%s...", name);
 	runv(argv);
 	for(cnt = 10; cnt > 0 && access(file, AEXIST) < 0; cnt--)
 		sleep(100);
 	if (cnt <= 0) {
-		if(debugboot)
-			fprint(2, "no %s...", file);
+		dprint("no %s...", file);
 		return -1;
 	}
 	return 0;
@@ -54,8 +50,7 @@ chmod(char *file, int mode)
 
 	dir = dirstat(file);
 	if (dir == nil) {
-		if(debugboot)
-			fprint(2, "can't stat %s: %r\n", file);
+		dprint("can't stat %s: %r\n", file);
 		return -1;
 	}
 	dir->mode &= ~0777;
@@ -108,8 +103,7 @@ mountusb(void)
 {
 	int fd;
 
-	if(debugboot)
-		fprint(2, "mount usbd...");
+	dprint("mount usbd...");
 	fd = open("/srv/usb", ORDWR);
 	if(fd < 0)
 		warning("can't open /srv/usb");
@@ -140,8 +134,7 @@ usbinit(int post)
 
 	if(access("#u/usb/ctl", AEXIST) < 0 || bind("#u", "/dev", MAFTER) < 0)
 		return;
-	if(debugboot)
-		fprint(2, "usbinit...");
+	dprint("usbinit...");
 	start("usbd", usbdv, "/srv/usb");
 
 	/* allow a little time for usbd's device discovery */
@@ -149,6 +142,6 @@ usbinit(int post)
 		sleep(100);
 	if(cnt > 0)
 		startpartfs(post);
-	else if(debugboot)
-		fprint(2, "no usb disk...");
+	else
+		dprint("no usb disk...");
 }

+ 21 - 2
sys/src/9/pc/ether8169.c

@@ -297,6 +297,10 @@ typedef struct Ctlr {
 	int	rcr;			/* receive configuration register */
 	int	imr;
 
+	Watermark wmrb;
+	Watermark wmrd;
+	Watermark wmtd;
+
 	QLock	slock;			/* statistics */
 	Dtcc*	dtcc;
 	uint	txdu;
@@ -494,7 +498,7 @@ rtl8169multicast(void* ether, uchar *eaddr, int add)
 static long
 rtl8169ifstat(Ether* edev, void* a, long n, ulong offset)
 {
-	char *p;
+	char *p, *s, *e;
 	Ctlr *ctlr;
 	Dtcc *dtcc;
 	int i, l, r, timeo;
@@ -535,6 +539,7 @@ rtl8169ifstat(Ether* edev, void* a, long n, ulong offset)
 
 	if((p = malloc(READSTR)) == nil)
 		error(Enomem);
+	e = p + READSTR;
 
 	l = snprint(p, READSTR, "TxOk: %llud\n", dtcc->txok);
 	l += snprint(p+l, READSTR-l, "RxOk: %llud\n", dtcc->rxok);
@@ -575,6 +580,11 @@ rtl8169ifstat(Ether* edev, void* a, long n, ulong offset)
 		}
 		snprint(p+l, READSTR-l, "\n");
 	}
+	s = p + l + 1;
+//	s = seprintmark(s, e, &ctlr->wmrb);
+	s = seprintmark(s, e, &ctlr->wmrd);
+	s = seprintmark(s, e, &ctlr->wmtd);
+	USED(s);
 
 	n = readstr(offset, a, n, p);
 
@@ -851,6 +861,9 @@ rtl8169attach(Ether* edev)
 		}
 		memset(ctlr->dtcc, 0, sizeof(Dtcc));	/* paranoia */
 		rtl8169init(edev);
+		initmark(&ctlr->wmrb, Nrd, "rcv bufs unprocessed");
+		initmark(&ctlr->wmrd, Nrd-1, "rcv descrs processed at once");
+		initmark(&ctlr->wmtd, Ntd-1, "xmit descr queue len");
 		ctlr->init = 1;
 	}
 	qunlock(&ctlr->alock);
@@ -942,6 +955,8 @@ rtl8169transmit(Ether* edev)
 		coherence();
 		d->control |= Own | Fs | Ls | BLEN(bp);
 
+		/* note size of queue of tds awaiting transmission */
+		notemark(&ctlr->wmtd, (x + Ntd - ctlr->tdh) % Ntd);
 		x = NEXT(x, ctlr->ntd);
 		ctlr->ntq++;
 	}
@@ -959,7 +974,7 @@ static void
 rtl8169receive(Ether* edev)
 {
 	D *d;
-	int rdh;
+	int rdh, passed;
 	Block *bp;
 	Ctlr *ctlr;
 	u32int control;
@@ -967,6 +982,7 @@ rtl8169receive(Ether* edev)
 	ctlr = edev->ctlr;
 
 	rdh = ctlr->rdh;
+	passed = 0;
 	for(;;){
 		d = &ctlr->rd[rdh];
 
@@ -1009,6 +1025,7 @@ rtl8169receive(Ether* edev)
 				break;
 			}
 			etheriq(edev, bp, 1);
+			passed++;
 		}else{
 			if(!(control & Res))
 				ctlr->frag++;
@@ -1023,6 +1040,8 @@ rtl8169receive(Ether* edev)
 		if(ctlr->nrdfree < ctlr->nrd/2)
 			rtl8169replenish(ctlr);
 	}
+	/* note how many rds had full buffers */
+	notemark(&ctlr->wmrd, passed);
 	ctlr->rdh = rdh;
 }
 

+ 61 - 43
sys/src/9/pc/ether82563.c

@@ -488,6 +488,10 @@ struct Ctlr {
 	Rendez	lrendez;
 	int	lim;
 
+	Watermark wmrb;
+	Watermark wmrd;
+	Watermark wmtd;
+
 	QLock	slock;
 	uint	statistics[Nstatistics];
 	uint	lsleep;
@@ -688,6 +692,10 @@ i82563ifstat(Ether* edev, void* a, long n, ulong offset)
 //	}
 //	p = seprint(p, e, "\n");
 
+	p = seprintmark(p, e, &ctlr->wmrb);
+	p = seprintmark(p, e, &ctlr->wmrd);
+	p = seprintmark(p, e, &ctlr->wmtd);
+
 	USED(p);
 	n = readstr(offset, a, n, s);
 	free(s);
@@ -871,24 +879,24 @@ i82563txinit(Ctlr* ctlr)
 #define Next(x, m)	(((x)+1) & (m))
 
 static int
-i82563cleanup(Ctlr *c)
+i82563cleanup(Ctlr *ctlr)
 {
 	Block *b;
 	int tdh, m, n;
 
-	tdh = c->tdh;
-	m = c->ntd-1;
-	while(c->tdba[n = Next(tdh, m)].status & Tdd){
+	tdh = ctlr->tdh;
+	m = ctlr->ntd-1;
+	while(ctlr->tdba[n = Next(tdh, m)].status & Tdd){
 		tdh = n;
-		if((b = c->tb[tdh]) != nil){
-			c->tb[tdh] = nil;
+		if((b = ctlr->tb[tdh]) != nil){
+			ctlr->tb[tdh] = nil;
 			freeb(b);
 		}else
 			iprint("82563 tx underrun!\n");
-		c->tdba[tdh].status = 0;
+		ctlr->tdba[tdh].status = 0;
 	}
 
-	return c->tdh = tdh;
+	return ctlr->tdh = tdh;
 }
 
 static void
@@ -925,6 +933,8 @@ i82563transmit(Ether* edev)
 		td->addr[0] = PCIWADDR(bp->rp);
 		td->control = Ide|Rs|Ifcs|Teop|BLEN(bp);
 		ctlr->tb[tdt] = bp;
+		/* note size of queue of tds awaiting transmission */
+		notemark(&ctlr->wmtd, (tdt + Ntd - tdh) % Ntd);
 		tdt = Next(tdt, m);
 	}
 	if(ctlr->tdt != tdt){
@@ -1059,7 +1069,7 @@ i82563rproc(void* arg)
 	Rd *rd;
 	Block *bp;
 	Ctlr *ctlr;
-	int r, m, rdh, rim;
+	int r, m, rdh, rim, passed;
 	Ether *edev;
 
 	edev = arg;
@@ -1078,6 +1088,7 @@ i82563rproc(void* arg)
 		sleep(&ctlr->rrendez, i82563rim, ctlr);
 
 		rdh = ctlr->rdh;
+		passed = 0;
 		for(;;){
 			rim = ctlr->rim;
 			ctlr->rim = 0;
@@ -1120,7 +1131,9 @@ i82563rproc(void* arg)
 				ilock(&i82563rblock);
 				nrbfull++;
 				iunlock(&i82563rblock);
+				notemark(&ctlr->wmrb, nrbfull);
 				etheriq(edev, bp, 1);
+				passed++;
 			} else {
 				if (rd->status & Reop && rd->errors)
 					print("%s: input packet error %#ux\n",
@@ -1140,13 +1153,15 @@ i82563rproc(void* arg)
 			if(ctlr->rdfree <= ctlr->nrd - 32 || (rim & Rxdmt0))
 				i82563replenish(ctlr);
 		}
+		/* note how many rds had full buffers */
+		notemark(&ctlr->wmrd, passed);
 	}
 }
 
 static int
-i82563lim(void* c)
+i82563lim(void* ctlr)
 {
-	return ((Ctlr*)c)->lim != 0;
+	return ((Ctlr*)ctlr)->lim != 0;
 }
 
 static int speedtab[] = {
@@ -1154,14 +1169,14 @@ static int speedtab[] = {
 };
 
 static uint
-phyread(Ctlr *c, int reg)
+phyread(Ctlr *ctlr, int reg)
 {
 	uint phy, i;
 
-	csr32w(c, Mdic, MDIrop | 1<<MDIpSHIFT | reg<<MDIrSHIFT);
+	csr32w(ctlr, Mdic, MDIrop | 1<<MDIpSHIFT | reg<<MDIrSHIFT);
 	phy = 0;
 	for(i = 0; i < 64; i++){
-		phy = csr32r(c, Mdic);
+		phy = csr32r(ctlr, Mdic);
 		if(phy & (MDIe|MDIready))
 			break;
 		microdelay(1);
@@ -1172,14 +1187,14 @@ phyread(Ctlr *c, int reg)
 }
 
 static uint
-phywrite(Ctlr *c, int reg, ushort val)
+phywrite(Ctlr *ctlr, int reg, ushort val)
 {
 	uint phy, i;
 
-	csr32w(c, Mdic, MDIwop | 1<<MDIpSHIFT | reg<<MDIrSHIFT | val);
+	csr32w(ctlr, Mdic, MDIwop | 1<<MDIpSHIFT | reg<<MDIrSHIFT | val);
 	phy = 0;
 	for(i = 0; i < 64; i++){
-		phy = csr32r(c, Mdic);
+		phy = csr32r(ctlr, Mdic);
 		if(phy & (MDIe|MDIready))
 			break;
 		microdelay(1);
@@ -1196,29 +1211,29 @@ static void
 i82563lproc(void *v)
 {
 	uint phy, i, a;
-	Ctlr *c;
+	Ctlr *ctlr;
 	Ether *e;
 
 	e = v;
-	c = e->ctlr;
+	ctlr = e->ctlr;
 
-	if(c->type == i82573 && (phy = phyread(c, Phyier)) != ~0)
-		phywrite(c, Phyier, phy | Lscie | Ancie | Spdie | Panie);
+	if(ctlr->type == i82573 && (phy = phyread(ctlr, Phyier)) != ~0)
+		phywrite(ctlr, Phyier, phy | Lscie | Ancie | Spdie | Panie);
 	for(;;){
-		phy = phyread(c, Physsr);
+		phy = phyread(ctlr, Physsr);
 		if(phy == ~0)
 			goto next;
 		i = (phy>>14) & 3;
 
-		switch(c->type){
+		switch(ctlr->type){
 		case i82563:
-			a = phyread(c, Phyisr) & Ane;
+			a = phyread(ctlr, Phyisr) & Ane;
 			break;
 		case i82571:
 		case i82572:
 		case i82575:
 		case i82576:
-			a = phyread(c, Phylhr) & Anf;
+			a = phyread(ctlr, Phylhr) & Anf;
 			i = (i-1) & 3;
 			break;
 		default:
@@ -1226,18 +1241,18 @@ i82563lproc(void *v)
 			break;
 		}
 		if(a)
-			phywrite(c, Phyctl, phyread(c, Phyctl) | Ran | Ean);
+			phywrite(ctlr, Phyctl, phyread(ctlr, Phyctl) | Ran | Ean);
 		e->link = (phy & Rtlink) != 0;
 		if(e->link){
-			c->speeds[i]++;
+			ctlr->speeds[i]++;
 			if (speedtab[i])
 				e->mbps = speedtab[i];
 		}
 next:
-		c->lim = 0;
-		i82563im(c, Lsc);
-		c->lsleep++;
-		sleep(&c->lrendez, i82563lim, c);
+		ctlr->lim = 0;
+		i82563im(ctlr, Lsc);
+		ctlr->lsleep++;
+		sleep(&ctlr->lrendez, i82563lim, ctlr);
 	}
 }
 
@@ -1245,12 +1260,12 @@ static void
 i82563tproc(void *v)
 {
 	Ether *e;
-	Ctlr *c;
+	Ctlr *ctlr;
 
 	e = v;
-	c = e->ctlr;
+	ctlr = e->ctlr;
 	for(;;){
-		sleep(&c->trendez, return0, 0);
+		sleep(&ctlr->trendez, return0, 0);
 		i82563transmit(e);
 	}
 }
@@ -1307,6 +1322,9 @@ i82563attach(Ether* edev)
 
 	ctlr->edev = edev;			/* point back to Ether* */
 	ctlr->attached = 1;
+	initmark(&ctlr->wmrb, Nrb, "rcv bufs unprocessed");
+	initmark(&ctlr->wmrd, Nrd-1, "rcv descrs processed at once");
+	initmark(&ctlr->wmtd, Ntd-1, "xmit descr queue len");
 
 	snprint(name, sizeof name, "#l%dl", edev->ctlrno);
 	kproc(name, i82563lproc, edev);
@@ -1511,13 +1529,13 @@ fcycle(Ctlr *, Flash *f)
 }
 
 static int
-fread(Ctlr *c, Flash *f, int ladr)
+fread(Ctlr *ctlr, Flash *f, int ladr)
 {
 	ushort s;
 	ulong n;
 
 	delay(1);
-	if(fcycle(c, f) == -1)
+	if(fcycle(ctlr, f) == -1)
 		return -1;
 	f->reg[Fsts] |= Fdone;
 	f->reg32[Faddr] = ladr;
@@ -1539,32 +1557,32 @@ fread(Ctlr *c, Flash *f, int ladr)
 }
 
 static int
-fload(Ctlr *c)
+fload(Ctlr *ctlr)
 {
 	ulong data, io, r, adr;
 	ushort sum;
 	Flash f;
 
-	io = c->pcidev->mem[1].bar & ~0x0f;
-	f.reg = vmap(io, c->pcidev->mem[1].size);
+	io = ctlr->pcidev->mem[1].bar & ~0x0f;
+	f.reg = vmap(io, ctlr->pcidev->mem[1].size);
 	if(f.reg == nil)
 		return -1;
 	f.reg32 = (void*)f.reg;
 	f.base = f.reg32[Bfpr] & FMASK(0, 13);
 	f.lim = (f.reg32[Bfpr]>>16) & FMASK(0, 13);
-	if(csr32r(c, Eec) & (1<<22))
+	if(csr32r(ctlr, Eec) & (1<<22))
 		f.base += (f.lim + 1 - f.base) >> 1;
 	r = f.base << 12;
 
 	sum = 0;
 	for (adr = 0; adr < 0x40; adr++) {
-		data = fread(c, &f, r + adr*2);
+		data = fread(ctlr, &f, r + adr*2);
 		if(data == -1)
 			break;
-		c->eeprom[adr] = data;
+		ctlr->eeprom[adr] = data;
 		sum += data;
 	}
-	vunmap(f.reg, c->pcidev->mem[1].size);
+	vunmap(f.reg, ctlr->pcidev->mem[1].size);
 	return sum;
 }
 

+ 313 - 267
sys/src/9/pc/ether82598.c

@@ -24,7 +24,7 @@ enum {
 	/* tunable parameters */
 	Nrd	= 256,		/* multiple of 8, power of 2 for NEXTPOW2 */
 	Nrb	= 1024,
-	Ntd	= 128,		/* multiple of 8, power of 2 for NEXTPOW2 */
+	Ntd	= 64,		/* multiple of 8, power of 2 for NEXTPOW2 */
 	Goslow	= 0,		/* flag: go slow by throttling intrs, etc. */
 };
 
@@ -312,8 +312,12 @@ struct Ctlr {
 	int	procsrunning;
 	int	attached;
 
-	Lock	slock;
-	Lock	alock;			/* attach lock */
+	Watermark wmrb;
+	Watermark wmrd;
+	Watermark wmtd;
+
+	QLock	slock;
+	QLock	alock;			/* attach lock */
 	QLock	tlock;
 	Rendez	lrendez;
 	Rendez	trendez;
@@ -350,16 +354,17 @@ static	Ctlr	*ctlrtab[4];
 static	int	nctlr;
 static	Lock	rblock;
 static	Block	*rbpool;
+static	int	nrbfull;  /* # of rcv Blocks with data awaiting processing */
 
 static void
-readstats(Ctlr *c)
+readstats(Ctlr *ctlr)
 {
 	int i;
 
-	lock(&c->slock);
-	for(i = 0; i < nelem(c->stats); i++)
-		c->stats[i] += c->reg[stattab[i].reg >> 2];
-	unlock(&c->slock);
+	qlock(&ctlr->slock);
+	for(i = 0; i < nelem(ctlr->stats); i++)
+		ctlr->stats[i] += ctlr->reg[stattab[i].reg >> 2];
+	qunlock(&ctlr->slock);
 }
 
 static int speedtab[] = {
@@ -369,28 +374,32 @@ static int speedtab[] = {
 };
 
 static long
-ifstat(Ether *e, void *a, long n, ulong offset)
+ifstat(Ether *edev, void *a, long n, ulong offset)
 {
 	uint i, *t;
-	char *s, *p, *q;
-	Ctlr *c;
+	char *s, *p, *e;
+	Ctlr *ctlr;
 
-	c = e->ctlr;
+	ctlr = edev->ctlr;
 	p = s = malloc(READSTR);
 	if(p == nil)
 		error(Enomem);
-	q = p + READSTR;
+	e = p + READSTR;
 
-	readstats(c);
+	readstats(ctlr);
 	for(i = 0; i < nelem(stattab); i++)
-		if(c->stats[i] > 0)
-			p = seprint(p, q, "%.10s  %uld\n", stattab[i].name,
-				c->stats[i]);
-	t = c->speeds;
-	p = seprint(p, q, "speeds: 0:%d 1000:%d 10000:%d\n", t[0], t[1], t[2]);
-	p = seprint(p, q, "mtu: min:%d max:%d\n", e->minmtu, e->maxmtu);
-	seprint(p, q, "rdfree %d rdh %d rdt %d\n", c->rdfree, c->reg[Rdt],
-		c->reg[Rdh]);
+		if(ctlr->stats[i] > 0)
+			p = seprint(p, e, "%.10s  %uld\n", stattab[i].name,
+				ctlr->stats[i]);
+	t = ctlr->speeds;
+	p = seprint(p, e, "speeds: 0:%d 1000:%d 10000:%d\n", t[0], t[1], t[2]);
+	p = seprint(p, e, "mtu: min:%d max:%d\n", edev->minmtu, edev->maxmtu);
+	p = seprint(p, e, "rdfree %d rdh %d rdt %d\n", ctlr->rdfree, ctlr->reg[Rdt],
+		ctlr->reg[Rdh]);
+	p = seprintmark(p, e, &ctlr->wmrb);
+	p = seprintmark(p, e, &ctlr->wmrd);
+	p = seprintmark(p, e, &ctlr->wmtd);
+	USED(p);
 	n = readstr(offset, a, n, s);
 	free(s);
 
@@ -398,12 +407,12 @@ ifstat(Ether *e, void *a, long n, ulong offset)
 }
 
 static void
-ienable(Ctlr *c, int i)
+ienable(Ctlr *ctlr, int i)
 {
-	ilock(&c->imlock);
-	c->im |= i;
-	c->reg[Ims] = c->im;
-	iunlock(&c->imlock);
+	ilock(&ctlr->imlock);
+	ctlr->im |= i;
+	ctlr->reg[Ims] = ctlr->im;
+	iunlock(&ctlr->imlock);
 }
 
 static int
@@ -416,23 +425,23 @@ static void
 lproc(void *v)
 {
 	int r, i;
-	Ctlr *c;
+	Ctlr *ctlr;
 	Ether *e;
 
 	e = v;
-	c = e->ctlr;
+	ctlr = e->ctlr;
 	for (;;) {
-		r = c->reg[Links];
+		r = ctlr->reg[Links];
 		e->link = (r & Lnkup) != 0;
 		i = 0;
 		if(e->link)
 			i = 1 + ((r & Lnkspd) != 0);
-		c->speeds[i]++;
+		ctlr->speeds[i]++;
 		e->mbps = speedtab[i];
-		c->lim = 0;
-		ienable(c, Lsc);
-		sleep(&c->lrendez, lim, c);
-		c->lim = 0;
+		ctlr->lim = 0;
+		ienable(ctlr, Lsc);
+		sleep(&ctlr->lrendez, lim, ctlr);
+		ctlr->lim = 0;
 	}
 }
 
@@ -466,23 +475,24 @@ rbfree(Block *b)
 	ilock(&rblock);
 	b->next = rbpool;
 	rbpool = b;
+	nrbfull--;
 	iunlock(&rblock);
 }
 
 static int
-cleanup(Ctlr *c, int tdh)
+cleanup(Ctlr *ctlr, int tdh)
 {
 	Block *b;
 	uint m, n;
 
-	m = c->ntd - 1;
-	while(c->tdba[n = NEXTPOW2(tdh, m)].status & Tdd){
+	m = ctlr->ntd - 1;
+	while(ctlr->tdba[n = NEXTPOW2(tdh, m)].status & Tdd){
 		tdh = n;
-		b = c->tb[tdh];
-		c->tb[tdh] = 0;
+		b = ctlr->tb[tdh];
+		ctlr->tb[tdh] = 0;
 		if (b)
 			freeb(b);
-		c->tdba[tdh].status = 0;
+		ctlr->tdba[tdh].status = 0;
 	}
 	return tdh;
 }
@@ -491,42 +501,44 @@ void
 transmit(Ether *e)
 {
 	uint i, m, tdt, tdh;
-	Ctlr *c;
+	Ctlr *ctlr;
 	Block *b;
 	Td *t;
 
-	c = e->ctlr;
-	if(!canqlock(&c->tlock)){
-		ienable(c, Itx0);
+	ctlr = e->ctlr;
+	if(!canqlock(&ctlr->tlock)){
+		ienable(ctlr, Itx0);
 		return;
 	}
-	tdh = c->tdh = cleanup(c, c->tdh);
-	tdt = c->tdt;
-	m = c->ntd - 1;
+	tdh = ctlr->tdh = cleanup(ctlr, ctlr->tdh);
+	tdt = ctlr->tdt;
+	m = ctlr->ntd - 1;
 	for(i = 0; ; i++){
 		if(NEXTPOW2(tdt, m) == tdh){	/* ring full? */
-			ienable(c, Itx0);
+			ienable(ctlr, Itx0);
 			break;
 		}
 		if((b = qget(e->oq)) == nil)
 			break;
-		assert(c->tdba != nil);
-		t = c->tdba + tdt;
+		assert(ctlr->tdba != nil);
+		t = ctlr->tdba + tdt;
 		t->addr[0] = PCIWADDR(b->rp);
 		t->length = BLEN(b);
 		t->cmd = Ifcs | Teop;
 		if (!Goslow)
 			t->cmd |= Rs;
-		c->tb[tdt] = b;
+		ctlr->tb[tdt] = b;
+		/* note size of queue of tds awaiting transmission */
+		notemark(&ctlr->wmtd, (tdt + Ntd - tdh) % Ntd);
 		tdt = NEXTPOW2(tdt, m);
 	}
 	if(i) {
 		coherence();
-		c->reg[Tdt] = c->tdt = tdt;	/* make new Tds active */
+		ctlr->reg[Tdt] = ctlr->tdt = tdt;  /* make new Tds active */
 		coherence();
-		ienable(c, Itx0);
+		ienable(ctlr, Itx0);
 	}
-	qunlock(&c->tlock);
+	qunlock(&ctlr->tlock);
 }
 
 static int
@@ -538,101 +550,114 @@ tim(void *c)
 static void
 tproc(void *v)
 {
-	Ctlr *c;
+	Ctlr *ctlr;
 	Ether *e;
 
 	e = v;
-	c = e->ctlr;
+	ctlr = e->ctlr;
 	for (;;) {
-		sleep(&c->trendez, tim, c);  /* transmit interrupt kicks us */
-		c->tim = 0;
+		sleep(&ctlr->trendez, tim, ctlr); /* xmit interrupt kicks us */
+		ctlr->tim = 0;
 		transmit(e);
 	}
 }
 
 static void
-rxinit(Ctlr *c)
+rxinit(Ctlr *ctlr)
 {
-	int i, is598;
+	int i, is598, autoc;
+	ulong until;
 	Block *b;
 
-	c->reg[Rxctl] &= ~Rxen;
-	c->reg[Rxdctl] = 0;
-	for(i = 0; i < c->nrd; i++){
-		b = c->rb[i];
-		c->rb[i] = 0;
+	ctlr->reg[Rxctl] &= ~Rxen;
+	ctlr->reg[Rxdctl] = 0;
+	for(i = 0; i < ctlr->nrd; i++){
+		b = ctlr->rb[i];
+		ctlr->rb[i] = 0;
 		if(b)
 			freeb(b);
 	}
-	c->rdfree = 0;
+	ctlr->rdfree = 0;
 
 	coherence();
-	c->reg[Fctrl] |= Bam;
-	c->reg[Fctrl] &= ~(Upe | Mpe);
+	ctlr->reg[Fctrl] |= Bam;
+	ctlr->reg[Fctrl] &= ~(Upe | Mpe);
 
 	/* intel gets some csums wrong (e.g., errata 44) */
-	c->reg[Rxcsum] &= ~Ippcse;
-	c->reg[Hlreg0] &= ~Jumboen;		/* jumbos are a bad idea */
-	c->reg[Hlreg0] |= Txcrcen | Rxcrcstrip | Txpaden;
-	c->reg[Srrctl] = (c->rbsz + 1024 - 1) / 1024;
-	c->reg[Mhadd] = c->rbsz << 16;
-
-	c->reg[Rbal] = PCIWADDR(c->rdba);
-	c->reg[Rbah] = 0;
-	c->reg[Rdlen] = c->nrd*sizeof(Rd);	/* must be multiple of 128 */
-	c->reg[Rdh] = 0;
-	c->reg[Rdt] = c->rdt = 0;
+	ctlr->reg[Rxcsum] &= ~Ippcse;
+	ctlr->reg[Hlreg0] &= ~Jumboen;		/* jumbos are a bad idea */
+	ctlr->reg[Hlreg0] |= Txcrcen | Rxcrcstrip | Txpaden;
+	ctlr->reg[Srrctl] = (ctlr->rbsz + 1024 - 1) / 1024;
+	ctlr->reg[Mhadd] = ctlr->rbsz << 16;
+
+	ctlr->reg[Rbal] = PCIWADDR(ctlr->rdba);
+	ctlr->reg[Rbah] = 0;
+	ctlr->reg[Rdlen] = ctlr->nrd*sizeof(Rd); /* must be multiple of 128 */
+	ctlr->reg[Rdh] = 0;
+	ctlr->reg[Rdt] = ctlr->rdt = 0;
 	coherence();
 
-	is598 = (c->type == I82598);
+	is598 = (ctlr->type == I82598);
 	if (is598)
-		c->reg[Rdrxctl] = Rdmt¼;
+		ctlr->reg[Rdrxctl] = Rdmt¼;
 	else {
-		c->reg[Rdrxctl] |= Crcstrip;
-		c->reg[Rdrxctl] &= ~Rscfrstsize;
+		ctlr->reg[Rdrxctl] |= Crcstrip;
+		ctlr->reg[Rdrxctl] &= ~Rscfrstsize;
 	}
 	if (Goslow && is598)
-		c->reg[Rxdctl] = 8<<Wthresh | 8<<Pthresh | 4<<Hthresh | Renable;
+		ctlr->reg[Rxdctl] = 8<<Wthresh | 8<<Pthresh | 4<<Hthresh | Renable;
 	else
-		c->reg[Rxdctl] = Renable;
+		ctlr->reg[Rxdctl] = Renable;
 	coherence();
-	while (!(c->reg[Rxdctl] & Renable))
+
+	/*
+	 * don't wait forever like an idiot (and hang the system),
+	 * maybe it's disconnected.
+	 */
+	until = TK2MS(MACHP(0)->ticks) + 250;
+	while (!(ctlr->reg[Rxdctl] & Renable) && TK2MS(MACHP(0)->ticks) < until)
 		;
-	c->reg[Rxctl] |= Rxen | (is598? Dmbyps: 0);
+	if(!(ctlr->reg[Rxdctl] & Renable))
+		print("#l%d: Renable didn't come on, might be disconnected\n",
+			ctlr->edev->ctlrno);
+
+	ctlr->reg[Rxctl] |= Rxen | (is598? Dmbyps: 0);
 
 	if (is598){
-		print("82598: autoc %#ux; lms %d (3 is 10g sfp)\n",
-			c->reg[Autoc], ((c->reg[Autoc]>>Lmsshift) & Lmsmask));
-		c->reg[Autoc] |= Flu;
+		autoc = ctlr->reg[Autoc];
+		/* what is this rubbish and why do we care? */
+		print("#l%d: autoc %#ux; lms %d (3 is 10g sfp)\n",
+			ctlr->edev->ctlrno, autoc, (autoc>>Lmsshift) & Lmsmask);
+		ctlr->reg[Autoc] |= Flu;
 		coherence();
 		delay(50);
 	}
 }
 
 static void
-replenish(Ctlr *c, uint rdh)
+replenish(Ctlr *ctlr, uint rdh)
 {
 	int rdt, m, i;
 	Block *b;
 	Rd *r;
 
-	m = c->nrd - 1;
+	m = ctlr->nrd - 1;
 	i = 0;
-	for(rdt = c->rdt; NEXTPOW2(rdt, m) != rdh; rdt = NEXTPOW2(rdt, m)){
-		r = c->rdba + rdt;
+	for(rdt = ctlr->rdt; NEXTPOW2(rdt, m) != rdh; rdt = NEXTPOW2(rdt, m)){
+		r = ctlr->rdba + rdt;
 		if((b = rballoc()) == nil){
-			print("82598: no buffers\n");
+			print("#l%d: no buffers\n", ctlr->edev->ctlrno);
 			break;
 		}
-		c->rb[rdt] = b;
+		ctlr->rb[rdt] = b;
 		r->addr[0] = PCIWADDR(b->rp);
 		r->status = 0;
-		c->rdfree++;
+		ctlr->rdfree++;
 		i++;
 	}
 	if(i) {
 		coherence();
-		c->reg[Rdt] = c->rdt = rdt;	/* hand back recycled rdescs */
+		ctlr->reg[Rdt] = ctlr->rdt = rdt; /* hand back recycled rdescs */
 		coherence();
 	}
 }
@@ -646,63 +671,75 @@ rim(void *v)
 void
 rproc(void *v)
 {
+	int passed;
 	uint m, rdh;
-	Block *b;
-	Ctlr *c;
+	Block *bp;
+	Ctlr *ctlr;
 	Ether *e;
 	Rd *r;
 
 	e = v;
-	c = e->ctlr;
-	m = c->nrd - 1;
+	ctlr = e->ctlr;
+	m = ctlr->nrd - 1;
 	for (rdh = 0; ; ) {
-		replenish(c, rdh);
-		ienable(c, Irx0);
-		sleep(&c->rrendez, rim, c);
+		replenish(ctlr, rdh);
+		ienable(ctlr, Irx0);
+		sleep(&ctlr->rrendez, rim, ctlr);
+		passed = 0;
 		for (;;) {
-			c->rim = 0;
-			r = c->rdba + rdh;
+			ctlr->rim = 0;
+			r = ctlr->rdba + rdh;
 			if(!(r->status & Rdd))
 				break;		/* wait for pkts to arrive */
-			b = c->rb[rdh];
-			c->rb[rdh] = 0;
+			bp = ctlr->rb[rdh];
+			ctlr->rb[rdh] = 0;
 			if (r->length > ETHERMAXTU)
-				print("82598: got jumbo of %d bytes\n", r->length);
-			b->wp += r->length;
-			b->lim = b->wp;			/* lie like a dog */
+				print("#l%d: got jumbo of %d bytes\n",
+					e->ctlrno, r->length);
+			bp->wp += r->length;
+			bp->lim = bp->wp;		/* lie like a dog */
 //			r->status = 0;
-			etheriq(e, b, 1);
-			c->rdfree--;
+
+			ilock(&rblock);
+			nrbfull++;
+			iunlock(&rblock);
+			notemark(&ctlr->wmrb, nrbfull);
+			etheriq(e, bp, 1);
+
+			passed++;
+			ctlr->rdfree--;
 			rdh = NEXTPOW2(rdh, m);
-			if (c->rdfree <= c->nrd - 16)
-				replenish(c, rdh);
+			if (ctlr->rdfree <= ctlr->nrd - 16)
+				replenish(ctlr, rdh);
 		}
+		/* note how many rds had full buffers */
+		notemark(&ctlr->wmrd, passed);
 	}
 }
 
 static void
 promiscuous(void *a, int on)
 {
-	Ctlr *c;
+	Ctlr *ctlr;
 	Ether *e;
 
 	e = a;
-	c = e->ctlr;
+	ctlr = e->ctlr;
 	if(on)
-		c->reg[Fctrl] |= Upe | Mpe;
+		ctlr->reg[Fctrl] |= Upe | Mpe;
 	else
-		c->reg[Fctrl] &= ~(Upe | Mpe);
+		ctlr->reg[Fctrl] &= ~(Upe | Mpe);
 }
 
 static void
 multicast(void *a, uchar *ea, int on)
 {
 	int b, i;
-	Ctlr *c;
+	Ctlr *ctlr;
 	Ether *e;
 
 	e = a;
-	c = e->ctlr;
+	ctlr = e->ctlr;
 
 	/*
 	 * multiple ether addresses can hash to the same filter bit,
@@ -715,14 +752,14 @@ multicast(void *a, uchar *ea, int on)
 	b = (ea[5]&1)<<4 | ea[4]>>4;
 	b = 1 << b;
 	if(on)
-		c->mta[i] |= b;
+		ctlr->mta[i] |= b;
 //	else
-//		c->mta[i] &= ~b;
-	c->reg[Mta+i] = c->mta[i];
+//		ctlr->mta[i] &= ~b;
+	ctlr->reg[Mta+i] = ctlr->mta[i];
 }
 
 static void
-freemem(Ctlr *c)
+freemem(Ctlr *ctlr)
 {
 	Block *b;
 
@@ -730,46 +767,46 @@ freemem(Ctlr *c)
 		b->free = 0;
 		freeb(b);
 	}
-	free(c->rdba);
-	c->rdba = nil;
-	free(c->tdba);
-	c->tdba = nil;
-	free(c->rb);
-	c->rb = nil;
-	free(c->tb);
-	c->tb = nil;
+	free(ctlr->rdba);
+	ctlr->rdba = nil;
+	free(ctlr->tdba);
+	ctlr->tdba = nil;
+	free(ctlr->rb);
+	ctlr->rb = nil;
+	free(ctlr->tb);
+	ctlr->tb = nil;
 }
 
 static int
-detach(Ctlr *c)
+detach(Ctlr *ctlr)
 {
 	int i, is598;
 
-	c->reg[Imc] = ~0;
-	c->reg[Ctrl] |= Rst;
+	ctlr->reg[Imc] = ~0;
+	ctlr->reg[Ctrl] |= Rst;
 	for(i = 0; i < 100; i++){
 		delay(1);
-		if((c->reg[Ctrl] & Rst) == 0)
+		if((ctlr->reg[Ctrl] & Rst) == 0)
 			break;
 	}
 	if (i >= 100)
 		return -1;
-	is598 = (c->type == I82598);
+	is598 = (ctlr->type == I82598);
 	if (is598) {			/* errata */
 		delay(50);
-		c->reg[Ecc] &= ~(1<<21 | 1<<18 | 1<<9 | 1<<6);
+		ctlr->reg[Ecc] &= ~(1<<21 | 1<<18 | 1<<9 | 1<<6);
 	}
 
 	/* not cleared by reset; kill it manually. */
 	for(i = 1; i < 16; i++)
-		c->reg[is598? Rah98: Rah99] &= ~Enable;
+		ctlr->reg[is598? Rah98: Rah99] &= ~Enable;
 	for(i = 0; i < 128; i++)
-		c->reg[Mta + i] = 0;
+		ctlr->reg[Mta + i] = 0;
 	for(i = 1; i < (is598? 640: 128); i++)
-		c->reg[Vfta + i] = 0;
+		ctlr->reg[Vfta + i] = 0;
 
-//	freemem(c);			// TODO
-	c->attached = 0;
+//	freemem(ctlr);			// TODO
+	ctlr->attached = 0;
 	return 0;
 }
 
@@ -782,133 +819,133 @@ shutdown(Ether *e)
 
 /* ≤ 20ms */
 static ushort
-eeread(Ctlr *c, int i)
+eeread(Ctlr *ctlr, int i)
 {
-	c->reg[Eerd] = EEstart | i<<2;
-	while((c->reg[Eerd] & EEdone) == 0)
+	ctlr->reg[Eerd] = EEstart | i<<2;
+	while((ctlr->reg[Eerd] & EEdone) == 0)
 		;
-	return c->reg[Eerd] >> 16;
+	return ctlr->reg[Eerd] >> 16;
 }
 
 static int
-eeload(Ctlr *c)
+eeload(Ctlr *ctlr)
 {
 	ushort u, v, p, l, i, j;
 
-	if((eeread(c, 0) & 0xc0) != 0x40)
+	if((eeread(ctlr, 0) & 0xc0) != 0x40)
 		return -1;
 	u = 0;
 	for(i = 0; i < 0x40; i++)
-		u +=  eeread(c, i);
+		u +=  eeread(ctlr, i);
 	for(i = 3; i < 0xf; i++){
-		p = eeread(c, i);
-		l = eeread(c, p++);
+		p = eeread(ctlr, i);
+		l = eeread(ctlr, p++);
 		if((int)p + l + 1 > 0xffff)
 			continue;
 		for(j = p; j < p + l; j++)
-			u += eeread(c, j);
+			u += eeread(ctlr, j);
 	}
 	if(u != 0xbaba)
 		return -1;
-	if(c->reg[Status] & (1<<3))
-		u = eeread(c, 10);
+	if(ctlr->reg[Status] & (1<<3))
+		u = eeread(ctlr, 10);
 	else
-		u = eeread(c, 9);
+		u = eeread(ctlr, 9);
 	u++;
 	for(i = 0; i < Eaddrlen;){
-		v = eeread(c, u + i/2);
-		c->ra[i++] = v;
-		c->ra[i++] = v>>8;
+		v = eeread(ctlr, u + i/2);
+		ctlr->ra[i++] = v;
+		ctlr->ra[i++] = v>>8;
 	}
-	c->ra[5] += (c->reg[Status] & 0xc) >> 2;
+	ctlr->ra[5] += (ctlr->reg[Status] & 0xc) >> 2;
 	return 0;
 }
 
 static int
-reset(Ctlr *c)
+reset(Ctlr *ctlr)
 {
 	int i, is598;
 	uchar *p;
 
-	if(detach(c)){
+	if(detach(ctlr)){
 		print("82598: reset timeout\n");
 		return -1;
 	}
-	if(eeload(c)){
+	if(eeload(ctlr)){
 		print("82598: eeprom failure\n");
 		return -1;
 	}
-	p = c->ra;
-	is598 = (c->type == I82598);
-	c->reg[is598? Ral98: Ral99] = p[3]<<24 | p[2]<<16 | p[1]<<8 | p[0];
-	c->reg[is598? Rah98: Rah99] = p[5]<<8 | p[4] | Enable;
+	p = ctlr->ra;
+	is598 = (ctlr->type == I82598);
+	ctlr->reg[is598? Ral98: Ral99] = p[3]<<24 | p[2]<<16 | p[1]<<8 | p[0];
+	ctlr->reg[is598? Rah98: Rah99] = p[5]<<8 | p[4] | Enable;
 
-	readstats(c);
-	for(i = 0; i<nelem(c->stats); i++)
-		c->stats[i] = 0;
+	readstats(ctlr);
+	for(i = 0; i<nelem(ctlr->stats); i++)
+		ctlr->stats[i] = 0;
 
-	c->reg[Ctrlext] |= 1 << 16;	/* required by errata (spec change 4) */
+	ctlr->reg[Ctrlext] |= 1 << 16;	/* required by errata (spec change 4) */
 	if (Goslow) {
 		/* make some guesses for flow control */
-		c->reg[Fcrtl] = 0x10000 | Enable;
-		c->reg[Fcrth] = 0x40000 | Enable;
-		c->reg[Rcrtv] = 0x6000;
+		ctlr->reg[Fcrtl] = 0x10000 | Enable;
+		ctlr->reg[Fcrth] = 0x40000 | Enable;
+		ctlr->reg[Rcrtv] = 0x6000;
 	} else
-		c->reg[Fcrtl] = c->reg[Fcrth] = c->reg[Rcrtv] = 0;
+		ctlr->reg[Fcrtl] = ctlr->reg[Fcrth] = ctlr->reg[Rcrtv] = 0;
 
 	/* configure interrupt mapping (don't ask) */
-	c->reg[Ivar+0] =     0 | 1<<7;
-	c->reg[Ivar+64/4] =  1 | 1<<7;
-//	c->reg[Ivar+97/4] = (2 | 1<<7) << (8*(97%4));
+	ctlr->reg[Ivar+0] =     0 | 1<<7;
+	ctlr->reg[Ivar+64/4] =  1 | 1<<7;
+//	ctlr->reg[Ivar+97/4] = (2 | 1<<7) << (8*(97%4));
 
 	if (Goslow) {
 		/* interrupt throttling goes here. */
 		for(i = Itr; i < Itr + 20; i++)
-			c->reg[i] = 128;		/* ¼µs intervals */
-		c->reg[Itr + Itx0] = 256;
+			ctlr->reg[i] = 128;		/* ¼µs intervals */
+		ctlr->reg[Itr + Itx0] = 256;
 	} else {					/* don't throttle */
 		for(i = Itr; i < Itr + 20; i++)
-			c->reg[i] = 0;			/* ¼µs intervals */
-		c->reg[Itr + Itx0] = 0;
+			ctlr->reg[i] = 0;		/* ¼µs intervals */
+		ctlr->reg[Itr + Itx0] = 0;
 	}
 	return 0;
 }
 
 static void
-txinit(Ctlr *c)
+txinit(Ctlr *ctlr)
 {
 	Block *b;
 	int i;
 
 	if (Goslow)
-		c->reg[Txdctl] = 16<<Wthresh | 16<<Pthresh;
+		ctlr->reg[Txdctl] = 16<<Wthresh | 16<<Pthresh;
 	else
-		c->reg[Txdctl] = 0;
-	if (c->type == I82599)
-		c->reg[Dtxctl99] = 0;
+		ctlr->reg[Txdctl] = 0;
+	if (ctlr->type == I82599)
+		ctlr->reg[Dtxctl99] = 0;
 	coherence();
-	for(i = 0; i < c->ntd; i++){
-		b = c->tb[i];
-		c->tb[i] = 0;
+	for(i = 0; i < ctlr->ntd; i++){
+		b = ctlr->tb[i];
+		ctlr->tb[i] = 0;
 		if(b)
 			freeb(b);
 	}
 
-	assert(c->tdba != nil);
-	memset(c->tdba, 0, c->ntd * sizeof(Td));
-	c->reg[Tdbal] = PCIWADDR(c->tdba);
-	c->reg[Tdbah] = 0;
-	c->reg[Tdlen] = c->ntd*sizeof(Td);	/* must be multiple of 128 */
-	c->reg[Tdh] = 0;
-	c->tdh = c->ntd - 1;
-	c->reg[Tdt] = c->tdt = 0;
+	assert(ctlr->tdba != nil);
+	memset(ctlr->tdba, 0, ctlr->ntd * sizeof(Td));
+	ctlr->reg[Tdbal] = PCIWADDR(ctlr->tdba);
+	ctlr->reg[Tdbah] = 0;
+	ctlr->reg[Tdlen] = ctlr->ntd*sizeof(Td); /* must be multiple of 128 */
+	ctlr->reg[Tdh] = 0;
+	ctlr->tdh = ctlr->ntd - 1;
+	ctlr->reg[Tdt] = ctlr->tdt = 0;
 	coherence();
-	if (c->type == I82599)
-		c->reg[Dtxctl99] |= Te;
+	if (ctlr->type == I82599)
+		ctlr->reg[Dtxctl99] |= Te;
 	coherence();
-	c->reg[Txdctl] |= Ten;
+	ctlr->reg[Txdctl] |= Ten;
 	coherence();
-	while (!(c->reg[Txdctl] & Ten))
+	while (!(ctlr->reg[Txdctl] & Ten))
 		;
 }
 
@@ -916,51 +953,58 @@ static void
 attach(Ether *e)
 {
 	Block *b;
-	Ctlr *c;
+	Ctlr *ctlr;
 	char buf[KNAMELEN];
 
-	c = e->ctlr;
-	c->edev = e;			/* point back to Ether* */
-	lock(&c->alock);
+	ctlr = e->ctlr;
+	ctlr->edev = e;			/* point back to Ether* */
+	qlock(&ctlr->alock);
 	if(waserror()){
-		unlock(&c->alock);
-		freemem(c);
+		reset(ctlr);
+		freemem(ctlr);
+		qunlock(&ctlr->alock);
 		nexterror();
 	}
-	if(c->rdba == nil) {
-		c->nrd = Nrd;
-		c->ntd = Ntd;
-		c->rdba = mallocalign(c->nrd * sizeof *c->rdba, Descalign, 0, 0);
-		c->tdba = mallocalign(c->ntd * sizeof *c->tdba, Descalign, 0, 0);
-		c->rb = malloc(c->nrd * sizeof(Block *));
-		c->tb = malloc(c->ntd * sizeof(Block *));
-		if (c->rdba == nil || c->tdba == nil ||
-		    c->rb == nil || c->tb == nil)
+	if(ctlr->rdba == nil) {
+		ctlr->nrd = Nrd;
+		ctlr->ntd = Ntd;
+		ctlr->rdba = mallocalign(ctlr->nrd * sizeof *ctlr->rdba,
+			Descalign, 0, 0);
+		ctlr->tdba = mallocalign(ctlr->ntd * sizeof *ctlr->tdba,
+			Descalign, 0, 0);
+		ctlr->rb = malloc(ctlr->nrd * sizeof(Block *));
+		ctlr->tb = malloc(ctlr->ntd * sizeof(Block *));
+		if (ctlr->rdba == nil || ctlr->tdba == nil ||
+		    ctlr->rb == nil || ctlr->tb == nil)
 			error(Enomem);
 
-		for(c->nrb = 0; c->nrb < 2*Nrb; c->nrb++){
-			b = allocb(c->rbsz + BY2PG);	/* see rbfree() */
+		for(ctlr->nrb = 0; ctlr->nrb < 2*Nrb; ctlr->nrb++){
+			b = allocb(ctlr->rbsz + BY2PG);	/* see rbfree() */
 			if(b == nil)
 				error(Enomem);
 			b->free = rbfree;
 			freeb(b);
 		}
 	}
-	if (!c->attached) {
-		rxinit(c);
-		txinit(c);
-		if (!c->procsrunning) {
+	if (!ctlr->attached) {
+		rxinit(ctlr);
+		txinit(ctlr);
+		nrbfull = 0;
+		if (!ctlr->procsrunning) {
 			snprint(buf, sizeof buf, "#l%dl", e->ctlrno);
 			kproc(buf, lproc, e);
 			snprint(buf, sizeof buf, "#l%dr", e->ctlrno);
 			kproc(buf, rproc, e);
 			snprint(buf, sizeof buf, "#l%dt", e->ctlrno);
 			kproc(buf, tproc, e);
-			c->procsrunning = 1;
+			ctlr->procsrunning = 1;
 		}
-		c->attached = 1;
+		initmark(&ctlr->wmrb, Nrb, "rcv bufs unprocessed");
+		initmark(&ctlr->wmrd, Nrd-1, "rcv descrs processed at once");
+		initmark(&ctlr->wmtd, Ntd-1, "xmit descr queue len");
+		ctlr->attached = 1;
 	}
-	unlock(&c->alock);
+	qunlock(&ctlr->alock);
 	poperror();
 }
 
@@ -968,33 +1012,33 @@ static void
 interrupt(Ureg*, void *v)
 {
 	int icr, im;
-	Ctlr *c;
+	Ctlr *ctlr;
 	Ether *e;
 
 	e = v;
-	c = e->ctlr;
-	ilock(&c->imlock);
-	c->reg[Imc] = ~0;			/* disable all intrs */
-	im = c->im;
-	while((icr = c->reg[Icr] & c->im) != 0){
+	ctlr = e->ctlr;
+	ilock(&ctlr->imlock);
+	ctlr->reg[Imc] = ~0;			/* disable all intrs */
+	im = ctlr->im;
+	while((icr = ctlr->reg[Icr] & ctlr->im) != 0){
 		if(icr & Irx0){
 			im &= ~Irx0;
-			c->rim = Irx0;
-			wakeup(&c->rrendez);
+			ctlr->rim = Irx0;
+			wakeup(&ctlr->rrendez);
 		}
 		if(icr & Itx0){
 			im &= ~Itx0;
-			c->tim = Itx0;
-			wakeup(&c->trendez);
+			ctlr->tim = Itx0;
+			wakeup(&ctlr->trendez);
 		}
 		if(icr & Lsc){
 			im &= ~Lsc;
-			c->lim = Lsc;
-			wakeup(&c->lrendez);
+			ctlr->lim = Lsc;
+			wakeup(&ctlr->lrendez);
 		}
 	}
-	c->reg[Ims] = c->im = im;  /* enable only intrs we didn't service */
-	iunlock(&c->imlock);
+	ctlr->reg[Ims] = ctlr->im = im; /* enable only intrs we didn't service */
+	iunlock(&ctlr->imlock);
 }
 
 static void
@@ -1003,7 +1047,7 @@ scan(void)
 	int pciregs, pcimsix, type;
 	ulong io, iomsi;
 	void *mem, *memmsi;
-	Ctlr *c;
+	Ctlr *ctlr;
 	Pcidev *p;
 
 	p = 0;
@@ -1030,7 +1074,7 @@ scan(void)
 			continue;
 		}
 		pciregs = 0;
-		if(nctlr == nelem(ctlrtab)){
+		if(nctlr >= nelem(ctlrtab)){
 			print("i82598: too many controllers\n");
 			return;
 		}
@@ -1052,28 +1096,28 @@ scan(void)
 			continue;
 		}
 
-		c = malloc(sizeof *c);
-		if(c == nil) {
+		ctlr = malloc(sizeof *ctlr);
+		if(ctlr == nil) {
 			vunmap(mem, p->mem[pciregs].size);
 			vunmap(memmsi, p->mem[pcimsix].size);
 			error(Enomem);
 		}
-		c->p = p;
-		c->type = type;
-		c->physreg = (u32int*)io;
-		c->physmsix = (u32int*)iomsi;
-		c->reg = (u32int*)mem;
-		c->msix = (u32int*)memmsi;	/* unused */
-		c->rbsz = Rbsz;
-		if(reset(c)){
+		ctlr->p = p;
+		ctlr->type = type;
+		ctlr->physreg = (u32int*)io;
+		ctlr->physmsix = (u32int*)iomsi;
+		ctlr->reg = (u32int*)mem;
+		ctlr->msix = (u32int*)memmsi;	/* unused */
+		ctlr->rbsz = Rbsz;
+		if(reset(ctlr)){
 			print("i82598: can't reset\n");
-			free(c);
+			free(ctlr);
 			vunmap(mem, p->mem[pciregs].size);
 			vunmap(memmsi, p->mem[pcimsix].size);
 			continue;
 		}
 		pcisetbme(p);
-		ctlrtab[nctlr++] = c;
+		ctlrtab[nctlr++] = ctlr;
 	}
 }
 
@@ -1081,27 +1125,28 @@ static int
 pnp(Ether *e)
 {
 	int i;
-	Ctlr *c = nil;
+	Ctlr *ctlr;
 
 	if(nctlr == 0)
 		scan();
+	ctlr = nil;
 	for(i = 0; i < nctlr; i++){
-		c = ctlrtab[i];
-		if(c == nil || c->flag & Factive)
+		ctlr = ctlrtab[i];
+		if(ctlr == nil || ctlr->flag & Factive)
 			continue;
-		if(e->port == 0 || e->port == (ulong)c->reg)
+		if(e->port == 0 || e->port == (ulong)ctlr->reg)
 			break;
 	}
 	if (i >= nctlr)
 		return -1;
-	c->flag |= Factive;
-	e->ctlr = c;
-	e->port = (uintptr)c->physreg;
-	e->irq = c->p->intl;
-	e->tbdf = c->p->tbdf;
+	ctlr->flag |= Factive;
+	e->ctlr = ctlr;
+	e->port = (uintptr)ctlr->physreg;
+	e->irq = ctlr->p->intl;
+	e->tbdf = ctlr->p->tbdf;
 	e->mbps = 10000;
 	e->maxmtu = ETHERMAXTU;
-	memmove(e->ea, c->ra, Eaddrlen);
+	memmove(e->ea, ctlr->ra, Eaddrlen);
 
 	e->arg = e;
 	e->attach = attach;
@@ -1121,4 +1166,5 @@ void
 ether82598link(void)
 {
 	addethercard("i82598", pnp);
+	addethercard("i10gbe", pnp);
 }

+ 29 - 3
sys/src/9/pc/etherigbe.c

@@ -477,6 +477,10 @@ struct Ctlr {
 
 	int	link;
 
+	Watermark wmrb;
+	Watermark wmrd;
+	Watermark wmtd;
+
 	QLock	slock;
 	uint	statistics[Nstatistics];
 	uint	lsleep;
@@ -521,6 +525,7 @@ static Ctlr* igbectlrtail;
 
 static Lock igberblock;		/* free receive Blocks */
 static Block* igberbpool;	/* receive Blocks for all igbe controllers */
+static int nrbfull;	/* # of rcv Blocks with data awaiting processing */
 
 static char* statistics[Nstatistics] = {
 	"CRC Error",
@@ -593,7 +598,7 @@ static long
 igbeifstat(Ether* edev, void* a, long n, ulong offset)
 {
 	Ctlr *ctlr;
-	char *p, *s;
+	char *p, *s, *e;
 	int i, l, r;
 	uvlong tuvl, ruvl;
 
@@ -667,6 +672,13 @@ igbeifstat(Ether* edev, void* a, long n, ulong offset)
 		}
 		snprint(p+l, READSTR-l, "\n");
 	}
+	e = p + READSTR;
+	s = p + l + 1;
+	s = seprintmark(s, e, &ctlr->wmrb);
+	s = seprintmark(s, e, &ctlr->wmrd);
+	s = seprintmark(s, e, &ctlr->wmtd);
+	USED(s);
+
 	n = readstr(offset, a, n, p);
 	free(p);
 	qunlock(&ctlr->slock);
@@ -789,6 +801,7 @@ igberbfree(Block* bp)
 	ilock(&igberblock);
 	bp->next = igberbpool;
 	igberbpool = bp;
+	nrbfull--;
 	iunlock(&igberblock);
 }
 
@@ -1013,6 +1026,8 @@ igbetransmit(Ether* edev)
 		td->control = ((BLEN(bp) & LenMASK)<<LenSHIFT);
 		td->control |= Dext|Ifcs|Teop|DtypeDD;
 		ctlr->tb[tdt] = bp;
+		/* note size of queue of tds awaiting transmission */
+		notemark(&ctlr->wmtd, (tdt + Ntd - tdh) % Ntd);
 		tdt = NEXT(tdt, ctlr->ntd);
 		if(NEXT(tdt, ctlr->ntd) == tdh){
 			td->control |= Rs;
@@ -1085,6 +1100,7 @@ igberxinit(Ctlr* ctlr)
 		}
 	}
 	igbereplenish(ctlr);
+	nrbfull = 0;
 
 	switch(ctlr->id){
 	case i82540em:
@@ -1120,7 +1136,7 @@ igberproc(void* arg)
 	Rd *rd;
 	Block *bp;
 	Ctlr *ctlr;
-	int r, rdh;
+	int r, rdh, passed;
 	Ether *edev;
 
 	edev = arg;
@@ -1130,7 +1146,6 @@ igberproc(void* arg)
 	r = csr32r(ctlr, Rctl);
 	r |= Ren;
 	csr32w(ctlr, Rctl, r);
-
 	for(;;){
 		ctlr->rim = 0;
 		igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq);
@@ -1138,6 +1153,7 @@ igberproc(void* arg)
 		sleep(&ctlr->rrendez, igberim, ctlr);
 
 		rdh = ctlr->rdh;
+		passed = 0;
 		for(;;){
 			rd = &ctlr->rdba[rdh];
 
@@ -1180,7 +1196,12 @@ igberproc(void* arg)
 					bp->checksum = rd->checksum;
 					bp->flag |= Bpktck;
 				}
+				ilock(&igberblock);
+				nrbfull++;
+				iunlock(&igberblock);
+				notemark(&ctlr->wmrb, nrbfull);
 				etheriq(edev, bp, 1);
+				passed++;
 			}
 			else if(ctlr->rb[rdh] != nil){
 				freeb(ctlr->rb[rdh]);
@@ -1196,6 +1217,8 @@ igberproc(void* arg)
 
 		if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0))
 			igbereplenish(ctlr);
+		/* note how many rds had full buffers */
+		notemark(&ctlr->wmrd, passed);
 	}
 }
 
@@ -1258,6 +1281,9 @@ igbeattach(Ether* edev)
 		bp->free = igberbfree;
 		freeb(bp);
 	}
+	initmark(&ctlr->wmrb, Nrb, "rcv bufs unprocessed");
+	initmark(&ctlr->wmrd, Nrd-1, "rcv descrs processed at once");
+	initmark(&ctlr->wmtd, Ntd-1, "xmit descr queue len");
 
 	snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
 	kproc(name, igbelproc, edev);

+ 4 - 2
sys/src/9/pc/etherm10g.c

@@ -5,6 +5,8 @@
  * the card is big endian.
  * we use uvlong rather than uintptr to hold addresses so that
  * we don't get "warning: stupid shift" on 32-bit architectures.
+ *
+ * appears to have massively-bloated buffers.
  */
 #include "u.h"
 #include "../port/lib.h"
@@ -34,9 +36,9 @@ static char	Etimeout[]	= "timeout";
 
 enum {
 	Epromsz	= 256,
-	Maxslots= 1024,
+	Maxslots= 1024,		/* rcv descriptors; wasteful: only 9 needed */
 	Align	= 4096,
-	Maxmtu	= 9000,
+	Maxmtu	= 9000,		/* jumbos; bad idea */
 	Noconf	= 0xffffffff,
 
 	Fwoffset= 1*MiB,

+ 32 - 5
sys/src/9/pc/main.c

@@ -89,6 +89,27 @@ fpsavealloc(void)
 		panic("cpu%d: can't allocate fpsavalign", m->machno);
 }
 
+static int
+isa20on(void)
+{
+	int r;
+	ulong o;
+	ulong *zp, *mb1p;
+
+	zp = (ulong *)KZERO;
+	mb1p = (ulong *)(KZERO|MB);
+	o = *zp;
+
+	*zp = 0x1234;
+	*mb1p = 0x8765;
+	coherence();
+	wbinvd();
+	r = *zp != *mb1p;
+
+	*zp = o;
+	return r;
+}
+
 void
 main(void)
 {
@@ -111,6 +132,8 @@ main(void)
 	meminit();
 	confinit();
 	archinit();
+	if(!isa20on())
+		panic("bootstrap didn't leave a20 address line enabled");
 	xinit();
 	if(i8237alloc != nil)
 		i8237alloc();
@@ -387,7 +410,8 @@ confinit(void)
 {
 	char *p;
 	int i, userpcnt;
-	ulong kpages;
+	unsigned mb;
+	ulong kpages, ksize;
 
 	if(p = getconf("*kernelpercent"))
 		userpcnt = 100 - strtol(p, 0, 0);
@@ -420,10 +444,13 @@ confinit(void)
 		 * The patch of nimage is a band-aid, scanning the whole
 		 * page list in imagereclaim just takes too long.
 		 */
-		if(kpages > (200*MB + conf.npage*sizeof(Page))/BY2PG){
-			kpages = (200*MB + conf.npage*sizeof(Page))/BY2PG;
-			conf.nimage = 2000;
-			kpages += (conf.nproc*KSTACK)/BY2PG;
+		for (mb = 400; mb >= 100; mb /= 2) {
+			ksize = mb*MB + conf.npage*sizeof(Page);
+			if(kpages > ksize/BY2PG && cankaddr(ksize)) {
+				kpages = ksize/BY2PG + (conf.nproc*KSTACK)/BY2PG;
+				conf.nimage = 2000;
+				break;
+			}
 		}
 	} else {
 		if(userpcnt < 10) {

+ 1 - 0
sys/src/9/pc/mkfile

@@ -45,6 +45,7 @@ PORT=\
 	sysproc.$O\
 	taslock.$O\
 	tod.$O\
+	watermarks.$O\
 	xalloc.$O\
 
 OBJ=\

+ 10 - 0
sys/src/9/port/portdat.h

@@ -48,6 +48,7 @@ typedef struct Uart	Uart;
 typedef struct Waitq	Waitq;
 typedef struct Walkqid	Walkqid;
 typedef struct Watchdog	Watchdog;
+typedef struct Watermark	Watermark;
 typedef int    Devgen(Chan*, char*, Dirtab*, int, int, Dir*);
 
 #pragma incomplete DevConf
@@ -993,6 +994,15 @@ struct Watchdog
 	void	(*stat)(char*, char*);	/* watchdog statistics */
 };
 
+struct Watermark
+{
+	int	highwater;
+	int	curr;
+	int	max;
+	int	hitmax;		/* count: how many times hit max? */
+	char	*name;
+};
+
 
 /* queue state bits,  Qmsg, Qcoalesce, and Qkick can be set in qopen */
 enum

+ 3 - 0
sys/src/9/port/portfns.h

@@ -134,6 +134,7 @@ long		ibrk(ulong, int);
 void		ilock(Lock*);
 void		iunlock(Lock*);
 long		incref(Ref*);
+void		initmark(Watermark *, int, char *);
 void		initseg(void);
 int		iprint(char*, ...);
 void		isdir(Chan*);
@@ -202,6 +203,7 @@ Pgrp*		newpgrp(void);
 Rgrp*		newrgrp(void);
 Proc*		newproc(void);
 void		nexterror(void);
+void		notemark(Watermark *, int);
 int		notify(Ureg*);
 int		nrand(int);
 uvlong		ns2fastticks(uvlong);
@@ -312,6 +314,7 @@ long		seconds(void);
 ulong		segattach(Proc*, ulong, char *, ulong, ulong);
 void		segclock(ulong);
 void		segpage(Segment*, Page*);
+char*		seprintmark(char *, char *, Watermark *);
 int		setcolor(ulong, ulong, ulong, ulong);
 void		setkernur(Ureg*, Proc*);
 int		setlabel(Label*);

+ 37 - 0
sys/src/9/port/watermarks.c

@@ -0,0 +1,37 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+
+void
+initmark(Watermark *wp, int max, char *name)
+{
+	memset(wp, 0, sizeof *wp);
+	wp->max = max;
+	wp->name = name;
+}
+
+void
+notemark(Watermark *wp, int val)
+{
+	/* enforce obvious limits */
+	if (val < 0)
+		val = 0;
+	else if (val > wp->max)
+		val = wp->max;
+
+	if (val > wp->highwater) {
+		wp->highwater = val;
+		if (val == wp->max && wp->curr < val)
+			wp->hitmax++;
+	}
+	wp->curr = val;
+}
+
+char *
+seprintmark(char *buf, char *ebuf, Watermark *wp)
+{
+	return seprint(buf, ebuf, "%s:\thighwater %d/%d curr %d hitmax %d\n",
+		wp->name, wp->highwater, wp->max, wp->curr, wp->hitmax);
+}