/*-
 * Copyright(c) 2002-2011 Exar Corp.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification are permitted provided the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 *    3. Neither the name of the Exar Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * 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 THE COPYRIGHT OWNER OR 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.
 */
/*$FreeBSD: releng/9.2/sys/dev/vxge/vxgehal/vxgehal-blockpool.c 221167 2011-04-28 14:33:15Z gnn $*/

#include <dev/vxge/vxgehal/vxgehal.h>

/*
 * __hal_blockpool_create - Create block pool
 * @devh: Pointer to HAL Device object.
 * @blockpool: Block pool to be created.
 * @pool_size: Number of blocks in the pool.
 * @pool_incr: Number of blocks to be request from OS at a time
 * @pool_min: Number of blocks below which new blocks to be requested.
 * @pool_max: Number of blocks above which block to be freed.
 *
 * This function creates block pool
 */

vxge_hal_status_e
__hal_blockpool_create(vxge_hal_device_h devh,
    __hal_blockpool_t *blockpool,
    u32 pool_size,
    u32 pool_incr,
    u32 pool_min,
    u32 pool_max)
{
	u32 i;
	__hal_device_t *hldev = (__hal_device_t *) devh;
	__hal_blockpool_entry_t *entry;
	void *memblock;
	dma_addr_t dma_addr;
	pci_dma_h dma_handle;
	pci_dma_acc_h acc_handle;
	vxge_hal_status_e status = VXGE_HAL_OK;

	vxge_assert(devh != NULL);
	vxge_os_memzero(&dma_handle, sizeof(pci_dma_h));
	vxge_os_memzero(&acc_handle, sizeof(pci_dma_acc_h));

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool("devh = 0x"VXGE_OS_STXFMT", "
	    "blockpool = 0x"VXGE_OS_STXFMT", pool_size = %d, pool_incr = %d, "
	    "pool_min = %d, pool_max = %d", (ptr_t) devh, (ptr_t) blockpool,
	    pool_size, pool_incr, pool_min, pool_max);

	if (blockpool == NULL) {
		vxge_hal_err_log_pool(
		    "%s:%d null pointer passed. blockpool is null",
		    __FILE__, __LINE__);
		vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
		    __FILE__, __func__, __LINE__, VXGE_HAL_FAIL);
		return (VXGE_HAL_FAIL);
	}

	blockpool->hldev = devh;
	blockpool->block_size = VXGE_OS_HOST_PAGE_SIZE;
	blockpool->pool_size = 0;
	blockpool->pool_incr = pool_incr;
	blockpool->pool_min = pool_min;
	blockpool->pool_max = pool_max;
	blockpool->req_out = 0;

#if defined(VXGE_HAL_DMA_CONSISTENT)
	blockpool->dma_flags = VXGE_OS_DMA_CONSISTENT;
#else
	blockpool->dma_flags = VXGE_OS_DMA_STREAMING;
