Blame hurd/hurdmalloc.c

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