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_ */

Reply via email to