#endif

	vxge_list_init(&blockpool->free_block_list);

	vxge_list_init(&blockpool->free_entry_list);

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_lock_init(&blockpool->pool_lock, hldev->header.pdev);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_lock_init_irq(&blockpool->pool_lock, hldev->header.irqh);
#endif

	for (i = 0; i < pool_size + pool_max; i++) {

		entry = (__hal_blockpool_entry_t *) vxge_os_malloc(
		    hldev->header.pdev,
		    sizeof(__hal_blockpool_entry_t));
		if (entry == NULL) {
			__hal_blockpool_destroy(blockpool);

			vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
			    __FILE__, __func__, __LINE__,
			    VXGE_HAL_ERR_OUT_OF_MEMORY);

			return (VXGE_HAL_ERR_OUT_OF_MEMORY);
		}

		vxge_list_insert(&entry->item, &blockpool->free_entry_list);
	}

	for (i = 0; i < pool_size; i++) {

		memblock = vxge_os_dma_malloc(
		    hldev->header.pdev,
		    VXGE_OS_HOST_PAGE_SIZE,
		    blockpool->dma_flags,
		    &dma_handle,
		    &acc_handle);

		if (memblock == NULL) {
			__hal_blockpool_destroy(blockpool);
			vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
			    __FILE__, __func__, __LINE__,
			    VXGE_HAL_ERR_OUT_OF_MEMORY);

			return (VXGE_HAL_ERR_OUT_OF_MEMORY);
		}

		dma_addr = vxge_os_dma_map(
		    hldev->header.pdev,
		    dma_handle,
		    memblock,
		    VXGE_OS_HOST_PAGE_SIZE,
		    VXGE_OS_DMA_DIR_BIDIRECTIONAL,
		    blockpool->dma_flags);

		if (dma_addr == VXGE_OS_INVALID_DMA_ADDR) {
			vxge_os_dma_free(hldev->header.pdev,
			    memblock,
			    VXGE_OS_HOST_PAGE_SIZE,
			    blockpool->dma_flags,
			    &dma_handle,
			    &acc_handle);
			__hal_blockpool_destroy(blockpool);

			vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
			    __FILE__, __func__, __LINE__,
			    VXGE_HAL_ERR_OUT_OF_MEMORY);

			return (VXGE_HAL_ERR_OUT_OF_MEMORY);
		}

		entry = (__hal_blockpool_entry_t *)
		    vxge_list_first_get(&blockpool->free_entry_list);

		if (entry == NULL) {
			entry = (__hal_blockpool_entry_t *) vxge_os_malloc(
			    hldev->header.pdev,
			    sizeof(__hal_blockpool_entry_t));
		}

		if (entry != NULL) {
			vxge_list_remove(&entry->item);
			entry->length = VXGE_OS_HOST_PAGE_SIZE;
			entry->memblock = memblock;
			entry->dma_addr = dma_addr;
			entry->acc_handle = acc_handle;
			entry->dma_handle = dma_handle;
			vxge_list_insert(&entry->item,
			    &blockpool->free_block_list);
			blockpool->pool_size++;
		} else {
			__hal_blockpool_destroy(blockpool);
			vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
			    __FILE__, __func__, __LINE__,
			    VXGE_HAL_ERR_OUT_OF_MEMORY);

			return (VXGE_HAL_ERR_OUT_OF_MEMORY);
		}
	}

	vxge_hal_info_log_pool(
	    "Blockpool  block size:%d block pool size: %d",
	    blockpool->block_size, blockpool->pool_size);

	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
	    __FILE__, __func__, __LINE__, status);

	return (status);
}

/*
 * __hal_blockpool_destroy - Deallocates the block pool
 * @blockpool: blockpool to be deallocated
 *
 * This function freeup the memory pool and removes the
 * block pool.
 */

void
__hal_blockpool_destroy(
    __hal_blockpool_t *blockpool)
{
	__hal_device_t *hldev;
	vxge_list_t *p, *n;

	vxge_assert(blockpool != NULL);

	hldev = (__hal_device_t *) blockpool->hldev;

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool("blockpool = 0x"VXGE_OS_STXFMT,
	    (ptr_t) blockpool);

	if (blockpool == NULL) {
		vxge_hal_err_log_pool(
		    "%s:%d null pointer passed blockpool = null",
		    __FILE__, __LINE__);
		vxge_hal_trace_log_pool("<== %s:%s:%d  Result: 1",
		    __FILE__, __func__, __LINE__);
		return;
	}

	vxge_list_for_each_safe(p, n, &blockpool->free_block_list) {

		vxge_os_dma_unmap(hldev->header.pdev,
		    ((__hal_blockpool_entry_t *) p)->dma_handle,
		    ((__hal_blockpool_entry_t *) p)->dma_addr,
		    ((__hal_blockpool_entry_t *) p)->length,
		    VXGE_OS_DMA_DIR_BIDIRECTIONAL);

		vxge_os_dma_free(hldev->header.pdev,
		    ((__hal_blockpool_entry_t *) p)->memblock,
		    ((__hal_blockpool_entry_t *) p)->length,
		    blockpool->dma_flags,
		    &((__hal_blockpool_entry_t *) p)->dma_handle,
		    &((__hal_blockpool_entry_t *) p)->acc_handle);

		vxge_list_remove(&((__hal_blockpool_entry_t *) p)->item);

		vxge_os_free(hldev->header.pdev,
		    (void *)p, sizeof(__hal_blockpool_entry_t));

		blockpool->pool_size--;
	}

	vxge_list_for_each_safe(p, n, &blockpool->free_entry_list) {

		vxge_list_remove(&((__hal_blockpool_entry_t *) p)->item);

		vxge_os_free(hldev->header.pdev,
		    (void *)p, sizeof(__hal_blockpool_entry_t));

	}

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_lock_destroy(&blockpool->pool_lock,
	    hldev->header.pdev);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_lock_destroy_irq(&blockpool->pool_lock,
	    hldev->header.pdev);
#endif

	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: 0",
	    __FILE__, __func__, __LINE__);
}

