~kris/9p

9hist

da7ee71a72ab667cd86cde902a7d72b0650aa9af — David du Colombier 24 years ago 17e3dae
Plan 9 from Bell Labs 2002-08-21
M alphapc/dat.h => alphapc/dat.h +2 -0
@@ 147,6 147,8 @@ struct Mach
	ulong	pcclast;
	uvlong	fastclock;
	vlong	intrts;			/* time stamp of last interrupt */
	ulong	inidle;			/* fastticks in idlehands() since last slowtick */
	ulong	avginidle;		/* avg fastticks in idlehands() per slowtick */

	int	tlbfault;		/* only used by devproc; no access to tlb */
	int	tlbpurge;		/* ... */

M bitsy/dat.h => bitsy/dat.h +4 -1
@@ 134,9 134,12 @@ struct Mach
	int	intr;
	vlong	fastclock;		/* last sampled value */
	vlong	intrts;			/* time stamp of last interrupt */
	uvlong	inidle;			/* time spent in idlehands() */
	ulong	spuriousintr;
	int	lastintr;
	int		ilockdepth;
	int	ilockdepth;
	ulong	inidle;			/* fastticks in idlehands() since last slowtick */
	ulong	avginidle;		/* avg fastticks in idlehands() per slowtick */

	int	flushmmu;		/* make current proc flush it's mmu state */
	Proc	*pid2proc[31];		/* what proc holds what pid */

M ip/tcp.c => ip/tcp.c +7 -4
@@ 354,7 354,7 @@ struct Tcppriv
 */
int tcpporthogdefense = 0;

int	addreseq(Tcpctl*, Tcp*, Block*, ushort);
int	addreseq(Tcpctl*, Tcppriv*, Tcp*, Block*, ushort);
void	getreseq(Tcpctl*, Tcp*, Block**, ushort*);
void	localclose(Conv*, char*);
void	procsyn(Conv*, Tcp*);


@@ 2064,8 2064,7 @@ reset:
	if(seg.seq != tcb->rcv.nxt)
	if(length != 0 || (seg.flags & (SYN|FIN))) {
		update(s, &seg);
		tpriv->stats[OutOfOrder]++;
		if(addreseq(tcb, &seg, bp, length) < 0)
		if(addreseq(tcb, tpriv, &seg, bp, length) < 0)
			print("reseq %I.%d -> %I.%d\n", s->raddr, s->rport, s->laddr, s->lport);
		tcb->flags |= FORCE;
		goto output;


@@ 2711,7 2710,7 @@ procsyn(Conv *s, Tcp *seg)
}

