shithub: getest

Download patch

ref: f242ed181ff7118392737a3493e840aed9de4be1
author: Ori Bernstein <ori@eigenstate.org>
date: Mon Jun 3 11:20:23 EDT 2024

initial commit: a collection of oddball scripts

--- /dev/null
+++ b/basic.rc
@@ -1,0 +1,7 @@
+#!/bin/rc -e
+
+. common.rc
+
+setup
+echo hi > $fs/test
+assert ~ `{cat $fs/test} hi
--- /dev/null
+++ b/build.rc
@@ -1,0 +1,21 @@
+#!/bin/rc -e
+
+. common.rc
+
+setup
+
+cd $fs
+
+# we don't want to clobber the installed
+# libs with something corrupt, so copy
+# them in and bind them over.
+mkdir -p $cputype/lib
+dircp /$cputype/lib $cputype/lib
+bind $cputype/lib /$cputype/lib
+
+# clone our repo
+git/clone /dist/plan9front
+bind plan9front/sys/include /sys/include
+
+cd plan9front/sys/src
+mk all
--- /dev/null
+++ b/common.rc
@@ -1,0 +1,28 @@
+#!/bin/rc
+
+rfork ne
+
+srv=gefstest.$pid
+fs=/n/$srv
+
+fn setup{
+	if(! test -f test.fs){
+		dd -if /dev/zero -of test.fs -bs 1kk -count 2k
+		chmod +t test.fs
+	}
+	../6.out -r -f test.fs
+	../6.out -m 32 -Au glenda -f test.fs -n $srv
+	mount -c /srv/$srv $fs
+}
+
+fn assert {
+	st=$status
+	if(! ~ $#st 0){
+		echo $st >[1=2]
+		exit $st
+	}
+}
+
+fn sigexit{
+	unmount $fs
+}
--- /dev/null
+++ b/freplay.c
@@ -1,0 +1,202 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+File*	ctlfile;
+File*	datfile;
+char*	mountpt	= "/mnt/replay";
+char*	srvname = "replay";
+char*	logfile;
+char*	replayfile;
+char*	membuf;
+vlong	membufsz;
+vlong	replaycount	= -1;
+int	logfd		= -1;
+int	replayfd	= -1;
+vlong	nwrites;
+
+void
+log1(int fd, void *buf, vlong off, vlong sz)
+{
+	char *p, hdr[12];
+
+	p = hdr;
+	PBIT64(p, off);	p += 8;
+	PBIT32(p, sz);
+	if(write(fd, hdr, sizeof(hdr)) == -1)
+		sysfatal("write header: %r");
+	if(write(fd, buf, sz) == -1)
+		sysfatal("write data: %r\n");
+}
+
+int
+replay1(int fd)
+{
+	uchar *p, hdr[12];
+	vlong o;
+	int n, r;
+
+	r = readn(fd, hdr, 12);
+	if(r == 0)
+		return 0;
+	if(r != 12)
+		sysfatal("failed to read operation header: %r");
+
+	p = hdr;
+	o = GBIT64(p);	p += 8;
+	n = GBIT32(p);
+	if(o + n > membufsz)
+		sysfatal("operation exceeds buffer size");
+	if(readn(fd, membuf + o, n) != n)
+		sysfatal("read op: %r");
+	nwrites++;
+	return 1;
+}
+
+void
+readmembuf(Req *r, void *s, vlong n)
+{
+	r->ofcall.count = r->ifcall.count;
+	if(r->ifcall.offset >= n){
+		r->ofcall.count = 0;
+		return;
+	}
+	if(r->ifcall.offset+r->ofcall.count > n)
+		r->ofcall.count = n - r->ifcall.offset;
+	memmove(r->ofcall.data, (char*)s+r->ifcall.offset, r->ofcall.count);
+}
+
+void
+fsread(Req *r)
+{
+	char buf[128];
+
+	if(r->fid->file == datfile){
+		readmembuf(r, membuf, membufsz);
+		respond(r, nil);
+	}else if(r->fid->file == ctlfile){
+		snprint(buf, sizeof(buf), "writes %lld\n", nwrites);
+		readstr(r, buf);
+		respond(r, nil);
+	}else
+		abort();
+}
+
+void
+fswrite(Req *r)
+{
+	if(r->fid->file == datfile){
+		if(logfile == nil){
+			respond(r, "read-only replay file: no log defined");
+			return;
+		}
+		if(r->ifcall.offset + r->ifcall.count > membufsz){
+			respond(r, "operation exceeds file size");
+			return;
+		}
+		log1(logfd, r->ifcall.data, r->ifcall.offset, r->ifcall.count);
+		memcpy(membuf + r->ifcall.offset, r->ifcall.data, r->ifcall.count);
+		r->ofcall.count = r->ifcall.count;
+		respond(r, nil);
+		nwrites++;
+	}else if(r->fid->file == ctlfile){
+		if(strncmp(r->ifcall.data, "exit", 4) == 0){
+			print("exiting...\n");
+			r->ofcall.count = r->ifcall.count;
+			respond(r, nil);
+			exits(nil);
+		}else if(strncmp(r->ifcall.data, "step", 4) == 0){
+			r->ofcall.count = r->ifcall.count;
+			if(replayfd == -1)
+				respond(r, "no active replay");
+			else if(!replay1(replayfd))
+				respond(r, "no replay left");
+			else
+				respond(r, nil);
+		}else
+			respond(r, "unknown ctl message");
+	}else
+		abort();
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-l log] [-r replay] [-c count] file\n", argv0);
+	exits("usage");
+}	
+
+static Srv fs = {
+	.read = fsread,
+	.write = fswrite,
+};
+void
+main(int argc, char *argv[])
+{
+	int fd;
+	vlong n, off;
+	char *uid;
+	Dir *d;
+	int i;
+	
+	ARGBEGIN{
+	case 'd':
+		chatty9p++;
+		break;
+	case 'l':
+		logfile = EARGF(usage());
+		break;
+	case 'r':
+		replayfile = EARGF(usage());
+		break;
+	case 'c':
+		replaycount = atoi(EARGF(usage()));
+		break;
+	case 'm':
+		mountpt = EARGF(usage());
+		break;
+	case 's':
+		srvname = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND;
+	
+	if(argc != 1)
+		usage();
+	
+	if((fd = open(argv[0], OREAD)) == -1)
+		sysfatal("open %s: %r", argv[0]);
+	if((d = dirfstat(fd)) == nil)
+		sysfatal("failed to stat file: %r");
+	if((membuf = sbrk(d->length)) == nil)
+		sysfatal("failed to allocate buffer: %r");
+	memset(membuf, 0, d->length);
+	for(off = 0; off < d->length; off += n)
+		if((n = read(fd, membuf+off, IOUNIT)) <= 0)
+			sysfatal("read %s@%lld: short read: %r", argv[0], off);
+	membufsz = d->length;
+	free(d);
+	if(replayfile != nil){
+		if((replayfd = open(replayfile, OREAD)) == -1)
+			sysfatal("failed to open replay file: %r");
+		for(i = 0; i < replaycount || replaycount == -1; i++)
+			if(replay1(replayfd) == 0)
+				break;
+		print("replayed %d ops\n", i);
+	}
+
+	if(logfile != nil){
+		if((logfd = create(logfile, OWRITE, 0666)) == -1)
+			sysfatal("failed to open log file: %r");
+	}
+	uid = getuser();
+	fs.tree = alloctree(uid, uid, DMDIR|0555, nil);
+	ctlfile = createfile(fs.tree->root, "ctl", uid, 0666, nil);
+	datfile = createfile(fs.tree->root, "data", uid, 0666, nil);
+	datfile->length = membufsz;
+	postmountsrv(&fs, srvname, mountpt, MREPL);
+	exits(nil);
+}
--- /dev/null
+++ b/fsbench.c
@@ -1,0 +1,518 @@
+#include <u.h>
+#include <libc.h>
+#include <libsec.h>
+#include <thread.h>
+
+int mainstacksize = 2*1024*1024;
+typedef struct Bench Bench;
+enum {
+	KiB	= 1024ULL,
+	MiB	= 1024ULL*KiB,
+	GiB	= 1024ULL*MiB,
+	Bufsz	= IOUNIT,
+};
+
+enum {
+	Bps,
+	Fps,
+};
+	
+struct Bench {
+	char	*name;
+	char	*unit;
+	vlong	(*fn)(Bench*);
+	vlong	reps;
+	int	nproc;
+	int	id;
+	Channel	*rc;
+
+	vlong	i0;
+	vlong	i1;
+	vlong	i2;
+	char	*s0;
+	char	*s1;
+};
+
+#define	GBIT64(p)	((u32int)(((uchar*)(p))[0]|(((uchar*)(p))[1]<<8)|\
+				(((uchar*)(p))[2]<<16)|(((uchar*)(p))[3]<<24)) |\
+			((uvlong)(((uchar*)(p))[4]|(((uchar*)(p))[5]<<8)|\
+				(((uchar*)(p))[6]<<16)|(((uchar*)(p))[7]<<24)) << 32))
+vlong
+vrand(vlong n)
+{
+	uchar buf[8];
+	vlong slop, v;
+
+	slop = 0x7fffffffffffffffULL % n;
+	do{
+		prng(buf, 8);
+		v = GBIT64(buf);
+	}while(v <= slop);
+	return v % n;
+}
+
+vlong
+wrfile_la(Bench *b)
+{
+	char buf[Bufsz];
+	vlong i;
+	int fd;
+
+	if((fd = create(b->s0, OWRITE, 0666)) == -1)
+		sysfatal("open: %r");
+	for(i = 0; i < b->i0; i += Bufsz)
+		if(write(fd, buf, Bufsz) != Bufsz)
+			sysfatal("write: %r");
+	close(fd);
+	return b->i0/MiB;
+}
+
+vlong
+wrfile_ra(Bench *b)
+{
+	char buf[Bufsz];
+	vlong i, n, j, t, *off;
+	int fd;
+
+	n = b->i0/Bufsz;
+	if((fd = create(b->s0, OWRITE, 0666)) == -1)
+		sysfatal("open: %r");
+	if((off = malloc(n*sizeof(vlong))) == nil)
+		sysfatal("malloc: %r");
+	for(i = 0; i < n; i++)
+		off[i] = i*Bufsz;
+	for (i = n - 1; i > 0; i--) {
+		j = vrand(i+1);
+		t = off[i];
+		off[i] = off[j];
+		off[j] = t;
+	}
+	for(i = 0; i < n; i++)
+		if(pwrite(fd, buf, Bufsz, off[i]) != Bufsz)
+			sysfatal("write: %r");
+	close(fd);
+	free(off);
+	return b->i0/MiB;
+}
+
+vlong
+wrfile_rr(Bench *b)
+{
+	char buf[Bufsz];
+	vlong i, n, j, t, *off;
+	int fd;
+
+	n = b->i0/Bufsz;
+	if((fd = create(b->s0, OWRITE, 0666)) == -1)
+		sysfatal("open: %r");
+	if((off = malloc(n*sizeof(vlong))) == nil)
+		sysfatal("malloc: %r");
+	for(i = 0; i < n; i++)
+		off[i] = i*Bufsz;
+	for (i = n - 1; i > 0; i--) {
+		j = vrand(i+1);
+		t = off[i];
+		off[i] = off[j];
+		off[j] = t;
+	}
+	for(i = 0; i < n; i++)
+		if(pwrite(fd, buf, Bufsz, off[i] + vrand(100)) != Bufsz)
+			sysfatal("write: %r");
+	close(fd);
+	free(off);
+	return b->i0/MiB;
+}
+
+vlong
+rdfile_la(Bench *b)
+{
+	char path[128], buf[Bufsz];
+	vlong i, rep;
+	int fd;
+
+	if(b->id == -1)
+		snprint(path, sizeof(path), "%s", b->s0);
+	else if(b->i1 != 0)
+		snprint(path, sizeof(path), "%s.%lld", b->s0, b->id % b->i1);
+	else
+		snprint(path, sizeof(path), "%s.%d", b->s0, b->id);
+	if((fd = open(path, OREAD)) == -1)
+		sysfatal("open: %r");
+	for(rep = 0; rep < b->reps; rep++){
+		seek(fd, 0, 0);
+		for(i = 0; i < b->i0; i += Bufsz)
+			if(read(fd, buf, Bufsz) != Bufsz)
+				sysfatal("write: %r");
+	}
+	close(fd);
+	return b->reps*(b->i0/MiB);
+}
+vlong
+rdfile_ra(Bench *b)
+{
+	char path[128], buf[Bufsz];
+	vlong i, rep;
+	uvlong off;
+	int fd;
+
+	if(b->id == -1)
+		snprint(path, sizeof(path), "%s", b->s0);
+	else if(b->i1 != 0)
+		snprint(path, sizeof(path), "%s.%lld", b->s0, b->id % b->i1);
+	else
+		snprint(path, sizeof(path), "%s.%d", b->s0, b->id);
+	if((fd = open(path, OREAD)) == -1)
+		sysfatal("open: %r");
+	for(rep = 0; rep < b->reps; rep++){
+		seek(fd, 0, 0);
+		for(i = 0; i < b->i0; i += Bufsz){
+			off = vrand(b->i0-Bufsz) & ~((vlong)Bufsz-1);
+			if(pread(fd, buf, Bufsz, off) != Bufsz)
+				sysfatal("write: %r");
+		}
+	}
+	close(fd);
+	return b->reps*(b->i0/MiB);
+}
+
+vlong
+rwfile_lala(Bench *b)
+{
+	char buf[64];
+	Bench bb;
+
+	bb = *b;
+	if(b->id >= b->i1)
+		return rdfile_la(&bb);
+	else{
+		snprint(buf, sizeof(buf), "%s%d.w%d", b->s0, getpid(), b->id);
+		bb.s0 = buf;
+		return wrfile_la(&bb);
+	}
+}
+
+vlong
+rdfile_rr(Bench *b)
+{
+	char path[128], buf[Bufsz];
+	vlong i, rep;
+	uvlong off;
+	int fd;
+
+	if(b->id == -1)
+		snprint(path, sizeof(path), "%s", b->s0);
+	else if(b->i1 != 0)
+		snprint(path, sizeof(path), "%s.%lld", b->s0, b->id % b->i1);
+	else
+		snprint(path, sizeof(path), "%s.%d", b->s0, b->id);
+	if((fd = open(path, OREAD)) == -1)
+		sysfatal("open: %r");
+	for(rep = 0; rep < b->reps; rep++){
+		for(i = 0; i < b->i0; i += Bufsz){
+			off = vrand(b->i0-Bufsz);
+			if(pread(fd, buf, Bufsz, off) != Bufsz)
+				sysfatal("read: %r");
+		}
+	}
+	close(fd);
+	return b->reps*(b->i0/MiB);
+}
+
+vlong
+createflat(Bench *b)
+{
+	char buf[Bufsz];
+	int i, fd;
+
+	for(i = 0; i < b->i0; i++){
+		snprint(buf, sizeof(buf), "%s%d", b->s0, i);
+		if((fd = create(buf, OWRITE, 0666)) == -1)
+			sysfatal("create: %r");
+		if(b->i1 != 0)
+			write(fd, buf, b->i1);
+		close(fd);
+	}
+	return b->i0;
+}
+
+int
+createlevel(int n, int d)
+{
+	char buf[Bufsz];
+	int i, s, fd;
+
+	s = 0;
+	for(i = 0; i < n; i++){
+		snprint(buf, sizeof(buf), "%d", i);
+		if(d > 0){
+			if((fd = create(buf, OWRITE, 0777|DMDIR)) == -1)
+				sysfatal("create: %r");
+			if(chdir(buf) == -1)
+				sysfatal("chdir %s: %r", buf);
+			s += createlevel(n, d-1);
+			chdir("..");
+		}else{
+			if((fd = create(buf, OWRITE, 0666)) == -1)
+				sysfatal("create: %r");
+			s++;
+		}
+		close(fd);
+	}
+	return s;
+}
+
+vlong
+createhier(Bench *b)
+{
+	return createlevel(b->i0, b->i1);
+}
+
+vlong
+listfiles(Bench *b)
+{
+	char buf[Bufsz];
+	int i, r, fd;
+
+	for(i = 0; i < b->reps; i++){
+		if((fd = open(".", OREAD)) == -1)
+			sysfatal("open .: %r");
+		while(1){
+			if((r = read(fd, buf, sizeof(buf))) == -1)
+				sysfatal("read: %r");
+			if(r == 0)
+				break;
+		}
+		close(fd);
+	}
+	return b->reps*b->i0;
+}
+
+vlong
+randopen(Bench *b)
+{
+	char buf[Bufsz];
+	int i, fd;
+
+	for(i = 0; i < b->reps; i++){
+		snprint(buf, sizeof(buf), "%s%lld", b->s0, vrand(b->i0));
+		if((fd = open(buf, OREAD)) == -1)
+			sysfatal("open: %r");
+		if(b->i0)
+			if(read(fd, buf, sizeof(buf)) == -1)
+				sysfatal("read: %r");
+		close(fd);	
+	}
+	return b->reps;
+}
+
+void
+launch(void *p)
+{
+	Bench *b;
+	vlong r;
+
+	b = p;
+	r = b->fn(b);
+	send(b->rc, &r);
+}
+
+vlong
+runpar(Bench *b)
+{
+	vlong r, sum;
+	Bench *sub;
+	int i;
+	
+	sum = 0;
+	b->rc = chancreate(sizeof(vlong), b->nproc);
+	if((sub = calloc(b->nproc, sizeof(Bench))) == nil)
+		sysfatal("malloc: %r");
+	for(i = 0; i < b->nproc; i++){
+		sub[i] = *b;
+		sub[i].id = i;
+		proccreate(launch, &sub[i], mainstacksize);
+	}
+	for(i = 0; i < b->nproc; i++){
+		recv(b->rc, &r);
+		sum += r;
+	}
+	free(sub);
+	return sum;
+}
+
+void
+runbench(Bench *b, int nb)
+{
+	char *unit[] = {"ns", "us", "ms", "s"};
+	double oc, dt;
+	vlong t0, t1;
+	int i, j;
+
+	for(i = 0; i < nb; i++){
+		if(b[i].reps == 0)
+			b[i].reps = 1;
+		print("%20s:\t", b[i].name);
+		t0 = nsec();
+		if(b[i].nproc <= 1){
+			b[i].id = -1;
+			oc = b[i].fn(&b[i]);
+		}else
+			oc = runpar(&b[i]);
+		t1 = nsec();
+		dt = (t1 - t0);
+		for(j = 0; j < nelem(unit)-1; j++)
+			if(dt/100 < 1)
+				break;
+			else
+				dt /= 1000.0;
+		print("%f%s (%f %s/%s)\n", dt, unit[j], (double)oc/dt, b[i].unit, unit[j]);
+	}
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	Bench marks[] = {
+		/* l => linear, a => aligned, r => random */
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.0"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.1"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.2"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.3"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.4"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.5"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.6"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.7"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.8"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.9"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.10"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.11"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.12"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.13"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.14"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.15"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.16"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.17"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.18"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.19"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.20"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.21"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.22"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.23"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.24"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.25"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.26"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.27"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.28"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.29"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.30"},
+		{.name="wrcached_la",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="cached0.31"},
+
+		{.name="rdcached_lala",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_la, .s0="cached0"},
+		{.name="rdcached_lara",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_ra, .s0="cached0"},
+		{.name="rdcached_larr",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_rr, .s0="cached0"},
+
+		{.name="rdcached_lala_2p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_la, .s0="cached0", .nproc=2},
+		{.name="rdcached_lara_2p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_ra, .s0="cached0", .nproc=2},
+		{.name="rdcached_larr_2p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_rr, .s0="cached0", .nproc=2},
+
+		{.name="rdcached_lala_4p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_la, .s0="cached0", .nproc=4},
+		{.name="rdcached_lara_4p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_ra, .s0="cached0", .nproc=4},
+		{.name="rdcached_larr_4p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_rr, .s0="cached0", .nproc=4},
+
+		{.name="rdcached_lala_8p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_la, .s0="cached0", .nproc=8},
+		{.name="rdcached_lara_8p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_ra, .s0="cached0", .nproc=8},
+		{.name="rdcached_larr_8p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_rr, .s0="cached0", .nproc=8},
+
+		{.name="rdcached_lala_12p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_la, .s0="cached0", .nproc=12},
+		{.name="rdcached_lara_12p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_ra, .s0="cached0", .nproc=12},
+		{.name="rdcached_larr_12p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_rr, .s0="cached0", .nproc=12},
+
+		{.name="rdcached_lala_16p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_la, .s0="cached0", .nproc=16},
+		{.name="rdcached_lara_16p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_ra, .s0="cached0", .nproc=16},
+		{.name="rdcached_larr_16p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_rr, .s0="cached0", .nproc=16},
+
+		{.name="rdcached_lala_16p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_la, .s0="cached0", .nproc=16},
+		{.name="rdcached_lara_16p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_ra, .s0="cached0", .nproc=16},
+		{.name="rdcached_larr_16p",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_rr, .s0="cached0", .nproc=16},
+
+		{.name="rdcached_lala_16p_1f",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_la, .s0="cached0", .nproc=16, .i1=1},
+		{.name="rdcached_lara_16p_8f",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_ra, .s0="cached0", .nproc=16, .i1=8},
+		{.name="rdcached_larr_32p_16f",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_rr, .s0="cached0", .nproc=32, .i1=16},
+		{.name="rdcached_larr_32p_32f",	.i0=128*MiB,	.reps=2, .unit="MiB", .fn=rdfile_rr, .s0="cached0", .nproc=32, .i1=32},
+
+		{.name="wrcached_ra",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_ra, .s0="cached1"},
+		{.name="rdcached_rala",	.i0=128*MiB,	.reps=10, .unit="MiB", .fn=rdfile_la, .s0="cached1"},
+		{.name="rdcached_rara",	.i0=128*MiB,	.reps=10, .unit="MiB", .fn=rdfile_ra, .s0="cached1"},
+		{.name="rdcached_rarr",	.i0=128*MiB,	.reps=10, .unit="MiB", .fn=rdfile_rr, .s0="cached1"},
+
+		{.name="wrcached_rr",	.i0=128*MiB,	.reps=1, .unit="MiB", .fn=wrfile_rr, .s0="cached2"},
+		{.name="rdcached_rrla",	.i0=128*MiB,	.reps=10, .unit="MiB", .fn=rdfile_la, .s0="cached2"},
+		{.name="rdcached_rrra",	.i0=128*MiB,	.reps=10, .unit="MiB", .fn=rdfile_ra, .s0="cached2"},
+		{.name="rdcached_rrrr",	.i0=128*MiB,	.reps=10, .unit="MiB", .fn=rdfile_rr, .s0="cached2"},
+
+		{.name="rwcached_la_r0_w2_w",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=2, .i1=2},
+		{.name="rwcached_la_r0_w4_w",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=4, .i1=4},
+		{.name="rwcached_la_r1_w1_w",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=2, .i1=1},
+		{.name="rwcached_la_r3_w1_w",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=4, .i1=1},
+		{.name="rwcached_la_r2_w2_w",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=4, .i1=2},
+		{.name="rwcached_la_r6_w2_w",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=8, .i1=2},
+		{.name="rwcached_la_r4_w4_w",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=8, .i1=4},
+
+		{.name="rwcached_la_r1_w1_r",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=2, .i1=1},
+		{.name="rwcached_la_r2_w1_r",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=3, .i1=1},
+		{.name="rwcached_la_r3_w1_r",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=4, .i1=1},
+		{.name="rwcached_la_r1_w2_r",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=4, .i1=2},
+		{.name="rwcached_la_r1_w2_r",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=3, .i1=2},
+		{.name="rwcached_la_r2_w2_r",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=4, .i1=2},
+		{.name="rwcached_la_r4_w2_r",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=4, .i1=2},
+		{.name="rwcached_la_r6_w2_r",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=8, .i1=2},
+		{.name="rwcached_la_r4_w4_r",	.i0=64*MiB,	.reps=10, .unit="MiB", .fn=rwfile_lala, .s0="cached0", .nproc=8, .i1=4},
+
+		{.name="createflat",	.i0=100*1000,	.reps=1, 	.unit="files", .fn=createflat, .s0="cz"},
+		{.name="write1flat",	.i0=100*1000,	.reps=1, 	.unit="files", .fn=createflat, .i1=1, .s0="c1"},
+		{.name="write100flat",	.i0=100*1000,	.reps=1, 	.unit="files", .fn=createflat, .i1=100, .s0="c100"},
+		{.name="write1027flat",	.i0=100*1000,	.reps=1, 	.unit="files", .fn=createflat, .i1=1027, .s0="c1027"},
+		{.name="listfflat",	.i0=100*1000,	.reps=10,	.unit="files", .fn=listfiles},
+		{.name="openfflat",	.i0=100*1000,	.reps=100*1000, .unit="files", .fn=randopen, .s0="cz"},
+		{.name="read0flat",	.i0=100*1000,	.reps=100*1000, .unit="files", .fn=randopen, .i1=1, .s0="cz"},
+		{.name="read1flat",	.i0=100*1000,	.reps=100*1000, .unit="files", .fn=randopen, .i1=1, .s0="c1"},
+		{.name="read100flat",	.i0=100*1000,	.reps=100*1000, .unit="files", .fn=randopen, .i1=1, .s0="c100"},
+		{.name="read1027flat",	.i0=100*1000,	.reps=100*1000, .unit="files", .fn=randopen, .i1=1, .s0="c1027"},
+
+//		{.name="rwcached_lara",	.i0=512*MiB,	.reps=10, .unit="MiB", .fn=rwfile_la, .s0="cached0", .i0=1, .i1=3},
+//		{.name="rwcached_la",	.i0=512*MiB,	.reps=10, .unit="MiB", .fn=rwfile_la, .s0="cached0", .i0=3, .i1=3},
+//		{.name="rwcached_la",	.i0=512*MiB,	.reps=10, .unit="MiB", .fn=rwfile_la, .s0="cached0", .i0=10, .i1=10},
+
+
+		{.name="wrlarge_la",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=wrfile_la, .s0="large0"},
+		{.name="rdlarge_lala",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=rdfile_la, .s0="large0"},
+		{.name="rdlarge_lara",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=rdfile_ra, .s0="large0"},
+//		{.name="rdlarge_larr",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=rdfile_rr, .s0="large0"},
+
+		{.name="wrlarge_ra",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=wrfile_ra, .s0="large1"},
+		{.name="rdlarge_lara",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=rdfile_la, .s0="large1"},
+		{.name="rdlarge_rara",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=rdfile_ra, .s0="large1"},
+//		{.name="rdlarge_rarr",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=rdfile_rr, .s0="large1"},
+
+//		{.name="wrlarge_rr",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=wrfile_rr, .s0="large2"},
+//		{.name="rdlarge_larr",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=rdfile_la, .s0="large2"},
+//		{.name="rdlarge_rarr",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=rdfile_ra, .s0="large2"},
+//		{.name="rdlarge_rrrr",	.i0=16*GiB,	.reps=1, .unit="MiB", .fn=rdfile_rr, .s0="large2"},
+
+//		{.name="createheir",	.i0=3, .i1=10,	.reps=1, .unit="files", .fn=createhier},
+//		{.name="openheir",	.i0=3, .i1=10,	.reps=1, .unit="files", .fn=randwalk},
+	};
+
+	ARGBEGIN{
+	}ARGEND;
+	
+	if(argc != 1){
+		fprint(2, "usage: %s wdir\n", argv0);
+		exits("usage");
+	}
+	if(chdir(argv[0]) == -1)
+		sysfatal("chdir: %r");
+	runbench(marks, nelem(marks));
+	exits(nil);
+}
--- /dev/null
+++ b/grind.rc
@@ -1,0 +1,69 @@
+#!/bin/rc
+
+dev = testdev.fs
+gefs = ../$O.out
+
+fn ream {
+	mk all && 6.out -r $user -f $dev
+}
+
+fn startfs {
+	$O.out -A -f $dev
+}
+
+fn endfs {
+	kill $O.out | rc
+	while(test -f /n/gefs.grid)
+		sleep 0.1
+}
+
+fn build9 {@{
+	rfork ne
+	m
+	cd /n/gefs
+	. /sys/lib/rootstub
+	if(! test -e plan9front)
+		git/clone /dist/plan9front
+	bind -c $objtype/lib /$objtype/lib
+	bind -c plan9front/sys/include /sys/include
+	bind -c tmp /tmp
+	cd plan9front/sys/src
+	mk clean >> /tmp/log
+	mk all >> /tmp/log
+}}
+
+fn buildself {@{
+	rfork ne
+	m
+	cd /n/gefs
+	if(! test -e gefs)
+		git/clone $home/src/gefs
+	cd gefs
+	mk clean > /tmp/log
+	mk all > /tmp/log
+}}
+
+fn replay_fast {
+	ream
+	mount -c 
+
+	build9
+	endfs
+}
+
+fn replay_slow {
+}
+
+fn filldisk {
+}
+
+fn lotsa9 {
+}
+
+fn lotsaself {
+}
+
+dev = testdev.fs
+rm -f $dev
+dd -if /dev/zero -of $dev -bs 1kk -count 512
+replay_fast
\ No newline at end of file
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,15 @@
+</$objtype/mkfile
+
+TESTS=\
+	basic\
+	build\
+
+all:V: 6.freplay 6.fsbench
+
+test:VQ:
+	@{cd .. && mk 6.out}
+	for(t in $TESTS){
+		echo $t...
+		./$t.rc >[2=1] >$t.log
+	}
+</sys/src/cmd/mktest
--- /dev/null
+++ b/mkgefs.rc
@@ -1,0 +1,8 @@
+#!/bin/rc
+
+@{cd .. && mk all}
+../6.out -r $user -f $1
+../6.out -n gefs.test -A -m 2048 -f $1
+
+mount -c /srv/gefs.test /n/gefs
+mount -c /srv/gefs.test /n/gefs.adm adm
--- /dev/null
+++ b/psnap.rc
@@ -1,0 +1,45 @@
+#!/bin/rc -e
+
+
+srv=/srv/gefs
+if(~ $#* 1)
+	srv=$1
+
+fn cloneto {
+	echo clone to $1
+	@{cd /n/gefs.$1 && git/clone /dist/plan9front}
+	echo clone $1 done
+}
+
+fn buildin {
+	@{
+	rfork n
+	cd /n/gefs.$1/plan9front/
+	. /sys/lib/rootstub
+	bind -c sys/include /sys/include
+	bind -c $objtype/lib /$objtype/lib
+	bind -bc $objtype/bin /bin
+	bind -bc $objtype/bin /$objtype/bin
+	cd sys/src
+	mk all >[2=1] >/tmp/ptest.$1.log
+	}
+}
+
+echo 'create snap'
+echo 'snap -m empty test1' >> $srv.cmd
+echo 'snap -m empty test2' >> $srv.cmd
+echo 'sync' >> $srv.cmd
+mount -c $srv /n/gefs.a	test1
+mount -c $srv /n/gefs.b	test2
+
+echo 'clone repos'
+cloneto a &
+cloneto b &
+wait
+
+echo 'build repos'
+buildin a &
+buildin b &
+wait
+
+echo 'done'
--- /dev/null
+++ b/run.rc
@@ -1,0 +1,187 @@
+#!/bin/rc
+
+rfork ne
+
+dev=$testdev
+if(~ $#testdev 0)
+	dev = testdev.fs
+switch($cputype){
+case amd64;	O=6
+case arm64;	O=7
+case arm;	O=5
+case 386;	O=8
+}
+
+fn sigexit sigint {
+	rm -f /srv/gefs.test /srv/gefs.test.cmd /srv/replay
+}
+
+fn die {
+	echo $* >[1=2]
+	exit $"*
+}
+
+fn log {
+	echo $* >[1=2]
+}
+
+fn ge_ream {
+	$O.out -m 512 -r $user -f $1
+}
+
+fn ge_start {
+	$O.out -m 512 -A -f $1 -n gefs.test
+	while(! test -e /srv/gefs.test)
+		sleep 0.1
+	mount -c /srv/gefs.test /n/gefs
+}
+
+fn ge_kill {
+	kill $O.out | rc
+	while(test -e /srv/gefs.test)
+		sleep 0.1
+}
+
+fn ge_replay {@{
+	# prepare the test run
+	log reaming...
+	ge_ream $dev
+	log preparing replay...
+	rm -f replay.log
+	test/6.freplay -l replay.log $dev
+	ge_start /mnt/replay/data
+	$*
+	echo save trace /tmp/trace >> /srv/gefs.test.cmd
+	sleep 5
+	ge_kill
+	cat /mnt/replay/ctl
+	count=`{awk '/writes/{print $2}' /mnt/replay/ctl}
+	log did $count writes.
+	echo exit > /mnt/replay/ctl 
+	while(test -e /srv/replay)
+		sleep 0.1
+
+	# check blockwise consistency
+	log starting replay...
+	test/6.freplay -c 1 -r replay.log $dev
+	for(i in `{seq 2 $count}){
+		$O.out -c -f /mnt/replay/data >[2]/tmp/log || die 'broken'
+		log stepping $i...
+		echo step > /mnt/replay/ctl
+	}
+	echo exit > /mnt/replay/ctl 
+	while(test -e /srv/replay)
+		sleep 0.1
+	exit ''
+}}
+
+fn ge_ok {@{
+	# prepare the test run
+	log reaming...
+	ge_ream $dev
+	log preparing build-and-verify...
+	test/6.freplay -l replay.log $dev
+	ge_start $dev
+	$*
+	echo save trace /tmp/trace >> /srv/gefs.test.cmd
+	ge_kill
+	$O.out -c -f $dev
+}}
+
+fn buildsys{@{
+	rfork ne
+	cd /n/gefs
+	. /sys/lib/rootstub
+	if(! test -e plan9front)
+		git/clone /dist/plan9front
+	bind -c $objtype/lib /$objtype/lib
+	bind -c plan9front/sys/include /sys/include
+	bind -c tmp /tmp
+	cd plan9front/sys/src
+	mk clean >> /tmp/gefsbuild.log
+	mk all >> /tmp/gefsbuild.log
+	echo save trace >> /srv/gefs.test.cmd
+	echo check >> /srv/gefs.test.cmd
+}}
+
+fn frobsnap {@{
+	rfork ne
+	sleep 1; echo snap main x >> /srv/gefs.test.cmd
+	sleep 1; dd -if /dev/zero -of /n/gefs/file0 -bs 16k -count 128
+	sleep 1; echo snap main y >> /srv/gefs.test.cmd;
+	sleep 1; dd -if /dev/zero -of /n/gefs/file1 -bs 16k -count 128
+	sleep 1; echo snap main z >> /srv/gefs.test.cmd;
+	sleep 1; rm /n/gefs/^(file0 file1);
+	sleep 1; echo snap -d y >> /srv/gefs.test.cmd;
+	sleep 1; echo check >> /srv/gefs.test.cmd
+	sleep 1; echo snap -d z >> /srv/gefs.test.cmd;
+	sleep 1; echo check >> /srv/gefs.test.cmd
+	sleep 1; echo snap -d x >> /srv/gefs.test.cmd;
+	sleep 1; echo check >> /srv/gefs.test.cmd
+}}
+
+fn buildgo {@{
+	rfork ne
+	GOROOT=/n/gefs/go
+	GOROOT_BOOTSTRAP=/n/gefs/go-plan9-amd64-bootstrap
+	go=go1.17.13-plan9-$cputype-bootstrap
+
+	cd /n/gefs
+	if(! test -e /tmp/$go.tbz){
+		echo getting https://9legacy.org/download/go/$go.tbz ...
+		hget -o /tmp/$go.tbz https://9legacy.org/download/go/$go.tbz
+	}
+	if(! test -e $GOROOT_BOOTSTRAP){
+		echo extracting /tmp/$go.tbz ...
+ 		bunzip2 -c /tmp/$go.tbz | tar x >[2]/dev/null
+	}
+	mkdir go
+	mkdir tmpw
+	dircp go-plan9-amd64-bootstrap go
+	bind -c tmp /tmp
+	cd go/src
+	./all.rc
+}}
+
+fn buildgefs {@{
+	cd /n/gefs
+	if(! test -e gefs)
+		git/clone $home/src/gefs
+	cd gefs
+	mk clean > /tmp/gefsbuild.log
+	mk all > /tmp/gefsbuild.log
+	echo check >> /srv/gefs.test.cmd
+}}
+
+fn buildgefs_slowrep {
+	for(i in `{seq $1}){
+		echo @@ buildgefs $i
+		buildgefs
+		sleep 10
+	}
+}
+
+fn fillfs {
+	dd -if /dev/zero -of /n/gefs/stuff
+}
+
+fn dotest {
+	rm -f /tmp/gefs.log /tmp/gefsbuild.log
+	echo $*
+	$* || die $status
+}
+
+if(! test -e $dev)
+	dd -if /dev/zero -of $dev -bs 1kk -count 2k 
+rm -f /srv/gefs.test /srv/gefs.test.cmd /srv/replay
+mk all
+@{cd test && mk all}
+
+dotest ge_replay frobsnap
+dotest ge_replay buildgefs_slowrep 50
+dotest ge_ok buildsys
+dotest ge_ok fillfs
+
+# disabled by default: go doesn't fit into a
+# small fs; make a bigger one for testing.
+#	dotest ge_ok buildgo