/*
 * __hal_blockpool_blocks_add - Request additional blocks
 * @blockpool: Block pool.
 *
 * Requests additional blocks to block pool
 */
static inline void
__hal_blockpool_blocks_add(
    __hal_blockpool_t * blockpool)
{
	u32 nreq = 0, i;
	__hal_device_t *hldev;

	vxge_assert(blockpool != NULL);

	hldev = (__hal_device_t *) blockpool->hldev;

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool("blockpool = 0x"VXGE_OS_STXFMT,
	    (ptr_t) blockpool);

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif
	if ((blockpool->pool_size + blockpool->req_out) <
	    blockpool->pool_min) {
		nreq = blockpool->pool_incr;
		blockpool->req_out += nreq;
	}

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif

	for (i = 0; i < nreq; i++) {
		vxge_os_dma_malloc_async(
		    ((__hal_device_t *) blockpool->hldev)->header.pdev,
		    blockpool->hldev,
		    VXGE_OS_HOST_PAGE_SIZE,
		    blockpool->dma_flags);
	}

	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: 0",
	    __FILE__, __func__, __LINE__);
}

/*
 * __hal_blockpool_blocks_remove - Free additional blocks
 * @blockpool: Block pool.
 *
 * Frees additional blocks over maximum from the block pool
 */
static inline void
__hal_blockpool_blocks_remove(
    __hal_blockpool_t * blockpool)
{
	vxge_list_t *p, *n;
	__hal_device_t *hldev;

	vxge_assert(blockpool != NULL);

	hldev = (__hal_device_t *) blockpool->hldev;

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool("blockpool = 0x"VXGE_OS_STXFMT,
	    (ptr_t) blockpool);

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif
	vxge_list_for_each_safe(p, n, &blockpool->free_block_list) {

		if (blockpool->pool_size < blockpool->pool_max)
			break;

		vxge_os_dma_unmap(
		    ((__hal_device_t *) blockpool->hldev)->header.pdev,
		    ((__hal_blockpool_entry_t *) p)->dma_handle,
		    ((__hal_blockpool_entry_t *) p)->dma_addr,
		    ((__hal_blockpool_entry_t *) p)->length,
		    VXGE_OS_DMA_DIR_BIDIRECTIONAL);

		vxge_os_dma_free(
		    ((__hal_device_t *) blockpool->hldev)->header.pdev,
		    ((__hal_blockpool_entry_t *) p)->memblock,
		    ((__hal_blockpool_entry_t *) p)->length,
		    blockpool->dma_flags,
		    &((__hal_blockpool_entry_t *) p)->dma_handle,
		    &((__hal_blockpool_entry_t *) p)->acc_handle);

		vxge_list_remove(&((__hal_blockpool_entry_t *) p)->item);

		vxge_list_insert(p, &blockpool->free_entry_list);

		blockpool->pool_size--;

	}

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif

	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: 0",
	    __FILE__, __func__, __LINE__);
}

/*
 * vxge_hal_blockpool_block_add - callback for vxge_os_dma_malloc_async
 * @devh: HAL device handle.
 * @block_addr: Virtual address of the block
 * @length: Length of the block.
 * @p_dma_h: Physical address of the block
 * @acc_handle: DMA acc handle
 *
 * Adds a block to block pool
 */