int
addreseq(Tcpctl *tcb, Tcp *seg, Block *bp, ushort length)
addreseq(Tcpctl *tcb, Tcppriv *tpriv, Tcp *seg, Block *bp, ushort length)
{
	Reseq *rp, *rp1;
	int i;


@@ 2732,6 2731,8 @@ addreseq(Tcpctl *tcb, Tcp *seg, Block *bp, ushort length)
	if(rp1 == nil || seq_lt(seg->seq, rp1->seg.seq)) {
		rp->next = rp1;
		tcb->reseq = rp;
		if(rp->next != nil)
			tpriv->stats[OutOfOrder]++;
		return 0;
	}



@@ 2741,6 2742,8 @@ addreseq(Tcpctl *tcb, Tcp *seg, Block *bp, ushort length)
		if(rp1->next == nil || seq_lt(seg->seq, rp1->next->seg.seq)) {
			rp->next = rp1->next;
			rp1->next = rp;
			if(rp->next != nil)
				tpriv->stats[OutOfOrder]++;
			break;
		}
		rp1 = rp1->next;

M mtx/dat.h => mtx/dat.h +2 -0
@@ 142,6 142,8 @@ struct Mach
	ulong	pcclast;
	uvlong	fastclock;
	vlong	intrts;			/* time stamp of last interrupt */
	ulong	inidle;			/* fastticks in idlehands() since last slowtick */
	ulong	avginidle;		/* avg fastticks in idlehands() per slowtick */

	int	tlbfault;		/* only used by devproc; no access to tlb */
	int	tlbpurge;		/* ... */

M pc/dat.h => pc/dat.h +2 -0
@@ 178,6 178,8 @@ struct Mach
	uvlong	intrts;			/* time stamp of last interrupt */
	int	flushmmu;		/* make current proc flush it's mmu state */
	int	ilockdepth;
	ulong	inidle;			/* fastticks in idlehands() since last slowtick */
	ulong	avginidle;		/* avg fastticks in idlehands() per slowtick */

	ulong	spuriousintr;
	int	lastintr;

M pc/devtv.c => pc/devtv.c +0 -1
@@ 1517,7 1517,6 @@ astart(Tv *tv, char *input, uint rate, uint nab, uint nasz)
			break;

	print("astart: sampleshift %d, decimation %d\n", s, d);
	s = d = 0;

	tv->narblocks = 0;
	bt878->gpiodmactl = gpiodmactl_fifoenable | 

M pc/ether82543gc.c => pc/ether82543gc.c +565 -252
@@ 6,14 6,9 @@
 * The datasheet is not very clear about running on a big-endian system
 * and this driver assumes little-endian throughout.
 * To do:
 *	auto-negotiation and flow control setup and enable
 *	GMII/MII
 *	receive tuning
 *	check recovery from receive no buffers condition
 *	transmit tuning
 *	automatic ett adjustment
 *	chip-specific offload...
 *	check PCI MWI and CLS initialisation
 */
#include "u.h"
#include "../port/lib.h"


@@ 41,12 36,19 @@ enum {
	Imc		= 0x000000D8,	/* Interrupt mask Clear */
	Rctl		= 0x00000100,	/* Receive Control */
	Fcttv		= 0x00000170,	/* Flow Control Transmit Timer Value */
	Txcw		= 0x00000178,	/* Transmit configuration word reg. */
	Rxcw		= 0x00000180,	/* Receive configuration word reg. */
	Tctl		= 0x00000400,	/* Transmit Control */
	Tipg		= 0x00000410,	/* Transmit IPG */
	Tbt		= 0x00000448,	/* Transmit Burst Timer */
	Ait		= 0x00000458,	/* Adaptive IFS Throttle */
	Fcrtl		= 0x00002160,	/* Flow Control RX Threshold Low */
	Fcrth		= 0x00002168,	/* Flow Control Rx Threshold High */
	Rdfh		= 0x00002410,	/* Receive data fifo head */
	Rdft		= 0x00002418,	/* Receive data fifo tail */
	Rdfhs		= 0x00002420,	/* Receive data fifo head saved */
	Rdfts		= 0x00002428,	/* Receive data fifo tail saved */
	Rdfpc		= 0x00002430,	/* Receive data fifo packet count */
	Rdbal		= 0x00002800,	/* Rdesc Base Address Low */
	Rdbah		= 0x00002804,	/* Rdesc Base Address High */
	Rdlen		= 0x00002808,	/* Receive Descriptor Length */


@@ 56,6 58,11 @@ enum {
	Rxdctl		= 0x00002828,	/* Receive Descriptor Control */
	Txdmac		= 0x00003000,	/* Transfer DMA Control */
	Ett		= 0x00003008,	/* Early Transmit Control */
	Tdfh		= 0x00003410,	/* Transmit data fifo head */
	Tdft		= 0x00003418,	/* Transmit data fifo tail */
	Tdfhs		= 0x00003420,	/* Transmit data Fifo Head saved */
	Tdfts		= 0x00003428,	/* Transmit data fifo tail saved */
	Tdfpc		= 0x00003430,	/* Trasnmit data Fifo packet count */
	Tdbal		= 0x00003800,	/* Tdesc Base Address Low */
	Tdbah		= 0x00003804,	/* Tdesc Base Address High */
	Tdlen		= 0x00003808,	/* Transmit Descriptor Length */


@@ 87,7 94,15 @@ enum {					/* Ctrl */
	Frcspd		= 0x00000800,	/* Force Speed */
	Frcdplx		= 0x00001000,	/* Force Duplex */
	Swdpinslo	= 0x003C0000,	/* Software Defined Pins - lo nibble */
	Swdpiolo	= 0x03C00000,	/* Software Defined Pins - I or O */
	Swdpin0		= 0x00040000,
	Swdpin1		= 0x00080000,
	Swdpin2		= 0x00100000,
	Swdpin3		= 0x00200000,
	Swdpiolo	= 0x03C00000,	/* Software Defined I/O Pins */
	Swdpio0		= 0x00400000,
	Swdpio1		= 0x00800000,
	Swdpio2		= 0x01000000,
	Swdpio3		= 0x02000000,
	Devrst		= 0x04000000,	/* Device Reset */
	Rfce		= 0x08000000,	/* Receive Flow Control Enable */
	Tfce		= 0x10000000,	/* Transmit Flow Control Enable */


@@ 116,7 131,7 @@ enum {					/* Ctrl and Status */
	Asdv100		= 0x00000100,	/* 100Mb/s */
	Asdv1000	= 0x00000200,	/* 1000Mb/s */
};
	

enum {					/* Eecd */
	Sk		= 0x00000001,	/* Clock input to the EEPROM */
	Cs		= 0x00000002,	/* Chip Select */


@@ 134,6 149,18 @@ enum {					/* Ctrlext */
	Spdbyps		= 0x00008000,	/* Speed Select Bypass */
};

enum {					/* EEPROM content offsets */
	Ea		= 0x00,		/* Ethernet Address */
	Cf		= 0x03,		/* Compatibility Field */
	Pba		= 0x08,		/* Printed Board Assembly number */
	Icw1		= 0x0A,		/* Initialization Control Word 1 */
	Sid		= 0x0B,		/* Subsystem ID */
	Svid		= 0x0C,		/* Subsystem Vendor ID */
	Did		= 0x0D,		/* Device ID */
	Vid		= 0x0E,		/* Vendor ID */
	Icw2		= 0x0F,		/* Initialization Control Word 2 */
};

enum {					/* Mdic */
	MDIdMASK	= 0x0000FFFF,	/* Data */
	MDIdSHIFT	= 0,


@@ 164,6 191,25 @@ enum {					/* Icr, Ics, Ims, Imc */
	Gpi3		= 0x00004000,
};

enum {					/* Txcw */
	Ane		= 0x80000000,	/* Autonegotiate enable */
	Np		= 0x00008000,	/* Next Page */
	As		= 0x00000100,	/* Asymmetric Flow control desired */
	Ps		= 0x00000080,	/* Pause supported */
	Hd		= 0x00000040,	/* Half duplex supported */
	TxcwFd		= 0x00000020,	/* Full Duplex supported */
};

enum {					/* Rxcw */
	Rxword		= 0x0000FFFF,	/* Data from auto-negotiation process */
	Rxnocarrier	= 0x04000000,	/* Carrier Sense indication */
	Rxinvalid	= 0x08000000,	/* Invalid Symbol during configuration */
	Rxchange	= 0x10000000,	/* Change to the Rxword indication */
	Rxconfig	= 0x20000000,	/* /C/ order set reception indication */
	Rxsync		= 0x40000000,	/* Lost bit synchronization indication */
	Anc		= 0x80000000,	/* Auto Negotiation Complete */
};

enum {					/* Rctl */
	Rrst		= 0x00000001,	/* Receiver Software Reset */
	Ren		= 0x00000002,	/* Receiver Enable */


@@ 183,10 229,11 @@ enum {					/* Rctl */
	MoMASK		= 0x00003000,	/* Multicast Offset */
	Bam		= 0x00008000,	/* Broadcast Accept Mode */
	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */
	Bsize2048	= 0x00000000,
	Bsize1024	= 0x00010000,
	Bsize512	= 0x00020000,
	Bsize256	= 0x00030000,
	Bsize2048	= 0x00000000,	/* Bsex = 0 */
	Bsize1024	= 0x00010000,	/* Bsex = 0 */
	Bsize512	= 0x00020000,	/* Bsex = 0 */
	Bsize256	= 0x00030000,	/* Bsex = 0 */
	Bsize16384	= 0x00010000,	/* Bsex = 1 */
	Vfe		= 0x00040000,	/* VLAN Filter Enable */
	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
	Cfi		= 0x00100000,	/* Canonical Form Indicator value */


@@ 215,9 262,10 @@ enum {					/* [RT]xdctl */
	PthreshSHIFT	= 0,
	HthreshMASK	= 0x00003F00,	/* Host Threshold */
	HthreshSHIFT	= 8,
	WthreshMASK	= 0x003F0000,	/* Writebacj Threshold */
	WthreshMASK	= 0x003F0000,	/* Writeback Threshold */
	WthreshSHIFT	= 16,
	Gran		= 0x01000000,	/* Granularity */
	Gran		= 0x00000000,	/* Granularity */
	RxGran		= 0x01000000,	/* Granularity */
};

enum {					/* Rxcsum */


@@ 227,6 275,10 @@ enum {					/* Rxcsum */
	Tuofl		= 0x00000200,	/* TCP/UDP Checksum Off-load Enable */
};

enum {					/* Receive Delay Timer Ring */
	Fpd		= 0x80000000,	/* Flush partial Descriptor Block */
};

typedef struct Rdesc {			/* Receive Descriptor */
	uint	addr[2];
	ushort	length;


@@ 286,8 338,15 @@ enum {					/* Tdesc status */
};

enum {
	Nrdesc		= 128,		/* multiple of 8 */
	Ntdesc		= 128,		/* multiple of 8 */
	Nrdesc		= 256,		/* multiple of 8 */
	Ntdesc		= 256,		/* multiple of 8 */
	Nblocks		= 4098,		/* total number of blocks to use */

	SBLOCKSIZE	= 2048,
	JBLOCKSIZE	= 16384,

	NORMAL		= 1,
	JUMBO		= 2,
};

typedef struct Ctlr Ctlr;


@@ 296,6 355,7 @@ typedef struct Ctlr {
	Pcidev*	pcidev;
	Ctlr*	next;
	int	active;
	int	started;
	int	id;
	ushort	eeprom[0x40];



@@ 305,47 365,69 @@ typedef struct Ctlr {
	Lock	slock;
	uint	statistics[Nstatistics];

	Lock	rdlock;
	Rdesc*	rdba;			/* receive descriptor base address */
	Block**	rb;			/* receive buffers */
	Block*	rb[Nrdesc];		/* receive buffers */
	int	rdh;			/* receive descriptor head */
	int	rdt;			/* receive descriptor tail */
	Block**	freehead;		/* points to long or short head */

	Tdesc*	tdba;			/* transmit descriptor base address */
	Lock	tdlock;
	Block**	tb;			/* transmit buffers */
	Tdesc*	tdba;			/* transmit descriptor base address */
	Block*	tb[Ntdesc];		/* transmit buffers */
	int	tdh;			/* transmit descriptor head */
	int	tdt;			/* transmit descriptor tail */
	int	ett;			/* early transmit threshold */
	Rendez	timer;			/* watchdog timer */
	int	txstalled;		/* count of times unable to send */

	int	txcw;
	int	fcrtl;
	int	fcrth;

	ulong	multimask[128];		/* bit mask for multicast addresses */
} Ctlr;

static Ctlr* ctlrhead;
static Ctlr* ctlrtail;
static Ctlr* gc82543ctlrhead;
static Ctlr* gc82543ctlrtail;

static Lock freelistlock;
static Block* freeShortHead;
static Block* freeJumboHead;

#define csr32r(c, r)	(*((c)->nic+((r)/4)))
#define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))

static void gc82543watchdog(void* arg);

static void
gc82543attach(Ether* edev)
{
	int ctl;
	Ctlr *ctlr;
	char name[KNAMELEN];

	/*
	 * To do here:
	 *	one-time stuff;
	 *	start off a kproc for link status change:
	 *		adjust queue length depending on speed;
	 *		flow control.
	 *	more needed here...
	 */
	ctlr = edev->ctlr;
	csr32w(ctlr, Ims, ctlr->im);
	lock(&ctlr->slock);
	if(ctlr->started == 0){
		ctlr->started = 1;
		snprint(name, KNAMELEN, "#l%d82543", edev->ctlrno);
		kproc(name, gc82543watchdog, edev);
	}
	unlock(&ctlr->slock);

	ctl = csr32r(ctlr, Rctl)|Ren;
	csr32w(ctlr, Rctl, ctl);
	ctl = csr32r(ctlr, Tctl)|Ten;
	csr32w(ctlr, Tctl, ctl);

	csr32w(ctlr, Ims, ctlr->im);
}

static char* statistics[Nstatistics] = {


@@ 424,10 506,9 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset)
	uvlong tuvl, ruvl;

	ctlr = edev->ctlr;

	lock(&ctlr->slock);
	p = malloc(2*READSTR);
	l = 0;

	for(i = 0; i < Nstatistics; i++){
		r = csr32r(ctlr, Statistics+i*4);
		if((s = statistics[i]) == nil)


@@ 450,7 531,7 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset)
				s, tuvl, ruvl);
			i++;
			break;
		

		default:
			ctlr->statistics[i] += r;
			if(ctlr->statistics[i] == 0)


@@ 461,7 542,6 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset)
		}
	}

	l += snprint(p+l, 2*READSTR-l, "ett: %d\n", ctlr->ett);
	l += snprint(p+l, 2*READSTR-l, "eeprom:");
	for(i = 0; i < 0x40; i++){
		if(i && ((i & 0x07) == 0))


@@ 469,74 549,220 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset)
		l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]);
	}

	snprint(p+l, 2*READSTR-l, "\n");
	snprint(p+l, 2*READSTR-l, "\ntxstalled %d\n", ctlr->txstalled);
	n = readstr(offset, a, n, p);
	free(p);
	unlock(&ctlr->slock);

	return n;
}

static void
gc82543transmit(Ether* edev)
gc82543promiscuous(void* arg, int on)
{
	Block *bp;
	int rctl;
	Ctlr *ctlr;
	Tdesc *tdesc;
	int tdh, tdt;
	Ether *edev;

	/*
	 * For now there are no smarts here. Tuning comes later.
	 */
	edev = arg;
	ctlr = edev->ctlr;
	ilock(&ctlr->tdlock);

	rctl = csr32r(ctlr, Rctl);
	rctl &= ~MoMASK;		/* make sure we're using bits 47:36 */
	if(on)
		rctl |= Upe|Mpe;
	else
		rctl &= ~(Upe|Mpe);
	csr32w(ctlr, Rctl, rctl);
}

static void
gc82543multicast(void* arg, uchar* addr, int on)
{
	int bit, x;
	Ctlr *ctlr;
	Ether *edev;

	edev = arg;
	ctlr = edev->ctlr;
	x = addr[5]>>1;
	bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
	if(on)
		ctlr->multimask[x] |= 1<<bit;
	else
		ctlr->multimask[x] &= ~(1<<bit);
	
	csr32w(ctlr, Mta+x*4, ctlr->multimask[x]);
}

static long
gc82543ctl(Ether* edev, void* buf, long n)
{
	Cmdbuf *cb;
	Ctlr *ctlr;
	int ctrl, i, r;

	ctlr = edev->ctlr;
	if(ctlr == nil)
		error(Enonexist);

	lock(&ctlr->slock);
	r = 0;
	cb = parsecmd(buf, n);
	if(cb->nf < 2)
		r = -1;
	else if(cistrcmp(cb->f[0], "auto") == 0){
		ctrl = csr32r(ctlr, Ctrl);
		if(cistrcmp(cb->f[1], "off") == 0){
			csr32w(ctlr, Txcw, ctlr->txcw & ~Ane);
			ctrl |= (Slu|Fd);
			if(ctlr->txcw & As)
				ctrl |= Rfce;
			if(ctlr->txcw & Ps)
				ctrl |= Tfce;
			csr32w(ctlr, Ctrl, ctrl);
		}
		else if(cistrcmp(cb->f[1], "on") == 0){
			csr32w(ctlr, Txcw, ctlr->txcw);
			ctrl &= ~(Slu|Fd);
			csr32w(ctlr, Ctrl, ctrl);
		}
		else
			r = -1;
	}
	else if(cistrcmp(cb->f[0], "clear") == 0){
		if(cistrcmp(cb->f[1], "stats") == 0){
			for(i = 0; i < Nstatistics; i++)
				ctlr->statistics[i] = 0;
		}
		else
			r = -1;
	}
	else
		r = -1;
	unlock(&ctlr->slock);

	free(cb);
	return (r == 0) ? n : r;
}

static void
gc82543txinit(Ctlr* ctlr)
{
	int i;
	int tdsize;
	Block *bp, **bpp;

	tdsize = ROUND(Ntdesc*sizeof(Tdesc), 4096);

	if(ctlr->tdba == nil)
		ctlr->tdba = xspanalloc(tdsize, 32, 0);

	for(i = 0; i < Ntdesc; i++){
		bpp = &ctlr->tb[i];
		bp = *bpp;
		if(bp != nil){
			*bpp = nil;
			freeb(bp);
		}
		memset(&ctlr->tdba[i], 0, sizeof(Tdesc));
	}

	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
	csr32w(ctlr, Tdbah, 0);
	csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc));

	/*
	 * Free any completed packets
	 * - try to get the soft tdh to catch the tdt;
	 * - if the packet had an underrun bump the threshold
	 *   - the Tu bit doesn't seem to ever be set, perhaps
	 *     because Rs mode is used?
	 * set the ring head and tail pointers.
	 */
	ctlr->tdh = 0;
	csr32w(ctlr, Tdh, ctlr->tdh);
	ctlr->tdt = 0;
	csr32w(ctlr, Tdt, ctlr->tdt);

	csr32w(ctlr, Tipg, (6<<20)|(8<<10)|6);
	csr32w(ctlr, Tidv, 128);
	csr32w(ctlr, Ait, 0);
	csr32w(ctlr, Txdmac, 0);
	csr32w(ctlr, Txdctl, Gran|(4<<WthreshSHIFT)|(1<<HthreshSHIFT)|16);
	csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(6<<ColdSHIFT));

	ctlr->im |= Txdw;
}

