/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright(c) 2007-2022 Intel Corporation */
/**
 ***************************************************************************
 * @file lac_mem_pools.c
 *
 * @ingroup LacMemPool
 *
 * Memory Pool creation and mgmt function implementations
 *
 ***************************************************************************/

#include "cpa.h"
#include "qat_utils.h"
#include "icp_accel_devices.h"
#include "icp_adf_init.h"
#include "icp_adf_transport.h"
#include "icp_adf_debug.h"
#include "lac_lock_free_stack.h"
#include "lac_mem_pools.h"
#include "lac_mem.h"
#include "lac_common.h"
#include "cpa_dc.h"
#include "dc_session.h"
#include "dc_datapath.h"
#include "icp_qat_fw_comp.h"
#include "icp_buffer_desc.h"
#include "lac_sym.h"

#define LAC_MEM_POOLS_NUM_SUPPORTED 32000
/**< @ingroup LacMemPool
 * Number of mem pools supported */

#define LAC_MEM_POOLS_NAME_SIZE 17
/**< @ingroup LacMemPool
 * 16 bytes plus '\\0' terminator */

/**< @ingroup LacMemPool
 *     This structure is used to manage each pool created using this utility
 * feature. The client will maintain a pointer (identifier) to the created
 * structure per pool.
 */
typedef struct lac_mem_pool_hdr_s {
	lock_free_stack_t stack;
	char poolName[LAC_MEM_POOLS_NAME_SIZE]; /*16 bytes of a pool name */
	/**< up to 16 bytes of a pool name */
	unsigned int numElementsInPool;
	/**< number of elements in the Pool */
	unsigned int blkSizeInBytes;
	/**< Block size in bytes */
	unsigned int blkAlignmentInBytes;
	/**< block alignment in bytes */
	lac_mem_blk_t **trackBlks;
	/* An array of mem block pointers to track the allocated entries in pool
	 */
	volatile size_t availBlks;
	/* Number of blocks available for allocation in this pool */
} lac_mem_pool_hdr_t;

static lac_mem_pool_hdr_t *lac_mem_pools[LAC_MEM_POOLS_NUM_SUPPORTED] = {
	NULL
};
/**< @ingroup LacMemPool
 * Array of pointers to the mem pool header structure
 */

LAC_DECLARE_HIGHEST_BIT_OF(lac_mem_blk_t);
/**< @ingroup LacMemPool
 * local constant for quickening computation of additional space allocated
 * for holding lac_mem_blk_t container-structure
 */

/**
 *******************************************************************************
 * @ingroup LacMemPool
 * This function cleans up a mem pool.
 ******************************************************************************/
void Lac_MemPoolCleanUpInternal(lac_mem_pool_hdr_t *pPoolID);

static inline Cpa32U
Lac_MemPoolGetElementRealSize(Cpa32U blkSizeInBytes, Cpa32U blkAlignmentInBytes)
{
	Cpa32U addSize = (blkAlignmentInBytes >= sizeof(lac_mem_blk_t) ?
			      blkAlignmentInBytes :
			      1 << (highest_bit_of_lac_mem_blk_t + 1));
	return blkSizeInBytes + addSize;
}

