#include <u.h>
#include <libc.h>
#include <ip.h>
#include <auth.h>
#include "ppp.h"
enum
{
PAD = 128,
NLIST = (1<<5),
BPOW = 10,
};
typedef struct Barena Barena;
struct Barena
{
QLock;
Block* list[NLIST];
};
static Barena area;
#define ADEBUG if(0)
/*
* allocation tracing
*/
enum
{
Npc= 64,
};
typedef struct Arefent Arefent;
struct Arefent
{
uint pc;
uint n;
};
typedef struct Aref Aref;
struct Aref
{
Arefent tab[Npc];
QLock;
};
Aref arefblock;
static ulong callerpc(void*);
static void aref(Aref*, ulong);
static void aunref(Aref*, ulong);
Block*
allocb(int len)
{
int sz;
Block *bp, **l;
len += PAD;
sz = (len>>BPOW)&(NLIST-1);
qlock(&area);
l = &area.list[sz];
for(bp = *l; bp; bp = bp->flist) {
if(bp->bsz >= len) {
*l = bp->flist;
qunlock(&area);
bp->next = nil;
bp->list = nil;
bp->flist = nil;
bp->base = (uchar*)bp+sizeof(Block);
bp->rptr = bp->base+PAD;
bp->wptr = bp->rptr;
bp->lim = bp->base+bp->bsz;
bp->flow = nil;
bp->flags= 0;
ADEBUG {
bp->pc = callerpc(&len);
aref(&arefblock, bp->pc);
}
return bp;
}
l = &bp->flist;
}
qunlock(&area);
bp = mallocz(sizeof(Block)+len, 1);
bp->bsz = len;
bp->base = (uchar*)bp+sizeof(Block);
bp->rptr = bp->base+PAD;
bp->wptr = bp->rptr;
bp->lim = bp->base+len;
ADEBUG {
bp->pc = callerpc(&len);
aref(&arefblock, bp->pc);
}
return bp;
}
void
freeb(Block *bp)
{
int sz;
Block **l, *next;
qlock(&area);
while(bp) {
sz = (bp->bsz>>BPOW)&(NLIST-1);
l = &area.list[sz];
bp->flist = *l;
*l = bp;
next = bp->next;
/* to catch use after free */
bp->rptr = (uchar*)0xdeadbabe;
bp->wptr = (uchar*)0xdeadbabe;
bp->next = (Block*)0xdeadbabe;
bp->list = (Block*)0xdeadbabe;
ADEBUG aunref(&arefblock, bp->pc);
bp = next;
}
qunlock(&area);
}
/*
* concatenate a list of blocks into a single one and make sure
* the result is at least min uchars
*/
Block*
concat(Block *bp)
{
int len;
Block *nb, *f;
nb = allocb(blen(bp));
for(f = bp; f; f = f->next) {
len = BLEN(f);
memmove(nb->wptr, f->rptr, len);
nb->wptr += len;
}
freeb(bp);
return nb;
}
int
blen(Block *bp)
{
int len;
len = 0;
while(bp) {
len += BLEN(bp);
bp = bp->next;
}
return len;
}
Block *
pullup(Block *bp, int n)
{
int i;
Block *nbp;
/*
* this should almost always be true, the rest it
* just for to avoid every caller checking.
*/
if(BLEN(bp) >= n)
return bp;
/*
* if not enough room in the first block,
* add another to the front of the list.
*/
if(bp->lim - bp->rptr < n){
nbp = allocb(n);
nbp->next = bp;
bp = nbp;
}
/*
* copy uchars from the trailing blocks into the first
*/
n -= BLEN(bp);
while(nbp = bp->next){
i = BLEN(nbp);
if(i >= n) {
memmove(bp->wptr, nbp->rptr, n);
bp->wptr += n;
nbp->rptr += n;
return bp;
}
else {
memmove(bp->wptr, nbp->rptr, i);
bp->wptr += i;
bp->next = nbp->next;
nbp->next = nil;
freeb(nbp);
n -= i;
}
}
freeb(bp);
return nil;
}
/*
* Pad a block to the front with n uchars. This is used to add protocol
* headers to the front of blocks.
*/
Block*
padb(Block *bp, int n)
{
Block *nbp;
if(bp->rptr-bp->base >= n) {
bp->rptr -= n;
return bp;
}
else {
/* fprint(2, "padb: required %d PAD %d\n", n, PAD) = malloc(sizeof(*required %d PAD %d\n", n, PAD))); */
nbp = allocb(n);
nbp->wptr = nbp->lim;
nbp->rptr = nbp->wptr - n;
nbp->next = bp;
return nbp;
}
}
Block *
btrim(Block *bp, int offset, int len)
{
ulong l;
Block *nb, *startb;
if(blen(bp) < offset+len) {
freeb(bp);
return nil;
}
while((l = BLEN(bp)) < offset) {
offset -= l;
nb = bp->next;
bp->next = nil;
freeb(bp);
bp = nb;
}
startb = bp;
bp->rptr += offset;
while((l = BLEN(bp)) < len) {
len -= l;
bp = bp->next;
}
bp->wptr -= (BLEN(bp) - len);
bp->flags |= S_DELIM;
if(bp->next) {
freeb(bp->next);
bp->next = nil;
}
return startb;
}
Block*
copyb(Block *bp, int count)
{
int l;
Block *nb, *head, **p;
p = &head;
head = nil;
while(bp != nil && count != 0) {
l = BLEN(bp);
if(count < l)
l = count;
nb = allocb(l);
memmove(nb->wptr, bp->rptr, l);
nb->wptr += l;
count -= l;
nb->flags = bp->flags;
*p = nb;
p = &nb->next;
bp = bp->next;
}
if(count) {
nb = allocb(count);
memset(nb->wptr, 0, count);
nb->wptr += count;
nb->flags |= S_DELIM;
*p = nb;
}
if(blen(head) == 0)
fprint(2, "copyb: zero length\n");
return head;
}
int
pullb(Block **bph, int count)
{
Block *bp;
int n, uchars;
uchars = 0;
if(bph == nil)
return 0;
while(*bph != nil && count != 0) {
bp = *bph;
n = BLEN(bp);
if(count < n)
n = count;
uchars += n;
count -= n;
bp->rptr += n;
if(BLEN(bp) == 0) {
*bph = bp->next;
bp->next = nil;
freeb(bp);
}
}
return uchars;
}
/*
* handy routines for keeping track of allocations
*/
static ulong
callerpc(void *a)
{
return ((ulong*)a)[-1];
}
static void
aref(Aref *ap, ulong pc)
{
Arefent *a, *e;
e = &ap->tab[Npc-1];
qlock(ap);
for(a = ap->tab; a < e; a++)
if(a->pc == pc || a->pc == 0)
break;
a->pc = pc;
a->n++;
qunlock(ap);
}
static void
aunref(Aref *ap, ulong pc)
{
Arefent *a, *e;
e = &ap->tab[Npc-1];
qlock(ap);
for(a = ap->tab; a < e; a++)
if(a->pc == pc || a->pc == 0)
break;
a->n--;
qunlock(ap);
}
|