/*++
"This software program is available to you under a choice of one of two
licenses.  You may choose to be licensed under either the GNU General Public
License (GPL) Version 2, June 1991, available at
http://www.fsf.org/copyleft/gpl.html, or the Intel BSD + Patent License,
the text of which follows:

"Recipient" has requested a license and Intel Corporation ("Intel") is willing
to grant a license for the software entitled InfiniBand(tm) System Software
(the "Software") being provided by Intel Corporation.

The following definitions apply to this License:

"Licensed Patents" means patent claims licensable by Intel Corporation which
are necessarily infringed by the use or sale of the Software alone or when
combined with the operating system referred to below.

"Recipient" means the party to whom Intel delivers this Software.
"Licensee" means Recipient and those third parties that receive a license to
any operating system available under the GNU Public License version 2.0 or
later.

Copyright (c) 1996-2003 Intel Corporation. All rights reserved.

The license is provided to Recipient and Recipient's Licensees under the
following terms.

Redistribution and use in source and binary forms of the Software, with or
without modification, are permitted provided that the following conditions are
met:
Redistributions of source code of the Software may retain the above copyright
notice, this list of conditions and the following disclaimer.

Redistributions in binary form of the Software may reproduce the above
copyright notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

Neither the name of Intel Corporation nor the names of its contributors shall
be used to endorse or promote products derived from this Software without
specific prior written permission.

Intel hereby grants Recipient and Licensees a non-exclusive, worldwide,
royalty-free patent license under Licensed Patents to make, use, sell, offer
to sell, import and otherwise transfer the Software, if any, in source code
and object code form. This license shall include changes to the Software that
are error corrections or other minor changes to the Software that do not add
functionality or features when the Software is incorporated in any version of
a operating system that has been distributed under the GNU General Public
License 2.0 or later.  This patent license shall apply to the combination of
the Software and any operating system licensed under the GNU Public License
version 2.0 or later if, at the time Intel provides the Software to Recipient,
such addition of the Software to the then publicly available versions of such
operating system available under the GNU Public License version 2.0 or later
(whether in gold, beta or alpha form) causes such combination to be covered by
the Licensed Patents. The patent license shall not apply to any other
combinations which include the Software. No hardware per se is licensed
hereunder.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL INTEL OR ITS CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
--*/

/*
 * Abstract:
 *	Implementation of the grow pools.  The grow pools manage a pool of objects.
 *	The pools can grow to meet demand, limited only by system memory.
 *
 * Environment:
 *	All
 *
 * $Revision: 1.1 $
 */

#include <complib/cl_qcomppool.h>
#include <complib/cl_comppool.h>
#include <complib/cl_qpool.h>
#include <complib/cl_pool.h>
#include <complib/cl_memory.h>
#include <complib/cl_math.h>

/*
 * IMPLEMENTATION OF QUICK COMPOSITE POOL
 */

void
cl_qcpool_construct(IN cl_qcpool_t * const p_pool)
{
	CL_ASSERT(p_pool);

	cl_memclr(p_pool, sizeof (cl_qcpool_t));

	p_pool->state = CL_UNINITIALIZED;
}

