~kris/9p

9hist

75d90bb5b7dfe29c85fbe05091c09cfc3e640de7 — David du Colombier 35 years ago 2412f28
Plan 9 from Bell Labs 1991-08-28
7 files changed, 112 insertions(+), 658 deletions(-)

M gnot/mmu.c
M pc/main.c
M port/devdk.c
M port/devlance.c
M port/devsrv.c
D port/filsys.c
D port/service.c
M gnot/mmu.c => gnot/mmu.c +9 -3
@@ 143,9 143,13 @@ KMap*
kmap(Page *pg)
{
	KMap *k;
	int s;
	int s = 0;

	s = splhi();
	if(u) {
		s = u->p->state;
		u->p->state = MMUing;
	}
	
	lock(&kmapalloc);
	k = kmapalloc.free;
	if(k == 0){


@@ 154,10 158,12 @@ kmap(Page *pg)
	}
	kmapalloc.free = k->next;
	unlock(&kmapalloc);
	splx(s);

	k->pa = pg->pa;
	putkmmu(k->va, PPN(k->pa) | PTEVALID | PTEKERNEL);
	if(s)
		u->p->state = s;

	return k;
}


M pc/main.c => pc/main.c +3 -3
@@ 181,14 181,14 @@ confinit(void)
	conf.maxialloc = 2*1024*1024;

	mul = 1;
	conf.nproc = 10 + 10*mul;
	conf.nproc = 10 + 20*mul;
	conf.npgrp = conf.nproc/2;
	conf.nseg = conf.nproc*3;
	conf.npagetab = (conf.nseg*14)/10;
	conf.nswap = conf.nproc*80;
	conf.nimage = 50;
	conf.nalarm = 1000;
	conf.nchan = 4*conf.nproc;
	conf.nchan = 6*conf.nproc;
	conf.nenv = 4*conf.nproc;
	conf.nenvchar = 8000*mul;
	conf.npgenv = 200*mul;


@@ 198,7 198,7 @@ confinit(void)
	conf.nmntbuf = conf.nmntdev+3;
	conf.nmnthdr = 2*conf.nmntdev;
	conf.nsrv = 16*mul;			/* was 32 */
	conf.nbitmap = 256*mul;
	conf.nbitmap = 512*mul;
	conf.nbitbyte = conf.nbitmap*1024*screenbits();
	conf.nfont = 10*mul;
	conf.nnoifc = 1;

M port/devdk.c => port/devdk.c +1 -1
@@ 1469,7 1469,7 @@ dkcsckproc(void *a)
		
		case T_RESTART:	/* datakit reboot */
			if(line >=0 && line<dp->lines)
				dp->lines=line+1;
				dp->lines = line+1;
			break;
		
		default:

M port/devlance.c => port/devlance.c +27 -10
@@ 183,7 183,9 @@ static SoftLance l;
 *  predeclared
 */
static void lancekproc(void *);
static void lancestart(int);
static void lancestart(int, int);
static void lancedump(void);

/*
 *  lance stream module definition
 */


@@ 228,7 230,7 @@ lancestclose(Queue *q)
		qlock(&l);
		l.prom--;
		if(l.prom == 0)
			lancestart(0);
			lancestart(0, 1);
		qunlock(&l);
	}
	qlock(et);


@@ 266,7 268,7 @@ lanceoput(Queue *q, Block *bp )
			qlock(&l);
			l.prom++;
			if(l.prom == 1)
				lancestart(PROM);
				lancestart(PROM, 1);
			qunlock(&l);
		}
		freeb(bp);


@@ 283,13 285,15 @@ lanceoput(Queue *q, Block *bp )
	 *  only one transmitter at a time
	 */
	qlock(&l.tlock);
	if(waserror()){
		qunlock(&l.tlock);
		nexterror();
	}

	/*
	 *  Wait till we get an output buffer
	 */
	if(TSUCC(l.tc) == l.tl){
		sleep(&l.tr, isobuf, (void *)0);
	}
	sleep(&l.tr, isobuf, (void *)0);
	p = &l.tp[l.tc];

	/*


@@ 335,6 339,7 @@ lanceoput(Queue *q, Block *bp )
	*l.rdp = INEA|TDMD; /**/
	wbflush();
	qunlock(&l.tlock);
	poperror();
}

/*


@@ 377,7 382,7 @@ lancereset(void)
 *  It may be used to restart a dead lance.
 */
