/* memory allocation routines * Copyright 1991 Phil Karn, KA9Q * * Adapted from alloc routine in K&R; memory statistics and interrupt * protection added for use with net package. Must be used in place of * standard Turbo-C library routines because the latter check for stack/heap * collisions. This causes erroneous failures because process stacks are * allocated off the heap. */ /* Mods by G1EMM , PA0GRI */ #include #include #include #include "global.h" #include "proc.h" #include "cmdparse.h" #include "mbuf.h" static unsigned long Memfail; /* Count of allocation failures */ static unsigned long Allocs; /* Total allocations */ static unsigned long Frees; /* Total frees */ static unsigned long Invalid; /* Total calls to free with garbage arg */ static unsigned long Yellows; /* Yellow alert garbage collections */ static unsigned long Reds; /* Red alert garbage collections */ static unsigned long Overuse; /* Total calls to free with overused arg */ static unsigned long Intalloc; /* Calls to malloc with ints disabled */ static unsigned long Intfree; /* Calls to free with ints disabled */ static int Memwait; /* Number of tasks waiting for memory */ unsigned long Availmem; /* Heap memory, ABLKSIZE units */ static int Garbstate; /* 0 = normal, 1 = yellow, 2 = red */ static unsigned long Morecores; static int Efficient = 0; /* 0 = normal/fast, 1 = effecient/slow */ static int Circular = 0; /* 0 = linear , 1 = circular */ static unsigned long Sizes[16]; static int dostat __ARGS((int argc,char *argv[],void *p)); static int dofreelist __ARGS((int argc,char *argv[],void *p)); static int dogarbage __ARGS((int argc,char *argv[],void *p)); static int doibufsize __ARGS((int argc,char *argv[],void *p)); static int donibufs __ARGS((int argc,char *argv[],void *p)); static int dothresh __ARGS((int argc,char *argv[],void *p)); static int dosizes __ARGS((int argc,char *argv[],void *p)); static int doefficient __ARGS((int argc,char *argv[],void *p)); static int docircular __ARGS((int argc,char *argv[],void *p)); struct cmds Memcmds[] = { "circular", docircular, 0, 0, NULLCHAR, "efficient", doefficient, 0, 0, NULLCHAR, "freelist", dofreelist, 0, 0, NULLCHAR, "garbage", dogarbage, 0, 0, NULLCHAR, "ibufsize", doibufsize, 0, 0, NULLCHAR, "nibufs", donibufs, 0, 0, NULLCHAR, "sizes", dosizes, 0, 0, NULLCHAR, "status", dostat, 0, 0, NULLCHAR, "thresh", dothresh, 0, 0, NULLCHAR, NULLCHAR, }; #ifdef LARGEDATA #define HUGE huge #else #define HUGE #endif union header { struct { union header HUGE *ptr; unsigned long size; } s; long l[2]; }; typedef union header HEADER; #define NULLHDR (HEADER HUGE *)NULL #define ABLKSIZE (sizeof (HEADER)) static HEADER HUGE *morecore __ARGS((unsigned nu)); static HEADER Base; static HEADER HUGE *Allocp = NULLHDR; static unsigned long Heapsize; #define MARKER 0x766c654bL /* Kelv in reverse */ /* Allocate block of 'nb' bytes */ void * malloc(nb) unsigned nb; { register HEADER HUGE *p, HUGE *q; register unsigned nu; int i; void (**fp)(); if(!istate()) Intalloc++; /* If memory is low, collect some garbage. If memory is VERY * low, invoke the garbage collection routines in "red" mode. * The Garbstate == 0 check is to prevent infinite recursion * when we're called again by the mbuf_crunch routine. */ if((availmem() < Memthresh) && (Garbstate == 0)){ if(availmem() < Memthresh/2){ Garbstate = 2; Reds++; } else { Garbstate = 1; Yellows++; } for(fp = Gcollect;*fp != NULL;fp++) (**fp)(Garbstate == 2); Garbstate = 0; } if(nb == 0) return NULL; /* Record the size of this request */ if((i = log2(nb)) >= 0) Sizes[i]++; /* Round up to full block, then add one for header and one for debug */ nu = ((nb + ABLKSIZE - 1) / ABLKSIZE) + 4; /* force allcated memory */ nu &= 0xfffffffeL; /* to be on offset 0x0008 */ if ((q = Allocp) == NULLHDR){ Base.s.ptr = Allocp = q = &Base; Base.s.size = 1; } if(Efficient) { Allocp = q = &Base; /* Start at the very beginning again */ } for (p = q->s.ptr; ; q = p, p = p->s.ptr){ if (p->s.size >= nu){ /* This chunk is at least as large as we need */ if (p->s.size <= nu + 1){ /* This is either a perfect fit (size == nu) * or the free chunk is just one unit larger. * In either case, alloc the whole thing, * because there's no point in keeping a free * block only large enough to hold the header. */ q->s.ptr = p->s.ptr; } else { /* Carve out piece from end of entry */ p->s.size -= nu; p += p->s.size; p->s.size = nu; } if(Circular) Allocp = q; p->s.ptr = p; /* for auditing */ p->l[(p->s.size * 2) - 2] = (long)p; /* debug */ p->l[(p->s.size * 2) - 1] = MARKER; /* debug */ Allocs++; Availmem -= p->s.size; p++; #ifdef LARGEDATA /* On the brain-damaged Intel CPUs in * "large data" model, make sure the offset field * in the pointer we return isn't null. * The Turbo C compiler and certain * library functions like strrchr() assume this. */ if(FP_OFF(p) == 0){ /* Return denormalized but equivalent pointer */ return (void *)MK_FP(FP_SEG(p)-1,16); } #endif return (void *)p; } if (p == Allocp && ((p = morecore(nu)) == NULLHDR)){ Memfail++; return NULL; } } } /* Get more memory from the system and put it on the heap */ static HEADER HUGE * morecore(nu) unsigned nu; { register char HUGE *cp; register HEADER HUGE *up; Morecores++; if ((int)(cp = (char HUGE *)sbrk(nu * ABLKSIZE)) == -1) return NULLHDR; up = (HEADER *)cp; up->s.size = nu; up->s.ptr = up; /* satisfy audit */ up->l[(up->s.size * 2) - 2] = (long)up; /* satisfy debug */ up->l[(up->s.size * 2) - 1] = MARKER; /* satisfy debug */ free((void *)(up + 1)); Heapsize += nu*ABLKSIZE; Frees--; /* Nullify increment inside free() */ return Allocp; } /* Put memory block back on heap */ void free(blk) void *blk; { register HEADER HUGE *p, HUGE *q; unsigned short HUGE *ptr; if(!istate()) Intfree++; if(blk == NULL) return; /* Required by ANSI */ p = (HEADER HUGE *)blk - 1; /* Audit check */ if(p->s.ptr != p){ ptr = (unsigned short *)&blk; printf("free: WARNING! invalid pointer (%Fp) pc = %04x:%04x proc %s\n",blk, ptr[-1],ptr[-2],Curproc->name); fflush(stdout); Invalid++; log(-1,"free: WARNING! invalid pointer (%Fp) pc = %04x:%04x proc %s\n",blk, ptr[-1],ptr[-2],Curproc->name); return; } if(p->l[(p->s.size * 2) - 2] != (long)p || p->l[(p->s.size * 2) - 1] != MARKER){ ptr = (unsigned short *)&blk; printf("free: WARNING!! overused buffer (%Fp) pc = %04x:%04x proc %s\n",blk, ptr[-1],ptr[-2],Curproc->name); fflush(stdout); Overuse++; log(-1,"free: WARNING!! overused buffer (%Fp) pc = %04x:%04x proc %s\n",blk, ptr[-1],ptr[-2],Curproc->name); return; } Availmem += p->s.size; /* Search the free list looking for the right place to insert */ for(q = Allocp; !(p > q && p < q->s.ptr); q = q->s.ptr){ /* Highest address on circular list? */ if(q >= q->s.ptr && (p > q || p < q->s.ptr)) break; } if(p + p->s.size == q->s.ptr){ /* Combine with front of this entry */ p->s.size += q->s.ptr->s.size; p->s.ptr = q->s.ptr->s.ptr; } else { /* Link to front of this entry */ p->s.ptr = q->s.ptr; } if(q + q->s.size == p){ /* Combine with end of this entry */ q->s.size += p->s.size; q->s.ptr = p->s.ptr; } else { /* Link to end of this entry */ q->s.ptr = p; } if(Circular) Allocp = q; Frees++; if(Memwait != 0) psignal(&Memwait,0); } #ifdef notdef /* Not presently used */ /* Move existing block to new area */ void * realloc(area,size) void *area; unsigned size; { unsigned osize; HEADER HUGE *hp; char HUGE *cp; hp = ((HEADER *)area) - 1; osize = (hp->s.size -1) * ABLKSIZE; free(area); /* Hopefully you have your interrupts off , Phil. */ if((cp = malloc(size)) != NULL && cp != area) memcpy((char *)cp,(char *)area,size>osize? osize : size); return cp; } #endif /* Allocate block of cleared memory */ void * calloc(nelem,size) unsigned nelem; /* Number of elements */ unsigned size; /* Size of each element */ { register unsigned i; register char *cp; i = nelem * size; if((cp = malloc(i)) != NULL) memset(cp,0,i); return cp; } /* Version of malloc() that waits if necessary for memory to become available */ void * mallocw(nb) unsigned nb; { register void *p; while((p = malloc(nb)) == NULL){ Memwait++; pwait(&Memwait); Memwait--; } return p; } /* Version of calloc that waits if necessary for memory to become available */ void * callocw(nelem,size) unsigned nelem; /* Number of elements */ unsigned size; /* Size of each element */ { register unsigned i; register char *cp; i = nelem * size; cp = mallocw(i); memset(cp,0,i); return cp; } /* Return available memory on our heap plus available system memory */ unsigned long availmem() { return Availmem * ABLKSIZE + coreleft(); } /* Print heap stats */ static int dostat(argc,argv,envp) int argc; char *argv[]; void *envp; { tprintf("heap size %lu, avail %lu (%lu%%), morecores %lu, coreleft %lu\n", Heapsize,Availmem*ABLKSIZE, 100L*Availmem*ABLKSIZE/Heapsize,Morecores,coreleft()); tprintf("allocs %lu, frees %lu (diff %lu), alloc fails %lu, invalid frees %lu, overused %lu\n", Allocs,Frees,Allocs-Frees,Memfail,Invalid,Overuse); tprintf("garbage collections yellow %lu, red %lu\n",Yellows,Reds); tprintf("interrupts-off calls to malloc %lu, free %lu\n",Intalloc,Intfree); iqstat(); return 0; } /* Print heap free list */ static int dofreelist(argc,argv,envp) int argc; char *argv[]; void *envp; { HEADER HUGE *p; int i = 0; for(p = Base.s.ptr;p != &Base;p = p->s.ptr){ tprintf("%5lx %6lu",ptol((void *)p),p->s.size * ABLKSIZE); if(++i == 4){ i = 0; if(tprintf("\n") == EOF) return 0; } else tprintf(" | "); } if(i != 0) tprintf("\n"); return 0; } static int dosizes(argc,argv,p) int argc; char *argv[]; void *p; { int i; for(i=0;i<16;i += 4){ tprintf("N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld\n", 1< 1) red = atoi(argv[1]); if(red) Reds++; else Yellows++; for(fp = Gcollect;*fp != NULL;fp++) (**fp)(red); return 0; } static int doefficient(argc,argv,p) int argc; char *argv[]; void *p; { return setbool(&Efficient,"Efficient/slower mode",argc,argv); } static int docircular(argc,argv,p) int argc; char *argv[]; void *p; { return setbool(&Circular,"Circular mode",argc,argv); }