~kris/9p

9hist

32eb3adf284e2993c993384c9dc457d6715f1d98 — David du Colombier 26 years ago b1989be
Plan 9 from Bell Labs 2000-06-17
9 files changed, 657 insertions(+), 71 deletions(-)

M mpc/random.c
M mpc/trap.c
M pc/pci.c
M pc/sdata.c
M pc/sdscsi.c
A port/devloopback.c
M port/devsd.c
M port/devsdp.c
M port/sd.h
M mpc/random.c => mpc/random.c +2 -2
@@ 81,7 81,7 @@ randomclock(void)
		wakeup(&rb.consumer);
}

static void
void
randominit(void)
{
	addclock0link(randomclock);


@@ 93,7 93,7 @@ randominit(void)
/*
 *  consume random bytes from a circular buffer
 */
static ulong
ulong
randomread(void *xp, ulong n)
{
	uchar *e, *p;

M mpc/trap.c => mpc/trap.c +1 -0
@@ 656,6 656,7 @@ syscall(Ureg* ureg)
	}
	if(up->nerrlab){
		print("bad errstack [%d]: %d extra\n", scallnr, up->nerrlab);
		print("scall %s lr =%lux\n", sysctab[scallnr], ureg->lr);
		for(i = 0; i < NERR; i++)
			print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc);
		panic("error stack");

M pc/pci.c => pc/pci.c +4 -1
@@ 61,6 61,7 @@ enum
static Lock pcicfglock;
static Lock pcicfginitlock;
static int pcicfgmode = -1;
static int pcimaxbno = 255;
static int pcimaxdno;
static Pcidev* pciroot;
static Pcidev* pcilist;


@@ 534,11 535,13 @@ pcicfginit(void)

	fmtinstall('T', tbdfconv);

	if(p = getconf("*pcimaxbno"))
		pcimaxbno = strtoul(p, 0, 0);
	if(p = getconf("*pcimaxdno"))
		pcimaxdno = strtoul(p, 0, 0);

	list = &pciroot;
	for(bno = 0; bno < 256; bno++) {
	for(bno = 0; bno <= pcimaxbno; bno++) {
		bno = pciscan(bno, list);
		while(*list)
			list = &(*list)->link;

M pc/sdata.c => pc/sdata.c +1 -1
@@ 1758,7 1758,7 @@ atarctl(SDunit* unit, char* p, int l)
		n += snprint(p+n, l-n, " rwm %ud rwmctl %ud",
			drive->rwm, drive->rwmctl);
	n += snprint(p+n, l-n, "\n");
	if(!unit->changed && unit->sectors){
	if(unit->sectors){
		n += snprint(p+n, l-n, "geometry %ld %ld",
			unit->sectors, unit->secsize);
		if(drive->pkt == 0)

M pc/sdscsi.c => pc/sdscsi.c +14 -5
@@ 217,6 217,14 @@ scsionline(SDunit* unit)
			 */
			unit->sectors++;
			unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];

			/*
			 * Some ATAPI CD readers lie about the block size.
			 * Since we don't read audio via this interface
			 * it's okay to always fudge this.
			 */
			if((unit->inquiry[0] & 0x1F) == 0x05 && unit->secsize == 2352)
				unit->secsize = 2048;
			ok = 1;
			break;
		case 1:


@@ 230,7 238,10 @@ scsionline(SDunit* unit)
	free(p);
	free(r);

	return ok;
	if(ok)
		return ok+retries;
	else
		return 0;
}

int


@@ 331,15 342,13 @@ again:
		case 0x06:		/* check condition */
			/*
			 * Check for a removeable media change.
			 * If so, mark it and zap the geometry info
			 * If so, mark it by zapping the geometry info
			 * to force an online request.
			 */
			if(r->sense[12] != 0x28 || r->sense[13] != 0)
				break;
			if(unit->inquiry[1] & 0x80){
				unit->changed = 1;
			if(unit->inquiry[1] & 0x80)
				unit->sectors = 0;
			}
			break;
		case 0x02:		/* not ready */
			/*

A port/devloopback.c => port/devloopback.c +582 -0
@@ 0,0 1,582 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

#include	"netif.h"

typedef struct Link	Link;
typedef struct Loop	Loop;

struct Link
{
	Lock;

	int	ref;

	int	nodrop;		/* disable dropping on iq overflow */
	int	soverflows;	/* packets dropped because iq overflowed */
	int	drops;		/* packets deliberately dropped */

	long	delay0;		/* fastticks of delay in the link */
	long	delayn;		/* fastticks of delay per byte */

	Block	*tq;		/* transmission queue */
	Block	*tqtail;
	vlong	tout;		/* time the last packet in tq is really out */
	vlong	tin;		/* time the head packet in tq enters the remote side  */

	Queue	*oq;		/* output queue from other side & packets in the link */
	Queue	*iq;
};

struct Loop
{
	QLock;
	int	ref;
	int	minmtu;		/* smallest block transmittable */
	Loop	*next;
	ulong	path;
	long	limit;		/* queue buffering limit */
	Link	link[2];
};

static struct
{
	Lock;
	ulong	path;
} loopbackalloc;

enum
{
	Qdir,
	Qctl,
	Qstatus,
	Qstats,
	Qdata0,
	Qdata1,

	TMSIZE		= 8,

	NLOOPBACKS	= 1,
	LOOPBACKSIZE	= 32*1024,		/*ZZZ change to settable; size of queues */
};

Dirtab loopbackdir[] =
{
	"ctl",		{Qctl},		0,			0222,
	"status",	{Qstatus},	0,			0222,
	"stats",	{Qstats},	0,			0444,
	"data",		{Qdata0},	0,			0666,
	"data1",	{Qdata1},	0,			0666,
};

static Loop	loopbacks[NLOOPBACKS];

static void	looper(Loop *lb);
static long	loopoput(Loop *lb, Link *link, Block *bp);
static void	ptime(uchar *p, vlong t);
static vlong	gtime(uchar *p);
static void	closelink(Link *link, int dofree);
static vlong	pushlink(Link *link, vlong now);
static void	freelb(Loop *lb);

static void
loopbackinit(void)
{
	int i;

	for(i = 0; i < NLOOPBACKS; i++)
		loopbacks[i].path = i;
}

static Chan*
loopbackattach(char *spec)
{
	Loop *lb;
	Queue *q;
	Chan *c;
	int chan;

	c = devattach('X', spec);
	lb = &loopbacks[0];

	qlock(lb);
	if(waserror()){
		qunlock(lb);
		nexterror();
	}

	lb->ref++;
	if(lb->ref == 1){
		lb->limit = LOOPBACKSIZE;
		for(chan = 0; chan < 2; chan++){
			q = qopen(lb->limit, 0, 0, 0);
			lb->link[chan].iq = q;
			if(q == nil){
				freelb(lb);
				exhausted("memory");
			}
			q = qopen(lb->limit, 0, 0, 0);
			lb->link[chan].oq = q;
			if(q == nil){
				freelb(lb);
				exhausted("memory");
			}
			lb->link[chan].nodrop = 1;
		}
	}
	poperror();
	qunlock(lb);

	c->qid = (Qid){CHDIR|NETQID(2*lb->path, Qdir), 0};
	c->aux = lb;
	c->dev = 0;
	return c;
}

static Chan*
loopbackclone(Chan *c, Chan *nc)
{
	Loop *lb;
	int chan;

	lb = c->aux;
	nc = devclone(c, nc);
	qlock(lb);
	lb->ref++;
	if(c->flag & COPEN){
		switch(chan = NETTYPE(c->qid.path)){
		case Qdata0:
		case Qdata1:
			chan -= Qdata0;
			lb->link[chan].ref++;
			break;
		}
	}
	qunlock(lb);
	return nc;
}

static int
loopbackgen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp)
{
	Loop *lb;
	int id, len, chan;

	if(i == DEVDOTDOT){
		devdir(c, c->qid, "#X", 0, eve, CHDIR|0555, dp);
		return 1;
	}

	id = NETID(c->qid.path);
	if(i > 1)
		id++;
	if(tab==nil || i>=ntab)
		return -1;
	tab += i;
	lb = c->aux;
	switch(chan = tab->qid.path){
	case Qdata0:
	case Qdata1:
		chan -= Qdata0;
		len = qlen(lb->link[chan].iq);
		break;
	default:
		len = tab->length;
		break;
	}
	devdir(c, (Qid){NETQID(id, tab->qid.path),0}, tab->name, len, eve, tab->perm, dp);
	return 1;
}


static int
loopbackwalk(Chan *c, char *name)
{
	return devwalk(c, name, loopbackdir, nelem(loopbackdir), loopbackgen);
}

static void
loopbackstat(Chan *c, char *db)
{
	Loop *lb;
	Dir dir;
	int chan;

	lb = c->aux;
	switch(chan = NETTYPE(c->qid.path)){
	case Qdir:
		devdir(c, c->qid, ".", nelem(loopbackdir)*DIRLEN, eve, CHDIR|0555, &dir);
		break;
	case Qdata0:
	case Qdata1:
		chan -= Qdata0;
		devdir(c, c->qid, "data", qlen(lb->link[chan].iq), eve, 0660, &dir);
		break;
	default:
		panic("loopbackstat");
	}
	convD2M(&dir, db);
}

/*
 *  if the stream doesn't exist, create it
 */
static Chan*
loopbackopen(Chan *c, int omode)
{
	Loop *lb;
	int chan;

	if(c->qid.path & CHDIR){
		if(omode != OREAD)
			error(Ebadarg);
		c->mode = omode;
		c->flag |= COPEN;
		c->offset = 0;
		return c;
	}

	lb = c->aux;
	qlock(lb);
	switch(chan = NETTYPE(c->qid.path)){
	case Qdata0:
	case Qdata1:
		chan -= Qdata0;
		lb->link[chan].ref++;
		break;
	}
	qunlock(lb);

	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

static void
loopbackclose(Chan *c)
{
	Loop *lb;
	int ref, chan;

	lb = c->aux;

	qlock(lb);
	if(c->flag & COPEN){
		/*
		 *  closing either side hangs up the stream
		 */
		switch(chan = NETTYPE(c->qid.path)){
		case Qdata0:
		case Qdata1:
			chan -= Qdata0;
			if(--lb->link[chan].ref == 0){
				qhangup(lb->link[chan ^ 1].oq, nil);
				looper(lb);
			}
			break;
		}
	}


	/*
	 *  if both sides are closed, they are reusable
	 */
	if(lb->link[0].ref == 0 && lb->link[1].ref == 0){
		for(chan = 0; chan < 2; chan++){
			closelink(&lb->link[chan], 0);
			qreopen(lb->link[chan].iq);
			qreopen(lb->link[chan].oq);
		}
	}
	ref = --lb->ref;
	if(ref == 0)
		freelb(lb);
	qunlock(lb);
}

static void
freelb(Loop *lb)
{
	int chan;

	for(chan = 0; chan < 2; chan++)
		closelink(&lb->link[chan], 1);
}

/*
 * called with the Loop qlocked,
 * so only pushlink can mess with the queues
 */
static void
closelink(Link *link, int dofree)
{
	Queue *iq, *oq;
	Block *bp;

	ilock(link);
	iq = link->iq;
	oq = link->oq;
	bp = link->tq;
	link->tq = nil;
	link->tqtail = nil;
	link->tout = 0;
	link->tin = 0;
	iunlock(link);
	if(iq != nil){
		qclose(iq);
		if(dofree){
			ilock(link);
			free(iq);
			link->iq = nil;
			iunlock(link);
		}
	}
	if(oq != nil){
		qclose(oq);
		if(dofree){
			ilock(link);
			free(oq);
			link->oq = nil;
			iunlock(link);
		}
	}
	freeblist(bp);
}

static long
loopbackread(Chan *c, void *va, long n, vlong)
{
	Loop *lb;
	int chan;

	lb = c->aux;
//ZZZ ctl message to set q limit -- qsetlimit(q, limit)
//ZZZ ctl message to set blocking/dropping	qnoblock(q, dropit)
//ZZZ ctl message for delays
	switch(chan = NETTYPE(c->qid.path)){
	case Qdir:
		return devdirread(c, va, n, loopbackdir, nelem(loopbackdir), loopbackgen);
	case Qdata0:
	case Qdata1:
		chan -= Qdata0;
		return qread(lb->link[chan].iq, va, n);
	default:
		panic("loopbackread");
	}
	return -1;	/* not reached */
}

static Block*
loopbackbread(Chan *c, long n, ulong offset)
{
	Loop *lb;
	int chan;

	lb = c->aux;
	switch(chan = NETTYPE(c->qid.path)){
	case Qdata0:
	case Qdata1:
		chan -= Qdata0;
		return qbread(lb->link[chan].iq, n);
	}

	return devbread(c, n, offset);
}

static long
loopbackbwrite(Chan *c, Block *bp, ulong off)
{
	Loop *lb;
	int chan;

	lb = c->aux;
	switch(chan = NETTYPE(c->qid.path)){
	case Qdata0:
	case Qdata1:
		chan -= Qdata0;
		return loopoput(lb, &lb->link[chan ^ 1], bp);
	default:
		return devbwrite(c, bp, off);
	}
}

static long
loopbackwrite(Chan *c, void *va, long n, vlong off)
{
	Block *bp;

	if(!islo())
		print("loopbackwrite hi %lux\n", getcallerpc(&c));

	switch(NETTYPE(c->qid.path)){
	case Qdata0:
	case Qdata1:
		bp = allocb(n);
		if(waserror()){
			freeb(bp);
			nexterror();
		}
		memmove(bp->wp, va, n);
		poperror();
		bp->wp += n;
		return loopbackbwrite(c, bp, off);
	case Qctl:
	default:
		panic("loopbackwrite");
	}

	return n;
}

static long
loopoput(Loop *lb, Link *link, Block *bp)
{
	long n;

	n = BLEN(bp);

	/* make it a single block with space for the loopback header */
	bp = padblock(bp, TMSIZE);
	if(bp->next)
		bp = concatblock(bp);
	if(BLEN(bp) < lb->minmtu)
		bp = adjustblock(bp, lb->minmtu);

	qbwrite(link->oq, bp);
	looper(lb);
	return n;
}

/*
 * move blocks between queues if they are ready.
 * schedule an interrupt for the next interesting time
 */
static void
looper(Loop *lb)
{
	vlong t, tt;

	tt = fastticks(nil);
again:;
	t = pushlink(&lb->link[0], tt);
	tt = pushlink(&lb->link[1], tt);
	if(t > tt && tt)
		t = tt;
	if(t){
		tt = fastticks(nil);
		if(tt <= t)
			goto again;
		//schedule an intr at tt-t fastticks
	}
}

static vlong
pushlink(Link *link, vlong now)
{
	Block *bp;
	vlong t;

	/*
	 * put another block in the link queue
	 */
	ilock(link);
	if(link->iq == nil || link->oq == nil){
		iunlock(link);
		return 0;
	}
	t = link->tout;
	if(!t || t < now){
		bp = qget(link->oq);
		if(bp != nil){
			if(!t)
				t = now;
			link->tout = t + BLEN(bp) * link->delayn;
			ptime(bp->rp, t + link->delay0);
//ZZZ drop or introduce errors here
			if(link->tq == nil)
				link->tq = bp;
			else
				link->tqtail->next = bp;
			link->tqtail = bp;
		}else
			link->tout = 0;
	}

	/*
	 * put more blocks into the receive queue
	 */
	t = 0;
	while(bp = link->tq){
		t = gtime(bp->rp);
		if(t > now)
			break;
		bp->rp += TMSIZE;
		link->tq = bp->next;
		bp->next = nil;
		if(link->nodrop)
			qpassnolim(link->iq, bp);
		else if(qpass(link->iq, bp) < 0)
			link->soverflows++;
		t = 0;
	}
	if(bp == nil && qisclosed(link->oq) && !qcanread(link->oq) && !qisclosed(link->iq))
		qhangup(link->iq, nil);
	link->tin = t;
	if(!t || t < link->tout)
		t = link->tout;
	iunlock(link);
	return t;
}

static void
ptime(uchar *p, vlong t)
{
	ulong tt;

	tt = t >> 32;
	p[0] = tt >> 24;
	p[1] = tt >> 16;
	p[2] = tt >> 8;
	p[3] = tt;
	tt = t;
	p[4] = tt >> 24;
	p[5] = tt >> 16;
	p[6] = tt >> 8;
	p[7] = tt;
}

static vlong
gtime(uchar *p)
{
	ulong t1, t2;

	t1 = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
	t2 = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7];
	return ((vlong)t1 << 32) | t2;
}

Dev loopbackdevtab = {
	'X',
	"loopback",

	devreset,
	loopbackinit,
	loopbackattach,
	loopbackclone,
	loopbackwalk,
	loopbackstat,
	loopbackopen,
	devcreate,
	loopbackclose,
	loopbackread,
	loopbackbread,
	loopbackwrite,
	loopbackbwrite,
	devremove,
	devwstat,
};

M port/devsd.c => port/devsd.c +50 -56
@@ 78,7 78,9 @@ sdaddpart(SDunit* unit, char* name, ulong start, ulong end)
	/*
	 * Check there is a free slot and size and extent are valid.
	 */
	if(partno == -1 || start > end || end > unit->sectors)
	if(partno == -1)
		error(Ebadctl);
	if(start > end || end > unit->sectors)
		error(Eio);
	pp = &unit->part[partno];
	pp->start = start;


@@ 87,7 89,6 @@ sdaddpart(SDunit* unit, char* name, ulong start, ulong end)
	strncpy(pp->user, eve, NAMELEN);
	pp->perm = 0640;
	pp->valid = 1;
	unit->npart++;
}