static void
lancestart(int mode)
lancestart(int mode, int dolock)
{
	int i;
	Etherpkt *p;


@@ 388,8 393,10 @@ lancestart(int mode)
	 *   wait till both receiver and transmitter are
	 *   quiescent
	 */
	qlock(&l.tlock);
	qlock(&l.rlock);
	if(dolock){
		qlock(&l.tlock);
		qlock(&l.rlock);
	}

	lancereset();
	l.rl = 0;


@@ 509,7 516,7 @@ lanceattach(char *spec)
	if(l.kstarted == 0){
		kproc("lancekproc", lancekproc, 0);/**/
		l.kstarted = 1;
		lancestart(0);
		lancestart(0, 1);
	}
	return devattach('l', spec);
}


@@ 625,6 632,7 @@ lanceintr(void)
	int i;
	ushort csr;
	Lancemem *lm = LANCEMEM;
	static int misses;

	csr = *l.rdp;



@@ 638,6 646,8 @@ lanceintr(void)
	 */
	if(csr & (BABL|MISS|MERR)){
		print("lance err %ux\n", csr);
		if(csr & MISS)
			lancedump();
	}

	if(csr & IDON){


@@ 650,6 660,7 @@ lanceintr(void)
	 *  look for rcv'd packets, just wakeup the input process
	 */
	if(l.rl!=l.rc && (MPus(lm->rmr[l.rl].flags) & OWN)==0){
		misses = 0;
		wakeup(&l.rr);
	}



@@ 757,3 768,9 @@ stage:
		sleep(&l.rr, isinput, 0);
	}
}

static void
lancedump(void)
{
	print("l.rl %d l.rc %d l.tl %d l.tc %d\n", l.rl, l.rc, l.tl, l.tc);
}

M port/devsrv.c => port/devsrv.c +72 -169
@@ 4,27 4,26 @@
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"
#include	"devtab.h"
#include	"fcall.h"

void *calloc(unsigned, unsigned);
void free(void *);
#include	"devtab.h"

/* This structure holds the contents of a directory entry.  Entries are kept
 * in a linked list.
 */
typedef struct Entry Entry;
struct Entry {
typedef struct	Srv Srv;
struct Srv{
	Lock;
	Entry *next;		/* next entry */
	Entry **back;		/* entry pointer */
	Entry *parent;		/* parent directory */
	Dir dir;		/* dir structure */
	union{
		Chan *chan;	/* if not a subdirectory */
		Entry *entries;	/* directory entries */
	};
};
	char	*name;
	Chan	**chan;
}srv;

int
srvgen(Chan *c, Dirtab *tab, int ntab, int s, Dir *dp)
{
	if(s >= conf.nsrv)
		return -1;
	if(srv.chan[s] == 0)
		return 0;
	devdir(c, (Qid){s, 0}, &srv.name[s*NAMELEN], 0, 0666, dp);
	return 1;
}

void
srvinit(void)


@@ 34,92 33,40 @@ srvinit(void)
void
srvreset(void)
{
}

Entry *
srvalloc(int mode){
	Entry *e;
	static Lock qidlock;
	static nextqid;

	e = calloc(1, sizeof(Entry));
	e->dir.atime = e->dir.mtime = seconds();
	lock(&qidlock);	/* for qid allocation */
	e->dir.qid = (Qid){mode|nextqid++, 0};
	unlock(&qidlock);
	return e;
	srv.chan = ialloc(conf.nsrv*sizeof(Chan*), 0);
	srv.name = ialloc(conf.nsrv*NAMELEN, 0);
}

Chan *
srvattach(char *spec)
{
	Chan *c;
	static Lock rootlock;
	static Entry *root;

	lock(&rootlock);
	if(root==0){
		root = srvalloc(CHDIR);
		root->dir.mode = CHDIR | 0777;
	}
	unlock(&rootlock);
	c = devattach('s', spec);
	c->qid = root->dir.qid;
	c->aux = root;
	return c;
	return devattach('s', spec);
}

Chan *
srvclone(Chan *c, Chan *nc)
{
	nc = devclone(c, nc);
	return nc;
	return devclone(c, nc);
}

