[Patch] TEST - prom-libc malloc

Paul Nasrat pnasrat at redhat.com
Thu Apr 19 01:15:18 EST 2007


Przemek Iskra <sparky at pld-linux.org> sent in a patch to replace the
malloc in yaboot with one Ethan originally developed in prom-libc. It's
possible this might be of use in increasing the tftp filesize limit.

Various people have expressed interest in testing it out so I'm
resending a rediffed version against git HEAD to enable testing. This
probably will be destined for 1.3.15 not 1.3.14 though. Upping the tftp
limit and trying kernels/images over 6MB would be useful.

Paul

diff --git a/include/stdlib.h b/include/stdlib.h
index 0b5e99a..def230c 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -6,19 +6,30 @@
 #define __STDLIB_H
 
 #include "stdarg.h"
+#include "stddef.h"
 
-extern void malloc_init(void *bottom, unsigned long size);
-extern void malloc_dispose(void);
+extern int __alloc_freelist_init(void);
+//extern void malloc_init(void *bottom, unsigned long size);
+//extern void malloc_dispose(void);
 
 extern void *malloc(unsigned int size);
 extern void *realloc(void *ptr, unsigned int size);
 extern void free (void *m);
-extern void mark (void **ptr);
-extern void release (void *ptr);
+//extern void mark (void **ptr);
+//extern void release (void *ptr);
 
 extern int sprintf(char * buf, const char *fmt, ...);
 extern int vsprintf(char *buf, const char *fmt, va_list args);
 extern long simple_strtol(const char *cp,char **endp,unsigned int base);
 #define strtol(x,y,z) simple_strtol(x,y,z)
 
+extern inline int atoi( const char *ptr );
+
+extern inline int
+atoi (const char *ptr)
+{
+	return (int) strtol (ptr, (char **) NULL, 10);
+}
+
+
 #endif
diff --git a/lib/malloc.c b/lib/malloc.c
index 672bb3e..87cc7f5 100644
--- a/lib/malloc.c
+++ b/lib/malloc.c
@@ -21,87 +21,219 @@
 
 #include "types.h"
 #include "stddef.h"
+#include "string.h"
+
+#define MALLOC_DEBUG 1
 
 /* Imported functions */
 extern void prom_printf (char *fmt, ...);
+extern void *prom_claim (void *virt, unsigned int size, unsigned int align);
+
+/*
+ * memory allocation routines from prom-libc
+ */
+
+void free(void *memory);
 
-static char *malloc_ptr = 0;
-static char *malloc_top = 0;
-static char *last_alloc = 0;
+/*** bits/malloc.h ***/
+struct __alloc_area
+{
+	struct __alloc_area *next;
+	unsigned int length;
+	long long foo;
+};
+
+/*** __freelist.c ***/
+/* the list of free areas */
+struct __alloc_area *__freelist = NULL;
 
-void malloc_init(void *bottom, unsigned long size)
+/*** __alloc_freelist_init.c ***/
+int __alloc_freelist_init(void)
 {
-	malloc_ptr = bottom;
-	malloc_top = bottom + size;
+     /* return if the freelist has already been set up. this will mean that
+      * one can call _init() to more than once without probs
+      */
+     if (__freelist)
+	  return 0;
+
+     /* assign an initial area */
+     __freelist = (struct __alloc_area *) prom_claim(
+	  (void *)MALLOCADDR, ((MALLOCSIZE / sizeof(struct __alloc_area))
+				* sizeof(struct __alloc_area)) , 0);
+     if ((void *)__freelist == (void *)-1)
+	  return 1;
+     __freelist->next = NULL;
+     __freelist->length = (MALLOCSIZE / sizeof(struct __alloc_area)) - 1;
+     return 0;
 }
 
