diff options
Diffstat (limited to 'cpu/ixp/npe/IxEthDBHashtable.c')
-rw-r--r-- | cpu/ixp/npe/IxEthDBHashtable.c | 642 |
1 files changed, 642 insertions, 0 deletions
diff --git a/cpu/ixp/npe/IxEthDBHashtable.c b/cpu/ixp/npe/IxEthDBHashtable.c new file mode 100644 index 0000000000..f1b18e6b48 --- /dev/null +++ b/cpu/ixp/npe/IxEthDBHashtable.c @@ -0,0 +1,642 @@ +/** + * @file ethHash.c + * + * @brief Hashtable implementation + * + * @par + * IXP400 SW Release version 2.0 + * + * -- Copyright Notice -- + * + * @par + * Copyright 2001-2005, Intel Corporation. + * All rights reserved. + * + * @par + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that 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 Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * @par + * 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. + * + * @par + * -- End of Copyright Notice -- + */ + + +#include "IxEthDB_p.h" +#include "IxEthDBLocks_p.h" + +/** + * @addtogroup EthDB + * + * @{ + */ + +/** + * @brief initializes a hash table object + * + * @param hashTable uninitialized hash table structure + * @param numBuckets number of buckets to use + * @param entryHashFunction hash function used + * to hash entire hash node data block (for adding) + * @param matchFunctions array of match functions, indexed on type, + * used to differentiate records with the same hash value + * @param freeFunction function used to free node data blocks + * + * Initializes the given hash table object. + * + * @internal + */ +void ixEthDBInitHash(HashTable *hashTable, + UINT32 numBuckets, + HashFunction entryHashFunction, + MatchFunction *matchFunctions, + FreeFunction freeFunction) +{ + UINT32 bucketIndex; + UINT32 hashSize = numBuckets * sizeof(HashNode *); + + /* entry hashing, matching and freeing methods */ + hashTable->entryHashFunction = entryHashFunction; + hashTable->matchFunctions = matchFunctions; + hashTable->freeFunction = freeFunction; + + /* buckets */ + hashTable->numBuckets = numBuckets; + + /* set to 0 all buckets */ + memset(hashTable->hashBuckets, 0, hashSize); + + /* init bucket locks - note that initially all mutexes are unlocked after MutexInit()*/ + for (bucketIndex = 0 ; bucketIndex < numBuckets ; bucketIndex++) + { + ixOsalFastMutexInit(&hashTable->bucketLocks[bucketIndex]); + } +} + +/** + * @brief adds an entry to the hash table + * + * @param hashTable hash table to add the entry to + * @param entry entry to add + * + * The entry will be hashed using the entry hashing function and added to the + * hash table, unless a locking blockage occurs, in which case the caller + * should retry. + * + * @retval IX_ETH_DB_SUCCESS if adding <i>entry</i> has succeeded + * @retval IX_ETH_DB_NOMEM if there's no memory left in the hash node pool + * @retval IX_ETH_DB_BUSY if there's a locking failure on the insertion path + * + * @internal + */ +IX_ETH_DB_PUBLIC IxEthDBStatus ixEthDBAddHashEntry(HashTable *hashTable, void *entry) +{ + UINT32 hashValue = hashTable->entryHashFunction(entry); + UINT32 bucketIndex = hashValue % hashTable->numBuckets; + HashNode *bucket = hashTable->hashBuckets[bucketIndex]; + HashNode *newNode; + + LockStack locks; + + INIT_STACK(&locks); + + /* lock bucket */ + PUSH_LOCK(&locks, &hashTable->bucketLocks[bucketIndex]); + + /* lock insertion element (first in chain), if any */ + if (bucket != NULL) + { + PUSH_LOCK(&locks, &bucket->lock); + } + + /* get new node */ + newNode = ixEthDBAllocHashNode(); + + if (newNode == NULL) + { + /* unlock everything */ + UNROLL_STACK(&locks); + + return IX_ETH_DB_NOMEM; + } + + /* init lock - note that mutexes are unlocked after MutexInit */ + ixOsalFastMutexInit(&newNode->lock); + + /* populate new link */ + newNode->data = entry; + + /* add to bucket */ + newNode->next = bucket; + hashTable->hashBuckets[bucketIndex] = newNode; + + /* unlock bucket and insertion point */ + UNROLL_STACK(&locks); + + return IX_ETH_DB_SUCCESS; +} + +/** + * @brief removes an entry from the hashtable + * + * @param hashTable hash table to remove entry from + * @param keyType type of record key used for matching + * @param reference reference key used to identify the entry + * + * The reference key will be hashed using the key hashing function, + * the entry is searched using the hashed value and then examined + * against the reference entry using the match function. A positive + * match will trigger the deletion of the entry. + * Locking failures are reported and the caller should retry. + * + * @retval IX_ETH_DB_SUCCESS if the removal was successful + * @retval IX_ETH_DB_NO_SUCH_ADDR if the entry was not found + * @retval IX_ETH_DB_BUSY if a locking failure occured during the process + * + * @internal + */ +IxEthDBStatus ixEthDBRemoveHashEntry(HashTable *hashTable, int keyType, void *reference) +{ + UINT32 hashValue = hashTable->entryHashFunction(reference); + UINT32 bucketIndex = hashValue % hashTable->numBuckets; + HashNode *node = hashTable->hashBuckets[bucketIndex]; + HashNode *previousNode = NULL; + + LockStack locks; + + INIT_STACK(&locks); + + while (node != NULL) + { + /* try to lock node */ + PUSH_LOCK(&locks, &node->lock); + + if (hashTable->matchFunctions[keyType](reference, node->data)) + { + /* found entry */ + if (node->next != NULL) + { + PUSH_LOCK(&locks, &node->next->lock); + } + + if (previousNode == NULL) + { + /* node is head of chain */ + PUSH_LOCK(&locks, &hashTable->bucketLocks[bucketIndex]); + + hashTable->hashBuckets[bucketIndex] = node->next; + + POP_LOCK(&locks); + } + else + { + /* relink */ + previousNode->next = node->next; + } + + UNROLL_STACK(&locks); + + /* free node */ + hashTable->freeFunction(node->data); + ixEthDBFreeHashNode(node); + + return IX_ETH_DB_SUCCESS; + } + else + { + if (previousNode != NULL) + { + /* unlock previous node */ + SHIFT_STACK(&locks); + } + + /* advance to next element in chain */ + previousNode = node; + node = node->next; + } + } + + UNROLL_STACK(&locks); + + /* not found */ + return IX_ETH_DB_NO_SUCH_ADDR; +} + +/** + * @brief retrieves an entry from the hash table + * + * @param hashTable hash table to perform the search into + * @param reference search key (a MAC address) + * @param keyType type of record key used for matching + * @param searchResult pointer where a reference to the located hash node + * is placed + * + * Searches the entry with the same key as <i>reference</i> and places the + * pointer to the resulting node in <i>searchResult</i>. + * An implicit write access lock is granted after a search, which gives the + * caller the opportunity to modify the entry. + * Access should be released as soon as possible using @ref ixEthDBReleaseHashNode(). + * + * @see ixEthDBReleaseHashNode() + * + * @retval IX_ETH_DB_SUCCESS if the search was completed successfully + * @retval IX_ETH_DB_NO_SUCH_ADDRESS if no entry with the given key was found + * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case + * the caller should retry + * + * @warning unless the return value is <b>IX_ETH_DB_SUCCESS</b> the searchResult + * location is NOT modified and therefore using a NULL comparison test when the + * value was not properly initialized would be an error + * + * @internal + */ +IxEthDBStatus ixEthDBSearchHashEntry(HashTable *hashTable, int keyType, void *reference, HashNode **searchResult) +{ + UINT32 hashValue; + HashNode *node; + + hashValue = hashTable->entryHashFunction(reference); + node = hashTable->hashBuckets[hashValue % hashTable->numBuckets]; + + while (node != NULL) + { + TRY_LOCK(&node->lock); + + if (hashTable->matchFunctions[keyType](reference, node->data)) + { + *searchResult = node; + + return IX_ETH_DB_SUCCESS; + } + else + { + UNLOCK(&node->lock); + + node = node->next; + } + } + + /* not found */ + return IX_ETH_DB_NO_SUCH_ADDR; +} + +/** + * @brief reports the existence of an entry in the hash table + * + * @param hashTable hash table to perform the search into + * @param reference search key (a MAC address) + * @param keyType type of record key used for matching + * + * Searches the entry with the same key as <i>reference</i>. + * No implicit write access lock is granted after a search, hence the + * caller cannot access or modify the entry. The result is only temporary. + * + * @see ixEthDBReleaseHashNode() + * + * @retval IX_ETH_DB_SUCCESS if the search was completed successfully + * @retval IX_ETH_DB_NO_SUCH_ADDRESS if no entry with the given key was found + * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case + * the caller should retry + * + * @internal + */ +IxEthDBStatus ixEthDBPeekHashEntry(HashTable *hashTable, int keyType, void *reference) +{ + UINT32 hashValue; + HashNode *node; + + hashValue = hashTable->entryHashFunction(reference); + node = hashTable->hashBuckets[hashValue % hashTable->numBuckets]; + + while (node != NULL) + { + TRY_LOCK(&node->lock); + + if (hashTable->matchFunctions[keyType](reference, node->data)) + { + UNLOCK(&node->lock); + + return IX_ETH_DB_SUCCESS; + } + else + { + UNLOCK(&node->lock); + + node = node->next; + } + } + + /* not found */ + return IX_ETH_DB_NO_SUCH_ADDR; +} + +/** + * @brief releases the write access lock + * + * @pre the node should have been obtained via @ref ixEthDBSearchHashEntry() + * + * @see ixEthDBSearchHashEntry() + * + * @internal + */ +void ixEthDBReleaseHashNode(HashNode *node) +{ + UNLOCK(&node->lock); +} + +/** + * @brief initializes a hash iterator + * + * @param hashTable hash table to be iterated + * @param iterator iterator object + * + * If the initialization is successful the iterator will point to the + * first hash table record (if any). + * Testing if the iterator has not passed the end of the table should be + * done using the IS_ITERATOR_VALID(iteratorPtr) macro. + * An implicit write access lock is granted on the entry pointed by the iterator. + * The access is automatically revoked when the iterator is incremented. + * If the caller decides to terminate the iteration before the end of the table is + * passed then the manual access release method, @ref ixEthDBReleaseHashIterator, + * must be called. + * + * @see ixEthDBReleaseHashIterator() + * + * @retval IX_ETH_DB_SUCCESS if initialization was successful and the iterator points + * to the first valid table node + * @retval IX_ETH_DB_FAIL if the table is empty + * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller + * should retry + * + * @warning do not use ixEthDBReleaseHashNode() on entries pointed by the iterator, as this + * might place the database in a permanent invalid lock state + * + * @internal + */ +IxEthDBStatus ixEthDBInitHashIterator(HashTable *hashTable, HashIterator *iterator) +{ + iterator->bucketIndex = 0; + iterator->node = NULL; + iterator->previousNode = NULL; + + return ixEthDBIncrementHashIterator(hashTable, iterator); +} + +/** + * @brief releases the write access locks of the iterator nodes + * + * @warning use of this function is required only when the caller terminates an iteration + * before reaching the end of the table + * + * @see ixEthDBInitHashIterator() + * @see ixEthDBIncrementHashIterator() + * + * @param iterator iterator whose node(s) should be unlocked + * + * @internal + */ +void ixEthDBReleaseHashIterator(HashIterator *iterator) +{ + if (iterator->previousNode != NULL) + { + UNLOCK(&iterator->previousNode->lock); + } + + if (iterator->node != NULL) + { + UNLOCK(&iterator->node->lock); + } +} + +/** + * @brief incremenents an iterator so that it points to the next valid entry of the table + * (if any) + * + * @param hashTable hash table to iterate + * @param iterator iterator object + * + * @pre the iterator object must be initialized using @ref ixEthDBInitHashIterator() + * + * If the increment operation is successful the iterator will point to the + * next hash table record (if any). + * Testing if the iterator has not passed the end of the table should be + * done using the IS_ITERATOR_VALID(iteratorPtr) macro. + * An implicit write access lock is granted on the entry pointed by the iterator. + * The access is automatically revoked when the iterator is re-incremented. + * If the caller decides to terminate the iteration before the end of the table is + * passed then the manual access release method, @ref ixEthDBReleaseHashIterator, + * must be called. + * Is is guaranteed that no other thread can remove or change the iterated entry until + * the iterator is incremented successfully. + * + * @see ixEthDBReleaseHashIterator() + * + * @retval IX_ETH_DB_SUCCESS if the operation was successful and the iterator points + * to the next valid table node + * @retval IX_ETH_DB_FAIL if the iterator has passed the end of the table + * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller + * should retry + * + * @warning do not use ixEthDBReleaseHashNode() on entries pointed by the iterator, as this + * might place the database in a permanent invalid lock state + * + * @internal + */ +IxEthDBStatus ixEthDBIncrementHashIterator(HashTable *hashTable, HashIterator *iterator) +{ + /* unless iterator is just initialized... */ + if (iterator->node != NULL) + { + /* try next in chain */ + if (iterator->node->next != NULL) + { + TRY_LOCK(&iterator->node->next->lock); + + if (iterator->previousNode != NULL) + { + UNLOCK(&iterator->previousNode->lock); + } + + iterator->previousNode = iterator->node; + iterator->node = iterator->node->next; + + return IX_ETH_DB_SUCCESS; + } + else + { + /* last in chain, prepare for next bucket */ + iterator->bucketIndex++; + } + } + + /* try next used bucket */ + for (; iterator->bucketIndex < hashTable->numBuckets ; iterator->bucketIndex++) + { + HashNode **nodePtr = &(hashTable->hashBuckets[iterator->bucketIndex]); + HashNode *node = *nodePtr; +#if (CPU!=SIMSPARCSOLARIS) && !defined (__wince) + if (((iterator->bucketIndex & IX_ETHDB_BUCKET_INDEX_MASK) == 0) && + (iterator->bucketIndex < (hashTable->numBuckets - IX_ETHDB_BUCKETPTR_AHEAD))) + { + /* preload next cache line (2 cache line ahead) */ + nodePtr += IX_ETHDB_BUCKETPTR_AHEAD; + __asm__ ("pld [%0];\n": : "r" (nodePtr)); + } +#endif + if (node != NULL) + { + TRY_LOCK(&node->lock); + + /* unlock last one or two nodes in the previous chain */ + if (iterator->node != NULL) + { + UNLOCK(&iterator->node->lock); + + if (iterator->previousNode != NULL) + { + UNLOCK(&iterator->previousNode->lock); + } + } + + /* redirect iterator */ + iterator->previousNode = NULL; + iterator->node = node; + + return IX_ETH_DB_SUCCESS; + } + } + + /* could not advance iterator */ + if (iterator->node != NULL) + { + UNLOCK(&iterator->node->lock); + + if (iterator->previousNode != NULL) + { + UNLOCK(&iterator->previousNode->lock); + } + + iterator->node = NULL; + } + + return IX_ETH_DB_END; +} + +/** + * @brief removes an entry pointed by an iterator + * + * @param hashTable iterated hash table + * @param iterator iterator object + * + * Removes the entry currently pointed by the iterator and repositions the iterator + * on the next valid entry (if any). Handles locking issues automatically and + * implicitely grants write access lock to the new pointed entry. + * Failures due to concurrent threads having write access locks in the same region + * preserve the state of the database and the iterator object, leaving the caller + * free to retry without loss of access. It is guaranteed that only the thread owning + * the iterator can remove the object pointed by the iterator. + * + * @retval IX_ETH_DB_SUCCESS if removal has succeeded + * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller + * should retry + * + * @internal + */ +IxEthDBStatus ixEthDBRemoveEntryAtHashIterator(HashTable *hashTable, HashIterator *iterator) +{ + HashIterator nextIteratorPos; + LockStack locks; + + INIT_STACK(&locks); + + /* set initial bucket index for next position */ + nextIteratorPos.bucketIndex = iterator->bucketIndex; + + /* compute iterator position before removing anything and lock ahead */ + if (iterator->node->next != NULL) + { + PUSH_LOCK(&locks, &iterator->node->next->lock); + + /* reposition on the next node in the chain */ + nextIteratorPos.node = iterator->node->next; + nextIteratorPos.previousNode = iterator->previousNode; + } + else + { + /* try next chain - don't know yet if we'll find anything */ + nextIteratorPos.node = NULL; + + /* if we find something it's a chain head */ + nextIteratorPos.previousNode = NULL; + + /* browse up in the buckets to find a non-null chain */ + while (++nextIteratorPos.bucketIndex < hashTable->numBuckets) + { + nextIteratorPos.node = hashTable->hashBuckets[nextIteratorPos.bucketIndex]; + + if (nextIteratorPos.node != NULL) + { + /* found a non-empty chain, try to lock head */ + PUSH_LOCK(&locks, &nextIteratorPos.node->lock); + + break; + } + } + } + + /* restore links over the to-be-deleted item */ + if (iterator->previousNode == NULL) + { + /* first in chain, lock bucket */ + PUSH_LOCK(&locks, &hashTable->bucketLocks[iterator->bucketIndex]); + + hashTable->hashBuckets[iterator->bucketIndex] = iterator->node->next; + + POP_LOCK(&locks); + } + else + { + /* relink */ + iterator->previousNode->next = iterator->node->next; + + /* unlock last remaining node in current chain when moving between chains */ + if (iterator->node->next == NULL) + { + UNLOCK(&iterator->previousNode->lock); + } + } + + /* delete entry */ + hashTable->freeFunction(iterator->node->data); + ixEthDBFreeHashNode(iterator->node); + + /* reposition iterator */ + *iterator = nextIteratorPos; + + return IX_ETH_DB_SUCCESS; +} + +/** + * @} + */ |