Blame hurd/hurdmalloc.c

Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
Packit 6c4009
#include "hurdmalloc.h"		/* XXX see that file */
Packit 6c4009
Packit 6c4009
#include <mach.h>
Packit 6c4009
#define vm_allocate __vm_allocate
Packit 6c4009
#define vm_page_size __vm_page_size
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Mach Operating System
Packit 6c4009
 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
Packit 6c4009
 * All Rights Reserved.
Packit 6c4009
 *
Packit 6c4009
 * Permission to use, copy, modify and distribute this software and its
Packit 6c4009
 * documentation is hereby granted, provided that both the copyright
Packit 6c4009
 * notice and this permission notice appear in all copies of the
Packit 6c4009
 * software, derivative works or modified versions, and any portions
Packit 6c4009
 * thereof, and that both notices appear in supporting documentation.
Packit 6c4009
 *
Packit 6c4009
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
Packit 6c4009
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
Packit 6c4009
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
Packit 6c4009
 *
Packit 6c4009
 * Carnegie Mellon requests users of this software to return to
Packit 6c4009
 *
Packit 6c4009
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
Packit 6c4009
 *  School of Computer Science
Packit 6c4009
 *  Carnegie Mellon University
Packit 6c4009
 *  Pittsburgh PA 15213-3890
Packit 6c4009
 *
Packit 6c4009
 * any improvements or extensions that they make and grant Carnegie Mellon
Packit 6c4009
 * the rights to redistribute these changes.
Packit 6c4009
 */
Packit 6c4009
/*
Packit 6c4009
 * (pre-GNU) HISTORY
Packit 6c4009
 *
Packit 6c4009
 * Revision 2.7  91/05/14  17:57:34  mrt
Packit 6c4009
 * 	Correcting copyright
Packit 6c4009
 *
Packit 6c4009
 * Revision 2.6  91/02/14  14:20:26  mrt
Packit 6c4009
 * 	Added new Mach copyright
Packit 6c4009
 * 	[91/02/13  12:41:21  mrt]
Packit 6c4009
 *
Packit 6c4009
 * Revision 2.5  90/11/05  14:37:33  rpd
Packit 6c4009
 * 	Added malloc_fork* code.
Packit 6c4009
 * 	[90/11/02            rwd]
Packit 6c4009
 *
Packit 6c4009
 * 	Add spin_lock_t.
Packit 6c4009
 * 	[90/10/31            rwd]
Packit 6c4009
 *
Packit 6c4009
 * Revision 2.4  90/08/07  14:31:28  rpd
Packit 6c4009
 * 	Removed RCS keyword nonsense.
Packit 6c4009
 *
Packit 6c4009
 * Revision 2.3  90/06/02  15:14:00  rpd
Packit 6c4009
 * 	Converted to new IPC.
Packit 6c4009
 * 	[90/03/20  20:56:57  rpd]
Packit 6c4009
 *
Packit 6c4009
 * Revision 2.2  89/12/08  19:53:59  rwd
Packit 6c4009
 * 	Removed conditionals.
Packit 6c4009
 * 	[89/10/23            rwd]
Packit 6c4009
 *
Packit 6c4009
 * Revision 2.1  89/08/03  17:09:46  rwd
Packit 6c4009
 * Created.
Packit 6c4009
 *
Packit 6c4009
 *
Packit 6c4009
 * 13-Sep-88  Eric Cooper (ecc) at Carnegie Mellon University
Packit 6c4009
 *	Changed realloc() to copy min(old size, new size) bytes.
Packit 6c4009
 *	Bug found by Mike Kupfer at Olivetti.
Packit 6c4009
 */