void
vxge_hal_blockpool_block_add(
    vxge_hal_device_h devh,
    void *block_addr,
    u32 length,
    pci_dma_h * dma_h,
    pci_dma_acc_h * acc_handle)
{
	__hal_blockpool_t *blockpool;
	__hal_blockpool_entry_t *entry;
	__hal_device_t *hldev;
	dma_addr_t dma_addr;
	vxge_hal_status_e status;
	u32 req_out;

	vxge_assert(devh != NULL);

	hldev = (__hal_device_t *) devh;

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool("devh = 0x"VXGE_OS_STXFMT", length = %d, "
	    "block_addr = 0x"VXGE_OS_STXFMT",  dma_h = 0x"VXGE_OS_STXFMT", "
	    "acc_handle = 0x"VXGE_OS_STXFMT, (ptr_t) devh, length,
	    (ptr_t) block_addr, (ptr_t) dma_h, (ptr_t) acc_handle);

	blockpool = &hldev->block_pool;

	if (block_addr == NULL) {
#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif
		blockpool->req_out--;

#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif
		vxge_hal_trace_log_pool("<== %s:%s:%d  Result: 1",
		    __FILE__, __func__, __LINE__);
		return;
	}

	dma_addr = vxge_os_dma_map(hldev->header.pdev,
	    *dma_h,
	    block_addr,
	    length,
	    VXGE_OS_DMA_DIR_BIDIRECTIONAL,
	    blockpool->dma_flags);

	if (dma_addr == VXGE_OS_INVALID_DMA_ADDR) {
		vxge_os_dma_free(hldev->header.pdev,
		    block_addr,
		    length,
		    blockpool->dma_flags,
		    dma_h,
		    acc_handle);
#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif
		blockpool->req_out--;

#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif
		vxge_hal_trace_log_pool("<== %s:%s:%d  Result: 1",
		    __FILE__, __func__, __LINE__);
		return;
	}

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif

	entry = (__hal_blockpool_entry_t *)
	    vxge_list_first_get(&blockpool->free_entry_list);

	if (entry == NULL) {
		entry = (__hal_blockpool_entry_t *) vxge_os_malloc(
		    hldev->header.pdev,
		    sizeof(__hal_blockpool_entry_t));
	} else {
		vxge_list_remove(&entry->item);
	}

	if (entry != NULL) {
		entry->length = length;
		entry->memblock = block_addr;
		entry->dma_addr = dma_addr;
		entry->acc_handle = *acc_handle;
		entry->dma_handle = *dma_h;
		vxge_list_insert(&entry->item, &blockpool->free_block_list);
		blockpool->pool_size++;
		status = VXGE_HAL_OK;
	} else {
		status = VXGE_HAL_ERR_OUT_OF_MEMORY;
	}

	blockpool->req_out--;

	req_out = blockpool->req_out;

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif

	if (req_out == 0)
		__hal_channel_process_pending_list(devh);

	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
	    __FILE__, __func__, __LINE__, status);
}

/*
 * __hal_blockpool_malloc - Allocate a memory block from pool
 * @devh: HAL device handle.
 * @size: Length of the block.
 * @dma_addr: Buffer to return DMA Address of the block.
 * @dma_handle: Buffer to return DMA handle of the block.
 * @acc_handle: Buffer to return DMA acc handle
 *
 *
 * Allocates a block of memory of given size, either from block pool
 * or by calling vxge_os_dma_malloc()
 */