static void
gc82543transmit(Ether* edev)
{
	Block *bp, **bpp;
	Ctlr *ctlr;
	Tdesc *tdesc;
	int tdh, tdt, s;

	ctlr = edev->ctlr;

	ilock(&ctlr->tdlock);
	tdh = ctlr->tdh;
	for(;;){
		/*
		 * Free any completed packets
		 */
		tdesc = &ctlr->tdba[tdh];
		if(!(tdesc->status & Tdd))
			break;
		if(tdesc->status & Tu){
			ctlr->ett++;
			csr32w(ctlr, Ett, ctlr->ett);
		}
		tdesc->status = 0;
		if(ctlr->tb[tdh] != nil){
			freeb(ctlr->tb[tdh]);
			ctlr->tb[tdh] = nil;
		memset(tdesc, 0, sizeof(Tdesc));
		bpp = &ctlr->tb[tdh];
		bp = *bpp;
		if(bp != nil){
			*bpp = nil;
			freeb(bp);
		}
		tdh = NEXT(tdh, Ntdesc);
	}
	ctlr->tdh = tdh;
	s = csr32r(ctlr, Status);

	/*
	 * Try to fill the ring back up.
	 * Try to fill the ring back up
	 * but only if link is up and transmission isn't paused.
	 */
	tdh = PREV(ctlr->tdh, Ntdesc);
	for(tdt = ctlr->tdt; tdt != tdh; tdt = NEXT(tdt, Ntdesc)){
		if((bp = qget(edev->oq)) == nil)
			break;
	if((s & (Txoff|Lu)) == Lu){
		tdt = ctlr->tdt;
		while(NEXT(tdt, Ntdesc) != tdh){
			if((bp = qget(edev->oq)) == nil)
				break;

		tdesc = &ctlr->tdba[tdt];
		tdesc->addr[0] = PCIWADDR(bp->rp);
		tdesc->addr[1] = 0;
		tdesc->control = Ide|Rs|Ifcs|Teop|BLEN(bp);
		tdesc->status = 0;
			tdesc = &ctlr->tdba[tdt];
			tdesc->addr[0] = PCIWADDR(bp->rp);
			tdesc->control = Ide|Rs|Ifcs|Teop|BLEN(bp);
			ctlr->tb[tdt] = bp;
			tdt = NEXT(tdt, Ntdesc);
		}

		ctlr->tb[tdt] = bp;
		if(tdt != ctlr->tdt){
			ctlr->tdt = tdt;
			csr32w(ctlr, Tdt, tdt);
		}
	}
	ctlr->tdt = tdt;
	csr32w(ctlr, Tdt, tdt);
	else
		ctlr->txstalled++;

	iunlock(&ctlr->tdlock);
}

static Block *
gc82543allocb(Ctlr* ctlr)
{
	Block *bp;

	ilock(&freelistlock);
	if((bp = *(ctlr->freehead)) != nil){
		*(ctlr->freehead) = bp->next;
		bp->next = nil;
	}
	iunlock(&freelistlock);
	return bp;
}

static void
gc82543replenish(Ctlr* ctlr)
{


@@ 544,72 770,171 @@ gc82543replenish(Ctlr* ctlr)
	Block *bp;
	Rdesc *rdesc;

	ilock(&ctlr->rdlock);
	rdt = ctlr->rdt;
	while(NEXT(rdt, Nrdesc) != ctlr->rdh){
		rdesc = &ctlr->rdba[rdt];
		if(ctlr->rb[rdt] != nil){
			/* nothing to do */
		}
		else if((bp = iallocb(2048)) != nil){
		if(ctlr->rb[rdt] == nil){
			bp = gc82543allocb(ctlr);
			if(bp == nil){
				iprint("no available buffers\n");
				break;
			}
			ctlr->rb[rdt] = bp;
			rdesc->addr[0] = PCIWADDR(bp->rp);
			rdesc->addr[1] = 0;
		}
		else
			break;
		coherence();
		rdesc->status = 0;

		rdt = NEXT(rdt, Nrdesc);
	}
	ctlr->rdt = rdt;
	csr32w(ctlr, Rdt, rdt);
	iunlock(&ctlr->rdlock);
}

static void
gc82543interrupt(Ureg*, void* arg)
gc82543rxinit(Ctlr* ctlr)
{
	int rdsize, i;

	csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);

	/*
	 * Allocate the descriptor ring and load its
	 * address and length into the NIC.
	 */
	rdsize = ROUND(Nrdesc*sizeof(Rdesc), 4096);
	if(ctlr->rdba == nil)
		ctlr->rdba = xspanalloc(rdsize, 32, 0);
	memset(ctlr->rdba, 0, rdsize);

	ctlr->rdh = 0;
	ctlr->rdt = 0;

	csr32w(ctlr, Rdtr, Fpd|64);
	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
	csr32w(ctlr, Rdbah, 0);
	csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc));
	csr32w(ctlr, Rdh, 0);
	csr32w(ctlr, Rdt, 0);
	for(i = 0; i < Nrdesc; i++){
		if(ctlr->rb[i] != nil){
			freeb(ctlr->rb[i]);
			ctlr->rb[i] = nil;
		}
	}
	gc82543replenish(ctlr);

	csr32w(ctlr, Rxdctl, RxGran|(8<<WthreshSHIFT)|(4<<HthreshSHIFT)|1);
	ctlr->im |= Rxt0|Rxo|Rxdmt0|Rxseq;
}