static void


@@ 99,8 100,6 @@ sddelpart(SDunit* unit,  char* name)
	/*
	 * Look for the partition to delete.
	 * Can't delete if someone still has it open.
	 * If it's the last valid partition zap the
	 * whole table.
	 */
	pp = unit->part;
	for(i = 0; i < SDnpart; i++){


@@ 112,29 111,24 @@ sddelpart(SDunit* unit,  char* name)
		error(Ebadctl);
	if(strncmp(up->user, pp->user, NAMELEN) && !iseve())
		error(Eperm);
	if(pp->nopen)
		error(Einuse);
	pp->valid = 0;

	unit->npart--;
	if(unit->npart == 0){
		free(unit->part);
		unit->part = nil;
	}
	pp->vers++;
}

static int
sdinitpart(SDunit* unit)
{
	int nf;
	int i, nf;
	ulong start, end;
	char *f[4], *p, *q, buf[10];

	unit->vers++;
	unit->sectors = unit->secsize = 0;
	unit->npart = 0;
	if(unit->part){
		free(unit->part);
		unit->part = nil;
		for(i = 0; i < SDnpart; i++){
			unit->part[i].valid = 0;
			unit->part[i].vers++;
		}
	}

	if(unit->inquiry[0] & 0xC0)


@@ 322,22 316,20 @@ sd2gen(Chan* c, int i, Dir* dp)
	SDpart *pp;
	SDunit *unit;

	unit = sdunit[UNIT(c->qid)];
	switch(i){
	case Qctl:
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qctl), c->qid.vers};
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qctl), unit->vers};
		devdir(c, q, "ctl", 0, eve, 0640, dp);
		return 1;
	case Qraw:
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qraw), c->qid.vers};
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qraw), unit->vers};
		devdir(c, q, "raw", 0, eve, CHEXCL|0600, dp);
		return 1;
	case Qpart:
		unit = sdunit[UNIT(c->qid)];
		if(unit->changed)
			break;
		pp = &unit->part[PART(c->qid)];
		l = (pp->end - pp->start) * (vlong)unit->secsize;
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qpart), c->qid.vers};
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qpart), unit->vers+pp->vers};
		if(pp->user[0] == '\0')
			strncpy(pp->user, eve, NAMELEN);
		devdir(c, q, pp->name, l, pp->user, pp->perm, dp);


