~kris/9p

9hist

17dd2cf113bef1c53c47f29940e331ed6980b631 — David du Colombier 28 years ago 6dd1aea
Plan 9 from Bell Labs 1998-03-25
M ip/ip.h => ip/ip.h +1 -1
@@ 237,7 237,7 @@ struct IProuter {
 */
struct Fs
{
	Lock;
	RWlock;

	int	np;
	Proto*	p[Maxproto+1];		/* list of supported protocols */

M ip/ipmux.c => ip/ipmux.c +92 -142
@@ 9,7 9,9 @@

#define DPRINT if(0)print

typedef struct Iphdr	Iphdr;
typedef struct Iphdr		Iphdr;
typedef struct Ipmuxrock	Ipmuxrock;
typedef struct Ipmux		Ipmux;

enum
{


@@ 28,6 30,7 @@ struct Iphdr
	uchar	cksum[2];	/* Header checksum */
	uchar	src[4];		/* IP source */
	uchar	dst[4];		/* IP destination */
	uchar	data[1];	/* start of data */
};

enum


@@ 48,8 51,9 @@ char *ftname[] =
[Tifc]		"ifc",
};

typedef struct Ipmux Ipmux;

/*
 *  a node in the decision tree
 */
struct Ipmux
{
	Ipmux	*yes;


@@ 64,6 68,15 @@ struct Ipmux
	int	ref;		/* so we can garbage collect */
};

/*
 *  someplace to hold per conversation data
 */
struct Ipmuxrock
{
	Ipmux	*chain;
	int	proto;
};

static char*
skipwhite(char *p)
{


@@ 409,7 422,7 @@ ipmuxmerge(Ipmux *a, Ipmux *b)
 *  remove a chain from a demux tree.  This is like merging accept that
 *  we remove instead of insert.
 */
int
static int
ipmuxremove(Ipmux **l, Ipmux *f)
{
	int n, rv;


@@ 459,137 472,6 @@ ipmuxremove(Ipmux **l, Ipmux *f)
	return ipmuxremove(&ft->yes, f->yes);
}

void
printtree(Biobuf *b, Ipmux *f, int level)
{
	int i, j;
	uchar *p;

	if(f == nil) {
		Bprint(b, "\n");
		return;
	}
	for(i = 0; i < level; i++)
		Bprint(b, " ");
	Bprint(b, "%s", ftname[f->type]);
	if(f->type == Tdata)
		Bprint(b, "[%d:%d]", f->off, f->off+f->len-1);
	if(f->mask){
		switch(f->type){
		case Tifc:
		case Tsrc:
		case Tdst:
			Bprint(b, " & %M", f->mask);
			break;
		case Tproto:
		case Tdata:
			Bprint(b, " & ");
			for(j = 0; j < f->len; j++)
				Bprint(b, "%2.2ux", f->mask[j]);
			break;
		}
	}
	p = f->val;
	Bprint(b, " =");
	for(i = 0; i < f->n; i++){
		switch(f->type){
		case Tifc:
		case Tsrc:
		case Tdst:
			Bprint(b, " %I", p);
			break;
		case Tproto:
		case Tdata:
			Bprint(b, " ");
			for(j = 0; j < f->len; j++)
				Bprint(b, "%2.2ux", p[j]);
			break;
		}
		p += f->len;
	}
	Bprint(b, "\n");
	printtree(b, f->yes, level+1);
	printtree(b, f->no, level+1);
}

void
main(void)
{
	Ipmux *f, *f1, *f2, *f1copy, *f2copy;
	Ipmux *root;
	Biobuf out;

	Binit(&out, 1, OWRITE);
	fmtinstall('I', eipconv);
	fmtinstall('M', eipconv);

	Bprint(&out, "demux 1:\n");
	f1 = parsedemux("data[0:3] = 12345678 23456789 3456789a");
	f = parsedemux("ifc = 135.104.9.2");
	ipmuxchain(&f1, f);
	f = parsedemux("src & 255.255.255.0 = 135.104.9.2");
	ipmuxchain(&f1, f);
	f = parsedemux("proto = 17");
	ipmuxchain(&f1, f);
	printtree(&out, f1, 0);

	f1copy = ipmuxcopy(f1);

	Bprint(&out, "demux 2:\n");
	f2 = parsedemux("data[0:3] = 12345678 23456789 3456789a");
	f = parsedemux("ifc = 135.104.9.1");
	ipmuxchain(&f2, f);
	f = parsedemux("src & 255.255.255.0 = 135.104.9.3");
	ipmuxchain(&f2, f);
	printtree(&out, f2, 0);

	f2copy = ipmuxcopy(f2);

	Bprint(&out, "merged demux:\n");
	root = ipmuxmerge(f1, f2);
	printtree(&out, root, 0);
	
	Bprint(&out, "demux 1 removed:\n");
	ipmuxremove(&root, f1copy);
	printtree(&out, root, 0);
	
	Bprint(&out, "demux 2 removed:\n");
	ipmuxremove(&root, f2copy);
	printtree(&out, root, 0);
}

enum
{
	Tproto,
	Tdata,
	Tdst,
	Tsrc,
	Tifc,
};

char *ftname[] = 
{
[Tdata]	"data",
[Tdst]	"dst",
[Tsrc]	"src",
[Tifc]	"ifc",
};

struct Ipmux
{
	Ipmux	*yes;
	Ipmux	*no;
	uchar	type;
	ushort	len;		/* length in bytes of item to compare */
	ushort	off;		/* offset of comparison */
	int	n;		/* number of items val points to */
	uchar	*val;
	uchar	*mask;

	Conv	*c;
	int	ref;		/* so we can garbage collect */
};

/*
 *  connection request is a semi separated list of filters
 *  e.g. proto=17;dat[0:4]=11aa22bb;ifc=135.104.9.2


@@ 602,6 484,7 @@ ipmuxconnect(Conv *c, char **argv, int argc)
	int n, proto;
	char *field[10];
	Ipmux *mux, *chain;
	Ipmuxrock *r;
	Fs *f;

	f = c->p->f;


@@ 636,12 519,14 @@ ipmuxconnect(Conv *c, char **argv, int argc)
	/* save a copy of the chain so we can later remove it */
	mux->conv = c;
	mux = ipmuxcopy(chain);
	*(Ipmux***)(c->ptcl) = chain;
	r = (Ipmuxrock*)(c->ptcl);
	r->chain = chain;
	r->proto = proto;

	/* add the chain to the protocol demultiplexor tree */
	qlock(p);
	wlock(f);
	f->t2m[proto] = ipmuxmerge(f->t2m[proto], mux);
	qunlock(p);
	wunlock(f);

	Fsconnected(c, nil);
	return nil;


@@ 671,6 556,12 @@ ipmuxannounce(Conv*, char**, int)
static void
ipmuxclose(Conv *c)
{
	Ipmuxrock *r;

	r = (Ipmuxrock*)(c->ptcl);
	r->chain = chain;
	r->proto = proto;

	qclose(c->rq);
	qclose(c->wq);
	qclose(c->eq);


@@ 679,6 570,11 @@ ipmuxclose(Conv *c)
	c->lport = 0;
	c->rport = 0;

	wlock(f);
	ipmuxremove(&(f->t2m[r->proto]), r->chain);
	wunlock(f);
	ipmuxtreefree(f->chain);

	unlock(c);
}



@@ 692,12 588,66 @@ ipmuxkick(Conv *c, int l)
}

static void
ipmuxiput(Proto *ipmux, uchar*, Block *bp)
ipmuxiput(Proto *p, uchar *ia, Block *bp)
{
	int len;
	Iphdr *ip;
	Fs *f = p->f;
	uchar *p;
	Conv *c;

	ip = bp->rptr;
	rlock(f);
	mux = f->t2m[ip->proto];
	if(mux == nil)
		goto out;

	/* run the v4 filter */
	len = BLEN(bp);
	if(len < 64 && bp->next){
		bp = concatblock(bp);
		len = BLEN(bp);
	}
	c = nil;
	while(mux != nil){
		while(mux){
			switch(mux->type){
			case Tia:
				p = ia;
				break;
			case Tsrc:
				p = ip->src;
				break;
			case Tdst:
				p = ip->dst;
				break;
			case Tdata:
				p = ip->data;
				if(mux->off+mux->len > len)
					goto no;
				break;
			}
		}
		if(mux->mask != nil){
		} else {
		}
no:
		mux = mux->no;
		continue;
	}
out:
	/* doesn't match any filter, hand it to the specific protocol handler */
	runlock(f);
	p = f->t2p[ip->proto];
	if(p)
		(*p->rcv)(p, ia, bp);
	else
		freeblist(bp);
	return;	
}

int
ipmuxstats(Proto *ipmux, char *buf, int len)
ipmuxstats(Proto *p, char *buf, int len)
{
	return 0;
}


@@ 708,7 658,7 @@ ipmuxinit(Fs *fs)
	Proto *ipmux;

	ipmux = smalloc(sizeof(Proto));
	ipmux->priv = smalloc(sizeof(GREpriv));
	ipmux->priv = nil;
	ipmux->name = "ipmux";
	ipmux->kick = ipmuxkick;
	ipmux->connect = ipmuxconnect;


@@ 722,7 672,7 @@ ipmuxinit(Fs *fs)
	ipmux->stats = ipmuxstats;
	ipmux->ipproto = -1;
	ipmux->nc = 64;
	ipmux->ptclsize = sizeof(Ipmux*);
	ipmux->ptclsize = sizeof(Ipmuxrock);

	Fsproto(fs, ipmux);
}