int
srvwalk(Chan *c, char *name)
{
	Entry *dir, *e;

	isdir(c);
	if(strcmp(name, ".") == 0)
		return 1;
	if((dir=c->aux) == 0)
		panic("bad aux pointer in srvwalk");
	if(strcmp(name, "..") == 0)
		e = dir->parent;
	else{
		lock(dir);
		for(e=dir->entries; e; e=e->next)
			if (strcmp(name, e->dir.name) == 0)
				break;
		unlock(dir);
	}
	if(e==0){
		strncpy(u->error, errstrtab[Enonexist], NAMELEN);
		return 0;
	}
	c->qid = e->dir.qid;
	c->aux = e;
	return 1;
	return devwalk(c, name, 0, 0, srvgen);
}

void
srvstat(Chan *c, char *db)
{
	Entry *e;

	e = c->aux;
	convD2M(&e->dir, db);
	devstat(c, db, 0, 0, srvgen);
}

Chan *
srvopen(Chan *c, int omode)
{
	Entry *e;
	Chan *f;

	if(c->qid.path & CHDIR){
	if(c->qid.path == CHDIR){
		if(omode != OREAD)
			error(Eisdir);
		c->mode = omode;


@@ 127,78 74,77 @@ srvopen(Chan *c, int omode)
		c->offset = 0;
		return c;
	}
	if((e=c->aux) == 0)
		error(Egreg);
	if((f=e->chan) == 0)
	lock(&srv);
	if(waserror()){
		unlock(&srv);
		nexterror();
	}
	f = srv.chan[c->qid.path];
	if(f == 0)
		error(Eshutdown);
	if(omode & OTRUNC)
	if(omode&OTRUNC)
		error(Eperm);
	if(omode!=f->mode && f->mode!=ORDWR)
		error(Eperm);
	close(c);
	incref(f);
	unlock(&srv);
	poperror();
	return f;
}

void
srvcreate(Chan *c, char *name, int omode, ulong perm)
{
	Entry *parent, *e;
	int j, i;

	parent = c->aux;
	isdir(c);
	lock(parent);
	if (waserror()){
		unlock(parent);
	if(omode != OWRITE)
		error(Eperm);
	lock(&srv);
	if(waserror()){
		unlock(&srv);
		nexterror();
	}
	for(e=parent->entries; e; e=e->next)
		if(strcmp(name, e->dir.name) == 0)
	j = -1;
	for(i=0; i<conf.nsrv; i++){
		if(srv.chan[i] == 0){
			if(j == -1)
				j = i;
		}else if(strcmp(name, &srv.name[i*NAMELEN]) == 0)
			error(Einuse);
	e = srvalloc(perm & CHDIR);
	e->parent = parent;
	strcpy(e->dir.name, name);
	e->dir.mode = perm & parent->dir.mode;
	strcpy(e->dir.gid, parent->dir.gid);
	if(e->next = parent->entries)	/* assign = */
		e->next->back = &e->next;
	e->back = &parent->entries;
	*e->back = e;
	parent->dir.mtime = e->dir.mtime;
	unlock(parent);
	}
	if(j == -1)
		error(Enosrv);
	srv.chan[j] = c;
	unlock(&srv);
	poperror();
	c->qid = e->dir.qid;
	c->aux = e;
	strcpy(&srv.name[j*NAMELEN], name);
	c->qid.path = j;
	c->flag |= COPEN;
	c->mode = omode;
	c->mode = OWRITE;
}

void
srvremove(Chan *c)
{
	Entry *e;
	Chan *f;

	e = c->aux;
	if(e->parent == 0)
	if(c->qid.path == CHDIR)
		error(Eperm);
	lock(e->parent);
	lock(&srv);
	if(waserror()){
		unlock(e->parent);
		unlock(&srv);
		nexterror();
	}
	if(e->dir.mode & CHDIR){
		if (e->entries != 0)
			error(Eperm);
	}else{
		if(e->chan == 0)
			error(Eshutdown);
		close(e->chan);
	}
	if(*e->back = e->next)	/* assign = */
		e->next->back = e->back;
	unlock(e->parent);
	f = srv.chan[c->qid.path];
	if(f == 0)
		error(Eshutdown);
	if(strcmp(&srv.name[c->qid.path*NAMELEN], "boot") == 0)
		error(Eperm);
	srv.chan[c->qid.path] = 0;
	unlock(&srv);
	poperror();
	free(e);
	close(f);
}

void


@@ 212,72 158,29 @@ srvclose(Chan *c)
{
}

/* A directory is being read.  The entries must be synthesized.  e points
 * to a list of entries in this directory.  Count is the size to be
 * read.
 */
int
srvdirentry(Entry *e, char *a, long count){
	Dir dir;
	int n;

	n = 0;
	while(n!=count && e!=0){
		n += convD2M(&e->dir, a + n);
		e = e->next;
	}
	return n;
}

long
srvread(Chan *c, void *va, long n, ulong offset)
{
	Entry *dir, *e;

	dir = c->aux;
	isdir(c);
	if(n <= 0)
		return 0;
	if(offset%DIRLEN || n%DIRLEN)
		error(Ebaddirread);
	lock(dir);
	for(e=dir->entries; e; e=e->next)
		if(offset <= 0){
			n = srvdirentry(e, va, n);
			unlock(dir);
			c->offset += n;
			return n;
		}else
			offset -= DIRLEN;
	unlock(dir);
	return 0;
	return devdirread(c, va, n, 0, 0, srvgen);
}

long
srvwrite(Chan *c, void *va, long n, ulong offset)
{
	Entry *e;
	int i, fd;
	char buf[32];

	e = c->aux;
	if(e->dir.mode & CHDIR)
		panic("write to directory");
	lock(e);
	if(waserror()){
		unlock(e);
		nexterror();
	}
	if(e->chan)
	i = c->qid.path;
	if(srv.chan[i] != c)	/* already been written to */
		error(Egreg);
	if(n >= sizeof buf)
		error(Egreg);
	memmove(buf, va, n);	/* so we can NUL-terminate */
	buf[n] = 0;
	fd = strtoul(buf, 0, 0);
	e->chan = fdtochan(fd, -1);
	incref(e->chan);
	unlock(e);
	poperror();
	fdtochan(fd, -1);	/* error check only */
	srv.chan[i] = u->p->fgrp->fd[fd];
	incref(u->p->fgrp->fd[fd]);
	return n;
}

D port/filsys.c => port/filsys.c +0 -250
@@ 1,250 0,0 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"

#include	"devtab.h"

#include	"fcall.h"

#define PRINT print

enum {
	Rreadhlen = 8,	/* number of chars in Rread message hdr */
};

typedef struct Ftrans Ftrans;

struct Ftrans{
	long	fid;
	Chan	*c;
	Chan	*pc;
};

static	Ftrans	*fsyshash;
static	Lock	fsyslock;

static	void	fsysnewchan(Chan *, long, Chan *);
static	Chan	*fsyschan(Chan *, long);
static	int	fsyserr(void);

void
filsysinit(void)
{
	fsyshash = ialloc(conf.nfsyschan * sizeof(Ftrans), 0);
}

void
filsys(Chan *protoc, char *msg, long msgn)
{
	Fcall r, t, *rf, *tf;
	Chan *c, *c0;
	long fid, n;

	if(!convM2S(msg, &r, msgn)){
		error(Ebadmsg);
	}
	rf = &r;
	tf = &t;
	if(waserror()){
		tf->type = Rerror;
		strncpy(tf->ename, u->error, sizeof(tf->ename));
		goto Errret;
	}
	fid = rf->fid;
	if(rf->type != Tattach)
		c = fsyschan(protoc, fid);
	switch(rf->type){
	case Tattach:
		c = clone(u->slash, 0);
		fsysnewchan(protoc, fid, c);
		break;
	case Tclone:
		c0 = clone(c, 0);
		c0 = domount(c0);
		fsysnewchan(protoc, rf->newfid, c0);
		break;
	case Twalk:
		if((c0 = walk(c, rf->name, 1)) == 0)
			error(Enonexist);
		if(c != c0)
			fsysnewchan(protoc, fid, c0);
		tf->qid = c0->qid;
		break;
	case Topen:
		c0 = (*devtab[c->type].open)(c, rf->mode);
		if(c != c0)
			fsysnewchan(protoc, fid, c0);
		if(rf->mode & OCEXEC)
			c0->flag |= CCEXEC;
		break;
	case Tcreate:
		if((c->flag&(CMOUNT|CCREATE)) == CMOUNT)
			c0 = createdir(c);
		(*devtab[c->type].create)(c, rf->name, rf->mode, rf->perm);
		if(rf->mode & OCEXEC)
			c->flag |= CCEXEC;
		break;
	case Tread:
		qlock(c);
		if(waserror()){
			qunlock(c);
			nexterror();
		}
		n = rf->count;
		tf->count = 0;
		/* overwrite the input msg -- assumes buffer is big enough */
		tf->data = msg+Rreadhlen;
		if(c->qid.path&CHDIR){
			n -= n%DIRLEN;
			if(c->offset%DIRLEN || n==0)
				error(Ebaddirread);
		}
		if((c->qid.path&CHDIR) && (c->flag&CMOUNT))
			n = unionread(c, tf->data, n);
		else
			n = (*devtab[c->type].read)(c, tf->data, n);
		c->offset += n;
		qunlock(c);
		poperror();
		tf->count = n;
		break;
	case Twrite:
		qlock(c);
		if(waserror()){
			qunlock(c);
			nexterror();
		}
		if(c->qid.path & CHDIR)
			error(Eisdir);
		n = rf->count;
		tf->count = 0;
		n = (*devtab[c->type].write)(c, rf->data, n);
		c->offset += n;
		qunlock(c);
		poperror();
		tf->count = n;
		break;
	case Tclunk:
		close(c);
		fsysnewchan(protoc, rf->fid, 0);
		break;
	case Tremove:
		(*devtab[c->type].remove)(c);
		break;
	case Tstat:
		(*devtab[c->type].stat)(c, tf->stat);
		break;
	case Twstat:
		(*devtab[c->type].wstat)(c, rf->stat);
		break;
	case Tflush:
		/* really, this message has to be handled by caller */
		break;
	default:
		error(Ebadmsg);
	}
	poperror();
	tf->type = rf->type+1;
   Errret:
	tf->fid = rf->fid;
	tf->tag = rf->tag;
	/* assume can reuse msg buf */
	n = convS2M(tf, msg);
	qlock(protoc);
	if(waserror()){
		qunlock(protoc);
		nexterror();
	}
	(*devtab[protoc->type].write)(protoc, msg, n);
	qunlock(protoc);
	poperror();
}

/*
 * hash table discipline: linear probe
 *    a fid of 0 means 'free'; a fid of -1 means 'deleted'
 *    use a function as per Knuth
 *    only a function of fid, but protoc checked for match
 */
static int
fhash(long fid)
{
	long x;

	x = fid*29128;
	if(x < 0)
		x = -x;
	x %= 262143;
	return x % conf.nfsyschan;
}

/*
 * Register channel c for fid (if c!= 0).
 * If c==0, unregister.
 * The protoc argument is used to disambiguate
 * identical fids from different attaches.
 */
static void
fsysnewchan(Chan *protoc, long fid, Chan *c)
{
	int i, m;
	Ftrans *f, *fstop, *fend;

	i = fhash(fid);
	f = fsyshash+i;
	m = conf.nfsyschan;
	fstop = f+(i+m-1)%m;
	fend = fsyshash+m;
	lock(&fsyslock);
	while(f != fstop){
		if(f->fid == fid && f->pc == protoc){
			if(c)
				f->c = c;
			else
				f->fid = -1;
			break;
		}
		if(f->fid <= 0){
			f->c = c;
			f->fid = fid;
			f->pc = protoc;
			break;
		}
		if(++f == fend)
			f = fsyshash;	
	}
	unlock(&fsyslock);
	if(f == fstop)
		error(Enofilsys);
}

/* Return the channel registered for fid */
static Chan*
fsyschan(Chan *protoc, long fid)
{
	int i, m;
	Ftrans *f, *fstop, *fend;

	i = fhash(fid);
	f = fsyshash+i;
	if(f->fid == fid && f->pc == protoc)
		return f->c;
	m = conf.nfsyschan;
	fstop = f;
	if(++f == fend)
		f = fsyshash;
	fend = fsyshash+m;
	while(f != fstop){
		if(f->fid == fid && f->pc == protoc)
			return f->c;
		if(f->fid == 0)
			break;
		if(++f == fend)
			f = fsyshash;	
	}
	error(Egreg);
}


D port/service.c => port/service.c +0 -222
@@ 1,222 0,0 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"

#include	"fcall.h"

#define	BUFSIZE	(MAXFDATA+500) 	/* BUG */
typedef struct Buffer Buffer;
struct Buffer
{
	Buffer	*next;
	char	buf[BUFSIZE];
};

struct Servalloc {
	Lock;
	Service *all;
	Service *freel;
	Buffer	*ball;
	Buffer	*bfreel;
} servalloc;


/*
 *  allocate the taks force structures and put them
 *  on the free list
 */
void
serviceinit(void)
{
	Service *s;
	Buffer *b;

	servalloc.all = ialloc(conf.nservice * sizeof(Service), 0);
	servalloc.freel = servalloc.all;
	for(s = servalloc.all; s < &servalloc.all[conf.nservice-1]; s++)
		s->next = s+1;

	servalloc.ball = ialloc(2*conf.nservice * sizeof(Service), 0);
	servalloc.bfreel = servalloc.ball;
	for(b = servalloc.ball; b < &servalloc.ball[2*conf.nservice-1]; b++)
		b->next = b+1;
}

/*
 *  Add a process to a service
 */
static Proc *
reinforce(Service *s)
{
	int i;
	Proc *p;

	/*
	 *  don't start one up if we already have too many,
	 *  return our own process number
	 */
	lock(s);
	if(s->ref == NSTUB){
		unlock(s);
		return u->p;
	}
	s->ref++;
	unlock(s);

	/*
	 *  create it
	 */
	return kproc(s->name, 0, 0);
}

/*
 *  resign from a service
 */
static void
resign(Service *s)
{
	int i;

	print("%lux resigning\n", u->p);
	if(decref(s) == 0){
		print("freeing service\n");
		close(s->inc);
		close(s->outc);
		lock(&servalloc);
		s->next = servalloc.freel;
		servalloc.freel = s;
		unlock(&servalloc);
	}
	pexit(0, 1);
}

/*
 *  Create a service of kernel processes to handle file system requests.
 *  (*doit)() is a function that handles that event.  Service
 *  dynamicly creates new processes (up to MAXTASK) to ensure that 1 process
 *  is always awaiting while others are doing.
 */
void
service(char *name, Chan *cin, Chan *cout, void (*doit)(Chan*, char*, long))
{
	void *a;
	Service *s;
	Buffer *b;
	long n;

	b = 0;

	/*
	 *  allocate a service
	 */
	lock(&servalloc);
	s = servalloc.freel;
	if(s == 0)
		panic("no more services");
	servalloc.freel = s->next;
	unlock(&servalloc);
	memset(s, 0, sizeof(Service));
	if(name==0 || *name==0)
		strcpy(name, "filsys");
	else
		strncpy(s->name, name, sizeof(s->name));
	s->inc = cin;
	s->outc = cout;
	s->die = 0;
	incref(cin);
	incref(cout);

	if(waserror()){
		/*
		 *  tell everyone else to die
		 */
		s->die = 1;

		/*
		 *  wake up the next guy
		 */
		qunlock(&s->alock);

		/*
		 *  free the buffer if we have one
		 */
		if(b){
			lock(&servalloc);
			b->next = servalloc.bfreel;
			servalloc.bfreel = b;
			unlock(&servalloc);
		}
		resign(s);
	}

	/*
	 *  Create the first process in the service.  The
	 *  caller returns.
	 */
	if(reinforce(s)){
		poperror();
		return;
	}

	/*
	 *  first process enters the infinite loop
	 */
	for(;;){
		b = 0;
		qlock(&s->alock);

		/*
		 *  if there's more than 1 process waiting, we're
		 *  superfluous
		 */
		if(s->die || s->alock.head && s->alock.head!=s->alock.tail){
			qunlock(&s->alock);
			resign(s);
		}

		/*
		 *  get a buffer
		 */
		lock(&servalloc);
		b = servalloc.bfreel;
		if(b == 0){
			print("no serv buffers\n");
			unlock(&servalloc);
			continue;
		}
		servalloc.bfreel = b->next;
		unlock(&servalloc);

		/*
		 *  wait for something to happen
		 */
		n = (*devtab[cin->type].read)(cin, b->buf, sizeof(b->buf));
		if(n <= 0)
			error(Ehungup);

		/*
		 *  create a new process if none are waiting to
		 *  take over
		 */
		if(s->alock.head == 0)
			if(reinforce(s) == 0)
				continue;
		qunlock(&s->alock);

		/*
		 *  process an event
		 */
		(*doit)(cout, b->buf, n);

		/*
		 *  release the buffer
		 */
		lock(&servalloc);
		b->next = servalloc.bfreel;
		servalloc.bfreel = b;
		unlock(&servalloc);
	}
}