-void malloc_dispose(void)
+
+/*** malloc.c ***/
+void *malloc(unsigned int bytes)
 {
-	malloc_ptr = 0;
-	last_alloc = 0;
+     struct __alloc_area *previous;
+     struct __alloc_area *current;
+     unsigned int units;
+
+     /* __freelist is still NULL, set up the initial entry */
+     if (!__freelist)
+	  __alloc_freelist_init();
+
+     /* determine the number of __alloc_area sized blocks needed to hold
+      * the number of bytes requested. needs to be an even number, otherwise
+      * the following header struct might be mis-aligned
+      */
+     units = (bytes + sizeof(struct __alloc_area) - 1) /
+	  sizeof(struct __alloc_area);
+
+     previous = NULL;
+     for (current = __freelist; current; current = current->next) {
+	  if (units < current->length + 1) {
+	       /* make area smaller, to accommodate a new area of
+		* the required size, and its header
+		*/
+	       current->length -= units;
+	       current->length -= 1;
+
+	       /* move the pointer so that it points to the new area
+		* being set up
+		*/
+	       current += current->length + 1;
+	       current->length = units;
+
+	       /* return a pointer to the user that starts
+		* immediately after the area header
+		*/
+	       return (void *)(current + 1);
+			
+	  } else if (units == current->length) {
+	       /* current area is exactly the right size */
+
+	       /* unlink this area from the list */
+	       if (previous)
+		    previous->next = current->next;
+
+	       return (void *)(current + 1);
+}
+	  previous = current;
+     }
+     return NULL;
 }
 