static void
gc82543recv(Ether* edev, int icr)
{
	Block *bp;
	Ctlr *ctlr;
	Ether *edev;
	Rdesc *rdesc;
	int icr, rdh;
	int rdh;

	ctlr = edev->ctlr;

	rdh = ctlr->rdh;
	for(;;){
		rdesc = &ctlr->rdba[rdh];

		if(!(rdesc->status & Rdd))
			break;

		if((rdesc->status & Reop) && rdesc->errors == 0){
			bp = ctlr->rb[rdh];
			ctlr->rb[rdh] = nil;
			bp->wp += rdesc->length;
			bp->next = nil;
			etheriq(edev, bp, 1);
		}

		if(ctlr->rb[rdh] != nil){
			/* either non eop packet, or error */
			freeb(ctlr->rb[rdh]);
			ctlr->rb[rdh] = nil;
		}
		memset(rdesc, 0, sizeof(Rdesc));
		coherence();
		rdh = NEXT(rdh, Nrdesc);
	}
	ctlr->rdh = rdh;

	if(icr & Rxdmt0)
		gc82543replenish(ctlr);
}

static void
freegc82543short(Block *bp)
{
	ilock(&freelistlock);
	/* reset read/write pointer to proper positions */
	bp->rp = bp->lim - ROUND(SBLOCKSIZE, BLOCKALIGN);
	bp->wp = bp->rp;
	bp->next = freeShortHead;
	freeShortHead = bp;
	iunlock(&freelistlock);
}