Packit 6c4009
/*
Packit 6c4009
 * 	File: 	malloc.c
Packit 6c4009
 *	Author: Eric Cooper, Carnegie Mellon University
Packit 6c4009
 *	Date:	July, 1988
Packit 6c4009
 *
Packit 6c4009
 * 	Memory allocator for use with multiple threads.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009

Packit 6c4009
#include <assert.h>
Packit 6c4009
Packit 6c4009
#include <cthreads.h>
Packit 6c4009
Packit 6c4009
#define MCHECK
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Structure of memory block header.
Packit 6c4009
 * When free, next points to next block on free list.
Packit 6c4009
 * When allocated, fl points to free list.
Packit 6c4009
 * Size of header is 4 bytes, so minimum usable block size is 8 bytes.
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
#define CHECK_BUSY  0x8a3c743e
Packit 6c4009
#define CHECK_FREE  0x66688b92
Packit 6c4009
Packit 6c4009
#ifdef MCHECK
Packit 6c4009
Packit 6c4009
typedef struct header {
Packit 6c4009
  long check;
Packit 6c4009
  union {
Packit 6c4009
    struct header *next;
Packit 6c4009
    struct free_list *fl;
Packit 6c4009
  } u;
Packit 6c4009
} *header_t;
Packit 6c4009
Packit 6c4009
#define HEADER_SIZE sizeof (struct header)
Packit 6c4009
#define HEADER_NEXT(h) ((h)->u.next)
Packit 6c4009
#define HEADER_FREE(h) ((h)->u.fl)
Packit 6c4009
#define HEADER_CHECK(h) ((h)->check)
Packit 6c4009
#define MIN_SIZE	16
Packit 6c4009
#define LOG2_MIN_SIZE	4
Packit 6c4009
Packit 6c4009
#else /* ! MCHECK */
Packit 6c4009
Packit 6c4009
typedef union header {
Packit 6c4009
	union header *next;
Packit 6c4009
	struct free_list *fl;
Packit 6c4009
} *header_t;
Packit 6c4009
Packit 6c4009
#define HEADER_SIZE sizeof (union header)
Packit 6c4009
#define HEADER_NEXT(h) ((h)->next)
Packit 6c4009
#define HEADER_FREE(h) ((h)->fl)
Packit 6c4009
#define MIN_SIZE	8	/* minimum block size */
Packit 6c4009
#define LOG2_MIN_SIZE	3
Packit 6c4009
Packit 6c4009
#endif /* MCHECK */
Packit 6c4009
Packit 6c4009
typedef struct free_list {
Packit 6c4009
	spin_lock_t lock;	/* spin lock for mutual exclusion */
Packit 6c4009
	header_t head;		/* head of free list for this size */
Packit 6c4009
#ifdef	DEBUG
Packit 6c4009
	int in_use;		/* # mallocs - # frees */
Packit 6c4009
#endif	/* DEBUG */
Packit 6c4009
} *free_list_t;
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Free list with index i contains blocks of size 2 ^ (i + LOG2_MIN_SIZE)
Packit 6c4009
 * including header.  Smallest block size is MIN_SIZE, with MIN_SIZE -
Packit 6c4009
 * HEADER_SIZE bytes available to user.  Size argument to malloc is a signed
Packit 6c4009
 * integer for sanity checking, so largest block size is 2^31.
Packit 6c4009
 */