void *
__hal_blockpool_malloc(vxge_hal_device_h devh,
    u32 size,
    dma_addr_t *dma_addr,
    pci_dma_h *dma_handle,
    pci_dma_acc_h *acc_handle)
{
	__hal_blockpool_entry_t *entry;
	__hal_blockpool_t *blockpool;
	__hal_device_t *hldev;
	void *memblock = NULL;

	vxge_assert(devh != NULL);

	hldev = (__hal_device_t *) devh;

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool(
	    "devh = 0x"VXGE_OS_STXFMT", size = %d, "
	    "dma_addr = 0x"VXGE_OS_STXFMT", dma_handle = 0x"VXGE_OS_STXFMT", "
	    "acc_handle = 0x"VXGE_OS_STXFMT, (ptr_t) devh, size,
	    (ptr_t) dma_addr, (ptr_t) dma_handle, (ptr_t) acc_handle);

	blockpool = &((__hal_device_t *) devh)->block_pool;

	if (size != blockpool->block_size) {

		memblock = vxge_os_dma_malloc(
		    ((__hal_device_t *) devh)->header.pdev,
		    size,
		    blockpool->dma_flags |
		    VXGE_OS_DMA_CACHELINE_ALIGNED,
		    dma_handle,
		    acc_handle);

		if (memblock == NULL) {
			vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
			    __FILE__, __func__, __LINE__,
			    VXGE_HAL_ERR_OUT_OF_MEMORY);
			return (NULL);
		}

		*dma_addr = vxge_os_dma_map(
		    ((__hal_device_t *) devh)->header.pdev,
		    *dma_handle,
		    memblock,
		    size,
		    VXGE_OS_DMA_DIR_BIDIRECTIONAL,
		    blockpool->dma_flags);

		if (*dma_addr == VXGE_OS_INVALID_DMA_ADDR) {
			vxge_os_dma_free(((__hal_device_t *) devh)->header.pdev,
			    memblock,
			    size,
			    blockpool->dma_flags |
			    VXGE_OS_DMA_CACHELINE_ALIGNED,
			    dma_handle,
			    acc_handle);

			vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
			    __FILE__, __func__, __LINE__,
			    VXGE_HAL_ERR_OUT_OF_MEMORY);

			return (NULL);
		}

	} else {

#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif

		entry = (__hal_blockpool_entry_t *)
		    vxge_list_first_get(&blockpool->free_block_list);

		if (entry != NULL) {
			vxge_list_remove(&entry->item);
			*dma_addr = entry->dma_addr;
			*dma_handle = entry->dma_handle;
			*acc_handle = entry->acc_handle;
			memblock = entry->memblock;

			vxge_list_insert(&entry->item,
			    &blockpool->free_entry_list);
			blockpool->pool_size--;
		}

#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif

		if (memblock != NULL)
			__hal_blockpool_blocks_add(blockpool);

	}

	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
	    __FILE__, __func__, __LINE__, !memblock);

	return (memblock);

}

/*
 * __hal_blockpool_free - Frees the memory allcoated with __hal_blockpool_malloc
 * @devh: HAL device handle.
 * @memblock: Virtual address block
 * @size: Length of the block.
 * @dma_addr: DMA Address of the block.
 * @dma_handle: DMA handle of the block.
 * @acc_handle: DMA acc handle
 *
 *
 * Frees the memory allocated with __hal_blockpool_malloc to blockpool or system
 */
void
__hal_blockpool_free(vxge_hal_device_h devh,
    void *memblock,
    u32 size,
    dma_addr_t *dma_addr,
    pci_dma_h *dma_handle,
    pci_dma_acc_h *acc_handle)
{
	__hal_blockpool_entry_t *entry;
	__hal_blockpool_t *blockpool;
	__hal_device_t *hldev;
	vxge_hal_status_e status = VXGE_HAL_OK;

	vxge_assert(devh != NULL);

	hldev = (__hal_device_t *) devh;

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool("devh = 0x"VXGE_OS_STXFMT", size = %d, "
	    "dma_addr = 0x"VXGE_OS_STXFMT", dma_handle = 0x"VXGE_OS_STXFMT", "
	    "acc_handle = 0x"VXGE_OS_STXFMT, (ptr_t) devh, size,
	    (ptr_t) dma_addr, (ptr_t) dma_handle, (ptr_t) acc_handle);

	blockpool = &((__hal_device_t *) devh)->block_pool;

	if (size != blockpool->block_size) {

		vxge_os_dma_unmap(((__hal_device_t *) devh)->header.pdev,
		    *dma_handle,
		    *dma_addr,
		    size,
		    VXGE_OS_DMA_DIR_BIDIRECTIONAL);

		vxge_os_dma_free(((__hal_device_t *) devh)->header.pdev,
		    memblock,
		    size,
		    blockpool->dma_flags |
		    VXGE_OS_DMA_CACHELINE_ALIGNED,
		    dma_handle,
		    acc_handle);
	} else {
#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif

		entry = (__hal_blockpool_entry_t *)
		    vxge_list_first_get(&blockpool->free_entry_list);

		if (entry == NULL) {
			entry = (__hal_blockpool_entry_t *) vxge_os_malloc(
			    ((__hal_device_t *) devh)->header.pdev,
			    sizeof(__hal_blockpool_entry_t));
		} else {
			vxge_list_remove(&entry->item);
		}

		if (entry != NULL) {
			entry->length = size;
			entry->memblock = memblock;
			entry->dma_addr = *dma_addr;
			entry->acc_handle = *acc_handle;
			entry->dma_handle = *dma_handle;
			vxge_list_insert(&entry->item,
			    &blockpool->free_block_list);
			blockpool->pool_size++;
			status = VXGE_HAL_OK;
		} else {
			status = VXGE_HAL_ERR_OUT_OF_MEMORY;
		}

#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif
		if (status == VXGE_HAL_OK)
			__hal_blockpool_blocks_remove(blockpool);

	}

	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
	    __FILE__, __func__, __LINE__, status);
}