static void
freegc82532jumbo(Block *bp)
{
	ilock(&freelistlock);
	/* reset read/write pointer to proper positions */
	bp->rp = bp->lim - ROUND(JBLOCKSIZE, BLOCKALIGN);
	bp->wp = bp->rp;
	bp->next = freeJumboHead;
	freeJumboHead = bp;
	iunlock(&freelistlock);
}

static void
linkintr(Ctlr* ctlr)
{
	int ctrl;

	ctrl = csr32r(ctlr, Ctrl);

	if((ctrl & Swdpin1) ||
	  ((csr32r(ctlr, Rxcw) & Rxconfig) && !(csr32r(ctlr, Txcw) & Ane))){
 		csr32w(ctlr, Txcw, ctlr->txcw);
		ctrl &= ~(Slu|Fd|Frcdplx);
		csr32w(ctlr, Ctrl, ctrl);
	}
}

static void
gc82543interrupt(Ureg*, void* arg)
{
	Ctlr *ctlr;
	Ether *edev;
	int icr;

	edev = arg;
	ctlr = edev->ctlr;

	for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){
	while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
		/*
		 * Link status changed.
		 */
		if(icr & (Rxseq|Lsc)){
			/*
			 * More here...
			 */
		}
		if(icr & (Lsc|Rxseq))
			linkintr(ctlr);

		/*
		 * Process any received packets.
		 * Process recv buffers.
		 */
		rdh = ctlr->rdh;
		for(;;){
			rdesc = &ctlr->rdba[rdh];
			if(!(rdesc->status & Rdd))
				break;
			if((rdesc->status & Reop) && rdesc->errors == 0){
				bp = ctlr->rb[rdh];
				ctlr->rb[rdh] = nil;
				bp->wp += rdesc->length-4;
				etheriq(edev, bp, 1);
			}
			rdesc->status = 0;
			rdh = NEXT(rdh, Nrdesc);
		}
		ctlr->rdh = rdh;
		gc82543recv(edev, icr);

		/*
		 * Refill transmit ring and free packets.
		 */
		gc82543transmit(edev);

		if(icr & Rxdmt0)
			gc82543replenish(ctlr);
	}
}