cl_status_t
cl_qcpool_init(IN cl_qcpool_t * const p_pool,
	       IN const size_t min_size,
	       IN const size_t max_size,
	       IN const size_t grow_size,
	       IN const size_t * const component_sizes,
	       IN const uint32_t num_components,
	       IN cl_pfn_qcpool_init_t pfn_initializer OPTIONAL,
	       IN cl_pfn_qcpool_dtor_t pfn_destructor OPTIONAL,
	       IN const void *const context)
{
	cl_status_t status;
	uint32_t i;

	CL_ASSERT(p_pool);
	/* Must have a minimum of 1 component. */
	CL_ASSERT(num_components);
	/* A component size array is required. */
	CL_ASSERT(component_sizes);
	/*
	 * If no initializer is provided, the first component must be large
	 * enough to hold a pool item.
	 */
	CL_ASSERT(pfn_initializer ||
		  (component_sizes[0] >= sizeof (cl_pool_item_t)));

	cl_qcpool_construct(p_pool);

	if (num_components > 1 && !pfn_initializer)
		return (CL_INVALID_SETTING);

	if (max_size && max_size < min_size)
		return (CL_INVALID_SETTING);

	/*
	 * Allocate the array of component sizes and component pointers all
	 * in one allocation.
	 */
	p_pool->component_sizes = (size_t *) cl_zalloc((sizeof (size_t) +
							sizeof (void *)) *
						       num_components);

	if (!p_pool->component_sizes)
		return (CL_INSUFFICIENT_MEMORY);

	/* Calculate the pointer to the array of pointers, used for callbacks. */
	p_pool->p_components =
	    (void **) (p_pool->component_sizes + num_components);

	/* Copy the user's sizes into our array for future use. */
	cl_memcpy(p_pool->component_sizes, component_sizes,
		  sizeof (uint32_t) * num_components);

	/* Store the number of components per object. */
	p_pool->num_components = num_components;

	/* Round up and store the size of the components. */
	for (i = 0; i < num_components; i++) {
		/*
		 * We roundup each component size so that all components
		 * are aligned on a natural boundary.
		 */
		p_pool->component_sizes[i] =
		    ROUNDUP(p_pool->component_sizes[i], sizeof (uintn_t));
	}

	p_pool->max_objects = max_size ? max_size : ~(size_t) 0;
	p_pool->grow_size = grow_size;

	/* Store callback function pointers. */
	p_pool->pfn_init = pfn_initializer;	/* may be NULL */
	p_pool->pfn_dtor = pfn_destructor;	/* may be NULL */
	p_pool->context = context;

	cl_qlist_init(&p_pool->alloc_list);

	cl_qlist_init(&p_pool->free_list);

	/*
	 * We are now initialized.  We change the initialized flag before
	 * growing since the grow function asserts that we are initialized.
	 */
	p_pool->state = CL_INITIALIZED;

	/* Allocate the minimum number of objects as requested. */
	if (!min_size)
		return (CL_SUCCESS);

	status = cl_qcpool_grow(p_pool, min_size);
	/* Trap for error and cleanup if necessary. */
	if (status != CL_SUCCESS)
		cl_qcpool_destroy(p_pool);

	return (status);
}

void
cl_qcpool_destroy(IN cl_qcpool_t * const p_pool)
{
	/* CL_ASSERT that a non-NULL pointer was provided. */
	CL_ASSERT(p_pool);
	/* CL_ASSERT that we are in a valid state (not uninitialized memory). */
	CL_ASSERT(cl_is_state_valid(p_pool->state));

	if (p_pool->state == CL_INITIALIZED) {
		/*
		 * Assert if the user hasn't put everything back in the pool
		 * before destroying it
		 * if they haven't, then most likely they are still using memory
		 * that will be freed, and the destructor will not be called!
		 */
		CL_ASSERT(cl_qcpool_count(p_pool) == p_pool->num_objects);

		/* call the user's destructor for each object in the pool */
		if (p_pool->pfn_dtor) {
			while (!cl_is_qlist_empty(&p_pool->free_list)) {
				p_pool->pfn_dtor((cl_pool_item_t *)
						 cl_qlist_remove_head(&p_pool->
								      free_list),
						 (void *) p_pool->context);
			}
		} else {
			cl_qlist_remove_all(&p_pool->free_list);
		}

		/* Free all alocated memory blocks. */
		while (!cl_is_qlist_empty(&p_pool->alloc_list))
			cl_free(cl_qlist_remove_head(&p_pool->alloc_list));

		if (p_pool->component_sizes) {
			cl_free(p_pool->component_sizes);
			p_pool->component_sizes = NULL;
		}
	}

	p_pool->state = CL_UNINITIALIZED;
}