/*
 * __hal_blockpool_block_allocate - Allocates a block from block pool
 * @hldev: Hal device
 * @size: Size of the block to be allocated
 *
 * This function allocates a block from block pool or from the system
 */
__hal_blockpool_entry_t *
__hal_blockpool_block_allocate(vxge_hal_device_h devh,
    u32 size)
{
	__hal_blockpool_entry_t *entry = NULL;
	__hal_device_t *hldev;
	__hal_blockpool_t *blockpool;

	vxge_assert(devh != NULL);

	hldev = (__hal_device_t *) devh;

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool("devh = 0x"VXGE_OS_STXFMT", size = %d",
	    (ptr_t) devh, size);

	blockpool = &((__hal_device_t *) devh)->block_pool;

	if (size == blockpool->block_size) {
#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif

		entry = (__hal_blockpool_entry_t *)
		    vxge_list_first_get(&blockpool->free_block_list);

		if (entry != NULL) {
			vxge_list_remove(&entry->item);
			blockpool->pool_size--;
		}

#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif
	}

	if (entry != NULL)
		__hal_blockpool_blocks_add(blockpool);


	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
	    __FILE__, __func__, __LINE__, !entry);

	return (entry);
}

/*
 * __hal_blockpool_block_free - Frees a block from block pool
 * @devh: Hal device
 * @entry: Entry of block to be freed
 *
 * This function frees a block from block pool
 */
void
__hal_blockpool_block_free(vxge_hal_device_h devh,
    __hal_blockpool_entry_t *entry)
{
	__hal_device_t *hldev;
	__hal_blockpool_t *blockpool;

	vxge_assert(devh != NULL);

	hldev = (__hal_device_t *) devh;

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool(
	    "devh = 0x"VXGE_OS_STXFMT", entry = 0x"VXGE_OS_STXFMT,
	    (ptr_t) devh, (ptr_t) entry);

	blockpool = &((__hal_device_t *) devh)->block_pool;

	if (entry->length == blockpool->block_size) {
#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif

		vxge_list_insert(&entry->item, &blockpool->free_block_list);
		blockpool->pool_size++;

#if defined(VXGE_HAL_BP_POST)
		vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
		vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif
	}

	__hal_blockpool_blocks_remove(blockpool);

	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: 0",
	    __FILE__, __func__, __LINE__);
}


/*
 * __hal_blockpool_list_allocate - Allocate blocks from block pool
 * @devh: Hal device
 * @blocklist: List into which the allocated blocks to be inserted
 * @count: Number of blocks to be allocated
 *
 * This function allocates a register from the register pool
 */
