text: #include <u.h>
  #include <libc.h>
  #include <venti.h>
  #include <libsec.h>
  
  typedef struct Mem Mem;
  typedef struct Frag Frag;
  
  enum {
  	BigMemSize = MaxFragSize,
  	SmallMemSize = BigMemSize/8,
  	NLocalFrag = 2
  };
  
  /* position to carve out of a Mem */
  enum {
  	PFront,
  	PMiddle,
  	PEnd
  };
  
  struct Mem
  {
  	Lock lk;
  	int ref;
  	uchar *bp;
  	uchar *ep;
  	uchar *rp;
  	uchar *wp;
  	Mem *next;
  };
  
  enum {
  	FragLocalFree,
  	FragLocalAlloc,
  	FragGlobal
  };
  	
  struct Frag
  {
  	int state;
  	Mem *mem;
  	uchar *rp;
  	uchar *wp;
  	Frag *next;
  	void (*free)(void*);
  	void *a;
  	Packet *p;	/* parent packet, for debugging only */
  };
  
  struct Packet
  {
  	int size;
  	int asize;  /* allocated memory - greater than size unless foreign frags */
  	ulong pc;
  
  	Packet *next;
  	
  	Frag *first;
  	Frag *last;
  	
  	Frag local[NLocalFrag];
  };
  
  static Frag *fragalloc(Packet*, int n, int pos, Frag *next);
  static Frag *fragdup(Packet*, Frag*);
  static void fragfree(Frag*);
  
  static Mem *memalloc(int, int);
  static void memfree(Mem*);
  static int memhead(Mem *m, uchar *rp, int n);
  static int memtail(Mem *m, uchar *wp, int n);
  
  static char EPacketSize[] = "bad packet size";
  static char EPacketOffset[] = "bad packet offset";
  static char EBadSize[] = "bad size";
  
  #ifdef NOTDEF
  static void checkpacket(Packet*);
  #endif
  
  /*
   * the free list is primarily for speed, but it is 
   * also necessary for packetsplit that packets
   * are never freed -- a packet can contain a different
   * packet's local fragments, thanks to packetsplit!
   */
  static struct {
  	Lock lk;
  	Packet *packet;
  	int npacket;
  	Frag *frag;
  	int nfrag;
  	Mem *bigmem;
  	int nbigmem;
  	Mem *smallmem;
  	int nsmallmem;
  } freelist;
  
  #define FRAGSIZE(f) ((f)->wp - (f)->rp)
  #define FRAGASIZE(f) ((f)->mem ? (f)->mem->ep - (f)->mem->bp : 0)
  
  #define NOTFREE(p) assert((p)->size>=0)/*; checkpacket(p)*/
  
  Packet *
  packetalloc(void)
  {
  	Packet *p;
  
  	lock(&freelist.lk);
  	p = freelist.packet;
  	if(p != nil)
  		freelist.packet = p->next;
  	else
  		freelist.npacket++;
  	unlock(&freelist.lk);
  
  	if(p == nil)
  		p = vtbrk(sizeof(Packet));
  	else
  		assert(p->size == -1);
  	p->size = 0;
  	p->asize = 0;
  	p->first = nil;
  	p->last = nil;
  	p->next = nil;
  	p->pc = getcallerpc((char*)&p+8);	/* might not work, but fine */
  
  	NOTFREE(p);
  	return p;
  }
  
  void
  packetfree(Packet *p)
  {
  	Frag *f, *ff;
  
  	if(p == nil)
  		return;
  
  	NOTFREE(p);
  	p->pc = getcallerpc(&p);
  
  	for(f=p->first; f!=nil; f=ff) {
  		ff = f->next;
  		fragfree(f);
  	}
  	p->first = (void*)0xDeadBeef;
  	p->last = (void*)0xDeadBeef;
  	p->size = -1;
  
  	lock(&freelist.lk);
  	p->next = freelist.packet;
  	freelist.packet = p;
  	unlock(&freelist.lk);
  }
  
  Packet *
  packetdup(Packet *p, int offset, int n)
  {	
  	Frag *f, *ff;
  	Packet *pp;
  
  	NOTFREE(p);
  	if(offset < 0 || n < 0 || offset+n > p->size) {
  		werrstr(EBadSize);
  		return nil;
  	}
  
  	pp = packetalloc();
  	pp->pc = getcallerpc(&p);
  	if(n == 0){
  		NOTFREE(pp);
  		return pp;
  	}
  
  	pp->size = n;
  
  	/* skip offset */
  	for(f=p->first; offset >= FRAGSIZE(f); f=f->next)
  		offset -= FRAGSIZE(f);
  	
  	/* first frag */
  	ff = fragdup(pp, f);
  	ff->rp += offset;
  	pp->first = ff;
  	n -= FRAGSIZE(ff);
  	pp->asize += FRAGASIZE(ff);
  
  	/* the remaining */
  	while(n > 0) {
  		f = f->next;
  		ff->next = fragdup(pp, f);
  		ff = ff->next;
  		n -= FRAGSIZE(ff);
  		pp->asize += FRAGASIZE(ff);
  	}
  	
  	/* fix up last frag: note n <= 0 */
  	ff->wp += n;
  	ff->next = nil;
  	pp->last = ff;
  
  	NOTFREE(pp);
  	NOTFREE(p);
  	return pp;
  }
  
  Packet *
  packetsplit(Packet *p, int n)
  {
  	Packet *pp;
  	Frag *f, *ff;
  
  	NOTFREE(p);
  	if(n < 0 || n > p->size) {
  		werrstr(EPacketSize);
  		return nil;
  	}
  
  	pp = packetalloc();
  	pp->pc = getcallerpc(&p);
  	if(n == 0){
  		NOTFREE(pp);
  		return pp;
  	}
  
  	pp->size = n;
  	p->size -= n;
  	ff = nil;
  	for(f=p->first; n > 0 && n >= FRAGSIZE(f); f=f->next) {
  		n -= FRAGSIZE(f);
  		p->asize -= FRAGASIZE(f);
  		pp->asize += FRAGASIZE(f);
  		f->p = pp;
  		ff = f;
  	}
  
  	/* split shared frag */
  	if(n > 0) {
  		f->p = pp;
  		ff = f;
  		f = fragdup(p, ff);
  		pp->asize += FRAGASIZE(ff);
  		ff->wp = ff->rp + n;
  		f->rp += n;
  	}
  
  	pp->first = p->first;
  	pp->last = ff;
  	ff->next = nil;
  	p->first = f;
  	if(f == nil || f->next == nil)
  		p->last = f;
  	NOTFREE(pp);
  	NOTFREE(p);
  	return pp;
  }
  
  int
  packetconsume(Packet *p, uchar *buf, int n)
  {
  	NOTFREE(p);
  	if(buf && packetcopy(p, buf, 0, n) < 0)
  		return -1;
  	return packettrim(p, n, p->size-n);
  }
  
  int
  packettrim(Packet *p, int offset, int n)
  {
  	Frag *f, *ff;
  
  	NOTFREE(p);
  	if(offset < 0 || offset > p->size) {
  		werrstr(EPacketOffset);
  		return -1;
  	}
  
  	if(n < 0 || offset + n > p->size) {
  		werrstr(EPacketOffset);
  		return -1;
  	}
  
  	p->size = n;
  
  	/* easy case */
  	if(n == 0) {
  		for(f=p->first; f != nil; f=ff) {
  			ff = f->next;
  			fragfree(f);
  		}
  		p->first = p->last = nil;
  		p->asize = 0;
  		NOTFREE(p);
  		return 0;
  	}
  	
  	/* free before offset */
  	for(f=p->first; offset >= FRAGSIZE(f); f=ff) {
  		p->asize -= FRAGASIZE(f);
  		offset -= FRAGSIZE(f);
  		ff = f->next;
  		fragfree(f);
  	}
  
  	/* adjust frag */
  	f->rp += offset;
  	p->first = f;
  
  	/* skip middle */
  	for(; n > 0 && n > FRAGSIZE(f); f=f->next)
  		n -= FRAGSIZE(f);
  
  	/* adjust end */
  	f->wp = f->rp + n;
  	p->last = f;
  	ff = f->next;
  	f->next = nil;
  
  	/* free after */
  	for(f=ff; f != nil; f=ff) {
  		p->asize -= FRAGASIZE(f);
  		ff = f->next;
  		fragfree(f);
  	}
  	NOTFREE(p);
  	return 0;
  }
  
  uchar *
  packetheader(Packet *p, int n)
  {
  	Frag *f;
  	Mem *m;
  
  	NOTFREE(p);
  	if(n <= 0 || n > MaxFragSize) {
  		werrstr(EPacketSize);
  		return nil;
  	}
  
  	p->size += n;
  	
  	/* try and fix in current frag */
  	f = p->first;
  	if(f != nil) {
  		m = f->mem;
  		if(n <= f->rp - m->bp)
  		if(m->ref == 1 || memhead(m, f->rp, n) >= 0) {
  			f->rp -= n;
  			NOTFREE(p);
  			return f->rp;
  		}
  	}
  
  	/* add frag to front */
  	f = fragalloc(p, n, PEnd, p->first);
  	p->asize += FRAGASIZE(f);
  	if(p->first == nil)
  		p->last = f;
  	p->first = f;
  	NOTFREE(p);
  	return f->rp;
  }
  
  uchar *
  packettrailer(Packet *p, int n)
  {
  	Mem *m;
  	Frag *f;
  
  	NOTFREE(p);
  	if(n <= 0 || n > MaxFragSize) {
  		werrstr(EPacketSize);
  		return nil;
  	}
  
  	p->size += n;
  	
  	/* try and fix in current frag */
  	if(p->first != nil) {
  		f = p->last;
  		m = f->mem;
  		if(n <= m->ep - f->wp)
  		if(m->ref == 1 || memtail(m, f->wp, n) >= 0) {
  			f->wp += n;
  			NOTFREE(p);
  			return f->wp - n;
  		}
  	}
  
  	/* add frag to end */
  	f = fragalloc(p, n, (p->first == nil)?PMiddle:PFront, nil);
  	p->asize += FRAGASIZE(f);
  	if(p->first == nil)
  		p->first = f;
  	else
  		p->last->next = f;
  	p->last = f;
  	NOTFREE(p);
  	return f->rp;
  }
  
  void
  packetprefix(Packet *p, uchar *buf, int n)
  {
  	Frag *f;
  	int nn;
  	Mem *m;
  
  	NOTFREE(p);
  	if(n <= 0)
  		return;
  
  	p->size += n;
  
  	/* try and fix in current frag */
  	f = p->first;
  	if(f != nil) {
  		m = f->mem;
  		nn = f->rp - m->bp;
  		if(nn > n)
  			nn = n;
  		if(m->ref == 1 || memhead(m, f->rp, nn) >= 0) {
  			f->rp -= nn;
  			n -= nn;
  			memmove(f->rp, buf+n, nn);
  		}
  	}
  
  	while(n > 0) {
  		nn = n;
  		if(nn > MaxFragSize)
  			nn = MaxFragSize;
  		f = fragalloc(p, nn, PEnd, p->first);	
  		p->asize += FRAGASIZE(f);
  		if(p->first == nil)
  			p->last = f;
  		p->first = f;
  		n -= nn;
  		memmove(f->rp, buf+n, nn);
  	}
  	NOTFREE(p);
  }
  
  void
  packetappend(Packet *p, uchar *buf, int n)
  {
  	Frag *f;
  	int nn;
  	Mem *m;
  
  	NOTFREE(p);
  	if(n <= 0)
  		return;
  
  	p->size += n;
  	/* try and fix in current frag */
  	if(p->first != nil) {
  		f = p->last;
  		m = f->mem;
  		nn = m->ep - f->wp;
  		if(nn > n)
  			nn = n;
  		if(m->ref == 1 || memtail(m, f->wp, nn) >= 0) {
  			memmove(f->wp, buf, nn);
  			f->wp += nn;
  			buf += nn;
  			n -= nn;
  		}
  	}
  	
  	while(n > 0) {
  		nn = n;
  		if(nn > MaxFragSize)
  			nn = MaxFragSize;
  		f = fragalloc(p, nn, (p->first == nil)?PMiddle:PFront, nil);
  		p->asize += FRAGASIZE(f);
  		if(p->first == nil)
  			p->first = f;
  		else
  			p->last->next = f;
  		p->last = f;
  		memmove(f->rp, buf, nn);
  		buf += nn;
  		n -= nn;
  	}
  	NOTFREE(p);
  }
  
  void
  packetconcat(Packet *p, Packet *pp)
  {
  	Frag *f;
  
  	NOTFREE(p);
  	NOTFREE(pp);
  	if(pp->size == 0)
  		return;
  	p->size += pp->size;
  	p->asize += pp->asize;
  	for(f=pp->first; f; f=f->next)
  		f->p = p;
  
  	if(p->first != nil)
  		p->last->next = pp->first;
  	else
  		p->first = pp->first;
  
  	p->last = pp->last;
  	pp->size = 0;
  	pp->asize = 0;
  	pp->first = nil;
  	pp->last = nil;
  	NOTFREE(p);
  	NOTFREE(pp);
  }
  
  uchar *
  packetpeek(Packet *p, uchar *buf, int offset, int n)
  {
  	Frag *f;
  	int nn;
  	uchar *b;
  
  	NOTFREE(p);
  	if(n == 0)
  		return buf;
  
  	if(offset < 0 || offset >= p->size) {
  		werrstr(EPacketOffset);
  		return nil;
  	}
  
  	if(n < 0 || offset + n > p->size) {
  		werrstr(EPacketSize);
  		return nil;
  	}
  	
  	/* skip up to offset */
  	for(f=p->first; offset >= FRAGSIZE(f); f=f->next)
  		offset -= FRAGSIZE(f);
  
  	/* easy case */
  	if(offset + n <= FRAGSIZE(f)){
  		NOTFREE(p);
  		return f->rp + offset;
  	}
  
  	for(b=buf; n>0; n -= nn) {
  		nn = FRAGSIZE(f) - offset;
  		if(nn > n)
  			nn = n;
  		memmove(b, f->rp+offset, nn);
  		offset = 0;
  		f = f->next;
  		b += nn;
  	}
  
  	NOTFREE(p);
  	return buf;
  }
  
  int
  packetcopy(Packet *p, uchar *buf, int offset, int n)
  {
  	uchar *b;
  
  	NOTFREE(p);
  	b = packetpeek(p, buf, offset, n);
  	if(b == nil)
  		return -1;
  	if(b != buf)
  		memmove(buf, b, n);
  	return 0;
  }
  
  int
  packetfragments(Packet *p, IOchunk *io, int nio, int offset)
  {
  	Frag *f;
  	int size;
  	IOchunk *eio;
  
  	NOTFREE(p);
  	if(p->size == 0 || nio <= 0)
  		return 0;
  	
  	if(offset < 0 || offset > p->size) {
  		werrstr(EPacketOffset);
  		return -1;
  	}
  
  	for(f=p->first; offset >= FRAGSIZE(f); f=f->next)
  		offset -= FRAGSIZE(f);
  
  	size = 0;
  	eio = io + nio;
  	for(; f != nil && io < eio; f=f->next) {
  		io->addr = f->rp + offset;
  		io->len = f->wp - (f->rp + offset);	
  		offset = 0;
  		size += io->len;
  		io++;
  	}
  	for(; io < eio; io++){
  		io->addr = nil;
  		io->len = 0;
  	}
  	return size;
  }
  
  void
  packetstats(void)
  {
  	Packet *p;
  	Frag *f;
  	Mem *m;
  
  	int np, nf, nsm, nbm;
  
  	lock(&freelist.lk);
  	np = 0;
  	for(p=freelist.packet; p; p=p->next)
  		np++;
  	nf = 0;
  	for(f=freelist.frag; f; f=f->next)
  		nf++;
  	nsm = 0;
  	for(m=freelist.smallmem; m; m=m->next)
  		nsm++;
  	nbm = 0;
  	for(m=freelist.bigmem; m; m=m->next)
  		nbm++;
  	
  	fprint(2, "packet: %d/%d frag: %d/%d small mem: %d/%d big mem: %d/%d\n",
  		np, freelist.npacket,
  		nf, freelist.nfrag,
  		nsm, freelist.nsmallmem,
  		nbm, freelist.nbigmem);
  
  	unlock(&freelist.lk);
  }
  
  
  uint
  packetsize(Packet *p)
  {
  	NOTFREE(p);
  	if(1) {
  		Frag *f;
  		int size = 0;
  	
  		for(f=p->first; f; f=f->next)
  			size += FRAGSIZE(f);
  		if(size != p->size)
  			fprint(2, "packetsize %d %d\n", size, p->size);
  		assert(size == p->size);
  	}
  	return p->size;
  }
  
  uint
  packetasize(Packet *p)
  {
  	NOTFREE(p);
  	if(0) {
  		Frag *f;
  		int asize = 0;
  	
  		for(f=p->first; f; f=f->next)
  			asize += FRAGASIZE(f);
  		if(asize != p->asize)
  			fprint(2, "packetasize %d %d\n", asize, p->asize);
  		assert(asize == p->asize);
  	}
  	return p->asize;
  }
  
  void
  packetsha1(Packet *p, uchar digest[VtScoreSize])
  {
  	DigestState ds;
  	Frag *f;
  	int size;
  
  	NOTFREE(p);
  	memset(&ds, 0, sizeof ds);
  	size = p->size;
  	for(f=p->first; f; f=f->next) {
  		sha1(f->rp, FRAGSIZE(f), nil, &ds);
  		size -= FRAGSIZE(f);
  	}
  	assert(size == 0);
  	sha1(nil, 0, digest, &ds);
  }
  
  int
  packetcmp(Packet *pkt0, Packet *pkt1)
  {
  	Frag *f0, *f1;
  	int n0, n1, x;
  
  	NOTFREE(pkt0);
  	NOTFREE(pkt1);
  	f0 = pkt0->first;
  	f1 = pkt1->first;
  
  	if(f0 == nil)
  		return (f1 == nil)?0:-1;
  	if(f1 == nil)
  		return 1;
  	n0 = FRAGSIZE(f0);
  	n1 = FRAGSIZE(f1);
  
  	for(;;) {
  		if(n0 < n1) {
  			x = memcmp(f0->wp - n0, f1->wp - n1, n0);
  			if(x != 0)
  				return x;
  			n1 -= n0;
  			f0 = f0->next;
  			if(f0 == nil)
  				return -1;
  			n0 = FRAGSIZE(f0);
  		} else if (n0 > n1) {
  			x = memcmp(f0->wp - n0, f1->wp - n1, n1);
  			if(x != 0)
  				return x;
  			n0 -= n1;
  			f1 = f1->next;
  			if(f1 == nil)
  				return 1;
  			n1 = FRAGSIZE(f1);
  		} else { /* n0 == n1 */
  			x = memcmp(f0->wp - n0, f1->wp - n1, n0);
  			if(x != 0)
  				return x;
  			f0 = f0->next;
  			f1 = f1->next;
  			if(f0 == nil)
  				return (f1 == nil)?0:-1;
  			if(f1 == nil)
  				return 1;
  			n0 = FRAGSIZE(f0);
  			n1 = FRAGSIZE(f1);
  		}
  	}
  }
  
  static Frag *
  fragalloc(Packet *p, int n, int pos, Frag *next)
  {
  	Frag *f, *ef;
  	Mem *m;
  
  	/* look for local frag */
  	f = &p->local[0];
  	ef = &p->local[NLocalFrag];
  	for(;f<ef; f++) {
  		if(f->state == FragLocalFree) {
  			f->state = FragLocalAlloc;
  			goto Found;
  		}
  	}
  	lock(&freelist.lk);	
  	f = freelist.frag;
  	if(f != nil)
  		freelist.frag = f->next;
  	else
  		freelist.nfrag++;
  	unlock(&freelist.lk);
  
  	if(f == nil) {
  		f = vtbrk(sizeof(Frag));
  		f->state = FragGlobal;
  	}
  
  Found:
  	f->next = next;
  	f->p = p;
  
  	if(n == 0){
  		f->mem = 0;
  		f->rp = 0;
  		f->wp = 0;
  		return f;
  	}
  
  	if(pos == PEnd && next == nil)
  		pos = PMiddle;
  	m = memalloc(n, pos);
  	f->mem = m;
  	f->rp = m->rp;
  	f->wp = m->wp;
  	return f;
  }
  
  Packet*
  packetforeign(uchar *buf, int n, void (*free)(void *a), void *a)
  {
  	Packet *p;
  	Frag *f;
  
  	p = packetalloc();
  	p->pc = getcallerpc(&buf);
  	f = fragalloc(p, 0, 0, nil);
  	f->free = free;
  	f->a = a;
  	f->next = nil;
  	f->rp = buf;
  	f->wp = buf+n;
  
  	p->first = f;
  	p->last = f;
  	p->size = n;
  	NOTFREE(p);
  	return p;
  }
  
  static Frag *
  fragdup(Packet *p, Frag *f)
  {
  	Frag *ff;
  	Mem *m;
  
  	m = f->mem;	
  
  	/*
  	 * m->rp && m->wp can be out of date when ref == 1
  	 * also, potentially reclaims space from previous frags
  	 */
  	if(m && m->ref == 1) {
  		m->rp = f->rp;
  		m->wp = f->wp;
  	}
  
  	ff = fragalloc(p, 0, 0, nil);
  	ff->mem = f->mem;
  	ff->rp = f->rp;
  	ff->wp = f->wp;
  	ff->next = f->next;
  
  	/*
  	 * We can't duplicate these -- there's no dup function.
  	 */
  	assert(f->free==nil && f->a==nil);
  
  	if(m){
  		lock(&m->lk);
  		m->ref++;
  		unlock(&m->lk);
  	}
  
  	
  	return ff;
  }
  
  
  static void
  fragfree(Frag *f)
  {
  	if(f->mem == nil){
  		if(f->free)
  			(*f->free)(f->a);
  	}else{
  		memfree(f->mem);
  		f->mem = 0;
  	}
  
  	if(f->state == FragLocalAlloc) {
  		f->state = FragLocalFree;
  		return;
  	}
  
  	lock(&freelist.lk);
  	f->next = freelist.frag;
  	freelist.frag = f;
  	unlock(&freelist.lk);	
  }
  
  static Mem *
  memalloc(int n, int pos)
  {
  	Mem *m;
  	int nn;
  
  	if(n < 0 || n > MaxFragSize) {
  		werrstr(EPacketSize);
  		return nil;
  	}
  	if(n <= SmallMemSize) {
  		lock(&freelist.lk);
  		m = freelist.smallmem;
  		if(m != nil)
  			freelist.smallmem = m->next;
  		else
  			freelist.nsmallmem++;
  		unlock(&freelist.lk);
  		nn = SmallMemSize;
  	} else {
  		lock(&freelist.lk);
  		m = freelist.bigmem;
  		if(m != nil)
  			freelist.bigmem = m->next;
  		else
  			freelist.nbigmem++;
  		unlock(&freelist.lk);
  		nn = BigMemSize;
  	}
  
  	if(m == nil) {
  		m = vtbrk(sizeof(Mem));
  		m->bp = vtbrk(nn);
  		m->ep = m->bp + nn;
  	}
  	assert(m->ref == 0);	
  	m->ref = 1;
  
  	switch(pos) {
  	default:
  		assert(0);
  	case PFront:
  		m->rp = m->bp;
  		break;
  	case PMiddle:
  		/* leave a little bit at end */
  		m->rp = m->ep - n - 32;
  		break;
  	case PEnd:
  		m->rp = m->ep - n;
  		break; 
  	}
  	/* check we did not blow it */
  	if(m->rp < m->bp)
  		m->rp = m->bp;
  	m->wp = m->rp + n;
  	assert(m->rp >= m->bp && m->wp <= m->ep);
  	return m;
  }
  
  static void
  memfree(Mem *m)
  {
  	lock(&m->lk);
  	m->ref--;
  	if(m->ref > 0) {
  		unlock(&m->lk);
  		return;
  	}
  	unlock(&m->lk);
  	assert(m->ref == 0);
  
  /*	memset(m->bp, 0xEF, m->ep-m->bp); */
  	switch(m->ep - m->bp) {
  	default:
  		assert(0);
  	case SmallMemSize:
  		lock(&freelist.lk);
  		m->next = freelist.smallmem;
  		freelist.smallmem = m;
  		unlock(&freelist.lk);
  		break;
  	case BigMemSize:
  		lock(&freelist.lk);
  		m->next = freelist.bigmem;
  		freelist.bigmem = m;
  		unlock(&freelist.lk);
  		break;
  	}
  }
  
  static int
  memhead(Mem *m, uchar *rp, int n)
  {
  	fprint(2, "memhead called\n");
  	abort();
  	lock(&m->lk);
  	if(m->rp != rp) {
  		unlock(&m->lk);
  		return -1;
  	}
  	m->rp -= n;
  	unlock(&m->lk);
  	return 0;
  }
  
  static int
  memtail(Mem *m, uchar *wp, int n)
  {
  	fprint(2, "memtail called\n");
  	abort();
  	lock(&m->lk);
  	if(m->wp != wp) {
  		unlock(&m->lk);
  		return -1;
  	}
  	m->wp += n;
  	unlock(&m->lk);
  	return 0;
  }
  
  #ifdef NOTDEF
  static void
  checkpacket(Packet *p)
  {
  	int s, as;
  	Frag *f;
  	Frag *ff;
  
  	s = 0;
  	as = 0;
  	ff=p->first;
  	for(f=p->first; f; ff=f,f=f->next){
  		assert(f->p == p);
  		s += FRAGSIZE(f);
  		as += FRAGASIZE(f);
  	}
  	assert(s == p->size);
  	assert(as == p->asize);
  	if(p->first)
  		assert(ff==p->last);
  }
  #endif
  