@@ 617,11 942,33 @@ static int
gc82543init(Ether* edev)
{
	int csr, i;

	Block *bp;
	Ctlr *ctlr;

	ctlr = edev->ctlr;

	/*
	 * Allocate private buffer pool to use for receiving packets.
	 */
	ilock(&freelistlock);
	if (ctlr->freehead == nil){
		for(i = 0; i < Nblocks; i++){
			bp = iallocb(SBLOCKSIZE);
			if(bp != nil){
				bp->next = freeShortHead;
				bp->free = freegc82543short;
				freeShortHead = bp;
			}
			else{
				print("82543gc: no memory\n");
				break;
			}
		}
		ctlr->freehead = &freeShortHead;
	}
	iunlock(&freelistlock);

	/*
	 * Set up the receive addresses.
	 * There are 16 addresses. The first should be the MAC address.
	 * The others are cleared and not marked valid (MS bit of Rah).


@@ 642,102 989,8 @@ gc82543init(Ether* edev)
	for(i = 0; i < 128; i++)
		csr32w(ctlr, Mta+i*4, 0);

	/*
	 * Receive initialisation.
	 * Mostly defaults from the datasheet, will
	 * need some tuning for performance:
	 *	Rctl	descriptor mimimum threshold size
	 *		discard pause frames
	 *		strip CRC
	 * 	Rdtr	interrupt delay
	 * 	Rxdctl	all the thresholds
	 */
	csr32w(ctlr, Rctl, 0);

	/*
	 * Allocate the descriptor ring and load its
	 * address and length into the NIC.
	 */
	ctlr->rdba = xspanalloc(Nrdesc*sizeof(Rdesc), 16, 0);
	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
	csr32w(ctlr, Rdbah, 0);
	csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc));

	/*
	 * Initialise the ring head and tail pointers and
	 * populate the ring with Blocks.
	 * The datasheet says the tail pointer is set to beyond the last
	 * descriptor hardware can process, which implies the initial
	 * condition is Rdh == Rdt. However, experience shows Rdt must
	 * always be 'behind' Rdh; the replenish routine ensures this.
	 */
	ctlr->rdh = 0;
	csr32w(ctlr, Rdh, ctlr->rdh);
	ctlr->rdt = 0;
	csr32w(ctlr, Rdt, ctlr->rdt);
	ctlr->rb = malloc(sizeof(Block*)*Nrdesc);
	gc82543replenish(ctlr);

	/*
	 * Set up Rctl but don't enable receiver (yet).
	 */
	csr32w(ctlr, Rdtr, 0);
	csr32w(ctlr, Rxdctl, Gran|(1<<WthreshSHIFT)|(1<<HthreshSHIFT)|4);

	csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
	ctlr->im |= Rxt0|Rxo|Rxdmt0|Rxseq;

	/*
	 * Transmit initialisation.
	 * Mostly defaults from the datasheet, will
	 * need some tuning for performance. The normal mode will
	 * be full-duplex and things to tune for half-duplex are
	 *	Tctl	re-transmit on late collision
	 *	Tipg	all IPG times
	 *	Tbt	burst timer
	 *	Ait	adaptive IFS throttle
	 * and in general
	 *	Txdmac	packet prefetching
	 *	Ett	transmit early threshold
	 *	Tidv	interrupt delay value
	 *	Txdctl	all the thresholds
	 */
	csr = (0x0F<<CtSHIFT)|Psp;
	if(csr32r(ctlr, Ctrl) & Fd)
		csr |= 64<<ColdSHIFT;
	else
		csr |= 512<<ColdSHIFT;
	csr32w(ctlr, Tctl, csr);
	csr32w(ctlr, Tipg, (6<<20)|(2<<10)|6);
	csr32w(ctlr, Tbt, 0x2000);
	csr32w(ctlr, Ait, 0);
	csr32w(ctlr, Txdmac, 0);
	if(csr32r(ctlr, Status) & Bus64)
		ctlr->ett = 120/8;
	else
		ctlr->ett = 0;
	csr32w(ctlr, Ett, ctlr->ett);
	csr32w(ctlr, Tidv, 128);
	csr32w(ctlr, Txdctl, Gran|(16<<WthreshSHIFT)|(1<<HthreshSHIFT)|4);

	/*
	 * Allocate the descriptor ring and load its
	 * address and length into the NIC.
	 */
	ctlr->tdba = xspanalloc(Ntdesc*sizeof(Tdesc), 16, 0);
	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
	csr32w(ctlr, Tdbah, 0);
	csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc));

	/*
	 * Initialise the ring head and tail pointers.
	 */
	ctlr->tdh = 0;
	csr32w(ctlr, Tdh, ctlr->tdh);
	ctlr->tdt = 0;
	csr32w(ctlr, Tdt, ctlr->tdt);
	ctlr->tb = malloc(sizeof(Block*)*Ntdesc);
	ctlr->im |= Txqe|Txdw;
	gc82543txinit(ctlr);
	gc82543rxinit(ctlr);

	return 0;
}


@@ 854,6 1107,12 @@ gc82543detach(Ctlr* ctlr)
	 * power-on state, followed by an EEPROM reset to read
	 * the defaults for some internal registers.
	 */
	csr32w(ctlr, Imc, ~0);
	csr32w(ctlr, Rctl, 0);
	csr32w(ctlr, Tctl, 0);

	delay(10);

	csr32w(ctlr, Ctrl, Devrst);
	while(csr32r(ctlr, Ctrl) & Devrst)
		;


@@ 861,26 1120,89 @@ gc82543detach(Ctlr* ctlr)
	csr32w(ctlr, Ctrlext, Eerst);
	while(csr32r(ctlr, Ctrlext) & Eerst)
		;

	csr32w(ctlr, Imc, ~0);
	while(csr32r(ctlr, Icr))
		;
}