CpaStatus
Lac_MemPoolCreate(lac_memory_pool_id_t *pPoolID,
		  char *poolName,
		  unsigned int numElementsInPool,   /*Number of elements*/
		  unsigned int blkSizeInBytes,      /*Block Size in bytes*/
		  unsigned int blkAlignmentInBytes, /*Block alignment (bytes)*/
		  CpaBoolean trackMemory,
		  Cpa32U node)
{
	unsigned int poolSearch = 0;
	unsigned int counter = 0;
	lac_mem_blk_t *pMemBlkCurrent = NULL;

	void *pMemBlk = NULL;

	if (pPoolID == NULL) {
		QAT_UTILS_LOG("Invalid Pool ID param\n");
		return CPA_STATUS_INVALID_PARAM; /*Error*/
	}

	/* Find First available Pool return error otherwise */
	while (lac_mem_pools[poolSearch] != NULL) {
		poolSearch++;
		if (LAC_MEM_POOLS_NUM_SUPPORTED == poolSearch) {
			QAT_UTILS_LOG(
			    "No more memory pools available for allocation.\n");
			return CPA_STATUS_FAIL;
		}
	}

	/* Allocate a Pool header */
	lac_mem_pools[poolSearch] = LAC_OS_MALLOC(sizeof(lac_mem_pool_hdr_t));
	if (NULL == lac_mem_pools[poolSearch]) {
		QAT_UTILS_LOG(
		    "Unable to allocate memory for creation of the pool.\n");
		return CPA_STATUS_RESOURCE; /*Error*/
	}
	memset(lac_mem_pools[poolSearch], 0, sizeof(lac_mem_pool_hdr_t));

	/* Copy in Pool Name */
	if (poolName != NULL) {
		snprintf(lac_mem_pools[poolSearch]->poolName,
			 LAC_MEM_POOLS_NAME_SIZE,
			 "%s",
			 poolName);
	} else {
		LAC_OS_FREE(lac_mem_pools[poolSearch]);
		lac_mem_pools[poolSearch] = NULL;
		QAT_UTILS_LOG("Invalid Pool Name pointer\n");
		return CPA_STATUS_INVALID_PARAM; /*Error*/
	}

	/* Allocate table for tracking memory blocks */
	if (CPA_TRUE == trackMemory) {
		lac_mem_pools[poolSearch]->trackBlks = LAC_OS_MALLOC(
		    (sizeof(lac_mem_blk_t *) * numElementsInPool));
		if (NULL == lac_mem_pools[poolSearch]->trackBlks) {
			LAC_OS_FREE(lac_mem_pools[poolSearch]);
			lac_mem_pools[poolSearch] = NULL;
			QAT_UTILS_LOG(
			    "Unable to allocate memory for tracking memory blocks.\n");
			return CPA_STATUS_RESOURCE; /*Error*/
		}
	} else {
		lac_mem_pools[poolSearch]->trackBlks = NULL;
	}

	lac_mem_pools[poolSearch]->availBlks = 0;
	lac_mem_pools[poolSearch]->stack = _init_stack();

	/* Calculate alignment needed for allocation   */
	for (counter = 0; counter < numElementsInPool; counter++) {
		CpaPhysicalAddr physAddr = 0;
		/* realSize is computed for allocation of  blkSize bytes +
		   additional
		   capacity for lac_mem_blk_t structure storage due to the some
		   OSes
		   (BSD) limitations for memory alignment to be power of 2;
		   sizeof(lac_mem_blk_t) is being round up to the closest power
		   of 2 -
		   optimised towards the least CPU overhead but at additional
		   memory
		   cost
		 */
		Cpa32U realSize =
		    Lac_MemPoolGetElementRealSize(blkSizeInBytes,
						  blkAlignmentInBytes);
		Cpa32U addSize = realSize - blkSizeInBytes;

		if (CPA_STATUS_SUCCESS != LAC_OS_CAMALLOC(&pMemBlk,
							  realSize,
							  blkAlignmentInBytes,
							  node)) {
			Lac_MemPoolCleanUpInternal(lac_mem_pools[poolSearch]);
			lac_mem_pools[poolSearch] = NULL;
			QAT_UTILS_LOG(
			    "Unable to allocate contiguous chunk of memory.\n");
			return CPA_STATUS_RESOURCE;
		}

		/* Calcaulate various offsets */
		physAddr = LAC_OS_VIRT_TO_PHYS_INTERNAL(
		    (void *)((LAC_ARCH_UINT)pMemBlk + addSize));

		/* physAddr is now already aligned to the greater power of 2:
		    blkAlignmentInBytes or sizeof(lac_mem_blk_t) round up
		    We safely put the structure right before the blkSize
		    real data block
		 */
		pMemBlkCurrent =
		    (lac_mem_blk_t *)(((LAC_ARCH_UINT)(pMemBlk)) + addSize -
				      sizeof(lac_mem_blk_t));

		pMemBlkCurrent->physDataPtr = physAddr;
		pMemBlkCurrent->pMemAllocPtr = pMemBlk;
		pMemBlkCurrent->pPoolID = lac_mem_pools[poolSearch];
		pMemBlkCurrent->isInUse = CPA_FALSE;
		pMemBlkCurrent->pNext = NULL;

		push(&lac_mem_pools[poolSearch]->stack, pMemBlkCurrent);

		/* Store allocated memory pointer */
		if (lac_mem_pools[poolSearch]->trackBlks != NULL) {
			(lac_mem_pools[poolSearch]->trackBlks[counter]) =
			    (lac_mem_blk_t *)pMemBlkCurrent;
		}
		__sync_add_and_fetch(&lac_mem_pools[poolSearch]->availBlks, 1);
		(lac_mem_pools[poolSearch])->numElementsInPool = counter + 1;
	}

	/* Set Pool details in the header */
	(lac_mem_pools[poolSearch])->blkSizeInBytes = blkSizeInBytes;
	(lac_mem_pools[poolSearch])->blkAlignmentInBytes = blkAlignmentInBytes;
	/* Set the Pool ID output parameter */
	*pPoolID = (LAC_ARCH_UINT)(lac_mem_pools[poolSearch]);
	/* Success */
	return CPA_STATUS_SUCCESS;
}

