/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident "$Header: subr_alloc.c 1.5 89/08/07 $"

/*
 * subr_alloc.c
 *	Generic Fixed-size Allocation procedures.
 */

/*
 * $Log:	subr_alloc.c,v $
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/genalloc.h>
#include <sys/mutex.h>
#include <sys/intctl.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>

#define	LOCK_GA(g, s)	s = p_lock(&ga->ga_mutex, SPLMEM)
#define	UNLOCK_GA(g, s)	v_lock(&ga->ga_mutex, s)

/*
 * geninit
 *	initialize an instantiation of the genalloc allocator.
 *
 * When geninit() gets called, init_alloc is called with initsize as
 * a parameter, and the result gets added to the freelist.  Other
 * fields of the instantiation record get initialized with the other
 * arguments.
 *
 * Arguments are:
 * ap - pointer to the instantiation record
 * size - size of objects which will be managed by the allocator
 * initsize - how much to initially allocate for the freelist (in bytes)
 * init_alloc - what to call to get initial bytes (usually calloc())
 * incr - how much to add to free list when it gets empty (in bytes)
 * incr_alloc - what to call to grow the list
 */
geninit(ga, size, initsize, init_alloc, incr, incr_alloc)
	struct genallocator	*ga;
	int			size;
	int			initsize;	/* initial size to allocate */
	caddr_t			(*init_alloc)();
	int			incr;
	caddr_t			(*incr_alloc)();
{
	caddr_t	p;
	caddr_t next;
	caddr_t f;

	ASSERT(ga != NULL, "geninit: null allocator");
	/*
	 *+ geninit() was called with a null allocation record.
	 *+ Whatever calls this internal routine should be
	 *+ checked for a valid first argument.
	 */

	/*
	 * allocate the initial list of objects
	 */

	p = (*init_alloc)(initsize);
	if (p == NULL) {
		return(-1);
	}
	next = p;

	f = NULL;
	while (next + size < (p + initsize)) {
		*(caddr_t *)next = f;
		f = next;
		next += size;
	}
	ga->ga_free = f;

	ga->ga_size = size;
	ga->ga_alloc_incr = incr;
	ga->ga_allocator = incr_alloc;
	init_lock(&ga->ga_mutex);
	init_sema(&ga->ga_sema, 1);
	return(0);
}

/*
 * genalloc
 *	allocate an object from ap
 *
 * onfailure tells what to do if the freelist is empty (currently
 * unimplemented, which means we always grow on freelist being
 * empty).
 */
/*ARGSUSED*/
caddr_t
genalloc(ga, onfailure)
	struct genallocator	*ga;
	int			onfailure;
{
	caddr_t	p;
	caddr_t	next;
	caddr_t f;
	int	size;
	spl_t	s;

	LOCK_GA(ga, s);

	if (ga->ga_free == (caddr_t)NULL) {
		f = NULL;
		size = ga->ga_size;
		p = (*ga->ga_allocator)(ga->ga_alloc_incr);
		if (p == NULL)
			return((caddr_t)NULL);
		
		next = p;
		while (next + ga->ga_size < (p + ga->ga_alloc_incr)) {
			*(caddr_t *)next = f;
			f = next;
			next += size;
		}
		ga->ga_free = f;
	}

	p = ga->ga_free;
	ga->ga_free = *(caddr_t *)p;
		
	UNLOCK_GA(ga, s);

	return(p);
}

/*
 * genfree
 *	free an object onto the list
 */
genfree(ga, p)
	struct genallocator	*ga;
	caddr_t			p;
{
	spl_t	s;

	LOCK_GA(ga, s);

	*(caddr_t *)p = ga->ga_free;
	ga->ga_free = p;

	UNLOCK_GA(ga, s);
}