M pc/etherelnk3.c => pc/etherelnk3.c +154 -16
@@ 1,12 1,13 @@
/*
 * Etherlink III and Fast EtherLink adapters.
 * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters.
 * To do:
 *	check robustness in the face of errors (e.g. busmaster & rxUnderrun);
 *	RxEarly and busmaster;
 *	autoSelect;
 *	PCI latency timer and master enable;
 *	errata list;
 *	rewrite all initialisation.
 *	rewrite all initialisation;
 *	handle the cyclone adapter.
 *
 * Product ID:
 *	9150 ISA	3C509[B]


@@ 48,6 49,8 @@

#include "etherif.h"

#define XCVRDEBUG		if(1)print

enum {
	IDport			= 0x0110,	/* anywhere between 0x0100 and 0x01F0 */
};


@@ 240,6 243,9 @@ enum {						/* Window 3 - FIFO management */
	deferTimerSelect	= 0x001E,	/* mask */
	fullDuplexEnable	= 0x0020,
	allowLargePackets	= 0x0040,
	extendAfterCollision	= 0x0080,	/* 3C90xB */
	flowControlEnable	= 0x0100,	/* 3C90xB */
	vltEnable		= 0x0200,	/* 3C90xB */
						/* ResetOptions bits */
	baseT4Available		= 0x0001,
	baseTXAvailable		= 0x0002,


@@ 264,6 270,11 @@ enum {						/* Window 4 - diagnostic */
	txOverrun		= 0x0400,
	rxUnderrun		= 0x2000,
	receiving		= 0x8000,
						/* PhysicalMgmt bits */
	mgmtClk			= 0x0001,
	mgmtData		= 0x0002,
	mgmtDir			= 0x0004,
	cat5LinkTestDefeat	= 0x8000,
						/* MediaStatus bits */
	dataRate100		= 0x0002,
	crcStripDisable		= 0x0004,


@@ 413,10 424,12 @@ typedef struct {
	long	stats[BytesRcvdOk+3];

	int	upqmax;
	int	upqmaxhw;
	long	upinterrupts;
	long	upqueued;
	int	upstalls;
	int	dnqmax;
	int	dnqmaxhw;
	long	dninterrupts;
	long	dnqueued;



@@ 1124,13 1137,18 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
	len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n", ctlr->stats[BytesRcvdOk+1]);

	if(ctlr->upenabled){
		len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d s %lud\n",
			ctlr->upqueued, ctlr->upinterrupts, ctlr->upqmax, ctlr->upstalls);
		if(ctlr->upqmax > ctlr->upqmaxhw)
			ctlr->upqmaxhw = ctlr->upqmax;
		len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n",
			ctlr->upqueued, ctlr->upinterrupts,
			ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls);
		ctlr->upqmax = 0;
	}
	if(ctlr->dnenabled){
		len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d\n",
			ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax);
		if(ctlr->dnqmax > ctlr->dnqmaxhw)
			ctlr->dnqmaxhw = ctlr->dnqmax;
		len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n",
			ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw);
		ctlr->dnqmax = 0;
	}