Packit 6c4009
#define NBUCKETS	29
Packit 6c4009
Packit 6c4009
static struct free_list malloc_free_list[NBUCKETS];
Packit 6c4009
Packit 6c4009
/* Initialization just sets everything to zero, but might be necessary on a
Packit 6c4009
   machine where spin_lock_init does otherwise, and is necessary when
Packit 6c4009
   running an executable that was written by something like Emacs's unexec.
Packit 6c4009
   It preserves the values of data variables like malloc_free_list, but
Packit 6c4009
   does not save the vm_allocate'd space allocated by this malloc.  */
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
malloc_init (void)
Packit 6c4009
{
Packit 6c4009
  int i;
Packit 6c4009
  for (i = 0; i < NBUCKETS; ++i)
Packit 6c4009
    {
Packit 6c4009
      spin_lock_init (&malloc_free_list[i].lock);
Packit 6c4009
      malloc_free_list[i].head = NULL;
Packit 6c4009
#ifdef DEBUG
Packit 6c4009
      malloc_free_list[i].in_use = 0;
Packit 6c4009
#endif
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* This not only suppresses a `defined but not used' warning,
Packit 6c4009
     but it is ABSOLUTELY NECESSARY to avoid the hyperclever
Packit 6c4009
     compiler from "optimizing out" the entire function!  */
Packit 6c4009
  (void) &malloc_init;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
more_memory(int size, free_list_t fl)
Packit 6c4009
{
Packit 6c4009
	int amount;
Packit 6c4009
	int n;
Packit 6c4009
	vm_address_t where;
Packit 6c4009
	header_t h;
Packit 6c4009
	kern_return_t r;
Packit 6c4009
Packit 6c4009
	if (size <= vm_page_size) {
Packit 6c4009
		amount = vm_page_size;
Packit 6c4009
		n = vm_page_size / size;
Packit 6c4009
		/* We lose vm_page_size - n*size bytes here.  */
Packit 6c4009
	} else {
Packit 6c4009
		amount = size;
Packit 6c4009
		n = 1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
	r = vm_allocate(mach_task_self(), &where, (vm_size_t) amount, TRUE);
Packit 6c4009
	assert_perror (r);
Packit 6c4009
Packit 6c4009
	h = (header_t) where;
Packit 6c4009
	do {
Packit 6c4009
		HEADER_NEXT (h) = fl->head;
Packit 6c4009
#ifdef MCHECK
Packit 6c4009
		HEADER_CHECK (h) = CHECK_FREE;
Packit 6c4009
#endif
Packit 6c4009
		fl->head = h;
Packit 6c4009
		h = (header_t) ((char *) h + size);
Packit 6c4009
	} while (--n != 0);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Declaration changed to standard one for GNU.  */
Packit 6c4009
void *
Packit 6c4009
malloc (size_t size)
Packit 6c4009
{
Packit 6c4009
	int i, n;
Packit 6c4009
	free_list_t fl;
Packit 6c4009
	header_t h;
Packit 6c4009
Packit 6c4009
	if ((int) size < 0)		/* sanity check */
Packit 6c4009
		return 0;
Packit 6c4009
	size += HEADER_SIZE;
Packit 6c4009
	/*
Packit 6c4009
	 * Find smallest power-of-two block size
Packit 6c4009
	 * big enough to hold requested size plus header.
Packit 6c4009
	 */
Packit 6c4009
	i = 0;
Packit 6c4009
	n = MIN_SIZE;
Packit 6c4009
	while (n < size) {
Packit 6c4009
		i += 1;
Packit 6c4009
		n <<= 1;
Packit 6c4009
	}
Packit 6c4009
	ASSERT(i < NBUCKETS);
Packit 6c4009
	fl = &malloc_free_list[i];
Packit 6c4009
	spin_lock(&fl->lock);
Packit 6c4009
	h = fl->head;
Packit 6c4009
	if (h == 0) {
Packit 6c4009
		/*
Packit 6c4009
		 * Free list is empty;
Packit 6c4009
		 * allocate more blocks.
Packit 6c4009
		 */
Packit 6c4009
		more_memory(n, fl);
Packit 6c4009
		h = fl->head;
Packit 6c4009
		if (h == 0) {
Packit 6c4009
			/*
Packit 6c4009
			 * Allocation failed.
Packit 6c4009
			 */
Packit 6c4009
			spin_unlock(&fl->lock);
Packit 6c4009
			return 0;
Packit 6c4009
		}
Packit 6c4009
	}
Packit 6c4009
	/*
Packit 6c4009
	 * Pop block from free list.
Packit 6c4009
	 */
Packit 6c4009
	fl->head = HEADER_NEXT (h);
Packit 6c4009
Packit 6c4009
#ifdef MCHECK
Packit 6c4009
	assert (HEADER_CHECK (h) == CHECK_FREE);
Packit 6c4009
	HEADER_CHECK (h) = CHECK_BUSY;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifdef	DEBUG
Packit 6c4009
	fl->in_use += 1;
Packit 6c4009
#endif	/* DEBUG */
Packit 6c4009
	spin_unlock(&fl->lock);
Packit 6c4009
	/*
Packit 6c4009
	 * Store free list pointer in block header
Packit 6c4009
	 * so we can figure out where it goes
Packit 6c4009
	 * at free() time.
Packit 6c4009
	 */
Packit 6c4009
	HEADER_FREE (h) = fl;
Packit 6c4009
	/*
Packit 6c4009
	 * Return pointer past the block header.
Packit 6c4009
	 */
Packit 6c4009
	return ((char *) h) + HEADER_SIZE;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Declaration changed to standard one for GNU.  */
Packit 6c4009
void
Packit 6c4009
free (void *base)
Packit 6c4009
{
Packit 6c4009
	header_t h;
Packit 6c4009
	free_list_t fl;
Packit 6c4009
	int i;
Packit 6c4009
Packit 6c4009
	if (base == 0)
Packit 6c4009
		return;
Packit 6c4009
	/*
Packit 6c4009
	 * Find free list for block.
Packit 6c4009
	 */
Packit 6c4009
	h = (header_t) (base - HEADER_SIZE);
Packit 6c4009
Packit 6c4009
#ifdef MCHECK
Packit 6c4009
	assert (HEADER_CHECK (h) == CHECK_BUSY);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
	fl = HEADER_FREE (h);
Packit 6c4009
	i = fl - malloc_free_list;
Packit 6c4009
	/*
Packit 6c4009
	 * Sanity checks.
Packit 6c4009
	 */
Packit 6c4009
	if (i < 0 || i >= NBUCKETS) {
Packit 6c4009
		ASSERT(0 <= i && i < NBUCKETS);
Packit 6c4009
		return;
Packit 6c4009
	}
Packit 6c4009
	if (fl != &malloc_free_list[i]) {
Packit 6c4009
		ASSERT(fl == &malloc_free_list[i]);
Packit 6c4009
		return;
Packit 6c4009
	}
Packit 6c4009
	/*
Packit 6c4009
	 * Push block on free list.
Packit 6c4009
	 */
Packit 6c4009
	spin_lock(&fl->lock);
Packit 6c4009
	HEADER_NEXT (h) = fl->head;
Packit 6c4009
#ifdef MCHECK
Packit 6c4009
	HEADER_CHECK (h) = CHECK_FREE;
Packit 6c4009
#endif
Packit 6c4009
	fl->head = h;
Packit 6c4009
#ifdef	DEBUG
Packit 6c4009
	fl->in_use -= 1;
Packit 6c4009
#endif	/* DEBUG */
Packit 6c4009
	spin_unlock(&fl->lock);
Packit 6c4009
	return;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Declaration changed to standard one for GNU.  */
Packit 6c4009
void *
Packit 6c4009
realloc (void *old_base, size_t new_size)
Packit 6c4009
{
Packit 6c4009
	header_t h;
Packit 6c4009
	free_list_t fl;
Packit 6c4009
	int i;
Packit 6c4009
	unsigned int old_size;
Packit 6c4009
	char *new_base;
Packit 6c4009
Packit 6c4009
	if (old_base == 0)
Packit 6c4009
	  return malloc (new_size);
Packit 6c4009
Packit 6c4009
	/*
Packit 6c4009
	 * Find size of old block.
Packit 6c4009
	 */
Packit 6c4009
	h = (header_t) (old_base - HEADER_SIZE);
Packit 6c4009
#ifdef MCHECK
Packit 6c4009
	assert (HEADER_CHECK (h) == CHECK_BUSY);
Packit 6c4009
#endif
Packit 6c4009
	fl = HEADER_FREE (h);
Packit 6c4009
	i = fl - malloc_free_list;
Packit 6c4009
	/*
Packit 6c4009
	 * Sanity checks.
Packit 6c4009
	 */
Packit 6c4009
	if (i < 0 || i >= NBUCKETS) {
Packit 6c4009
		ASSERT(0 <= i && i < NBUCKETS);
Packit 6c4009
		return 0;
Packit 6c4009
	}
Packit 6c4009
	if (fl != &malloc_free_list[i]) {
Packit 6c4009
		ASSERT(fl == &malloc_free_list[i]);
Packit 6c4009
		return 0;
Packit 6c4009
	}
Packit 6c4009
	/*
Packit 6c4009
	 * Free list with index i contains blocks of size
Packit 6c4009
	 * 2 ^ (i + * LOG2_MIN_SIZE) including header.
Packit 6c4009
	 */
Packit 6c4009
	old_size = (1 << (i + LOG2_MIN_SIZE)) - HEADER_SIZE;
Packit 6c4009
Packit 6c4009
	if (new_size <= old_size
Packit 6c4009
	    && new_size > (((old_size + HEADER_SIZE) >> 1) - HEADER_SIZE))
Packit 6c4009
	  /* The new size still fits in the same block, and wouldn't fit in
Packit 6c4009
	     the next smaller block!  */
Packit 6c4009
	  return old_base;
Packit 6c4009
Packit 6c4009
	/*
Packit 6c4009
	 * Allocate new block, copy old bytes, and free old block.
Packit 6c4009
	 */
Packit 6c4009
	new_base = malloc(new_size);
Packit 6c4009
	if (new_base)
Packit 6c4009
	  memcpy (new_base, old_base,
Packit 6c4009
		  (int) (old_size < new_size ? old_size : new_size));
Packit 6c4009
Packit 6c4009
	if (new_base || new_size == 0)
Packit 6c4009
	  /* Free OLD_BASE, but only if the malloc didn't fail.  */
Packit 6c4009
	  free (old_base);
Packit 6c4009
Packit 6c4009
	return new_base;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#ifdef	DEBUG
Packit 6c4009
void
Packit 6c4009
print_malloc_free_list (void)
Packit 6c4009
{
Packit 6c4009
	int i, size;
Packit 6c4009
	free_list_t fl;
Packit 6c4009
	int n;
Packit 6c4009
	header_t h;
Packit 6c4009
	int total_used = 0;
Packit 6c4009
	int total_free = 0;
Packit 6c4009
Packit 6c4009
	fprintf(stderr, "      Size     In Use       Free      Total\n");
Packit 6c4009
	for (i = 0, size = MIN_SIZE, fl = malloc_free_list;
Packit 6c4009
	     i < NBUCKETS;
Packit 6c4009
	     i += 1, size <<= 1, fl += 1) {
Packit 6c4009
		spin_lock(&fl->lock);
Packit 6c4009
		if (fl->in_use != 0 || fl->head != 0) {
Packit 6c4009
			total_used += fl->in_use * size;
Packit 6c4009
			for (n = 0, h = fl->head; h != 0; h = HEADER_NEXT (h), n += 1)
Packit 6c4009
				;
Packit 6c4009
			total_free += n * size;
Packit 6c4009
			fprintf(stderr, "%10d %10d %10d %10d\n",
Packit 6c4009
				size, fl->in_use, n, fl->in_use + n);
Packit 6c4009
		}
Packit 6c4009
		spin_unlock(&fl->lock);
Packit 6c4009
	}
Packit 6c4009
	fprintf(stderr, " all sizes %10d %10d %10d\n",
Packit 6c4009
		total_used, total_free, total_used + total_free);
Packit 6c4009
}
Packit 6c4009
#endif	/* DEBUG */
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_hurd_malloc_fork_prepare(void)
Packit 6c4009
/*
Packit 6c4009
 * Prepare the malloc module for a fork by insuring that no thread is in a
Packit 6c4009
 * malloc critical section.
Packit 6c4009
 */
Packit 6c4009
{
Packit 6c4009
    int i;
Packit 6c4009
Packit 6c4009
    for (i = 0; i < NBUCKETS; i++) {
Packit 6c4009
	spin_lock(&malloc_free_list[i].lock);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_hurd_malloc_fork_parent(void)
Packit 6c4009
/*
Packit 6c4009
 * Called in the parent process after a fork() to resume normal operation.
Packit 6c4009
 */
Packit 6c4009
{
Packit 6c4009
    int i;
Packit 6c4009
Packit 6c4009
    for (i = NBUCKETS-1; i >= 0; i--) {
Packit 6c4009
	spin_unlock(&malloc_free_list[i].lock);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_hurd_malloc_fork_child(void)
Packit 6c4009
/*
Packit 6c4009
 * Called in the child process after a fork() to resume normal operation.
Packit 6c4009
 */
Packit 6c4009
{
Packit 6c4009
    int i;
Packit 6c4009
Packit 6c4009
    for (i = NBUCKETS-1; i >= 0; i--) {
Packit 6c4009
	spin_unlock(&malloc_free_list[i].lock);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009

Packit 6c4009
Packit 6c4009
text_set_element (_hurd_preinit_hook, malloc_init);