4 * @brief Hashtable implementation
7 * IXP400 SW Release version 2.0
9 * -- Copyright Notice --
12 * Copyright 2001-2005, Intel Corporation.
13 * All rights reserved.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. Neither the name of the Intel Corporation nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
30 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * -- End of Copyright Notice --
46 #include "IxEthDB_p.h"
47 #include "IxEthDBLocks_p.h"
56 * @brief initializes a hash table object
58 * @param hashTable uninitialized hash table structure
59 * @param numBuckets number of buckets to use
60 * @param entryHashFunction hash function used
61 * to hash entire hash node data block (for adding)
62 * @param matchFunctions array of match functions, indexed on type,
63 * used to differentiate records with the same hash value
64 * @param freeFunction function used to free node data blocks
66 * Initializes the given hash table object.
70 void ixEthDBInitHash(HashTable *hashTable,
72 HashFunction entryHashFunction,
73 MatchFunction *matchFunctions,
74 FreeFunction freeFunction)
77 UINT32 hashSize = numBuckets * sizeof(HashNode *);
79 /* entry hashing, matching and freeing methods */
80 hashTable->entryHashFunction = entryHashFunction;
81 hashTable->matchFunctions = matchFunctions;
82 hashTable->freeFunction = freeFunction;
85 hashTable->numBuckets = numBuckets;
87 /* set to 0 all buckets */
88 memset(hashTable->hashBuckets, 0, hashSize);
90 /* init bucket locks - note that initially all mutexes are unlocked after MutexInit()*/
91 for (bucketIndex = 0 ; bucketIndex < numBuckets ; bucketIndex++)
93 ixOsalFastMutexInit(&hashTable->bucketLocks[bucketIndex]);
98 * @brief adds an entry to the hash table
100 * @param hashTable hash table to add the entry to
101 * @param entry entry to add
103 * The entry will be hashed using the entry hashing function and added to the
104 * hash table, unless a locking blockage occurs, in which case the caller
107 * @retval IX_ETH_DB_SUCCESS if adding <i>entry</i> has succeeded
108 * @retval IX_ETH_DB_NOMEM if there's no memory left in the hash node pool
109 * @retval IX_ETH_DB_BUSY if there's a locking failure on the insertion path
113 IX_ETH_DB_PUBLIC IxEthDBStatus ixEthDBAddHashEntry(HashTable *hashTable, void *entry)
115 UINT32 hashValue = hashTable->entryHashFunction(entry);
116 UINT32 bucketIndex = hashValue % hashTable->numBuckets;
117 HashNode *bucket = hashTable->hashBuckets[bucketIndex];
125 PUSH_LOCK(&locks, &hashTable->bucketLocks[bucketIndex]);
127 /* lock insertion element (first in chain), if any */
130 PUSH_LOCK(&locks, &bucket->lock);
134 newNode = ixEthDBAllocHashNode();
138 /* unlock everything */
139 UNROLL_STACK(&locks);
141 return IX_ETH_DB_NOMEM;
144 /* init lock - note that mutexes are unlocked after MutexInit */
145 ixOsalFastMutexInit(&newNode->lock);
147 /* populate new link */
148 newNode->data = entry;
151 newNode->next = bucket;
152 hashTable->hashBuckets[bucketIndex] = newNode;
154 /* unlock bucket and insertion point */
155 UNROLL_STACK(&locks);
157 return IX_ETH_DB_SUCCESS;
161 * @brief removes an entry from the hashtable
163 * @param hashTable hash table to remove entry from
164 * @param keyType type of record key used for matching
165 * @param reference reference key used to identify the entry
167 * The reference key will be hashed using the key hashing function,
168 * the entry is searched using the hashed value and then examined
169 * against the reference entry using the match function. A positive
170 * match will trigger the deletion of the entry.
171 * Locking failures are reported and the caller should retry.
173 * @retval IX_ETH_DB_SUCCESS if the removal was successful
174 * @retval IX_ETH_DB_NO_SUCH_ADDR if the entry was not found
175 * @retval IX_ETH_DB_BUSY if a locking failure occured during the process
179 IxEthDBStatus ixEthDBRemoveHashEntry(HashTable *hashTable, int keyType, void *reference)
181 UINT32 hashValue = hashTable->entryHashFunction(reference);
182 UINT32 bucketIndex = hashValue % hashTable->numBuckets;
183 HashNode *node = hashTable->hashBuckets[bucketIndex];
184 HashNode *previousNode = NULL;
192 /* try to lock node */
193 PUSH_LOCK(&locks, &node->lock);
195 if (hashTable->matchFunctions[keyType](reference, node->data))
198 if (node->next != NULL)
200 PUSH_LOCK(&locks, &node->next->lock);
203 if (previousNode == NULL)
205 /* node is head of chain */
206 PUSH_LOCK(&locks, &hashTable->bucketLocks[bucketIndex]);
208 hashTable->hashBuckets[bucketIndex] = node->next;
215 previousNode->next = node->next;
218 UNROLL_STACK(&locks);
221 hashTable->freeFunction(node->data);
222 ixEthDBFreeHashNode(node);
224 return IX_ETH_DB_SUCCESS;
228 if (previousNode != NULL)
230 /* unlock previous node */
234 /* advance to next element in chain */
240 UNROLL_STACK(&locks);
243 return IX_ETH_DB_NO_SUCH_ADDR;
247 * @brief retrieves an entry from the hash table
249 * @param hashTable hash table to perform the search into
250 * @param reference search key (a MAC address)
251 * @param keyType type of record key used for matching
252 * @param searchResult pointer where a reference to the located hash node
255 * Searches the entry with the same key as <i>reference</i> and places the
256 * pointer to the resulting node in <i>searchResult</i>.
257 * An implicit write access lock is granted after a search, which gives the
258 * caller the opportunity to modify the entry.
259 * Access should be released as soon as possible using @ref ixEthDBReleaseHashNode().
261 * @see ixEthDBReleaseHashNode()
263 * @retval IX_ETH_DB_SUCCESS if the search was completed successfully
264 * @retval IX_ETH_DB_NO_SUCH_ADDRESS if no entry with the given key was found
265 * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case
266 * the caller should retry
268 * @warning unless the return value is <b>IX_ETH_DB_SUCCESS</b> the searchResult
269 * location is NOT modified and therefore using a NULL comparison test when the
270 * value was not properly initialized would be an error
274 IxEthDBStatus ixEthDBSearchHashEntry(HashTable *hashTable, int keyType, void *reference, HashNode **searchResult)
279 hashValue = hashTable->entryHashFunction(reference);
280 node = hashTable->hashBuckets[hashValue % hashTable->numBuckets];
284 TRY_LOCK(&node->lock);
286 if (hashTable->matchFunctions[keyType](reference, node->data))
288 *searchResult = node;
290 return IX_ETH_DB_SUCCESS;
301 return IX_ETH_DB_NO_SUCH_ADDR;
305 * @brief reports the existence of an entry in the hash table
307 * @param hashTable hash table to perform the search into
308 * @param reference search key (a MAC address)
309 * @param keyType type of record key used for matching
311 * Searches the entry with the same key as <i>reference</i>.
312 * No implicit write access lock is granted after a search, hence the
313 * caller cannot access or modify the entry. The result is only temporary.
315 * @see ixEthDBReleaseHashNode()
317 * @retval IX_ETH_DB_SUCCESS if the search was completed successfully
318 * @retval IX_ETH_DB_NO_SUCH_ADDRESS if no entry with the given key was found
319 * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case
320 * the caller should retry
324 IxEthDBStatus ixEthDBPeekHashEntry(HashTable *hashTable, int keyType, void *reference)
329 hashValue = hashTable->entryHashFunction(reference);
330 node = hashTable->hashBuckets[hashValue % hashTable->numBuckets];
334 TRY_LOCK(&node->lock);
336 if (hashTable->matchFunctions[keyType](reference, node->data))
340 return IX_ETH_DB_SUCCESS;
351 return IX_ETH_DB_NO_SUCH_ADDR;
355 * @brief releases the write access lock
357 * @pre the node should have been obtained via @ref ixEthDBSearchHashEntry()
359 * @see ixEthDBSearchHashEntry()
363 void ixEthDBReleaseHashNode(HashNode *node)
369 * @brief initializes a hash iterator
371 * @param hashTable hash table to be iterated
372 * @param iterator iterator object
374 * If the initialization is successful the iterator will point to the
375 * first hash table record (if any).
376 * Testing if the iterator has not passed the end of the table should be
377 * done using the IS_ITERATOR_VALID(iteratorPtr) macro.
378 * An implicit write access lock is granted on the entry pointed by the iterator.
379 * The access is automatically revoked when the iterator is incremented.
380 * If the caller decides to terminate the iteration before the end of the table is
381 * passed then the manual access release method, @ref ixEthDBReleaseHashIterator,
384 * @see ixEthDBReleaseHashIterator()
386 * @retval IX_ETH_DB_SUCCESS if initialization was successful and the iterator points
387 * to the first valid table node
388 * @retval IX_ETH_DB_FAIL if the table is empty
389 * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller
392 * @warning do not use ixEthDBReleaseHashNode() on entries pointed by the iterator, as this
393 * might place the database in a permanent invalid lock state
397 IxEthDBStatus ixEthDBInitHashIterator(HashTable *hashTable, HashIterator *iterator)
399 iterator->bucketIndex = 0;
400 iterator->node = NULL;
401 iterator->previousNode = NULL;
403 return ixEthDBIncrementHashIterator(hashTable, iterator);
407 * @brief releases the write access locks of the iterator nodes
409 * @warning use of this function is required only when the caller terminates an iteration
410 * before reaching the end of the table
412 * @see ixEthDBInitHashIterator()
413 * @see ixEthDBIncrementHashIterator()
415 * @param iterator iterator whose node(s) should be unlocked
419 void ixEthDBReleaseHashIterator(HashIterator *iterator)
421 if (iterator->previousNode != NULL)
423 UNLOCK(&iterator->previousNode->lock);
426 if (iterator->node != NULL)
428 UNLOCK(&iterator->node->lock);
433 * @brief incremenents an iterator so that it points to the next valid entry of the table
436 * @param hashTable hash table to iterate
437 * @param iterator iterator object
439 * @pre the iterator object must be initialized using @ref ixEthDBInitHashIterator()
441 * If the increment operation is successful the iterator will point to the
442 * next hash table record (if any).
443 * Testing if the iterator has not passed the end of the table should be
444 * done using the IS_ITERATOR_VALID(iteratorPtr) macro.
445 * An implicit write access lock is granted on the entry pointed by the iterator.
446 * The access is automatically revoked when the iterator is re-incremented.
447 * If the caller decides to terminate the iteration before the end of the table is
448 * passed then the manual access release method, @ref ixEthDBReleaseHashIterator,
450 * Is is guaranteed that no other thread can remove or change the iterated entry until
451 * the iterator is incremented successfully.
453 * @see ixEthDBReleaseHashIterator()
455 * @retval IX_ETH_DB_SUCCESS if the operation was successful and the iterator points
456 * to the next valid table node
457 * @retval IX_ETH_DB_FAIL if the iterator has passed the end of the table
458 * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller
461 * @warning do not use ixEthDBReleaseHashNode() on entries pointed by the iterator, as this
462 * might place the database in a permanent invalid lock state
466 IxEthDBStatus ixEthDBIncrementHashIterator(HashTable *hashTable, HashIterator *iterator)
468 /* unless iterator is just initialized... */
469 if (iterator->node != NULL)
471 /* try next in chain */
472 if (iterator->node->next != NULL)
474 TRY_LOCK(&iterator->node->next->lock);
476 if (iterator->previousNode != NULL)
478 UNLOCK(&iterator->previousNode->lock);
481 iterator->previousNode = iterator->node;
482 iterator->node = iterator->node->next;
484 return IX_ETH_DB_SUCCESS;
488 /* last in chain, prepare for next bucket */
489 iterator->bucketIndex++;
493 /* try next used bucket */
494 for (; iterator->bucketIndex < hashTable->numBuckets ; iterator->bucketIndex++)
496 HashNode **nodePtr = &(hashTable->hashBuckets[iterator->bucketIndex]);
497 HashNode *node = *nodePtr;
498 #if (CPU!=SIMSPARCSOLARIS) && !defined (__wince)
499 if (((iterator->bucketIndex & IX_ETHDB_BUCKET_INDEX_MASK) == 0) &&
500 (iterator->bucketIndex < (hashTable->numBuckets - IX_ETHDB_BUCKETPTR_AHEAD)))
502 /* preload next cache line (2 cache line ahead) */
503 nodePtr += IX_ETHDB_BUCKETPTR_AHEAD;
504 __asm__ ("pld [%0];\n": : "r" (nodePtr));
509 TRY_LOCK(&node->lock);
511 /* unlock last one or two nodes in the previous chain */
512 if (iterator->node != NULL)
514 UNLOCK(&iterator->node->lock);
516 if (iterator->previousNode != NULL)
518 UNLOCK(&iterator->previousNode->lock);
522 /* redirect iterator */
523 iterator->previousNode = NULL;
524 iterator->node = node;
526 return IX_ETH_DB_SUCCESS;
530 /* could not advance iterator */
531 if (iterator->node != NULL)
533 UNLOCK(&iterator->node->lock);
535 if (iterator->previousNode != NULL)
537 UNLOCK(&iterator->previousNode->lock);
540 iterator->node = NULL;
543 return IX_ETH_DB_END;
547 * @brief removes an entry pointed by an iterator
549 * @param hashTable iterated hash table
550 * @param iterator iterator object
552 * Removes the entry currently pointed by the iterator and repositions the iterator
553 * on the next valid entry (if any). Handles locking issues automatically and
554 * implicitely grants write access lock to the new pointed entry.
555 * Failures due to concurrent threads having write access locks in the same region
556 * preserve the state of the database and the iterator object, leaving the caller
557 * free to retry without loss of access. It is guaranteed that only the thread owning
558 * the iterator can remove the object pointed by the iterator.
560 * @retval IX_ETH_DB_SUCCESS if removal has succeeded
561 * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller
566 IxEthDBStatus ixEthDBRemoveEntryAtHashIterator(HashTable *hashTable, HashIterator *iterator)
568 HashIterator nextIteratorPos;
573 /* set initial bucket index for next position */
574 nextIteratorPos.bucketIndex = iterator->bucketIndex;
576 /* compute iterator position before removing anything and lock ahead */
577 if (iterator->node->next != NULL)
579 PUSH_LOCK(&locks, &iterator->node->next->lock);
581 /* reposition on the next node in the chain */
582 nextIteratorPos.node = iterator->node->next;
583 nextIteratorPos.previousNode = iterator->previousNode;
587 /* try next chain - don't know yet if we'll find anything */
588 nextIteratorPos.node = NULL;
590 /* if we find something it's a chain head */
591 nextIteratorPos.previousNode = NULL;
593 /* browse up in the buckets to find a non-null chain */
594 while (++nextIteratorPos.bucketIndex < hashTable->numBuckets)
596 nextIteratorPos.node = hashTable->hashBuckets[nextIteratorPos.bucketIndex];
598 if (nextIteratorPos.node != NULL)
600 /* found a non-empty chain, try to lock head */
601 PUSH_LOCK(&locks, &nextIteratorPos.node->lock);
608 /* restore links over the to-be-deleted item */
609 if (iterator->previousNode == NULL)
611 /* first in chain, lock bucket */
612 PUSH_LOCK(&locks, &hashTable->bucketLocks[iterator->bucketIndex]);
614 hashTable->hashBuckets[iterator->bucketIndex] = iterator->node->next;
621 iterator->previousNode->next = iterator->node->next;
623 /* unlock last remaining node in current chain when moving between chains */
624 if (iterator->node->next == NULL)
626 UNLOCK(&iterator->previousNode->lock);
631 hashTable->freeFunction(iterator->node->data);
632 ixEthDBFreeHashNode(iterator->node);
634 /* reposition iterator */
635 *iterator = nextIteratorPos;
637 return IX_ETH_DB_SUCCESS;