cl_status_t
cl_qcpool_grow(IN cl_qcpool_t * const p_pool, IN uint32_t obj_count)
{
	cl_status_t status = CL_SUCCESS;
	uint8_t *p_objects;
	cl_pool_item_t *p_pool_item;
	uint32_t i;
	size_t obj_size;

	CL_ASSERT(p_pool);
	CL_ASSERT(p_pool->state == CL_INITIALIZED);
	CL_ASSERT(obj_count);

	/* Validate that growth is possible. */
	if (p_pool->num_objects == p_pool->max_objects)
		return (CL_INSUFFICIENT_MEMORY);

	/* Cap the growth to the desired maximum. */
	if (obj_count > (p_pool->max_objects - p_pool->num_objects))
		obj_count = p_pool->max_objects - p_pool->num_objects;

	/* Calculate the size of an object. */
	obj_size = 0;
	for (i = 0; i < p_pool->num_components; i++)
		obj_size += p_pool->component_sizes[i];

	/* Allocate the buffer for the new objects. */
	p_objects = (uint8_t *)
	    cl_zalloc(sizeof (cl_list_item_t) + (obj_size * obj_count));

	/* Make sure the allocation succeeded. */
	if (!p_objects)
		return (CL_INSUFFICIENT_MEMORY);

	/* Insert the allocation in our list. */
	cl_qlist_insert_tail(&p_pool->alloc_list, (cl_list_item_t *) p_objects);
	p_objects += sizeof (cl_list_item_t);

	/* initialize the new elements and add them to the free list */
	while (obj_count--) {
		/* Setup the array of components for the current object. */
		p_pool->p_components[0] = p_objects;
		for (i = 1; i < p_pool->num_components; i++) {
			/* Calculate the pointer to the next component. */
			p_pool->p_components[i] =
			    (uint8_t *) p_pool->p_components[i - 1] +
			    p_pool->component_sizes[i - 1];
		}

		/*
		 * call the user's initializer
		 * this can fail!
		 */
		if (p_pool->pfn_init) {
			p_pool_item = NULL;
			status = p_pool->pfn_init(p_pool->p_components,
						  p_pool->num_components,
						  (void *) p_pool->context,
						  &p_pool_item);
			if (status != CL_SUCCESS) {
				/*
				 * User initialization failed
				 * we may have only grown the pool by some partial amount
				 * Invoke the destructor for the object that failed
				 * initialization.
				 */
				if (p_pool->pfn_dtor)
					p_pool->pfn_dtor(p_pool_item,
							 (void *) p_pool->
							 context);

				/* Return the user's status. */
				return (status);
			}
			CL_ASSERT(p_pool_item);
		} else {
			/*
			 * If no initializer is provided, assume that the pool item
			 * is stored at the beginning of the first component.
			 */
			p_pool_item =
			    (cl_pool_item_t *) p_pool->p_components[0];
		}

#ifdef _DEBUG_
		/*
		 * Set the pool item's pool pointer to this pool so that we can
		 * check that items get returned to the correct pool.
		 */
		p_pool_item->p_pool = p_pool;
#endif

		/* Insert the new item in the free list, traping for failure. */
		cl_qlist_insert_head(&p_pool->free_list,
				     &p_pool_item->list_item);

		p_pool->num_objects++;

		/* move the pointer to the next item */
		p_objects += obj_size;
	}

	return (status);
}

cl_pool_item_t *
cl_qcpool_get(IN cl_qcpool_t * const p_pool)
{
	cl_list_item_t *p_list_item;

	CL_ASSERT(p_pool);
	CL_ASSERT(p_pool->state == CL_INITIALIZED);

	if (cl_is_qlist_empty(&p_pool->free_list)) {
		/*
		 * No object is available.
		 * Return NULL if the user does not want automatic growth.
		 */
		if (!p_pool->grow_size)
			return (NULL);

		/* We ran out of elements.  Get more */
		cl_qcpool_grow(p_pool, p_pool->grow_size);
		/*
		 * We may not have gotten everything we wanted but we might have
		 * gotten something.
		 */
		if (cl_is_qlist_empty(&p_pool->free_list))
			return (NULL);
	}

	p_list_item = cl_qlist_remove_head(&p_pool->free_list);
	/* OK, at this point we have an object */
	CL_ASSERT(p_list_item != cl_qlist_end(&p_pool->free_list));
	return ((cl_pool_item_t *) p_list_item);
}

cl_pool_item_t *
cl_qcpool_get_tail(IN cl_qcpool_t * const p_pool)
{
	cl_list_item_t *p_list_item;

	CL_ASSERT(p_pool);
	CL_ASSERT(p_pool->state == CL_INITIALIZED);

	if (cl_is_qlist_empty(&p_pool->free_list)) {
		/*
		 * No object is available.
		 * Return NULL if the user does not want automatic growth.
		 */
		if (!p_pool->grow_size)
			return (NULL);

		/* We ran out of elements.  Get more */
		cl_qcpool_grow(p_pool, p_pool->grow_size);
		/*
		 * We may not have gotten everything we wanted but we might have
		 * gotten something.
		 */
		if (cl_is_qlist_empty(&p_pool->free_list))
			return (NULL);
	}

	p_list_item = cl_qlist_remove_tail(&p_pool->free_list);
	/* OK, at this point we have an object */
	CL_ASSERT(p_list_item != cl_qlist_end(&p_pool->free_list));
	return ((cl_pool_item_t *) p_list_item);
}

/*
 * IMPLEMENTATION OF QUICK GROW POOL
 */

/*
 * Callback to translate quick composite to quick grow pool
 * initializer callback.
 */
static cl_status_t
__cl_qpool_init_cb(IN void **const p_comp_array,
		   IN const uint32_t num_components,
		   IN void *const context,
		   OUT cl_pool_item_t ** const pp_pool_item)
{
	cl_qpool_t *p_pool = (cl_qpool_t *) context;

	CL_ASSERT(p_pool);
	CL_ASSERT(p_pool->pfn_init);
	CL_ASSERT(num_components == 1);

	UNUSED_PARAM(num_components);

	return (p_pool->pfn_init(p_comp_array[0], (void *) p_pool->context,
				 pp_pool_item));
}

