home *** CD-ROM | disk | FTP | other *** search
- /* 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.
- */
-
- #include <stdio.h>
- #ifdef MSDOS
- #include <dos.h>
- #include <alloc.h>
- #endif
- #include "global.h"
- #include "mbuf.h"
- #include "proc.h"
- #include "cmdparse.h"
-
- #ifdef AMIGA
- #define LIBRARIES_MATHFFP_H /* Don't include this one! */
- #include <exec/lists.h>
- #include <exec/memory.h>
- #include <clib/exec_protos.h>
- #include <clib/alib_protos.h>
- #ifdef __SASC
- #include <pragmas/exec_sysbase_pragmas.h>
- #endif
- extern struct ExecBase *SysBase;
- #endif
-
- 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 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 */
- static unsigned long Yellows; /* Yellow alert garbage collections */
- static unsigned long Reds; /* Red alert garbage collections */
- unsigned long Availmem; /* Heap memory, ABLKSIZE units */
- static unsigned long Morecores;
-
- 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 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));
- int domem(int argc, char *argv[], void *p);
- void gcollect(int i, void *v1, void *v2);
-
- struct cmds Memcmds[] = {
- "freelist", dofreelist, 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,
- };
-
- #if defined(MSDOS) && defined(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;
-
- #ifdef MSDOS
- /* Memory blocks obtained from MS-DOS by allocmem() call */
- struct sysblock {
- unsigned seg;
- unsigned npar;
- };
- #define NSYSBLOCK 5
- struct sysblock Sysblock[NSYSBLOCK];
- #endif
- #ifdef AMIGA
- struct MemBlock {
- struct MinNode Node;
- long Size;
- long pad; /* size multiple of 8==ABLKSIZE */
- };
- struct MinList SysBlocks;
- static void freeblocks(void);
- static int Delayfree; /* freeblocks() is slow - don't do it too often */
- #define ALLOCMEMSIZE 8192
-
- void *
- NextNode(void *n)
- {
- struct Node *nn;
-
- nn = ((struct Node *)n)->ln_Succ;
- if (nn->ln_Pred != n)
- log(-1, "List inconsistent: pred(%x) == %x != %x\n", nn,
- nn->ln_Pred, n);
- if (nn->ln_Succ)
- return nn;
- return NULL;
- }
- #endif
-
- /* Allocate block of 'nb' bytes */
- void *
- malloc(nb)
- unsigned nb;
- {
- register HEADER HUGE *p, HUGE *q;
- register unsigned nu;
- int i;
-
- if(!istate())
- Intalloc++;
- 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 */
- nu = ((nb + ABLKSIZE - 1) / ABLKSIZE) + 1;
- if((q = Allocp) == NULLHDR){
- Base.s.ptr = Allocp = q = &Base;
- Base.s.size = 1;
- #ifdef AMIGA
- NewList((struct List *)&SysBlocks);
- #endif
- }
- 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;
- }
- #ifdef circular
- Allocp = q;
- #endif
- p->s.ptr = p; /* for auditing */
- Allocs++;
- Availmem -= p->s.size;
- p++;
- break;
- }
- if(p == Allocp && ((p = morecore(nu)) == NULLHDR)){
- Memfail++;
- break;
- }
- }
- #if defined(LARGEDATA) && defined(MSDOS)
- /* On the brain-damaged Intel CPUs in "large data" model,
- * make sure the pointer's offset field isn't null
- * (unless the entire pointer is null).
- * The Turbo C compiler and certain
- * library functions like strrchr() assume this.
- */
- if(FP_OFF(p) == 0 && FP_SEG(p) != 0){
- /* Return denormalized but equivalent pointer */
- return (void *)MK_FP(FP_SEG(p)-1,16);
- }
- #endif
- return (void *)p;
- }
- /* Get more memory from the system and put it on the heap */
- static HEADER HUGE *
- morecore(nu)
- unsigned nu;
- {
- #ifdef MSDOS
- char HUGE *cp;
- HEADER HUGE *up;
- unsigned size;
- unsigned segp;
- unsigned npar;
- struct sysblock *sp;
- int i;
-
- Morecores++;
- size = nu * ABLKSIZE;
- /* First try to expand our main memory block */
- if ((int)(cp = (char HUGE *)sbrk(size)) != -1){
- up = (HEADER *)cp;
- up->s.size = nu;
- up->s.ptr = up; /* satisfy audit */
- free((void *)(up + 1));
- Heapsize += size;
- Frees--; /* Nullify increment inside free() */
- return Allocp;
- }
- /* That failed; the main memory block must have grown into another
- * allocated block, or something else (e.g., the increase handles
- * call in ioinit()) must have allocated memory just beyond it.
- * Allocate or extend an additional memory block.
- */
- npar = (size+16)/16; /* Convert size from bytes to paragraphs */
- cp = NULL;
- for(sp=Sysblock,i=0;i < NSYSBLOCK;i++,sp++){
- if(sp->npar != 0){
- /* Try to expand this block */
- if(setblock(sp->seg,sp->npar + npar) != -1){
- /* Failed (-1 == SUCCESS; strange!) */
- continue;
- }
- /* Block expansion succeeded */
- cp = MK_FP(sp->seg + sp->npar,0);
- sp->npar += npar;
- } else {
- /* Allocate new block */
- if(allocmem(npar,&segp) != -1){
- return NULL; /* Complete failure */
- }
- /* -1 indicates SUCCESS (strange) */
- sp->seg = segp;
- sp->npar = npar;
- cp = MK_FP(segp,0);
- }
- break;
- }
- #endif
- #ifdef AMIGA
- char *cp;
- HEADER *up;
- unsigned long size;
- unsigned npar;
-
- Morecores++;
- size = nu * ABLKSIZE;
- /* add MemBlock and make a full nr of intel paragraphs */
- size = (size + sizeof(struct MemBlock) + 15) & ~15;
- size = max(size, ALLOCMEMSIZE); /* blocks of 8K or more */
- cp = AllocMem(size, MEMF_PUBLIC);
- if (cp) {
- ((struct MemBlock *)cp)->Size = size;
- AddTail((struct List *)&SysBlocks, (struct Node *)cp);
- cp += sizeof(struct MemBlock);
- npar = (size-sizeof(struct MemBlock))/16; /* Convert size from bytes to paragraphs */
- Delayfree++;
- }
- #endif
- if(cp != (char HUGE *)NULL){
- /* Expand or create succeeded, add to heap */
- up = (HEADER *)cp;
- up->s.size = (npar*16)/ABLKSIZE;
- up->s.ptr = up; /* satisfy audit */
- free((void *)(up + 1));
- Heapsize += npar*16;
- Frees--; /* Nullify increment inside free() */
- return Allocp;
- }
- return NULL;
- }
-
- /* 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){
- printf("free: WARNING! invalid pointer (0x%lx) proc %s\n",
- ptol(blk),Curproc->name);
- stktrace();
- Invalid++;
- #ifdef MSDOS
- ptr = (unsigned short *)&blk;
- log(-1,"free: WARNING! invalid pointer (0x%lx) pc = 0x%x %x proc %s\n",
- ptol(blk),ptr[-1],ptr[-2],Curproc->name);
- #endif
- #ifdef AMIGA
- /* Sorry, this is probably very SAS/C 6.x specific...
- * Stack layout:
- * <return addr>
- * <auto variable(s): ptr>
- * <saved registers>
- * Note there is no frame pointer, and no pushed args.
- */
- ptr = (unsigned short *)&ptr;
- log(-1,"free: WARNING! invalid pointer (0x%lx) pc = 0x%06x proc %s\n",
- blk,((unsigned long *)ptr)[1],Curproc->name);
- #endif
- 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;
- }
- #ifdef circular
- Allocp = q;
- #endif
- Frees++;
- if(Memwait != 0)
- psignal(&Memwait,0);
- #ifdef AMIGA
- else if ((Availmem * (ABLKSIZE / 2)) > Memthresh)
- freeblocks();
- #endif
- }
-
- #ifdef AMIGA
- void
- freeall(void)
- {
- struct MemBlock *p;
-
- while (p = (struct MemBlock *)RemHead((struct List *)&SysBlocks))
- FreeMem((char *)p, p->Size);
- Allocp = NULLHDR;
- Availmem = 0;
- Heapsize = 0;
- }
-
- static void
- freeblocks(void)
- {
- struct MBH { /* Layout of memory region */
- struct MemBlock m;
- HEADER h;
- } *m, *n;
-
- if (--Delayfree >= 0)
- return;
-
- Delayfree = 128;
-
- for (m = (struct MBH *)SysBlocks.mlh_Head;
- n = (struct MBH *)m->m.Node.mln_Succ; m = n) {
- int size;
-
- if (m->h.s.ptr == &m->h) /* Is he really free memory? */
- continue;
- size = m->h.s.size * ABLKSIZE;
- /* Does he go all the way? */
- if (m->m.Size == size + sizeof(struct MemBlock)) {
- void *v;
-
- v = malloc(size - ABLKSIZE); /* unlink from freelist */
- if (v != (m + 1)) {
- free(v);
- continue;
- }
- Remove((struct Node *)&m->m.Node);
-
- Heapsize -= size;
- Allocs--; /* nullify increment inside malloc() */
- FreeMem((char *)m, m->m.Size);
-
- return;
- }
- }
- }
- #endif
-
- #ifdef AMIGA /* Not presently used by pc */
- /* 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);
- 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 0 if at least Memthresh memory is available. Return 1 if
- * less than Memthresh but more than Memthresh/2 is available; i.e.,
- * if a yellow garbage collection should be performed. Return 2 if
- * less than Memthresh/2 is available, i.e., a red collection should
- * be performed.
- */
- int
- availmem()
- {
- void *p;
-
- if(Availmem*ABLKSIZE >= Memthresh)
- return 0; /* We're clearly OK */
-
- /* There's not enough on the heap; try calling malloc to see if
- * it can get more from the system
- */
- if((p = malloc(Memthresh)) != NULL){
- free(p);
- return 0; /* Okay */
- }
- if((p = malloc(Memthresh/2)) != NULL){
- free(p);
- return 1; /* Yellow alert */
- }
- return 2; /* Red alert */
- }
-
- /* Print heap stats */
- static int
- dostat(argc,argv,envp)
- int argc;
- char *argv[];
- void *envp;
- {
- struct sysblock *sp;
- int i;
-
- printf("heap size %lu avail %lu (%lu%%) morecores %lu\n",
- Heapsize,Availmem * ABLKSIZE,100L*Availmem*ABLKSIZE/Heapsize,
- Morecores);
- #ifdef MSDOS
- if(Sysblock[0].npar != 0){
- printf("Extra blocks:");
- for(i=0,sp=Sysblock;i< NSYSBLOCK;i++,sp++){
- if(sp->npar == 0)
- break;
- printf(" (%x0-%x0)",sp->seg,sp->seg+sp->npar);
- }
- printf("\n");
- }
- #endif
- #ifdef AMIGA
- {
- struct MemBlock *m;
-
- printf("Extra blocks:");
- m = (struct MemBlock *)&SysBlocks;
- while (m = NextNode(m)) {
- printf(" (%06x-%06x)", m, (char *)m + m->Size);
- }
- printf("\n");
- }
- #endif
- printf("allocs %lu frees %lu (diff %lu) alloc fails %lu invalid frees %lu\n",
- Allocs,Frees,Allocs-Frees,Memfail,Invalid);
- printf("pushdown calls %lu pushdown calls to malloc %lu\n",
- Pushdowns,Pushalloc);
- printf("interrupts-off calls to malloc %lu free %lu\n",Intalloc,Intfree);
- printf("garbage collections yellow %lu red %lu\n",Yellows,Reds);
- 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 != (HEADER HUGE *)&Base;p = p->s.ptr){
- printf("%6lx %6lu",ptol((void *)p),p->s.size * ABLKSIZE);
- if(++i == 4){
- i = 0;
- if(printf("\n") == EOF)
- return 0;
- } else
- printf(" | ");
- }
- if(i != 0)
- printf("\n");
- return 0;
- }
- static int
- dosizes(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- int i;
-
- for(i=0;i<16;i += 4){
- printf("N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld\n",
- 1<<i,Sizes[i], 2<<i,Sizes[i+1],
- 4<<i,Sizes[i+2],8<<i,Sizes[i+3]);
- }
- return 0;
- }
- int
- domem(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- return subcmd(Memcmds,argc,argv,p);
- }
-
- static int
- donibufs(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- return setint(&Nibufs,"Interrupt pool buffers",argc,argv);
- }
- static int
- doibufsize(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- return setuns(&Ibufsize,"Interrupt buffer size",argc,argv);
- }
-
- static int
- dothresh(argc,argv,p)
- int argc;
- char *argv[];
- void *p;
- {
- return setlong(&Memthresh,"Free memory threshold (bytes)",argc,argv);
- }
-
- /* Background memory compactor, used when memory runs low */
- void
- gcollect(i,v1,v2)
- int i; /* Args not used */
- void *v1;
- void *v2;
- {
- void (**fp)__ARGS((int));
- int red;
-
- for(;;){
- pause(1000L); /* Run every second */
- /* If memory is low, collect some garbage. If memory is VERY
- * low, invoke the garbage collection routines in "red" mode.
- */
- switch(availmem()){
- case 0:
- continue; /* All is well */
- case 1:
- red = 0;
- Yellows++;
- break;
- case 2:
- red = 1;
- Reds++;
- break;
- }
- for(fp = Gcollect;*fp != NULL;fp++)
- (**fp)(red);
- }
- }
-