Hi, I've had the need for a realloc() in the kernel several times before and am having it once again. Finally figured it's time to do something about it.
Does anyone have problems with the attached patch? This patch adds realloc() and also fixes the semantics of free() to be consistent with userland, in that free(NULL) is legal and does nothing. This will make it possible to simplify some netgraph code and probably other stuff too. Reviews appreciated as well. Thanks, -Archie __________________________________________________________________________ Archie Cobbs * Packet Design * http://www.packetdesign.com
Index: kern/kern_malloc.c =================================================================== RCS file: /home/ncvs/src/sys/kern/kern_malloc.c,v retrieving revision 1.93 diff -u -r1.93 kern_malloc.c --- kern_malloc.c 12 Sep 2001 08:37:44 -0000 1.93 +++ kern_malloc.c 12 Mar 2002 00:06:03 -0000 @@ -57,6 +57,16 @@ #include <machine/cpu.h> #endif +/* + * When realloc() is called, if the new size is sufficiently smaller than + * the old size, realloc() will allocate a new, smaller block to avoid + * wasting memory. 'Sufficiently smaller' is defined as: newsize <= + * oldsize / 2^n, where REALLOC_FRACTION defines the value of 'n'. + */ +#ifndef REALLOC_FRACTION +#define REALLOC_FRACTION 1 /* new block if <= half the size */ +#endif + MALLOC_DEFINE(M_CACHE, "cache", "Various Dynamically allocated caches"); MALLOC_DEFINE(M_DEVBUF, "devbuf", "device driver memory"); MALLOC_DEFINE(M_TEMP, "temp", "misc temporary data buffers"); @@ -294,6 +304,10 @@ #endif register struct malloc_type *ksp = type; + /* free(NULL, ...) does nothing */ + if (addr == NULL) + return; + KASSERT(kmembase <= (char *)addr && (char *)addr < kmemlimit, ("free: address %p out of range", (void *)addr)); kup = btokup(addr); @@ -397,6 +411,49 @@ #endif splx(s); mtx_unlock(&malloc_mtx); +} + +/* + * realloc: change the size of a memory block + */ +void * +realloc(addr, size, type, flags) + void *addr; + unsigned long size; + struct malloc_type *type; + int flags; +{ + struct kmemusage *kup; + unsigned long alloc; + void *newaddr; + + /* realloc(NULL, ...) is equivalent to malloc(...) */ + if (addr == NULL) + return (malloc(size, type, flags)); + + /* Sanity check */ + KASSERT(kmembase <= (char *)addr && (char *)addr < kmemlimit, + ("realloc: address %p out of range", (void *)addr)); + + /* Get the size of the original block */ + kup = btokup(addr); + alloc = 1 << kup->ku_indx; + if (alloc > MAXALLOCSAVE) + alloc = kup->ku_pagecnt << PAGE_SHIFT; + + /* Reuse the original block if appropriate */ + if (size <= alloc + && (size > (alloc >> REALLOC_FRACTION) || alloc == MINALLOCSIZE)) + return (addr); + + /* Allocate a new, bigger (or smaller) block */ + if ((newaddr = malloc(size, type, flags)) == NULL) + return (NULL); + + /* Copy over original contents */ + bcopy(addr, newaddr, min(size, alloc)); + free(addr, type); + return (newaddr); } /* Index: sys/malloc.h =================================================================== RCS file: /home/ncvs/src/sys/sys/malloc.h,v retrieving revision 1.54 diff -u -r1.54 malloc.h --- malloc.h 10 Aug 2001 06:37:04 -0000 1.54 +++ malloc.h 12 Mar 2002 00:06:12 -0000 @@ -173,6 +173,8 @@ void *malloc __P((unsigned long size, struct malloc_type *type, int flags)); void malloc_init __P((void *)); void malloc_uninit __P((void *)); +void *realloc __P((void *addr, unsigned long size, + struct malloc_type *type, int flags)); #endif /* _KERNEL */ #endif /* !_SYS_MALLOC_H_ */