@@ 1399,6 1417,117 @@ setxcvr(int port, int xcvr, int is9)
		;
}

static void
setfullduplex(int port)
{
	int x;

	COMMAND(port, SelectRegisterWindow, Wfifo);
	x = ins(port+MacControl);
	outs(port+MacControl, fullDuplexEnable|x);

	COMMAND(port, TxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
	COMMAND(port, RxReset, 0);
	while(STATUS(port) & commandInProgress)
		;
}

static int
miir(Ether* ether, int phyad, int regad)
{
	int data, i, port, w, x;

	port = ether->port+PhysicalMgmt;

	w = (STATUS(port)>>13) & 0x07;
	COMMAND(ether->port, SelectRegisterWindow, Wdiagnostic);

	/*
	 * Taken from the Cyclone manual appendix describing
	 * how to programme the MII Management Interface.
	 */
	/*
	 * Preamble
	 */
	for(i = 0; i < 32; i++){
		outs(port, mgmtDir|mgmtData);
		microdelay(1);
		outs(port, mgmtDir|mgmtData|mgmtClk);
		microdelay(1);
	}

	/*
	 * ST+OP+PHYAD+REGAD
	 */
	x = 0x1800|(phyad<<5)|regad;
	for(i = 14-1; i >= 0; i--){
		if(x & (1<<i))
			data = mgmtDir|mgmtData;
		else
			data = mgmtDir;
		outs(port, data);
		microdelay(1);
		outs(port, data|mgmtClk);
		microdelay(1);
	}

	/*
	 * "Z" cycle (turnaround) + one read (0 means there's a PHY responding).
	 */
	data = 0;
	outs(port, 0);
	microdelay(1);
	outs(port, mgmtClk);
	x = ins(port);
	if(x & mgmtData)
		data |= (1<<16);

	/*
	 * 16 data read cycles.
	 */
	for(i = 16-1; i >= 0; i--){
		outs(port, 0);
		microdelay(1);
		outs(port, mgmtClk);
		microdelay(1);
		x = ins(port);
		if(x & mgmtData)
			data |= (1<<i);
		microdelay(1);
	}

	/*
	 * "Z" cycle (turnaround).
	 */
	outs(port, 0);
	microdelay(1);
	outs(port, mgmtClk);

	COMMAND(ether->port, SelectRegisterWindow, w);

	if(data & 0x10000)
		return -1;
print("%d/%d: data=%uX\n", phyad, regad, data);

	return data & 0xFFFF;
}

static void
scanphy(Ether* ether)
{
	int i, x;

	for(i = 0; i < 32; i++){
		if((x = miir(ether, i, 2)) == -1)
			continue;
		x <<= 6;
		x |= miir(ether, i, 3)>>10;
		print("phy%d: oui %uX reg1 %uX\n", i, x, miir(ether, i, 1));
	}
}

#ifdef notdef
static struct xxx {
	int	available;


@@ 1416,10 1545,12 @@ static struct xxx {
#endif /* notdef */

static int
autoselect(int port, int , int is9)
autoselect(int port, int xcvr, int is9)
{
	int media, x;

	USED(xcvr);

	/*
	 * Pathetic attempt at automatic media selection.
	 * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX


@@ 1441,13 1572,13 @@ autoselect(int port, int , int is9)
		COMMAND(port, SelectRegisterWindow, Wfifo);
		media = ins(port+ResetOptions);
	}
//print("autoselect: media %uX\n", media);
	XCVRDEBUG("autoselect: media %uX\n", media);

	if(media & miiConnector)
		return xcvrMii;

//COMMAND(port, SelectRegisterWindow, Wdiagnostic);
//print("autoselect: media status %uX\n", ins(port+MediaStatus));
	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
	XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));

	if(media & baseTXAvailable){
		/*


@@ 1469,7 1600,7 @@ autoselect(int port, int , int is9)
	}
	delay(1);
  }
//print("count %d v %uX\n", i, ins(port+MediaStatus));
  XCVRDEBUG("count %d v %uX\n", i, ins(port+MediaStatus));
}

		if(ins(port+MediaStatus) & linkBeatDetect)


@@ 1485,6 1616,7 @@ autoselect(int port, int , int is9)
		outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
		delay(100);

		XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));
		if(ins(port+MediaStatus) & linkBeatDetect)
			return xcvr10BaseT;
		outs(port+MediaStatus, x);


@@ 1511,7 1643,7 @@ eepromdata(int port, int offset)
int
etherelnk3reset(Ether* ether)
{
	int busmaster, did, i, port, rxearly, rxstatus9, x, xcvr;
	int an, busmaster, did, i, phyaddr, port, rxearly, rxstatus9, x, xcvr;
	Block *bp, **bpp;
	Adapter *ap;
	uchar ea[Eaddrlen];


@@ 1617,18 1749,24 @@ etherelnk3reset(Ether* ether)
	 * busmastering can be used. Due to bugs in the first revision
	 * of the 3C59[05], don't use busmastering at 10Mbps.
	 */
//print("reset: xcvr %uX\n", xcvr);
	XCVRDEBUG("reset: xcvr %uX\n", xcvr);
	if(xcvr & autoSelect)
		xcvr = autoselect(port, xcvr, rxstatus9);
	XCVRDEBUG("autoselect returns: xcvr %uX\n", xcvr);
	switch(xcvr){

	case xcvrMii:
		/*
		 * Bug? the 3c905 always seems to have dataRate100 set.
		 * Quick hack.
		 */
		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
		if(ins(port+MediaStatus) & dataRate100)
		phyaddr = 24;
		an = miir(ether, phyaddr, 0x04);
		an &= miir(ether, phyaddr, 0x05) & 0x03E0;
		XCVRDEBUG("mii an: %uX\n", an);
		if(an & 0x380)
			ether->mbps = 100;
		if(an & 0x0140)
			setfullduplex(port);
		break;

	case xcvr100BaseTX:

M pc/segment.h => pc/segment.h +1 -0
@@ 8,5 8,6 @@ Physseg physseg[] =
	{ SG_SHARED,	"shared",	0,	SEGMAXSIZE,	0, 	0 },
	{ SG_PHYSICAL,	"dseg",		0xd0000, 64*1024,	0,	0 },
	{ SG_BSS,	"memory",	0,	SEGMAXSIZE,	0,	0 },
	{ SG_PHYSICAL,	"pcivctl",	0,	16*1024,	0, 0 },
	{ 0,		0,		0,	0,		0,	0 },
};

A pc/vgamga2164w.c => pc/vgamga2164w.c +88 -0
@@ 0,0 1,88 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#define	Image	IMAGE
#include <draw.h>
#include <memdraw.h>
#include "screen.h"

static void
mga2164wenable(VGAscr*)
{
	Pcidev *p;
	Physseg *s;
/*
	ulong mmio;
	uchar *rp;
	int i;
*/

	if((p = pcimatch(nil, 0x102B, 0x051B)) == nil)
		return;

	for(s = physseg; s->name; s++)
		if(strcmp("pcivctl", s->name) == 0)
			s->pa = p->mem[1].bar & ~0x0F;

/*
	mmio = p->mem[1].bar & ~0x0F;
	mmio = upamalloc(mmio, 16*1024, 0);
	if(mmio == 0){
		print("mmio == 0\n");
		return;
	}

	rp = (uchar*)(mmio+0x3C00);
	for(i = 0; i < 16; i++){
		print("%2.2uX ", *rp);
		rp++;
	}
	print("\n");
*/
}

static ulong
mga2164wlinear(VGAscr* scr, int* size, int* align)
{
	ulong aperture, oaperture;
	int oapsize, wasupamem;
	Pcidev *p;

	oaperture = scr->aperture;
	oapsize = scr->apsize;
	wasupamem = scr->isupamem;
	if(wasupamem)
		upafree(oaperture, oapsize);
	scr->isupamem = 0;

	if(p = pcimatch(nil, 0x102B, 0x051B)){
		aperture = p->mem[0].bar & ~0x0F;
		*size = p->mem[0].size;
	}
	else
		aperture = 0;

	aperture = upamalloc(aperture, *size, *align);
	if(aperture == 0){
		if(wasupamem && upamalloc(oaperture, oapsize, 0))
			scr->isupamem = 1;
	}
	else
		scr->isupamem = 1;

	return aperture;
}

VGAdev vgamga2164wdev = {
	"mga2164w",

	mga2164wenable,			/* enable */
	0,				/* disable */
	0,				/* page */
	mga2164wlinear,			/* linear */
};

M port/devsd.c => port/devsd.c +5 -6
@@ 60,7 60,7 @@ int	ndisk;
Disk	disk[Ndisk];

static	int	sdrdpart(Disk*);
static	long	sdio(Chan*, int, char*, ulong, ulong);
static	long	sdio(Chan*, int, char*, ulong, vlong);

static int types[] =
{


@@ 231,9 231,8 @@ sdclose(Chan *c)
}

static long
sdread(Chan *c, void *a, long n, vlong off)
sdread(Chan *c, void *a, long n, vlong offset)
{
	ulong offset = off;

	if(c->qid.path & CHDIR)
		return devdirread(c, a, n, 0, 0, sdgen);


@@ 242,10 241,9 @@ sdread(Chan *c, void *a, long n, vlong off)
}

static long
sdwrite(Chan *c, char *a, long n, vlong off)
sdwrite(Chan *c, char *a, long n, vlong offset)
{
	Disk *d;
	ulong offset = off;

	d = &disk[DRIVE(c->qid)];



@@ 350,12 348,13 @@ sdrdpart(Disk *d)
}

static long
sdio(Chan *c, int write, char *a, ulong len, ulong offset)
sdio(Chan *c, int write, char *a, ulong len, vlong off)
{
	Disk *d;
	Part *p;
	uchar *b;
	ulong block, n, max, x;
	ulong offset = off;

	d = &disk[DRIVE(c->qid)];
	p = &d->table[PART(c->qid)];

M port/netif.c => port/netif.c +19 -0
@@ 473,6 473,16 @@ matchtoken(char *p, char *token)
}

void
hnputv(void *p, vlong v)
{
	uchar *a;

	a = p;
	hnputl(a, v>>32);
	hnputl(a+8, v);
}

void
hnputl(void *p, ulong v)
{
	uchar *a;


@@ 494,6 504,15 @@ hnputs(void *p, ushort v)
	a[1] = v;
}

vlong
nhgetv(void *p)
{
	uchar *a;

	a = p;
	return ((vlong)nhgetl(a) << 32) | nhgetl(a+8);
}

ulong
nhgetl(void *p)
{

M port/portdat.h => port/portdat.h +6 -4
@@ 63,10 63,11 @@ struct QLock

struct RWlock
{
	Lock;				/* Lock modify lock */
	QLock	x;			/* Mutual exclusion lock */
	QLock	k;			/* Lock for waiting writers */
	int	readers;		/* Count of readers in lock */
	Lock	use;
	Proc	*head;		/* list of waiting processes */
	Proc	*tail;
	int	readers;	/* number of readers */
	int	writer;		/* number of writers */
};

struct Talarm


@@ 495,6 496,7 @@ enum
	Scheding,
	Running,
	Queueing,
	QueueingW,
	Wakeme,
	Broken,
	Stopped,

M port/portfns.h => port/portfns.h +2 -0
@@ 311,7 311,9 @@ Segment*	data2txt(Segment*);
Segment*	dupseg(Segment**, int, int);
Segment*	newseg(int, ulong, ulong);
Segment*	seg(Proc*, ulong, int);
void		hnputv(void*, vlong);
void		hnputl(void*, ulong);
void		hnputs(void*, ushort);
vlong		nhgetv(void*);
ulong		nhgetl(void*);
ushort		nhgets(void*);

M port/proc.c => port/proc.c +1 -0
@@ 41,6 41,7 @@ char *statename[] =
	"Scheding",
	"Running",
	"Queueing",
	"QueueingW",
	"Wakeme",
	"Broken",
	"Stopped",

M port/qlock.c => port/qlock.c +114 -18
@@ 4,17 4,28 @@
#include "dat.h"
#include "fns.h"

struct {
	ulong rlock;
	ulong rlockq;
	ulong wlock;
	ulong wlockq;
	ulong qlock;
	ulong qlockq;
} rwstats;

void
qlock(QLock *q)
{
	Proc *p, *mp;

	lock(&q->use);
rwstats.qlock++;
	if(!q->locked) {
		q->locked = 1;
		unlock(&q->use);
		return;
	}
rwstats.qlockq++;
	p = q->tail;
	mp = up;
	if(p == 0)


@@ 62,35 73,120 @@ qunlock(QLock *q)
}

void
rlock(RWlock *l)
rlock(RWlock *q)
{
	qlock(&l->x);		/* wait here for writers and exclusion */
	lock(l);
	l->readers++;
	canqlock(&l->k);	/* block writers if we are the first reader */
	unlock(l);
	qunlock(&l->x);
	Proc *p, *mp;

	lock(&q->use);
rwstats.rlock++;
	if(q->writer == 0 && q->head == nil){
		/* no writer, go for it */
		q->readers++;
		unlock(&q->use);
		return;
	}

rwstats.rlockq++;
	p = q->tail;
	mp = up;
	if(p == 0)
		q->head = mp;
	else
		p->qnext = mp;
	q->tail = mp;
	mp->qnext = 0;
	mp->state = Queueing;
	unlock(&q->use);
	sched();
}

void
runlock(RWlock *l)
runlock(RWlock *q)
{
	lock(l);
	if(--l->readers == 0)	/* last reader out allows writers */
		qunlock(&l->k);
	unlock(l);
	Proc *p;

	lock(&q->use);
	p = q->head;
	if(--(q->readers) > 0 || p == nil){
		unlock(&q->use);
		return;
	}

	/* start waiting writer */
	if(p->state != QueueingW)
		panic("runlock");
	q->head = p->qnext;
	if(q->head == 0)
		q->tail = 0;
	q->writer = 1;
	unlock(&q->use);
	ready(p);
}

void
wlock(RWlock *l)
wlock(RWlock *q)
{
	qlock(&l->x);		/* wait here for writers and exclusion */
	qlock(&l->k);		/* wait here for last reader */
	Proc *p, *mp;

	lock(&q->use);
rwstats.wlock++;
	if(q->readers == 0 && q->writer == 0){
		/* noone waiting, go for it */
		q->writer = 1;
		unlock(&q->use);
		return;
	}

	/* wait */
rwstats.wlockq++;
	p = q->tail;
	mp = up;
	if(p == nil)
		q->head = mp;
	else
		p->qnext = mp;
	q->tail = mp;
	mp->qnext = 0;
	mp->state = QueueingW;
	unlock(&q->use);
	sched();
}

void
wunlock(RWlock *l)
wunlock(RWlock *q)
{
	qunlock(&l->k);
	qunlock(&l->x);
	Proc *p;

	lock(&q->use);
	p = q->head;
	if(p == nil){
		q->writer = 0;
		unlock(&q->use);
		return;
	}

	if(p->state == QueueingW){
		/* start waiting writer */
		q->head = p->qnext;
		if(q->head == nil)
			q->tail = nil;
		unlock(&q->use);
		ready(p);
		return;
	}

	if(p->state != Queueing)
		panic("wunlock");

	/* waken waiting readers */
	while(q->head != nil && q->head->state == Queueing){
		p = q->head;
		q->head = p->qnext;
		q->readers++;
		ready(p);
	}
	if(q->head == nil)
		q->tail = nil;
	q->writer = 0;
	unlock(&q->use);
}