vxge_hal_status_e
__hal_blockpool_list_allocate(
    vxge_hal_device_h devh,
    vxge_list_t *blocklist,
    u32 count)
{
	u32 i;
	__hal_device_t *hldev;
	__hal_blockpool_t *blockpool;
	__hal_blockpool_entry_t *block_entry;
	vxge_hal_status_e status = VXGE_HAL_OK;

	vxge_assert(devh != NULL);

	hldev = (__hal_device_t *) devh;

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool("devh = 0x"VXGE_OS_STXFMT", blocklist = "
	    "0x"VXGE_OS_STXFMT", count = %d", (ptr_t) devh,
	    (ptr_t) blocklist, count);

	blockpool = &((__hal_device_t *) devh)->block_pool;

	if (blocklist == NULL) {
		vxge_hal_err_log_pool(
		    "null pointer passed blockpool = 0x"VXGE_OS_STXFMT", "
		    "blocklist = 0x"VXGE_OS_STXFMT, (ptr_t) blockpool,
		    (ptr_t) blocklist);
		vxge_hal_trace_log_pool("<== %s:%s:%d  Result: 1",
		    __FILE__, __func__, __LINE__);
		return (VXGE_HAL_FAIL);
	}

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif

	vxge_list_init(blocklist);

	for (i = 0; i < count; i++) {

		block_entry = (__hal_blockpool_entry_t *)
		    vxge_list_first_get(&blockpool->free_block_list);

		if (block_entry == NULL)
			break;

		vxge_list_remove(&block_entry->item);

		vxge_os_memzero(block_entry->memblock, blockpool->block_size);

		vxge_list_insert(&block_entry->item, blocklist);

		blockpool->pool_size++;
	}

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif

	if (i < count) {

		vxge_hal_err_log_pool("%s:%d Blockpool out of blocks",
		    __FILE__, __LINE__);

		vxge_assert(FALSE);

		__hal_blockpool_list_free(blockpool, blocklist);

		status = VXGE_HAL_ERR_OUT_OF_MEMORY;

	}

	__hal_blockpool_blocks_add(blockpool);

	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: %d",
	    __FILE__, __func__, __LINE__, status);

	return (status);
}

/*
 * __hal_blockpool_list_free - Free a list of blocks from block pool
 * @devh: Hal device
 * @blocklist: List of blocks to be freed
 *
 * This function frees a list of blocks to the block pool
 */
void
__hal_blockpool_list_free(
    vxge_hal_device_h devh,
    vxge_list_t *blocklist)
{
	__hal_device_t *hldev;
	__hal_blockpool_t *blockpool;
	__hal_blockpool_entry_t *block_entry;

	vxge_assert(devh != NULL);

	hldev = (__hal_device_t *) devh;

	vxge_hal_trace_log_pool("==> %s:%s:%d",
	    __FILE__, __func__, __LINE__);

	vxge_hal_trace_log_pool(
	    "devh = 0x"VXGE_OS_STXFMT", blocklist = 0x"VXGE_OS_STXFMT,
	    (ptr_t) devh, (ptr_t) blocklist);

	blockpool = &((__hal_device_t *) devh)->block_pool;

	if (blocklist == NULL) {
		vxge_hal_err_log_pool(
		    "null pointer passed blockpool = 0x"VXGE_OS_STXFMT", "
		    "blocklist = 0x"VXGE_OS_STXFMT, (ptr_t) blockpool,
		    (ptr_t) blocklist);
		vxge_hal_trace_log_pool("<== %s:%s:%d  Result: 1",
		    __FILE__, __func__, __LINE__);
		return;
	}

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_lock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_lock_irq(&blockpool->pool_lock, flags);
#endif

	while ((block_entry = (__hal_blockpool_entry_t *)
	    vxge_list_first_get(blocklist)) != NULL) {

		vxge_list_remove(&block_entry->item);

		vxge_list_insert(&block_entry->item,
		    &blockpool->free_block_list);

		blockpool->pool_size++;
	}

#if defined(VXGE_HAL_BP_POST)
	vxge_os_spin_unlock(&blockpool->pool_lock);
#elif defined(VXGE_HAL_BP_POST_IRQ)
	vxge_os_spin_unlock_irq(&blockpool->pool_lock, flags);
#endif

	__hal_blockpool_blocks_remove(blockpool);

	vxge_hal_trace_log_pool("<== %s:%s:%d  Result: 0",
	    __FILE__, __func__, __LINE__);
}