void *
Lac_MemPoolEntryAlloc(lac_memory_pool_id_t poolID)
{
	lac_mem_pool_hdr_t *pPoolID = (lac_mem_pool_hdr_t *)poolID;
	lac_mem_blk_t *pMemBlkCurrent = NULL;

	/* Explicitly removing NULL PoolID check for speed */
	if (pPoolID == NULL) {
		QAT_UTILS_LOG("Invalid Pool ID");
		return NULL;
	}

	/* Remove block from pool */
	pMemBlkCurrent = pop(&pPoolID->stack);
	if (NULL == pMemBlkCurrent) {
		return (void *)CPA_STATUS_RETRY;
	}
	__sync_sub_and_fetch(&pPoolID->availBlks, 1);
	pMemBlkCurrent->isInUse = CPA_TRUE;
	return (void *)((LAC_ARCH_UINT)(pMemBlkCurrent) +
			sizeof(lac_mem_blk_t));
}

void
Lac_MemPoolEntryFree(void *pEntry)
{
	lac_mem_blk_t *pMemBlk = NULL;

	/* Explicitly NULL pointer check */
	if (pEntry == NULL) {
		QAT_UTILS_LOG("Memory Handle NULL");
		return;
	}

	pMemBlk =
	    (lac_mem_blk_t *)((LAC_ARCH_UINT)pEntry - sizeof(lac_mem_blk_t));
	pMemBlk->isInUse = CPA_FALSE;

	push(&pMemBlk->pPoolID->stack, pMemBlk);
	__sync_add_and_fetch(&pMemBlk->pPoolID->availBlks, 1);
}

void
Lac_MemPoolDestroy(lac_memory_pool_id_t poolID)
{
	unsigned int poolSearch = 0;
	lac_mem_pool_hdr_t *pPoolID = (lac_mem_pool_hdr_t *)poolID;

	if (pPoolID != NULL) {
		/*Remove entry from table*/
		while (lac_mem_pools[poolSearch] != pPoolID) {
			poolSearch++;

			if (LAC_MEM_POOLS_NUM_SUPPORTED == poolSearch) {
				QAT_UTILS_LOG("Invalid Pool ID submitted.\n");
				return;
			}
		}

		lac_mem_pools[poolSearch] = NULL; /*Remove handle from pool*/

		Lac_MemPoolCleanUpInternal(pPoolID);
	}
}

void
Lac_MemPoolCleanUpInternal(lac_mem_pool_hdr_t *pPoolID)
{
	lac_mem_blk_t *pCurrentBlk = NULL;
	void *pFreePtr = NULL;
	Cpa32U count = 0;

	if (pPoolID->trackBlks == NULL) {
		pCurrentBlk = pop(&pPoolID->stack);

		while (pCurrentBlk != NULL) {
			/* Free Data Blocks */
			pFreePtr = pCurrentBlk->pMemAllocPtr;
			pCurrentBlk = pop(&pPoolID->stack);
			LAC_OS_CAFREE(pFreePtr);
		}
	} else {
		for (count = 0; count < pPoolID->numElementsInPool; count++) {
			pFreePtr = (pPoolID->trackBlks[count])->pMemAllocPtr;
			LAC_OS_CAFREE(pFreePtr);
		}
		LAC_OS_FREE(pPoolID->trackBlks);
	}
	LAC_OS_FREE(pPoolID);
}

unsigned int
Lac_MemPoolAvailableEntries(lac_memory_pool_id_t poolID)
{
	lac_mem_pool_hdr_t *pPoolID = (lac_mem_pool_hdr_t *)poolID;
	if (pPoolID == NULL) {
		QAT_UTILS_LOG("Invalid Pool ID\n");
		return 0;
	}
	return pPoolID->availBlks;
}

