blob: 3d3050d5ce43e850d83e3a45cea690ae0be5140d [file] [log] [blame]
/**
* @file IxEthDBDBCore.c
*
* @brief Database support functions
*
* @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"
/* list of database hashtables */
IX_ETH_DB_PUBLIC HashTable dbHashtable;
IX_ETH_DB_PUBLIC MatchFunction matchFunctions[IX_ETH_DB_MAX_KEY_INDEX + 1];
IX_ETH_DB_PUBLIC BOOL ixEthDBPortUpdateRequired[IX_ETH_DB_MAX_RECORD_TYPE_INDEX + 1];
IX_ETH_DB_PUBLIC UINT32 ixEthDBKeyType[IX_ETH_DB_MAX_RECORD_TYPE_INDEX + 1];
/* private initialization flag */
IX_ETH_DB_PRIVATE BOOL ethDBInitializationComplete = false;
/**
* @brief initializes EthDB
*
* This function must be called to initialize the component.
*
* It does the following things:
* - checks the port definition structure
* - scans the capabilities of the NPE images and sets the
* capabilities of the ports accordingly
* - initializes the memory pools internally used in EthDB
* for storing database records and handling data
* - registers automatic update handlers for add and remove
* operations
* - registers hashing match functions, depending on key sets
* - initializes the main database hashtable
* - allocates contiguous memory zones to be used for NPE
* updates
* - registers the serialize methods used to convert data
* into NPE-readable format
* - starts the event processor
*
* Note that this function is documented in the public
* component header file, IxEthDB.h.
*
* @return IX_ETH_DB_SUCCESS or an appropriate error if the
* component failed to initialize correctly
*/
IX_ETH_DB_PUBLIC
IxEthDBStatus ixEthDBInit(void)
{
IxEthDBStatus result;
if (ethDBInitializationComplete)
{
/* redundant */
return IX_ETH_DB_SUCCESS;
}
/* trap an invalid port definition structure */
IX_ETH_DB_PORTS_ASSERTION;
/* memory management */
ixEthDBInitMemoryPools();
/* register hashing search methods */
ixEthDBMatchMethodsRegister(matchFunctions);
/* register type-based automatic port updates */
ixEthDBUpdateTypeRegister(ixEthDBPortUpdateRequired);
/* register record to key type mappings */
ixEthDBKeyTypeRegister(ixEthDBKeyType);
/* hash table */
ixEthDBInitHash(&dbHashtable, NUM_BUCKETS, ixEthDBEntryXORHash, matchFunctions, (FreeFunction) ixEthDBFreeMacDescriptor);
/* NPE update zones */
ixEthDBNPEUpdateAreasInit();
/* register record serialization methods */
ixEthDBRecordSerializeMethodsRegister();
/* start the event processor */
result = ixEthDBEventProcessorInit();
/* scan NPE features */
if (result == IX_ETH_DB_SUCCESS)
{
ixEthDBFeatureCapabilityScan();
}
ethDBInitializationComplete = true;
return result;
}
/**
* @brief prepares EthDB for unloading
*
* This function must be called before removing the
* EthDB component from memory (e.g. doing rmmod in
* Linux) if the component is to be re-initialized again
* without rebooting the platform.
*
* All the EthDB ports must be disabled before this
* function is to be called. Failure to disable all
* the ports will return the IX_ETH_DB_BUSY error.
*
* This function will destroy mutexes, deallocate
* memory and stop the event processor.
*
* Note that this function is fully documented in the
* main component header file, IxEthDB.h.
*
* @return IX_ETH_DB_SUCCESS if de-initialization
* completed successfully or an appropriate error
* message otherwise
*/
IX_ETH_DB_PUBLIC
IxEthDBStatus ixEthDBUnload(void)
{
IxEthDBPortId portIndex;
if (!ethDBInitializationComplete)
{
/* redundant */
return IX_ETH_DB_SUCCESS;
}
/* check if any ports are enabled */
for (portIndex = 0 ; portIndex < IX_ETH_DB_NUMBER_OF_PORTS ; portIndex++)
{
if (ixEthDBPortInfo[portIndex].enabled)
{
return IX_ETH_DB_BUSY;
}
}
/* free port resources */
for (portIndex = 0 ; portIndex < IX_ETH_DB_NUMBER_OF_PORTS ; portIndex++)
{
if (ixEthDBPortDefinitions[portIndex].type == IX_ETH_NPE)
{
ixOsalMutexDestroy(&ixEthDBPortInfo[portIndex].npeAckLock);
}
ixEthDBPortInfo[portIndex].initialized = false;
}
/* shutdown event processor */
ixEthDBStopLearningFunction();
/* deallocate NPE update zones */
ixEthDBNPEUpdateAreasUnload();
ethDBInitializationComplete = false;
return IX_ETH_DB_SUCCESS;
}
/**
* @brief adds a new entry to the Ethernet database
*
* @param newRecordTemplate address of the record template to use
* @param updateTrigger port map containing the update triggers
* resulting from this update operation
*
* Creates a new database entry, populates it with the data
* copied from the given template and adds the record to the
* database hash table.
* It also checks whether the new record type is registered to trigger
* automatic updates; if it is, the update trigger will contain the
* port on which the record insertion was performed, as well as the
* old port in case the addition was a record migration (from one port
* to the other). The caller can use the updateTrigger to trigger
* automatic updates on the ports changed as a result of this addition.
*
* @retval IX_ETH_DB_SUCCESS addition successful
* @retval IX_ETH_DB_NOMEM insertion failed, no memory left in the mac descriptor memory pool
* @retval IX_ETH_DB_BUSY database busy, cannot insert due to locking
*
* @internal
*/
IX_ETH_DB_PUBLIC
IxEthDBStatus ixEthDBAdd(MacDescriptor *newRecordTemplate, IxEthDBPortMap updateTrigger)
{
IxEthDBStatus result;
MacDescriptor *newDescriptor;
IxEthDBPortId originalPortID;
HashNode *node = NULL;
BUSY_RETRY(ixEthDBSearchHashEntry(&dbHashtable, ixEthDBKeyType[newRecordTemplate->type], newRecordTemplate, &node));
TEST_FIXTURE_INCREMENT_DB_CORE_ACCESS_COUNTER;
if (node == NULL)
{
/* not found, create a new one */
newDescriptor = ixEthDBAllocMacDescriptor();
if (newDescriptor == NULL)
{
return IX_ETH_DB_NOMEM; /* no memory */
}
/* old port does not exist, avoid unnecessary updates */
originalPortID = newRecordTemplate->portID;
}
else
{
/* a node with the same key exists, will update node */
newDescriptor = (MacDescriptor *) node->data;
/* save original port id */
originalPortID = newDescriptor->portID;
}
/* copy/update fields into new record */
memcpy(newDescriptor->macAddress, newRecordTemplate->macAddress, sizeof (IxEthDBMacAddr));
memcpy(&newDescriptor->recordData, &newRecordTemplate->recordData, sizeof (IxEthDBRecordData));
newDescriptor->type = newRecordTemplate->type;
newDescriptor->portID = newRecordTemplate->portID;
newDescriptor->user = newRecordTemplate->user;
if (node == NULL)
{
/* new record, insert into hashtable */
BUSY_RETRY_WITH_RESULT(ixEthDBAddHashEntry(&dbHashtable, newDescriptor), result);
if (result != IX_ETH_DB_SUCCESS)
{
ixEthDBFreeMacDescriptor(newDescriptor);
return result; /* insertion failed */
}
}
if (node != NULL)
{
/* release access */
ixEthDBReleaseHashNode(node);
}
/* trigger add/remove update if required by type */
if (updateTrigger != NULL &&
ixEthDBPortUpdateRequired[newRecordTemplate->type])
{
/* add new port to update list */
JOIN_PORT_TO_MAP(updateTrigger, newRecordTemplate->portID);
/* check if record has moved, we'll need to update the old port as well */
if (originalPortID != newDescriptor->portID)
{
JOIN_PORT_TO_MAP(updateTrigger, originalPortID);
}
}
return IX_ETH_DB_SUCCESS;
}
/**
* @brief remove a record from the Ethernet database
*
* @param templateRecord template record used to determine
* what record is to be removed
* @param updateTrigger port map containing the update triggers
* resulting from this update operation
*
* This function will examine the template record it receives
* and attempts to delete a record of the same type and containing
* the same keys as the template record. If deletion is successful
* and the record type is registered for automatic port updates the
* port will also be set in the updateTrigger port map, so that the
* client can perform an update of the port.
*
* @retval IX_ETH_DB_SUCCESS removal was successful
* @retval IX_ETH_DB_NO_SUCH_ADDR the record with the given MAC address was not found
* @retval IX_ETH_DB_BUSY database busy, cannot remove due to locking
*
* @internal
*/
IX_ETH_DB_PUBLIC
IxEthDBStatus ixEthDBRemove(MacDescriptor *templateRecord, IxEthDBPortMap updateTrigger)
{
IxEthDBStatus result;
TEST_FIXTURE_INCREMENT_DB_CORE_ACCESS_COUNTER;
BUSY_RETRY_WITH_RESULT(ixEthDBRemoveHashEntry(&dbHashtable, ixEthDBKeyType[templateRecord->type], templateRecord), result);
if (result != IX_ETH_DB_SUCCESS)
{
return IX_ETH_DB_NO_SUCH_ADDR; /* not found */
}
/* trigger add/remove update if required by type */
if (updateTrigger != NULL
&&ixEthDBPortUpdateRequired[templateRecord->type])
{
/* add new port to update list */
JOIN_PORT_TO_MAP(updateTrigger, templateRecord->portID);
}
return IX_ETH_DB_SUCCESS;
}
/**
* @brief register record key types
*
* This function registers the appropriate key types,
* depending on record types.
*
* All filtering records use the MAC address as the key.
* WiFi and Firewall records use a compound key consisting
* in both the MAC address and the port ID.
*
* @return the number of registered record types
*/
IX_ETH_DB_PUBLIC
UINT32 ixEthDBKeyTypeRegister(UINT32 *keyType)
{
/* safety */
memset(keyType, 0, sizeof (keyType));
/* register all known record types */
keyType[IX_ETH_DB_FILTERING_RECORD] = IX_ETH_DB_MAC_KEY;
keyType[IX_ETH_DB_FILTERING_VLAN_RECORD] = IX_ETH_DB_MAC_KEY;
keyType[IX_ETH_DB_ALL_FILTERING_RECORDS] = IX_ETH_DB_MAC_KEY;
keyType[IX_ETH_DB_WIFI_RECORD] = IX_ETH_DB_MAC_PORT_KEY;
keyType[IX_ETH_DB_FIREWALL_RECORD] = IX_ETH_DB_MAC_PORT_KEY;
return 5;
}
/**
* @brief Sets a user-defined field into a database record
*
* Note that this function is fully documented in the main component
* header file.
*/
IX_ETH_DB_PUBLIC
IxEthDBStatus ixEthDBUserFieldSet(IxEthDBRecordType recordType, IxEthDBMacAddr *macAddr, IxEthDBPortId portID, IxEthDBVlanId vlanID, void *field)
{
HashNode *result = NULL;
if (macAddr == NULL)
{
return IX_ETH_DB_INVALID_ARG;
}
if (recordType == IX_ETH_DB_FILTERING_RECORD)
{
result = ixEthDBSearch(macAddr, recordType);
}
else if (recordType == IX_ETH_DB_FILTERING_VLAN_RECORD)
{
result = ixEthDBVlanSearch(macAddr, vlanID, recordType);
}
else if (recordType == IX_ETH_DB_WIFI_RECORD || recordType == IX_ETH_DB_FIREWALL_RECORD)
{
IX_ETH_DB_CHECK_PORT_EXISTS(portID);
result = ixEthDBPortSearch(macAddr, portID, recordType);
}
else
{
return IX_ETH_DB_INVALID_ARG;
}
if (result == NULL)
{
return IX_ETH_DB_NO_SUCH_ADDR;
}
((MacDescriptor *) result->data)->user = field;
ixEthDBReleaseHashNode(result);
return IX_ETH_DB_SUCCESS;
}
/**
* @brief Retrieves a user-defined field from a database record
*
* Note that this function is fully documented in the main component
* header file.
*/
IX_ETH_DB_PUBLIC
IxEthDBStatus ixEthDBUserFieldGet(IxEthDBRecordType recordType, IxEthDBMacAddr *macAddr, IxEthDBPortId portID, IxEthDBVlanId vlanID, void **field)
{
HashNode *result = NULL;
if (macAddr == NULL || field == NULL)
{
return IX_ETH_DB_INVALID_ARG;
}
if (recordType == IX_ETH_DB_FILTERING_RECORD)
{
result = ixEthDBSearch(macAddr, recordType);
}
else if (recordType == IX_ETH_DB_FILTERING_VLAN_RECORD)
{
result = ixEthDBVlanSearch(macAddr, vlanID, recordType);
}
else if (recordType == IX_ETH_DB_WIFI_RECORD || recordType == IX_ETH_DB_FIREWALL_RECORD)
{
IX_ETH_DB_CHECK_PORT_EXISTS(portID);
result = ixEthDBPortSearch(macAddr, portID, recordType);
}
else
{
return IX_ETH_DB_INVALID_ARG;
}
if (result == NULL)
{
return IX_ETH_DB_NO_SUCH_ADDR;
}
*field = ((MacDescriptor *) result->data)->user;
ixEthDBReleaseHashNode(result);
return IX_ETH_DB_SUCCESS;
}