/*
 * Callback to translate quick composite to quick grow pool
 * destructor callback.
 */
static void
__cl_qpool_dtor_cb(IN const cl_pool_item_t * const p_pool_item,
		   IN void *const context)
{
	cl_qpool_t *p_pool = (cl_qpool_t *) context;

	CL_ASSERT(p_pool);
	CL_ASSERT(p_pool->pfn_dtor);

	p_pool->pfn_dtor(p_pool_item, (void *) p_pool->context);
}

void
cl_qpool_construct(IN cl_qpool_t * const p_pool)
{
	cl_memclr(p_pool, sizeof (cl_qpool_t));

	cl_qcpool_construct(&p_pool->qcpool);
}

cl_status_t
cl_qpool_init(IN cl_qpool_t * const p_pool,
	      IN const size_t min_size,
	      IN const size_t max_size,
	      IN const size_t grow_size,
	      IN const size_t object_size,
	      IN cl_pfn_qpool_init_t pfn_initializer OPTIONAL,
	      IN cl_pfn_qpool_dtor_t pfn_destructor OPTIONAL,
	      IN const void *const context)
{
	cl_status_t status;
	CL_ASSERT(p_pool);

	p_pool->pfn_init = pfn_initializer;	/* may be NULL */
	p_pool->pfn_dtor = pfn_destructor;	/* may be NULL */
	p_pool->context = context;

	status = cl_qcpool_init(&p_pool->qcpool, min_size, max_size, grow_size,
				&object_size, 1,
				pfn_initializer ? __cl_qpool_init_cb : NULL,
				pfn_destructor ? __cl_qpool_dtor_cb : NULL,
				p_pool);

	return (status);
}

/*
 * IMPLEMENTATION OF COMPOSITE POOL
 */

/*
 * Callback to translate quick composite to compsite pool
 * initializer callback.
 */
static cl_status_t
__cl_cpool_init_cb(IN void **const p_comp_array,
		   IN const uint32_t num_components,
		   IN void *const context,
		   OUT cl_pool_item_t ** const pp_pool_item)
{
	cl_cpool_t *p_pool = (cl_cpool_t *) context;
	cl_pool_obj_t *p_pool_obj;
	cl_status_t status = CL_SUCCESS;

	CL_ASSERT(p_pool);

	/*
	 * Set our pointer to the list item, which is stored at the beginning of
	 * the first component.
	 */
	p_pool_obj = (cl_pool_obj_t *) p_comp_array[0];
	/* Set the pool item pointer for the caller. */
	*pp_pool_item = &p_pool_obj->pool_item;

	/* Calculate the pointer to the user's first component. */
	p_comp_array[0] =
	    ((uint8_t *) p_comp_array[0]) + sizeof (cl_pool_obj_t);

	/*
	 * Set the object pointer in the pool object to point to the first of the
	 * user's components.
	 */
	p_pool_obj->p_object = p_comp_array[0];

	/* Invoke the user's constructor callback. */
	if (p_pool->pfn_init) {
		status = p_pool->pfn_init(p_comp_array, num_components,
					  (void *) p_pool->context);
	}

	return (status);
}

/*
 * Callback to translate quick composite to composite pool
 * destructor callback.
 */
static void
__cl_cpool_dtor_cb(IN const cl_pool_item_t * const p_pool_item,
		   IN void *const context)
{
	cl_cpool_t *p_pool = (cl_cpool_t *) context;
	CL_ASSERT(p_pool);
	CL_ASSERT(p_pool->pfn_dtor);
	CL_ASSERT(((cl_pool_obj_t *) p_pool_item)->p_object);

	/* Invoke the user's destructor callback. */
	p_pool->pfn_dtor((void *) ((cl_pool_obj_t *) p_pool_item)->p_object,
			 (void *) p_pool->context);
}

void
cl_cpool_construct(IN cl_cpool_t * const p_pool)
{
	CL_ASSERT(p_pool);

	cl_memclr(p_pool, sizeof (cl_cpool_t));

	cl_qcpool_construct(&p_pool->qcpool);
}

