Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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