/** * @file IxEthDBDBMem.c * * @brief Memory handling routines for the MAC address database * * @par * IXP400 SW Release version 2.0 * * -- Copyright Notice -- * * @par * Copyright 2001-2005, Intel Corporation. * All rights reserved. * * @par * SPDX-License-Identifier: BSD-3-Clause * @par * -- End of Copyright Notice -- */ #include "IxEthDB_p.h" IX_ETH_DB_PRIVATE HashNode *nodePool = NULL; IX_ETH_DB_PRIVATE MacDescriptor *macPool = NULL; IX_ETH_DB_PRIVATE MacTreeNode *treePool = NULL; IX_ETH_DB_PRIVATE HashNode nodePoolArea[NODE_POOL_SIZE]; IX_ETH_DB_PRIVATE MacDescriptor macPoolArea[MAC_POOL_SIZE]; IX_ETH_DB_PRIVATE MacTreeNode treePoolArea[TREE_POOL_SIZE]; IX_ETH_DB_PRIVATE IxOsalMutex nodePoolLock; IX_ETH_DB_PRIVATE IxOsalMutex macPoolLock; IX_ETH_DB_PRIVATE IxOsalMutex treePoolLock; #define LOCK_NODE_POOL { ixOsalMutexLock(&nodePoolLock, IX_OSAL_WAIT_FOREVER); } #define UNLOCK_NODE_POOL { ixOsalMutexUnlock(&nodePoolLock); } #define LOCK_MAC_POOL { ixOsalMutexLock(&macPoolLock, IX_OSAL_WAIT_FOREVER); } #define UNLOCK_MAC_POOL { ixOsalMutexUnlock(&macPoolLock); } #define LOCK_TREE_POOL { ixOsalMutexLock(&treePoolLock, IX_OSAL_WAIT_FOREVER); } #define UNLOCK_TREE_POOL { ixOsalMutexUnlock(&treePoolLock); } /* private function prototypes */ IX_ETH_DB_PRIVATE MacDescriptor* ixEthDBPoolAllocMacDescriptor(void); IX_ETH_DB_PRIVATE void ixEthDBPoolFreeMacDescriptor(MacDescriptor *macDescriptor); /** * @addtogroup EthMemoryManagement * * @{ */ /** * @brief initializes the memory pools used by the ethernet database component * * Initializes the hash table node, mac descriptor and mac tree node pools. * Called at initialization time by @ref ixEthDBInit(). * * @internal */ IX_ETH_DB_PUBLIC void ixEthDBInitMemoryPools(void) { int local_index; /* HashNode pool */ ixOsalMutexInit(&nodePoolLock); for (local_index = 0 ; local_index < NODE_POOL_SIZE ; local_index++) { HashNode *freeNode = &nodePoolArea[local_index]; freeNode->nextFree = nodePool; nodePool = freeNode; } /* MacDescriptor pool */ ixOsalMutexInit(&macPoolLock); for (local_index = 0 ; local_index < MAC_POOL_SIZE ; local_index++) { MacDescriptor *freeDescriptor = &macPoolArea[local_index]; freeDescriptor->nextFree = macPool; macPool = freeDescriptor; } /* MacTreeNode pool */ ixOsalMutexInit(&treePoolLock); for (local_index = 0 ; local_index < TREE_POOL_SIZE ; local_index++) { MacTreeNode *freeNode = &treePoolArea[local_index]; freeNode->nextFree = treePool; treePool = freeNode; } } /** * @brief allocates a hash node from the pool * * Allocates a hash node and resets its value. * * @return the allocated hash node or NULL if the pool is empty * * @internal */ IX_ETH_DB_PUBLIC HashNode* ixEthDBAllocHashNode(void) { HashNode *allocatedNode = NULL; if (nodePool != NULL) { LOCK_NODE_POOL; allocatedNode = nodePool; nodePool = nodePool->nextFree; UNLOCK_NODE_POOL; memset(allocatedNode, 0, sizeof(HashNode)); } return allocatedNode; } /** * @brief frees a hash node into the pool * * @param hashNode node to be freed * * @internal */ IX_ETH_DB_PUBLIC void ixEthDBFreeHashNode(HashNode *hashNode) { if (hashNode != NULL) { LOCK_NODE_POOL; hashNode->nextFree = nodePool; nodePool = hashNode; UNLOCK_NODE_POOL; } } /** * @brief allocates a mac descriptor from the pool * * Allocates a mac descriptor and resets its value. * This function is not used directly, instead @ref ixEthDBAllocMacDescriptor() * is used, which keeps track of the pointer reference count. * * @see ixEthDBAllocMacDescriptor() * * @warning this function is not used directly by any other function * apart from ixEthDBAllocMacDescriptor() * * @return the allocated mac descriptor or NULL if the pool is empty * * @internal */ IX_ETH_DB_PRIVATE MacDescriptor* ixEthDBPoolAllocMacDescriptor(void) { MacDescriptor *allocatedDescriptor = NULL; if (macPool != NULL) { LOCK_MAC_POOL; allocatedDescriptor = macPool; macPool = macPool->nextFree; UNLOCK_MAC_POOL; memset(allocatedDescriptor, 0, sizeof(MacDescriptor)); } return allocatedDescriptor; } /** * @brief allocates and initializes a mac descriptor smart pointer * * Uses @ref ixEthDBPoolAllocMacDescriptor() to allocate a mac descriptor * from the pool and initializes its reference count. * * @see ixEthDBPoolAllocMacDescriptor() * * @return the allocated mac descriptor or NULL if the pool is empty * * @internal */ IX_ETH_DB_PUBLIC MacDescriptor* ixEthDBAllocMacDescriptor(void) { MacDescriptor *allocatedDescriptor = ixEthDBPoolAllocMacDescriptor(); if (allocatedDescriptor != NULL) { LOCK_MAC_POOL; allocatedDescriptor->refCount++; UNLOCK_MAC_POOL; } return allocatedDescriptor; } /** * @brief frees a mac descriptor back into the pool * * @param macDescriptor mac descriptor to be freed * * @warning this function is not to be called by anyone but * ixEthDBFreeMacDescriptor() * * @see ixEthDBFreeMacDescriptor() * * @internal */ IX_ETH_DB_PRIVATE void ixEthDBPoolFreeMacDescriptor(MacDescriptor *macDescriptor) { LOCK_MAC_POOL; macDescriptor->nextFree = macPool; macPool = macDescriptor; UNLOCK_MAC_POOL; } /** * @brief frees or reduces the usage count of a mac descriptor smart pointer * * If the reference count reaches 0 (structure is no longer used anywhere) * then the descriptor is freed back into the pool using ixEthDBPoolFreeMacDescriptor(). * * @see ixEthDBPoolFreeMacDescriptor() * * @internal */ IX_ETH_DB_PUBLIC void ixEthDBFreeMacDescriptor(MacDescriptor *macDescriptor) { if (macDescriptor != NULL) { LOCK_MAC_POOL; if (macDescriptor->refCount > 0) { macDescriptor->refCount--; if (macDescriptor->refCount == 0) { UNLOCK_MAC_POOL; ixEthDBPoolFreeMacDescriptor(macDescriptor); } else { UNLOCK_MAC_POOL; } } else { UNLOCK_MAC_POOL; } } } /** * @brief clones a mac descriptor smart pointer * * @param macDescriptor mac descriptor to clone * * Increments the usage count of the smart pointer * * @returns the cloned smart pointer * * @internal */ IX_ETH_DB_PUBLIC MacDescriptor* ixEthDBCloneMacDescriptor(MacDescriptor *macDescriptor) { LOCK_MAC_POOL; if (macDescriptor->refCount == 0) { UNLOCK_MAC_POOL; return NULL; } macDescriptor->refCount++; UNLOCK_MAC_POOL; return macDescriptor; } /** * @brief allocates a mac tree node from the pool * * Allocates and initializes a mac tree node from the pool. * * @return the allocated mac tree node or NULL if the pool is empty * * @internal */ IX_ETH_DB_PUBLIC MacTreeNode* ixEthDBAllocMacTreeNode(void) { MacTreeNode *allocatedNode = NULL; if (treePool != NULL) { LOCK_TREE_POOL; allocatedNode = treePool; treePool = treePool->nextFree; UNLOCK_TREE_POOL; memset(allocatedNode, 0, sizeof(MacTreeNode)); } return allocatedNode; } /** * @brief frees a mac tree node back into the pool * * @param macNode mac tree node to be freed * * @warning not to be used except from ixEthDBFreeMacTreeNode(). * * @see ixEthDBFreeMacTreeNode() * * @internal */ void ixEthDBPoolFreeMacTreeNode(MacTreeNode *macNode) { if (macNode != NULL) { LOCK_TREE_POOL; macNode->nextFree = treePool; treePool = macNode; UNLOCK_TREE_POOL; } } /** * @brief frees or reduces the usage count of a mac tree node smart pointer * * @param macNode mac tree node to free * * Reduces the usage count of the given mac node. If the usage count * reaches 0 the node is freed back into the pool using ixEthDBPoolFreeMacTreeNode() * * @internal */ IX_ETH_DB_PUBLIC void ixEthDBFreeMacTreeNode(MacTreeNode *macNode) { if (macNode->descriptor != NULL) { ixEthDBFreeMacDescriptor(macNode->descriptor); } if (macNode->left != NULL) { ixEthDBFreeMacTreeNode(macNode->left); } if (macNode->right != NULL) { ixEthDBFreeMacTreeNode(macNode->right); } ixEthDBPoolFreeMacTreeNode(macNode); } /** * @brief clones a mac tree node * * @param macNode mac tree node to be cloned * * Increments the usage count of the node, its associated descriptor * and recursively of all its child nodes. * * @warning this function is recursive and clones whole trees/subtrees, use only for * root nodes * * @internal */ IX_ETH_DB_PUBLIC MacTreeNode* ixEthDBCloneMacTreeNode(MacTreeNode *macNode) { if (macNode != NULL) { MacTreeNode *clonedMacNode = ixEthDBAllocMacTreeNode(); if (clonedMacNode != NULL) { if (macNode->right != NULL) { clonedMacNode->right = ixEthDBCloneMacTreeNode(macNode->right); } if (macNode->left != NULL) { clonedMacNode->left = ixEthDBCloneMacTreeNode(macNode->left); } if (macNode->descriptor != NULL) { clonedMacNode->descriptor = ixEthDBCloneMacDescriptor(macNode->descriptor); } } return clonedMacNode; } else { return NULL; } } #ifndef NDEBUG /* Debug statistical functions for memory usage */ extern HashTable dbHashtable; int ixEthDBNumHashElements(void); int ixEthDBNumHashElements(void) { UINT32 bucketIndex; int numElements = 0; HashTable *hashTable = &dbHashtable; for (bucketIndex = 0 ; bucketIndex < hashTable->numBuckets ; bucketIndex++) { if (hashTable->hashBuckets[bucketIndex] != NULL) { HashNode *node = hashTable->hashBuckets[bucketIndex]; while (node != NULL) { numElements++; node = node->next; } } } return numElements; } UINT32 ixEthDBSearchTreeUsageGet(MacTreeNode *tree) { if (tree == NULL) { return 0; } else { return 1 /* this node */ + ixEthDBSearchTreeUsageGet(tree->left) + ixEthDBSearchTreeUsageGet(tree->right); } } int ixEthDBShowMemoryStatus(void) { MacDescriptor *mac; MacTreeNode *tree; HashNode *node; int macCounter = 0; int treeCounter = 0; int nodeCounter = 0; int totalTreeUsage = 0; int totalDescriptorUsage = 0; int totalCloneDescriptorUsage = 0; int totalNodeUsage = 0; UINT32 portIndex; LOCK_NODE_POOL; LOCK_MAC_POOL; LOCK_TREE_POOL; mac = macPool; tree = treePool; node = nodePool; while (mac != NULL) { macCounter++; mac = mac->nextFree; if (macCounter > MAC_POOL_SIZE) { break; } } while (tree != NULL) { treeCounter++; tree = tree->nextFree; if (treeCounter > TREE_POOL_SIZE) { break; } } while (node != NULL) { nodeCounter++; node = node->nextFree; if (nodeCounter > NODE_POOL_SIZE) { break; } } for (portIndex = 0 ; portIndex < IX_ETH_DB_NUMBER_OF_PORTS ; portIndex++) { int treeUsage = ixEthDBSearchTreeUsageGet(ixEthDBPortInfo[portIndex].updateMethod.searchTree); totalTreeUsage += treeUsage; totalCloneDescriptorUsage += treeUsage; /* each tree node contains a descriptor */ } totalNodeUsage = ixEthDBNumHashElements(); totalDescriptorUsage += totalNodeUsage; /* each hash table entry contains a descriptor */ UNLOCK_NODE_POOL; UNLOCK_MAC_POOL; UNLOCK_TREE_POOL; printf("Ethernet database memory usage stats:\n\n"); if (macCounter <= MAC_POOL_SIZE) { printf("\tMAC descriptor pool : %d free out of %d entries (%d%%)\n", macCounter, MAC_POOL_SIZE, macCounter * 100 / MAC_POOL_SIZE); } else { printf("\tMAC descriptor pool : invalid state (ring within the pool), normally %d entries\n", MAC_POOL_SIZE); } if (treeCounter <= TREE_POOL_SIZE) { printf("\tTree node pool : %d free out of %d entries (%d%%)\n", treeCounter, TREE_POOL_SIZE, treeCounter * 100 / TREE_POOL_SIZE); } else { printf("\tTREE descriptor pool : invalid state (ring within the pool), normally %d entries\n", TREE_POOL_SIZE); } if (nodeCounter <= NODE_POOL_SIZE) { printf("\tHash node pool : %d free out of %d entries (%d%%)\n", nodeCounter, NODE_POOL_SIZE, nodeCounter * 100 / NODE_POOL_SIZE); } else { printf("\tNODE descriptor pool : invalid state (ring within the pool), normally %d entries\n", NODE_POOL_SIZE); } printf("\n"); printf("\tMAC descriptor usage : %d entries, %d cloned\n", totalDescriptorUsage, totalCloneDescriptorUsage); printf("\tTree node usage : %d entries\n", totalTreeUsage); printf("\tHash node usage : %d entries\n", totalNodeUsage); printf("\n"); /* search for duplicate nodes in the mac pool */ { MacDescriptor *reference = macPool; while (reference != NULL) { MacDescriptor *comparison = reference->nextFree; while (comparison != NULL) { if (reference == comparison) { printf("Warning: reached a duplicate (%p), invalid MAC pool state\n", reference); return 1; } comparison = comparison->nextFree; } reference = reference->nextFree; } } printf("No duplicates found in the MAC pool (sanity check ok)\n"); return 0; } #endif /* NDEBUG */ /** * @} EthMemoryManagement */