static void
gc82543checklink(Ctlr* ctlr)
{
	int ctrl, status, rxcw;

	ctrl = csr32r(ctlr, Ctrl);
	status = csr32r(ctlr, Status);
	rxcw = csr32r(ctlr, Rxcw);

	if(!(status & Lu)){
		if(!(ctrl & (Swdpin1|Slu)) && !(rxcw & Rxconfig)){
			csr32w(ctlr, Txcw, ctlr->txcw & ~Ane);
			ctrl |= (Slu|Fd);
			if(ctlr->txcw & As)
				ctrl |= Rfce;
			if(ctlr->txcw & Ps)
				ctrl |= Tfce;
			csr32w(ctlr, Ctrl, ctrl);
		}
	}
	else if((ctrl & Slu) && (rxcw & Rxconfig)){
		csr32w(ctlr, Txcw, ctlr->txcw);
		ctrl &= ~(Slu|Fd);
		csr32w(ctlr, Ctrl, ctrl);
	}
}

static void
gc82543shutdown(Ether* ether)
{
print("gc82543shutdown\n");
	gc82543detach(ether->ctlr);
}

static int
gc82543reset(Ctlr* ctlr)
{
	int ctl;
	int te;

	/*
	 * Read the EEPROM, validate the checksum
	 * then get the device back to a power-on state.
	 */
	if(at93c46r(ctlr) != 0xBABA)
		return -1;

	gc82543detach(ctlr);

	te = ctlr->eeprom[Icw2];
	if((te & 0x3000) == 0){
		ctlr->fcrtl = 0x00002000;
		ctlr->fcrth = 0x00004000;
		ctlr->txcw = Ane|TxcwFd;
	}
	else if((te & 0x3000) == 0x2000){
		ctlr->fcrtl = 0;
		ctlr->fcrth = 0;
		ctlr->txcw = Ane|TxcwFd|As;
	}
	else{
		ctlr->fcrtl = 0x00002000;
		ctlr->fcrth = 0x00004000;
		ctlr->txcw = Ane|TxcwFd|As|Ps;
	}

	csr32w(ctlr, Txcw, ctlr->txcw);

	csr32w(ctlr, Ctrlext, (te & 0x00f0)<<4);

	csr32w(ctlr, Tctl, csr32r(ctlr, Tctl)|(64<<ColdSHIFT));

	te = ctlr->eeprom[Icw1];
	ctl = ((te & 0x01E0)<<17)|(te & 0x0010)<<3;
	csr32w(ctlr, Ctrl, ctl);

	delay(10);

	/*
	 * Flow control - values from the datasheet.
	 */


@@ 889,23 1211,41 @@ gc82543reset(Ctlr* ctlr)
	csr32w(ctlr, Fct, 0x00008808);
	csr32w(ctlr, Fcttv, 0x00000100);

	/*
	 * More here...
	 * Need to take deal with making sure hardware auto-negotiation is
	 * set up, how Rxcw and Txcw are configured, Fcrtl, Fcrth, etc.
	 */
	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
	csr32w(ctlr, Fcrth, ctlr->fcrth);

	csr32w(ctlr, Imc, ~0);
	ctlr->im = Lsc;
	csr32w(ctlr, Ims, ctlr->im);
	gc82543checklink(ctlr);

	return 0;
}

static void
gc82543watchdog(void* arg)
{
	Ether *edev;
	Ctlr *ctlr;

	edev = arg;
	for(;;){
		ctlr = edev->ctlr;
		tsleep(&ctlr->timer, return0, 0, 1000);

		ctlr = edev->ctlr;
		if(ctlr == nil){
			print("%s: exiting\n", up->text);
			pexit("disabled", 0);
		}

		gc82543checklink(ctlr);
		gc82543replenish(ctlr);
	}
}

static void
gc82543pci(void)
{
	int port;
	int port, cls;
	Pcidev *p;
	Ctlr *ctlr;



@@ 931,64 1271,38 @@ gc82543pci(void)
			print("gc82543: can't map %8.8luX\n", p->mem[0].bar);
			continue;
		}

		cls = pcicfgr8(p, PciCLS);
		switch(cls){
			case 0x00:
			case 0xFF:
				print("82543gc: unusable cache line size\n");
				continue;
			case 0x08:
				break;
			default:
				print("82543gc: cache line size %d, expected 32\n",
					cls*4);
		}
		ctlr = malloc(sizeof(Ctlr));
		ctlr->port = port;
		ctlr->pcidev = p;
		ctlr->id = (p->did<<16)|p->vid;

		ctlr->nic = KADDR(ctlr->port);
print("status0 %8.8uX\n", csr32r(ctlr, Status));

		if(gc82543reset(ctlr)){
			free(ctlr);
			continue;
		}
print("status1 %8.8uX\n", csr32r(ctlr, Status));

		if(ctlrhead != nil)
			ctlrtail->next = ctlr;
		if(gc82543ctlrhead != nil)
			gc82543ctlrtail->next = ctlr;
		else
			ctlrhead = ctlr;
		ctlrtail = ctlr;
			gc82543ctlrhead = ctlr;
		gc82543ctlrtail = ctlr;
	}
}

static void
gc82543promiscuous(void* arg, int on)
{
	Ether *edev=arg;
	Ctlr *ctlr;
	ulong ctl;

	ctlr = edev->ctlr;
	ctl = csr32r(ctlr, Rctl);
	ctl &= ~MoMASK;			/* make sure we're using bits 47:36 */
	if(on)
		ctl |= Upe|Mpe;
	else
		ctl &= ~(Upe|Mpe);
	csr32w(ctlr, Rctl, ctl);
}

static void
gc82543multicast(void* arg, uchar *addr, int on)
{
	Ether *edev=arg;
	Ctlr *ctlr;
	int x;
	int bit;

	ctlr = edev->ctlr;
	x = addr[5]>>1;
	bit = ((addr[5]&1)<<4)|(addr[4]>>4);
	if(on)
		ctlr->multimask[x] |= 1<<bit;
	else
		ctlr->multimask[x] &= ~(1<<bit);
	
	csr32w(ctlr, Mta+x*4, ctlr->multimask[x]);
}