cl_status_t
cl_cpool_init(IN cl_cpool_t * const p_pool,
	      IN const size_t min_size,
	      IN const size_t max_size,
	      IN const size_t grow_size,
	      IN size_t * const component_sizes,
	      IN const uint32_t num_components,
	      IN cl_pfn_cpool_init_t pfn_initializer OPTIONAL,
	      IN cl_pfn_cpool_dtor_t pfn_destructor OPTIONAL,
	      IN const void *const context)
{
	cl_status_t status;

	CL_ASSERT(p_pool);
	CL_ASSERT(num_components);
	CL_ASSERT(component_sizes);

	/* Add the size of the pool object to the first component. */
	component_sizes[0] += sizeof (cl_pool_obj_t);

	/* Store callback function pointers. */
	p_pool->pfn_init = pfn_initializer;	/* may be NULL */
	p_pool->pfn_dtor = pfn_destructor;	/* may be NULL */
	p_pool->context = context;

	status = cl_qcpool_init(&p_pool->qcpool, min_size, max_size, grow_size,
				component_sizes, num_components,
				__cl_cpool_init_cb,
				pfn_destructor ? __cl_cpool_dtor_cb : NULL,
				p_pool);

	/* Restore the original value of the first component. */
	component_sizes[0] -= sizeof (cl_pool_obj_t);

	return (status);
}

/*
 * IMPLEMENTATION OF GROW POOL
 */

/*
 * Callback to translate quick composite to grow pool constructor callback.
 */
static cl_status_t
__cl_pool_init_cb(IN void **const pp_obj,
		  IN const uint32_t count,
		  IN void *const context,
		  OUT cl_pool_item_t ** const pp_pool_item)
{
	cl_pool_t *p_pool = (cl_pool_t *) context;
	cl_pool_obj_t *p_pool_obj;
	cl_status_t status = CL_SUCCESS;

	CL_ASSERT(p_pool);
	CL_ASSERT(pp_obj);
	CL_ASSERT(count == 1);

	UNUSED_PARAM(count);

	/*
	 * Set our pointer to the list item, which is stored at the beginning of
	 * the first component.
	 */
	p_pool_obj = (cl_pool_obj_t *) * pp_obj;
	*pp_pool_item = &p_pool_obj->pool_item;

	/* Calculate the pointer to the user's first component. */
	*pp_obj = ((uint8_t *) * pp_obj) + sizeof (cl_pool_obj_t);

	/*
	 * Set the object pointer in the pool item to point to the first of the
	 * user's components.
	 */
	p_pool_obj->p_object = *pp_obj;

	/* Invoke the user's constructor callback. */
	if (p_pool->pfn_init)
		status = p_pool->pfn_init(*pp_obj, (void *) p_pool->context);

	return (status);
}

/*
 * Callback to translate quick composite to grow pool destructor callback.
 */
static void
__cl_pool_dtor_cb(IN const cl_pool_item_t * const p_pool_item,
		  IN void *const context)
{
	cl_pool_t *p_pool = (cl_pool_t *) context;
	CL_ASSERT(p_pool);
	CL_ASSERT(p_pool->pfn_dtor);
	CL_ASSERT(((cl_pool_obj_t *) p_pool_item)->p_object);

	/* Invoke the user's destructor callback. */
	p_pool->pfn_dtor((void *) ((cl_pool_obj_t *) p_pool_item)->p_object,
			 (void *) p_pool->context);
}

void
cl_pool_construct(IN cl_pool_t * const p_pool)
{
	CL_ASSERT(p_pool);

	cl_memclr(p_pool, sizeof (cl_pool_t));

	cl_qcpool_construct(&p_pool->qcpool);
}

cl_status_t
cl_pool_init(IN cl_pool_t * const p_pool,
	     IN const size_t min_size,
	     IN const size_t max_size,
	     IN const size_t grow_size,
	     IN const size_t object_size,
	     IN cl_pfn_pool_init_t pfn_initializer OPTIONAL,
	     IN cl_pfn_pool_dtor_t pfn_destructor OPTIONAL,
	     IN const void *const context)
{
	cl_status_t status;
	size_t total_size;

	CL_ASSERT(p_pool);

	/* Add the size of the list item to the first component. */
	total_size = object_size + sizeof (cl_pool_obj_t);

	/* Store callback function pointers. */
	p_pool->pfn_init = pfn_initializer;	/* may be NULL */
	p_pool->pfn_dtor = pfn_destructor;	/* may be NULL */
	p_pool->context = context;

	/*
	 * We need an initializer in all cases for quick composite pool, since
	 * the user pointer must be manipulated to hide the prefixed cl_pool_obj_t.
	 */
	status = cl_qcpool_init(&p_pool->qcpool, min_size, max_size, grow_size,
				&total_size, 1, __cl_pool_init_cb,
				pfn_destructor ? __cl_pool_dtor_cb : NULL,
				p_pool);

	return (status);
}