@@ 392,8 384,16 @@ sdgen(Chan* c, Dirtab*, int, int s, Dir* dp)
		}
		unit = sdunit[UNIT(c->qid)];
		qlock(&unit->ctl);
		if(!unit->changed && unit->sectors == 0)

		/*
		 * Check for media change.
		 * If one has already been detected, sectors will be zero.
		 * If there is one waiting to be detected, online will return > 1.
		 * Online is a bit of a large hammer but does the job.
		 */
		if(unit->sectors == 0 || (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1))
			sdinitpart(unit);

		i = s+Qunitbase;
		if(i < Qpart){
			r = sd2gen(c, i, dp);


@@ 401,17 401,17 @@ sdgen(Chan* c, Dirtab*, int, int s, Dir* dp)
			return r;
		}
		i -= Qpart;
		if(unit->npart == 0 || i >= SDnpart){
		if(unit->part == nil || i >= SDnpart){
			qunlock(&unit->ctl);
			break;
		}
		pp = &unit->part[i];
		if(unit->changed || !pp->valid){
		if(!pp->valid){
			qunlock(&unit->ctl);
			return 0;
		}
		l = (pp->end - pp->start) * (vlong)unit->secsize;
		q = (Qid){QID(UNIT(c->qid), i, Qpart), c->qid.vers};
		q = (Qid){QID(UNIT(c->qid), i, Qpart), unit->vers+pp->vers};
		if(pp->user[0] == '\0')
			strncpy(pp->user, eve, NAMELEN);
		devdir(c, q, pp->name, l, pp->user, pp->perm, dp);


@@ 493,8 493,13 @@ sdopen(Chan* c, int omode)
	switch(TYPE(c->qid)){
	default:
		break;
	case Qctl:
		unit = sdunit[UNIT(c->qid)];
		c->qid.vers = unit->vers;
		break;
	case Qraw:
		unit = sdunit[UNIT(c->qid)];
		c->qid.vers = unit->vers;
		if(!canlock(&unit->rawinuse)){
			c->flag &= ~COPEN;
			error(Einuse);


@@ 509,11 514,8 @@ sdopen(Chan* c, int omode)
			c->flag &= ~COPEN;
			nexterror();
		}
		if(unit->changed)
			error(Eio);
		pp = &unit->part[PART(c->qid)];
		pp->nopen++;
		unit->nopen++;
		c->qid.vers = unit->vers+pp->vers;
		qunlock(&unit->ctl);
		poperror();
		break;


@@ 524,7 526,6 @@ sdopen(Chan* c, int omode)
static void
sdclose(Chan* c)
{
	SDpart *pp;
	SDunit *unit;

	if(c->qid.path & CHDIR)


@@ 539,28 540,13 @@ sdclose(Chan* c)
		unit = sdunit[UNIT(c->qid)];
		unlock(&unit->rawinuse);
		break;
	case Qpart:
		unit = sdunit[UNIT(c->qid)];
		qlock(&unit->ctl);
		if(waserror()){
			qunlock(&unit->ctl);
			c->flag &= ~COPEN;
			nexterror();
		}
		pp = &unit->part[PART(c->qid)];
		pp->nopen--;
		unit->nopen--;
		if(unit->nopen == 0)
			unit->changed = 0;
		qunlock(&unit->ctl);
		poperror();
		break;
	}
}

static long
sdbio(Chan* c, int write, char* a, long len, vlong off)
{
	int nchange;
	long l;
	uchar *b;
	SDpart *pp;


@@ 569,12 555,21 @@ sdbio(Chan* c, int write, char* a, long len, vlong off)

	unit = sdunit[UNIT(c->qid)];

	nchange = 0;
	qlock(&unit->ctl);
	if(waserror()){
	while(waserror()){
		/* notification of media change; go around again */
		if(strcmp(up->error, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){
			sdinitpart(unit);
			continue;
		}

		/* other errors; give up */
		qunlock(&unit->ctl);
		nexterror();
	}
	if(unit->changed)
	pp = &unit->part[PART(c->qid)];
	if(unit->vers+pp->vers != c->qid.vers)
		error(Eio);

	/*


@@ 588,7 583,6 @@ sdbio(Chan* c, int write, char* a, long len, vlong off)
	 * (sectors, secsize) can't change once the drive has
	 * been brought online.
	 */
	pp = &unit->part[PART(c->qid)];
	bno = (off/unit->secsize) + pp->start;
	nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno;
	max = SDmaxio/unit->secsize;


@@ 728,7 722,9 @@ sdread(Chan *c, void *a, long n, vlong off)
		 */
		if(unit->dev->ifc->rctl)
			l += unit->dev->ifc->rctl(unit, p+l, READSTR-l);
		if(!unit->changed && unit->sectors){
		if(unit->sectors == 0)
			sdinitpart(unit);
		if(unit->sectors){
			if(unit->dev->ifc->rctl == nil)
				l += snprint(p+l, READSTR-l,
					"geometry %ld %ld\n",


@@ 789,13 785,13 @@ sdwrite(Chan *c, void *a, long n, vlong off)
			free(cb);
			nexterror();
		}
		if(unit->changed)
		if(unit->vers != c->qid.vers)
			error(Eio);

		if(cb->nf < 1)
			error(Ebadctl);
		if(strcmp(cb->f[0], "part") == 0){
			if(cb->nf != 4 || unit->npart >= SDnpart)
			if(cb->nf != 4)
				error(Ebadctl);
			if(unit->sectors == 0 && !sdinitpart(unit))
				error(Eio);


@@ 874,10 870,8 @@ sdwstat(Chan* c, char* dp)
		nexterror();
	}

	if(unit->changed)
		error(Enonexist);
	pp = &unit->part[PART(c->qid)];
	if(!pp->valid)
	if(unit->vers+pp->vers != c->qid.vers)
		error(Enonexist);
	if(strncmp(up->user, pp->user, NAMELEN) && !iseve())
		error(Eperm);

M port/devsdp.c => port/devsdp.c +0 -1
@@ 528,7 528,6 @@ sdpclose(Chan* ch)
			}
		}
		qunlock(c);
		poperror();
		break;
	}
}

M port/sd.h => port/sd.h +3 -5
@@ 14,7 14,7 @@ typedef struct SDpart {
	char	user[NAMELEN];
	ulong	perm;
	int	valid;
	int	nopen;			/* of this partition */
	ulong	vers;
} SDpart;

typedef struct SDunit {


@@ 27,10 27,8 @@ typedef struct SDunit {
	QLock	ctl;
	ulong	sectors;
	ulong	secsize;
	SDpart*	part;
	int	npart;			/* of valid partitions */
	int	nopen;			/* of partitions on this unit */
	int	changed;
	SDpart*	part;		/* either nil or points at array of size SDnpart */
	ulong	vers;

	Lock	rawinuse;		/* really just a test-and-set */
	int	state;