static int
gc82543pnp(Ether* edev)
{


@@ 996,14 1310,14 @@ gc82543pnp(Ether* edev)
	Ctlr *ctlr;
	uchar ea[Eaddrlen];

	if(ctlrhead == nil)
	if(gc82543ctlrhead == nil)
		gc82543pci();

	/*
	 * Any adapter matches if no edev->port is supplied,
	 * otherwise the ports must match.
	 */
	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
	for(ctlr = gc82543ctlrhead; ctlr != nil; ctlr = ctlr->next){
		if(ctlr->active)
			continue;
		if(edev->port == 0 || edev->port == ctlr->port){


@@ 1027,7 1341,7 @@ gc82543pnp(Ether* edev)
	 */
	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
		for(i = 0; i < Eaddrlen/2; i++){
		for(i = Ea; i < Eaddrlen/2; i++){
			edev->ea[2*i] = ctlr->eeprom[i];
			edev->ea[2*i+1] = ctlr->eeprom[i]>>8;
		}


@@ 1042,12 1356,11 @@ gc82543pnp(Ether* edev)
	edev->interrupt = gc82543interrupt;
	edev->ifstat = gc82543ifstat;
	edev->shutdown = gc82543shutdown;
	edev->ctl = gc82543ctl;
	edev->arg = edev;
	edev->promiscuous = gc82543promiscuous;
	edev->multicast = gc82543multicast;

	edev->arg = edev;
	edev->promiscuous = nil;

	return 0;
}


M pc/etherga620.c => pc/etherga620.c +4 -2
@@ 627,7 627,8 @@ ga620interrupt(Ureg*, void* arg)

	if(!(csr32r(ctlr, Mhc) & Is))
		return;
	rdtsc(&tsc0);
	if(m->havetsc)
		rdtsc(&tsc0);

	ctlr->interrupts++;
	csr32w(ctlr, Hi, 1);


@@ 659,7 660,8 @@ ga620interrupt(Ureg*, void* arg)
		work = 0;
	}

	rdtsc(&tsc1);
	if(m->havetsc)
		rdtsc(&tsc1);
	ctlr->ticks += tsc1-tsc0;
}


M pc/main.c => pc/main.c +5 -0
@@ 104,6 104,11 @@ main(void)
	initseg();
	links();
conf.monitor = 1;
{int x; uvlong start, end;
rdtsc(&start);
for(x = 0; x < 10000; x++) rdtsc(&end);
print("%lld\n", end-start);
}
	chandevreset();
	i8253link();
	pageinit();

M pc/trap.c => pc/trap.c +2 -2
@@ 58,6 58,8 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
				vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
		v->next = vctl[vno];
	}
	if(intrtimes[vno] == nil)
		intrtimes[vno] = xalloc(Ntimevec*sizeof(ulong));
	vctl[vno] = v;
	iunlock(&vctllock);
}


@@ 260,8 262,6 @@ intrtime(Mach *m, int vno)
	diff /= m->cpumhz;
	if(diff >= 1000)
		diff = 999;
	if(intrtimes[vno] == nil)
		intrtimes[vno] = xalloc(Ntimevec*sizeof(ulong));
	intrtimes[vno][diff]++;
}


M port/devcons.c => port/devcons.c +24 -1
@@ 641,6 641,27 @@ consclose(Chan *c)
	}
}

/*
 *  calculate duty cycle for a processor (100-(%time in idlehands()))
 */
static int
dutycycle(Mach *mp)
{
	static ulong ticks;
	uvlong hz;
	int x;

	if(ticks == 0){
		fastticks(&hz);
		ticks = hz/HZ;
	}
	x = ticks - mp->avginidle;
	if(x < 0)
		return 0;
	x *= 100;
	return x/ticks;
}

static long
consread(Chan *c, void *buf, long n, vlong off)
{


@@ 757,7 778,7 @@ consread(Chan *c, void *buf, long n, vlong off)
		return 0;

	case Qsysstat:
		b = smalloc(conf.nmach*(NUMSIZE*8+1) + 1);	/* +1 for NUL */
		b = smalloc(conf.nmach*(NUMSIZE*9+1) + 1);	/* +1 for NUL */
		bp = b;
		for(id = 0; id < 32; id++) {
			if(active.machs & (1<<id)) {


@@ 778,6 799,8 @@ consread(Chan *c, void *buf, long n, vlong off)
				bp += NUMSIZE;
				readnum(0, bp, NUMSIZE, mp->load, NUMSIZE);
				bp += NUMSIZE;
				readnum(0, bp, NUMSIZE, dutycycle(mp), NUMSIZE);
				bp += NUMSIZE;
				*bp++ = '\n';
			}
		}

M port/portdat.h => port/portdat.h +1 -1
@@ 135,7 135,7 @@ struct Block
	uchar*	rp;			/* first unconsumed byte */
	uchar*	wp;			/* first empty byte */
	uchar*	lim;			/* 1 past the end of the buffer */
	uchar*	base;			/* start of the buffer */
	uchar*	base;		/* start of the buffer */
	void	(*free)(Block*);
	ulong	flag;
};

M port/proc.c => port/proc.c +13 -3
@@ 211,6 211,9 @@ runproc(void)
	Schedq *rq, *xrq;
	Proc *p, *l;
	ulong rt;
	uvlong start;

	start = 0;

	if ((p = edf->edfrunproc()) != nil)
		return p;


@@ 262,6 265,8 @@ loop:
				}
			}
		}
		if(start == 0)
			start = fastticks(nil);
		idlehands();
	}



@@ 301,6 306,8 @@ found:
		p->movetime = MACHP(0)->ticks + HZ/10;
	p->mp = MACHP(m->machno);

	if(start)
		m->inidle += fastticks(nil)-start;
	return p;
}



@@ 1228,8 1235,8 @@ void
accounttime(void)
{
	Proc *p;
	int n;
	static int nrun;
	ulong n;
	static ulong nrun;

	p = m->proc;
	if(p) {


@@ 1244,9 1251,12 @@ accounttime(void)
	/* calculate decaying load average */
	n = nrun;
	nrun = 0;

	n = (nrdy+n)*1000;
	m->load = (m->load*19+n)/20;

	/* calculate decaying duty cycle average */
	m->avginidle = (m->avginidle*(HZ-1)+m->inidle)/HZ;
	m->inidle = 0;
}

static void