blob: d19d85371965ae5765494e6ebad44b98b2dfda1e [file] [log] [blame]
#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