1/** 2 * @file ethHash.c 3 * 4 * @brief Hashtable implementation 5 * 6 * @par 7 * IXP400 SW Release version 2.0 8 * 9 * -- Copyright Notice -- 10 * 11 * @par 12 * Copyright 2001-2005, Intel Corporation. 13 * All rights reserved. 14 * 15 * @par 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 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. 27 * 28 * @par 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 39 * SUCH DAMAGE. 40 * 41 * @par 42 * -- End of Copyright Notice -- 43 */ 44 45 46#include "IxEthDB_p.h" 47#include "IxEthDBLocks_p.h" 48 49/** 50 * @addtogroup EthDB 51 * 52 * @{ 53 */ 54 55/** 56 * @brief initializes a hash table object 57 * 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 65 * 66 * Initializes the given hash table object. 67 * 68 * @internal 69 */ 70void ixEthDBInitHash(HashTable *hashTable, 71 UINT32 numBuckets, 72 HashFunction entryHashFunction, 73 MatchFunction *matchFunctions, 74 FreeFunction freeFunction) 75{ 76 UINT32 bucketIndex; 77 UINT32 hashSize = numBuckets * sizeof(HashNode *); 78 79 /* entry hashing, matching and freeing methods */ 80 hashTable->entryHashFunction = entryHashFunction; 81 hashTable->matchFunctions = matchFunctions; 82 hashTable->freeFunction = freeFunction; 83 84 /* buckets */ 85 hashTable->numBuckets = numBuckets; 86 87 /* set to 0 all buckets */ 88 memset(hashTable->hashBuckets, 0, hashSize); 89 90 /* init bucket locks - note that initially all mutexes are unlocked after MutexInit()*/ 91 for (bucketIndex = 0 ; bucketIndex < numBuckets ; bucketIndex++) 92 { 93 ixOsalFastMutexInit(&hashTable->bucketLocks[bucketIndex]); 94 } 95} 96 97/** 98 * @brief adds an entry to the hash table 99 * 100 * @param hashTable hash table to add the entry to 101 * @param entry entry to add 102 * 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 105 * should retry. 106 * 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 110 * 111 * @internal 112 */ 113IX_ETH_DB_PUBLIC IxEthDBStatus ixEthDBAddHashEntry(HashTable *hashTable, void *entry) 114{ 115 UINT32 hashValue = hashTable->entryHashFunction(entry); 116 UINT32 bucketIndex = hashValue % hashTable->numBuckets; 117 HashNode *bucket = hashTable->hashBuckets[bucketIndex]; 118 HashNode *newNode; 119 120 LockStack locks; 121 122 INIT_STACK(&locks); 123 124 /* lock bucket */ 125 PUSH_LOCK(&locks, &hashTable->bucketLocks[bucketIndex]); 126 127 /* lock insertion element (first in chain), if any */ 128 if (bucket != NULL) 129 { 130 PUSH_LOCK(&locks, &bucket->lock); 131 } 132 133 /* get new node */ 134 newNode = ixEthDBAllocHashNode(); 135 136 if (newNode == NULL) 137 { 138 /* unlock everything */ 139 UNROLL_STACK(&locks); 140 141 return IX_ETH_DB_NOMEM; 142 } 143 144 /* init lock - note that mutexes are unlocked after MutexInit */ 145 ixOsalFastMutexInit(&newNode->lock); 146 147 /* populate new link */ 148 newNode->data = entry; 149 150 /* add to bucket */ 151 newNode->next = bucket; 152 hashTable->hashBuckets[bucketIndex] = newNode; 153 154 /* unlock bucket and insertion point */ 155 UNROLL_STACK(&locks); 156 157 return IX_ETH_DB_SUCCESS; 158} 159 160/** 161 * @brief removes an entry from the hashtable 162 * 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 166 * 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. 172 * 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 176 * 177 * @internal 178 */ 179IxEthDBStatus ixEthDBRemoveHashEntry(HashTable *hashTable, int keyType, void *reference) 180{ 181 UINT32 hashValue = hashTable->entryHashFunction(reference); 182 UINT32 bucketIndex = hashValue % hashTable->numBuckets; 183 HashNode *node = hashTable->hashBuckets[bucketIndex]; 184 HashNode *previousNode = NULL; 185 186 LockStack locks; 187 188 INIT_STACK(&locks); 189 190 while (node != NULL) 191 { 192 /* try to lock node */ 193 PUSH_LOCK(&locks, &node->lock); 194 195 if (hashTable->matchFunctions[keyType](reference, node->data)) 196 { 197 /* found entry */ 198 if (node->next != NULL) 199 { 200 PUSH_LOCK(&locks, &node->next->lock); 201 } 202 203 if (previousNode == NULL) 204 { 205 /* node is head of chain */ 206 PUSH_LOCK(&locks, &hashTable->bucketLocks[bucketIndex]); 207 208 hashTable->hashBuckets[bucketIndex] = node->next; 209 210 POP_LOCK(&locks); 211 } 212 else 213 { 214 /* relink */ 215 previousNode->next = node->next; 216 } 217 218 UNROLL_STACK(&locks); 219 220 /* free node */ 221 hashTable->freeFunction(node->data); 222 ixEthDBFreeHashNode(node); 223 224 return IX_ETH_DB_SUCCESS; 225 } 226 else 227 { 228 if (previousNode != NULL) 229 { 230 /* unlock previous node */ 231 SHIFT_STACK(&locks); 232 } 233 234 /* advance to next element in chain */ 235 previousNode = node; 236 node = node->next; 237 } 238 } 239 240 UNROLL_STACK(&locks); 241 242 /* not found */ 243 return IX_ETH_DB_NO_SUCH_ADDR; 244} 245 246/** 247 * @brief retrieves an entry from the hash table 248 * 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 253 * is placed 254 * 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(). 260 * 261 * @see ixEthDBReleaseHashNode() 262 * 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 267 * 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 271 * 272 * @internal 273 */ 274IxEthDBStatus ixEthDBSearchHashEntry(HashTable *hashTable, int keyType, void *reference, HashNode **searchResult) 275{ 276 UINT32 hashValue; 277 HashNode *node; 278 279 hashValue = hashTable->entryHashFunction(reference); 280 node = hashTable->hashBuckets[hashValue % hashTable->numBuckets]; 281 282 while (node != NULL) 283 { 284 TRY_LOCK(&node->lock); 285 286 if (hashTable->matchFunctions[keyType](reference, node->data)) 287 { 288 *searchResult = node; 289 290 return IX_ETH_DB_SUCCESS; 291 } 292 else 293 { 294 UNLOCK(&node->lock); 295 296 node = node->next; 297 } 298 } 299 300 /* not found */ 301 return IX_ETH_DB_NO_SUCH_ADDR; 302} 303 304/** 305 * @brief reports the existence of an entry in the hash table 306 * 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 310 * 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. 314 * 315 * @see ixEthDBReleaseHashNode() 316 * 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 321 * 322 * @internal 323 */ 324IxEthDBStatus ixEthDBPeekHashEntry(HashTable *hashTable, int keyType, void *reference) 325{ 326 UINT32 hashValue; 327 HashNode *node; 328 329 hashValue = hashTable->entryHashFunction(reference); 330 node = hashTable->hashBuckets[hashValue % hashTable->numBuckets]; 331 332 while (node != NULL) 333 { 334 TRY_LOCK(&node->lock); 335 336 if (hashTable->matchFunctions[keyType](reference, node->data)) 337 { 338 UNLOCK(&node->lock); 339 340 return IX_ETH_DB_SUCCESS; 341 } 342 else 343 { 344 UNLOCK(&node->lock); 345 346 node = node->next; 347 } 348 } 349 350 /* not found */ 351 return IX_ETH_DB_NO_SUCH_ADDR; 352} 353 354/** 355 * @brief releases the write access lock 356 * 357 * @pre the node should have been obtained via @ref ixEthDBSearchHashEntry() 358 * 359 * @see ixEthDBSearchHashEntry() 360 * 361 * @internal 362 */ 363void ixEthDBReleaseHashNode(HashNode *node) 364{ 365 UNLOCK(&node->lock); 366} 367 368/** 369 * @brief initializes a hash iterator 370 * 371 * @param hashTable hash table to be iterated 372 * @param iterator iterator object 373 * 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, 382 * must be called. 383 * 384 * @see ixEthDBReleaseHashIterator() 385 * 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 390 * should retry 391 * 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 394 * 395 * @internal 396 */ 397IxEthDBStatus ixEthDBInitHashIterator(HashTable *hashTable, HashIterator *iterator) 398{ 399 iterator->bucketIndex = 0; 400 iterator->node = NULL; 401 iterator->previousNode = NULL; 402 403 return ixEthDBIncrementHashIterator(hashTable, iterator); 404} 405 406/** 407 * @brief releases the write access locks of the iterator nodes 408 * 409 * @warning use of this function is required only when the caller terminates an iteration 410 * before reaching the end of the table 411 * 412 * @see ixEthDBInitHashIterator() 413 * @see ixEthDBIncrementHashIterator() 414 * 415 * @param iterator iterator whose node(s) should be unlocked 416 * 417 * @internal 418 */ 419void ixEthDBReleaseHashIterator(HashIterator *iterator) 420{ 421 if (iterator->previousNode != NULL) 422 { 423 UNLOCK(&iterator->previousNode->lock); 424 } 425 426 if (iterator->node != NULL) 427 { 428 UNLOCK(&iterator->node->lock); 429 } 430} 431 432/** 433 * @brief incremenents an iterator so that it points to the next valid entry of the table 434 * (if any) 435 * 436 * @param hashTable hash table to iterate 437 * @param iterator iterator object 438 * 439 * @pre the iterator object must be initialized using @ref ixEthDBInitHashIterator() 440 * 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, 449 * must be called. 450 * Is is guaranteed that no other thread can remove or change the iterated entry until 451 * the iterator is incremented successfully. 452 * 453 * @see ixEthDBReleaseHashIterator() 454 * 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 459 * should retry 460 * 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 463 * 464 * @internal 465 */ 466IxEthDBStatus ixEthDBIncrementHashIterator(HashTable *hashTable, HashIterator *iterator) 467{ 468 /* unless iterator is just initialized... */ 469 if (iterator->node != NULL) 470 { 471 /* try next in chain */ 472 if (iterator->node->next != NULL) 473 { 474 TRY_LOCK(&iterator->node->next->lock); 475 476 if (iterator->previousNode != NULL) 477 { 478 UNLOCK(&iterator->previousNode->lock); 479 } 480 481 iterator->previousNode = iterator->node; 482 iterator->node = iterator->node->next; 483 484 return IX_ETH_DB_SUCCESS; 485 } 486 else 487 { 488 /* last in chain, prepare for next bucket */ 489 iterator->bucketIndex++; 490 } 491 } 492 493 /* try next used bucket */ 494 for (; iterator->bucketIndex < hashTable->numBuckets ; iterator->bucketIndex++) 495 { 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))) 501 { 502 /* preload next cache line (2 cache line ahead) */ 503 nodePtr += IX_ETHDB_BUCKETPTR_AHEAD; 504 __asm__ ("pld [%0];\n": : "r" (nodePtr)); 505 } 506#endif 507 if (node != NULL) 508 { 509 TRY_LOCK(&node->lock); 510 511 /* unlock last one or two nodes in the previous chain */ 512 if (iterator->node != NULL) 513 { 514 UNLOCK(&iterator->node->lock); 515 516 if (iterator->previousNode != NULL) 517 { 518 UNLOCK(&iterator->previousNode->lock); 519 } 520 } 521 522 /* redirect iterator */ 523 iterator->previousNode = NULL; 524 iterator->node = node; 525 526 return IX_ETH_DB_SUCCESS; 527 } 528 } 529 530 /* could not advance iterator */ 531 if (iterator->node != NULL) 532 { 533 UNLOCK(&iterator->node->lock); 534 535 if (iterator->previousNode != NULL) 536 { 537 UNLOCK(&iterator->previousNode->lock); 538 } 539 540 iterator->node = NULL; 541 } 542 543 return IX_ETH_DB_END; 544} 545 546/** 547 * @brief removes an entry pointed by an iterator 548 * 549 * @param hashTable iterated hash table 550 * @param iterator iterator object 551 * 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. 559 * 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 562 * should retry 563 * 564 * @internal 565 */ 566IxEthDBStatus ixEthDBRemoveEntryAtHashIterator(HashTable *hashTable, HashIterator *iterator) 567{ 568 HashIterator nextIteratorPos; 569 LockStack locks; 570 571 INIT_STACK(&locks); 572 573 /* set initial bucket index for next position */ 574 nextIteratorPos.bucketIndex = iterator->bucketIndex; 575 576 /* compute iterator position before removing anything and lock ahead */ 577 if (iterator->node->next != NULL) 578 { 579 PUSH_LOCK(&locks, &iterator->node->next->lock); 580 581 /* reposition on the next node in the chain */ 582 nextIteratorPos.node = iterator->node->next; 583 nextIteratorPos.previousNode = iterator->previousNode; 584 } 585 else 586 { 587 /* try next chain - don't know yet if we'll find anything */ 588 nextIteratorPos.node = NULL; 589 590 /* if we find something it's a chain head */ 591 nextIteratorPos.previousNode = NULL; 592 593 /* browse up in the buckets to find a non-null chain */ 594 while (++nextIteratorPos.bucketIndex < hashTable->numBuckets) 595 { 596 nextIteratorPos.node = hashTable->hashBuckets[nextIteratorPos.bucketIndex]; 597 598 if (nextIteratorPos.node != NULL) 599 { 600 /* found a non-empty chain, try to lock head */ 601 PUSH_LOCK(&locks, &nextIteratorPos.node->lock); 602 603 break; 604 } 605 } 606 } 607 608 /* restore links over the to-be-deleted item */ 609 if (iterator->previousNode == NULL) 610 { 611 /* first in chain, lock bucket */ 612 PUSH_LOCK(&locks, &hashTable->bucketLocks[iterator->bucketIndex]); 613 614 hashTable->hashBuckets[iterator->bucketIndex] = iterator->node->next; 615 616 POP_LOCK(&locks); 617 } 618 else 619 { 620 /* relink */ 621 iterator->previousNode->next = iterator->node->next; 622 623 /* unlock last remaining node in current chain when moving between chains */ 624 if (iterator->node->next == NULL) 625 { 626 UNLOCK(&iterator->previousNode->lock); 627 } 628 } 629 630 /* delete entry */ 631 hashTable->freeFunction(iterator->node->data); 632 ixEthDBFreeHashNode(iterator->node); 633 634 /* reposition iterator */ 635 *iterator = nextIteratorPos; 636 637 return IX_ETH_DB_SUCCESS; 638} 639 640/** 641 * @} 642 */ 643