-void *malloc (unsigned int size)
+/*** realloc.c ***/
+void *realloc(void *old, unsigned int size)
 {
-    char *caddr;
+     struct __alloc_area *optr;
+     void *new;
+     unsigned int olen;
 
-    if (!malloc_ptr)
+     if (old && !size) {
+	  free(old);
     	return NULL;
-    if ((malloc_ptr + size + sizeof(int)) > malloc_top) {
-	prom_printf("malloc failed\n");
+     } else if (!old && !size)
     	return NULL;
+
+     if (!(new = malloc(size)))
+	  return NULL;
+
+     if (old) {
+	  optr = (struct __alloc_area *)old;
+	  optr--;
+	  olen = (optr->length * sizeof(struct __alloc_area));
+	  memcpy(new, old, olen <= size ? olen : size);
+	  free(old);
     }
-    *(int *)malloc_ptr = size;
-    caddr = malloc_ptr + sizeof(int);
-    malloc_ptr += size + sizeof(int);
-    last_alloc = caddr;
-    malloc_ptr = (char *) ((((unsigned int) malloc_ptr) + 3) & (~3));
-    return caddr;
+     return new;
 }
 
-void *realloc(void *ptr, unsigned int size)
+/*** free.c ***/
+/* join sequential areas in freelist */
+static void __alloc_freelist_join(void)
 {
-    char *caddr, *oaddr = ptr;
+     struct __alloc_area *previous, *current;
 
-    if (!malloc_ptr)
-    	return NULL;
-    if (oaddr == last_alloc) {
-	if (oaddr + size > malloc_top) {
-		prom_printf("realloc failed\n");
-		return NULL;
-	}
-	*(int *)(oaddr - sizeof(int)) = size;
-	malloc_ptr = oaddr + size;
-	return oaddr;
+     previous = NULL;
+
+     /* loop through areas in the freelist */
+     for (current = __freelist; current; current = current->next) {
+	  if (previous && previous + previous->length + 1 == current) {
+	       /* current area starts immediately after the previous
+		* one, join them
+		*/
+
+	       /* increase previous area's length to include the
+		* current area's header and length
+		*/
+	       previous->length++;
+	       previous->length += current->length;
+
+	       /* take the current entry out of the list */
+	       previous->next = current->next;
+	       current = previous;
+	  } else
+	       previous = current;
     }
-    caddr = malloc(size);
-    if (caddr != 0 && oaddr != 0)
-	memcpy(caddr, oaddr, *(int *)(oaddr - sizeof(int)));
-    return caddr;
 }
 
-void free (void *m)
+void free(void *memory)
 {
-    if (!malloc_ptr)
+     struct __alloc_area *free;
+     struct __alloc_area *before;
+     struct __alloc_area *current;
+
+#ifdef MALLOC_DEBUG
+     /* check that memory isn't a NULL pointer (possibly from a failed
+      * malloc). this check is nice, but not compulsory
+      */
+     if (!memory) {
+	  prom_printf("WARNING: attempt to free a NULL pointer\n");
     	return;
-    if (m == last_alloc)
-	malloc_ptr = (char *) last_alloc - sizeof(int);
 }
+#endif /* MALLOC_DEBUG */
 
-void mark (void **ptr)
-{
-    if (!malloc_ptr)
+     /* set free to point to the pointer supplied, move free one element so
+      * that it points to the actual header
+      */
+     free = (struct __alloc_area *)memory;
+     free--;
+
+#ifdef MALLOC_DEBUG
+     /* check that this pointer isn't already in the freelist, or a pointer
+      * to something that's inside something in the freelist. again, this
+      * check isn't essential for a non-broken program
+      */
+     for (current = __freelist; current; current = current->next)
+	  if (free >= current && free < current + current->length) {
+	       prom_printf("WARNING: memory area already in freelist\n");
     	return;
-    *ptr = (void *) malloc_ptr;
 }
+#endif /* MALLOC_DEBUG */
 
-void release (void *ptr)
-{
-    if (!malloc_ptr)
-    	return;
-    malloc_ptr = (char *) ptr;
+     /* free puts areas back in the freelist in order of the pointers, so
+      * that sequential areas can be joined later
+      */
+
+     /* try and find the area that immediately preceeds this one */
+     before = NULL;
+     for (current = __freelist; current && free > current; 
+	  current = current->next)
+	  before = current;
+
+     if (before) {
+	  /* put this area in just after the area before it */
+	  free->next = before->next;
+	  before->next = free;
+     } else {
+	  /* nothing before this area, place at beginning of freelist */
+	  free->next = __freelist;
+	  __freelist = free;
+     }
+
+     /* join any sequential areas */
+     __alloc_freelist_join();
 }
 
+// Should not be here
 char *strdup(char const *str)
 {
     char *p = malloc(strlen(str) + 1);
diff --git a/second/yaboot.c b/second/yaboot.c
index 20296df..4af0129 100644
--- a/second/yaboot.c
+++ b/second/yaboot.c
@@ -194,6 +194,7 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
      /* Initialize OF interface */
      prom_init ((prom_entry) r5);
 
+#if 0
      /* Allocate some memory for malloc'ator */
      malloc_base = prom_claim((void *)MALLOCADDR, MALLOCSIZE, 0);
      if (malloc_base == (void *)-1) {
@@ -204,6 +205,13 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
      malloc_init(malloc_base, MALLOCSIZE);
      DEBUG_F("Malloc buffer allocated at %p (%d bytes)\n",
 	     malloc_base, MALLOCSIZE);
+#endif
+
+    if ( __alloc_freelist_init() ) {
+        prom_printf("Can't claim malloc buffer (%d bytes at 0x%08x)\n",
+		      MALLOCSIZE, MALLOCADDR);
+        return -1;
+    }
 
      /* A few useless DEBUG_F's */
      DEBUG_F("reloc_offset :  %ld         (should be 0)\n", reloc_offset());
@@ -235,10 +243,8 @@ yaboot_start (unsigned long r3, unsigned long r4, unsigned long r5)
      result = yaboot_main();
 
      /* Get rid of malloc pool */
-     malloc_dispose();
      prom_release(malloc_base, MALLOCSIZE);
-     DEBUG_F("Malloc buffer released. Exiting with code %d\n",
-	     result);
+	DEBUG_F("Malloc buffer released. Exiting with code %d\n", result);
 
      /* Return to OF */
      prom_exit();





More information about the Yaboot-devel mailing list