void
Lac_MemPoolStatsShow(void)
{
	unsigned int index = 0;
	QAT_UTILS_LOG(SEPARATOR BORDER
		      "           Memory Pools Stats\n" SEPARATOR);

	while (index < LAC_MEM_POOLS_NUM_SUPPORTED) {
		if (lac_mem_pools[index] != NULL) {
			QAT_UTILS_LOG(
			    BORDER " Pool Name:             %s \n" BORDER
				   " No. Elements in Pool:  %10u \n" BORDER
				   " Element Size in Bytes: %10u \n" BORDER
				   " Alignment in Bytes:    %10u \n" BORDER
				   " No. Available Blocks:  %10zu \n" SEPARATOR,
			    lac_mem_pools[index]->poolName,
			    lac_mem_pools[index]->numElementsInPool,
			    lac_mem_pools[index]->blkSizeInBytes,
			    lac_mem_pools[index]->blkAlignmentInBytes,
			    lac_mem_pools[index]->availBlks);
		}
		index++;
	}
}

static void
Lac_MemPoolInitSymCookies(lac_sym_cookie_t *pSymCookie)
{
	pSymCookie->keyContentDescPhyAddr =
	    LAC_OS_VIRT_TO_PHYS_INTERNAL(pSymCookie->u.keyCookie.contentDesc);
	pSymCookie->keyHashStateBufferPhyAddr = LAC_OS_VIRT_TO_PHYS_INTERNAL(
	    pSymCookie->u.keyCookie.hashStateBuffer);
	pSymCookie->keySslKeyInputPhyAddr = LAC_OS_VIRT_TO_PHYS_INTERNAL(
	    &(pSymCookie->u.keyCookie.u.sslKeyInput));
	pSymCookie->keyTlsKeyInputPhyAddr = LAC_OS_VIRT_TO_PHYS_INTERNAL(
	    &(pSymCookie->u.keyCookie.u.tlsKeyInput));
}

CpaStatus
Lac_MemPoolInitSymCookiesPhyAddr(lac_memory_pool_id_t poolID)
{
	lac_mem_pool_hdr_t *pPoolID = (lac_mem_pool_hdr_t *)poolID;
	lac_sym_cookie_t *pSymCookie = NULL;
	lac_mem_blk_t *pCurrentBlk = NULL;

	if (NULL == pPoolID) {
		QAT_UTILS_LOG("Invalid Pool ID\n");
		return CPA_STATUS_FAIL;
	}

	if (pPoolID->trackBlks == NULL) {
		pCurrentBlk = top(&pPoolID->stack);

		while (pCurrentBlk != NULL) {
			pSymCookie =
			    (lac_sym_cookie_t *)((LAC_ARCH_UINT)(pCurrentBlk) +
						 sizeof(lac_mem_blk_t));
			pCurrentBlk = pCurrentBlk->pNext;
			Lac_MemPoolInitSymCookies(pSymCookie);
		}
	} else {
		Cpa32U count = 0;

		for (count = 0; count < pPoolID->numElementsInPool; count++) {
			pCurrentBlk = pPoolID->trackBlks[count];
			pSymCookie =
			    (lac_sym_cookie_t *)((LAC_ARCH_UINT)(pCurrentBlk) +
						 sizeof(lac_mem_blk_t));
			Lac_MemPoolInitSymCookies(pSymCookie);
		}
	}
	return CPA_STATUS_SUCCESS;
}

CpaStatus
Lac_MemPoolInitDcCookiePhyAddr(lac_memory_pool_id_t poolID)
{
	lac_mem_pool_hdr_t *pPoolID = (lac_mem_pool_hdr_t *)poolID;
	lac_mem_blk_t *pCurrentBlk = NULL;

	if (NULL == pPoolID) {
		QAT_UTILS_LOG("Invalid Pool ID\n");
		return CPA_STATUS_FAIL;
	}

	if (NULL == pPoolID->trackBlks) {
		pCurrentBlk = top(&pPoolID->stack);

		while (pCurrentBlk != NULL) {
			pCurrentBlk = pCurrentBlk->pNext;
		}
	} else {
		Cpa32U count = 0;

		for (count = 0; count < pPoolID->numElementsInPool; count++) {
			pCurrentBlk = pPoolID->trackBlks[count];
		}
	}
	return CPA_STATUS_SUCCESS;
}
