IxEthDBHashtable.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  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. #include "IxEthDB_p.h"
  45. #include "IxEthDBLocks_p.h"
  46. /**
  47. * @addtogroup EthDB
  48. *
  49. * @{
  50. */
  51. /**
  52. * @brief initializes a hash table object
  53. *
  54. * @param hashTable uninitialized hash table structure
  55. * @param numBuckets number of buckets to use
  56. * @param entryHashFunction hash function used
  57. * to hash entire hash node data block (for adding)
  58. * @param matchFunctions array of match functions, indexed on type,
  59. * used to differentiate records with the same hash value
  60. * @param freeFunction function used to free node data blocks
  61. *
  62. * Initializes the given hash table object.
  63. *
  64. * @internal
  65. */
  66. void ixEthDBInitHash(HashTable *hashTable,
  67. UINT32 numBuckets,
  68. HashFunction entryHashFunction,
  69. MatchFunction *matchFunctions,
  70. FreeFunction freeFunction)
  71. {
  72. UINT32 bucketIndex;
  73. UINT32 hashSize = numBuckets * sizeof(HashNode *);
  74. /* entry hashing, matching and freeing methods */
  75. hashTable->entryHashFunction = entryHashFunction;
  76. hashTable->matchFunctions = matchFunctions;
  77. hashTable->freeFunction = freeFunction;
  78. /* buckets */
  79. hashTable->numBuckets = numBuckets;
  80. /* set to 0 all buckets */
  81. memset(hashTable->hashBuckets, 0, hashSize);
  82. /* init bucket locks - note that initially all mutexes are unlocked after MutexInit()*/
  83. for (bucketIndex = 0 ; bucketIndex < numBuckets ; bucketIndex++)
  84. {
  85. ixOsalFastMutexInit(&hashTable->bucketLocks[bucketIndex]);
  86. }
  87. }
  88. /**
  89. * @brief adds an entry to the hash table
  90. *
  91. * @param hashTable hash table to add the entry to
  92. * @param entry entry to add
  93. *
  94. * The entry will be hashed using the entry hashing function and added to the
  95. * hash table, unless a locking blockage occurs, in which case the caller
  96. * should retry.
  97. *
  98. * @retval IX_ETH_DB_SUCCESS if adding <i>entry</i> has succeeded
  99. * @retval IX_ETH_DB_NOMEM if there's no memory left in the hash node pool
  100. * @retval IX_ETH_DB_BUSY if there's a locking failure on the insertion path
  101. *
  102. * @internal
  103. */
  104. IX_ETH_DB_PUBLIC IxEthDBStatus ixEthDBAddHashEntry(HashTable *hashTable, void *entry)
  105. {
  106. UINT32 hashValue = hashTable->entryHashFunction(entry);
  107. UINT32 bucketIndex = hashValue % hashTable->numBuckets;
  108. HashNode *bucket = hashTable->hashBuckets[bucketIndex];
  109. HashNode *newNode;
  110. LockStack locks;
  111. INIT_STACK(&locks);
  112. /* lock bucket */
  113. PUSH_LOCK(&locks, &hashTable->bucketLocks[bucketIndex]);
  114. /* lock insertion element (first in chain), if any */
  115. if (bucket != NULL)
  116. {
  117. PUSH_LOCK(&locks, &bucket->lock);
  118. }
  119. /* get new node */
  120. newNode = ixEthDBAllocHashNode();
  121. if (newNode == NULL)
  122. {
  123. /* unlock everything */
  124. UNROLL_STACK(&locks);
  125. return IX_ETH_DB_NOMEM;
  126. }
  127. /* init lock - note that mutexes are unlocked after MutexInit */
  128. ixOsalFastMutexInit(&newNode->lock);
  129. /* populate new link */
  130. newNode->data = entry;
  131. /* add to bucket */
  132. newNode->next = bucket;
  133. hashTable->hashBuckets[bucketIndex] = newNode;
  134. /* unlock bucket and insertion point */
  135. UNROLL_STACK(&locks);
  136. return IX_ETH_DB_SUCCESS;
  137. }
  138. /**
  139. * @brief removes an entry from the hashtable
  140. *
  141. * @param hashTable hash table to remove entry from
  142. * @param keyType type of record key used for matching
  143. * @param reference reference key used to identify the entry
  144. *
  145. * The reference key will be hashed using the key hashing function,
  146. * the entry is searched using the hashed value and then examined
  147. * against the reference entry using the match function. A positive
  148. * match will trigger the deletion of the entry.
  149. * Locking failures are reported and the caller should retry.
  150. *
  151. * @retval IX_ETH_DB_SUCCESS if the removal was successful
  152. * @retval IX_ETH_DB_NO_SUCH_ADDR if the entry was not found
  153. * @retval IX_ETH_DB_BUSY if a locking failure occured during the process
  154. *
  155. * @internal
  156. */
  157. IxEthDBStatus ixEthDBRemoveHashEntry(HashTable *hashTable, int keyType, void *reference)
  158. {
  159. UINT32 hashValue = hashTable->entryHashFunction(reference);
  160. UINT32 bucketIndex = hashValue % hashTable->numBuckets;
  161. HashNode *node = hashTable->hashBuckets[bucketIndex];
  162. HashNode *previousNode = NULL;
  163. LockStack locks;
  164. INIT_STACK(&locks);
  165. while (node != NULL)
  166. {
  167. /* try to lock node */
  168. PUSH_LOCK(&locks, &node->lock);
  169. if (hashTable->matchFunctions[keyType](reference, node->data))
  170. {
  171. /* found entry */
  172. if (node->next != NULL)
  173. {
  174. PUSH_LOCK(&locks, &node->next->lock);
  175. }
  176. if (previousNode == NULL)
  177. {
  178. /* node is head of chain */
  179. PUSH_LOCK(&locks, &hashTable->bucketLocks[bucketIndex]);
  180. hashTable->hashBuckets[bucketIndex] = node->next;
  181. POP_LOCK(&locks);
  182. }
  183. else
  184. {
  185. /* relink */
  186. previousNode->next = node->next;
  187. }
  188. UNROLL_STACK(&locks);
  189. /* free node */
  190. hashTable->freeFunction(node->data);
  191. ixEthDBFreeHashNode(node);
  192. return IX_ETH_DB_SUCCESS;
  193. }
  194. else
  195. {
  196. if (previousNode != NULL)
  197. {
  198. /* unlock previous node */
  199. SHIFT_STACK(&locks);
  200. }
  201. /* advance to next element in chain */
  202. previousNode = node;
  203. node = node->next;
  204. }
  205. }
  206. UNROLL_STACK(&locks);
  207. /* not found */
  208. return IX_ETH_DB_NO_SUCH_ADDR;
  209. }
  210. /**
  211. * @brief retrieves an entry from the hash table
  212. *
  213. * @param hashTable hash table to perform the search into
  214. * @param reference search key (a MAC address)
  215. * @param keyType type of record key used for matching
  216. * @param searchResult pointer where a reference to the located hash node
  217. * is placed
  218. *
  219. * Searches the entry with the same key as <i>reference</i> and places the
  220. * pointer to the resulting node in <i>searchResult</i>.
  221. * An implicit write access lock is granted after a search, which gives the
  222. * caller the opportunity to modify the entry.
  223. * Access should be released as soon as possible using @ref ixEthDBReleaseHashNode().
  224. *
  225. * @see ixEthDBReleaseHashNode()
  226. *
  227. * @retval IX_ETH_DB_SUCCESS if the search was completed successfully
  228. * @retval IX_ETH_DB_NO_SUCH_ADDRESS if no entry with the given key was found
  229. * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case
  230. * the caller should retry
  231. *
  232. * @warning unless the return value is <b>IX_ETH_DB_SUCCESS</b> the searchResult
  233. * location is NOT modified and therefore using a NULL comparison test when the
  234. * value was not properly initialized would be an error
  235. *
  236. * @internal
  237. */
  238. IxEthDBStatus ixEthDBSearchHashEntry(HashTable *hashTable, int keyType, void *reference, HashNode **searchResult)
  239. {
  240. UINT32 hashValue;
  241. HashNode *node;
  242. hashValue = hashTable->entryHashFunction(reference);
  243. node = hashTable->hashBuckets[hashValue % hashTable->numBuckets];
  244. while (node != NULL)
  245. {
  246. TRY_LOCK(&node->lock);
  247. if (hashTable->matchFunctions[keyType](reference, node->data))
  248. {
  249. *searchResult = node;
  250. return IX_ETH_DB_SUCCESS;
  251. }
  252. else
  253. {
  254. UNLOCK(&node->lock);
  255. node = node->next;
  256. }
  257. }
  258. /* not found */
  259. return IX_ETH_DB_NO_SUCH_ADDR;
  260. }
  261. /**
  262. * @brief reports the existence of an entry in the hash table
  263. *
  264. * @param hashTable hash table to perform the search into
  265. * @param reference search key (a MAC address)
  266. * @param keyType type of record key used for matching
  267. *
  268. * Searches the entry with the same key as <i>reference</i>.
  269. * No implicit write access lock is granted after a search, hence the
  270. * caller cannot access or modify the entry. The result is only temporary.
  271. *
  272. * @see ixEthDBReleaseHashNode()
  273. *
  274. * @retval IX_ETH_DB_SUCCESS if the search was completed successfully
  275. * @retval IX_ETH_DB_NO_SUCH_ADDRESS if no entry with the given key was found
  276. * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case
  277. * the caller should retry
  278. *
  279. * @internal
  280. */
  281. IxEthDBStatus ixEthDBPeekHashEntry(HashTable *hashTable, int keyType, void *reference)
  282. {
  283. UINT32 hashValue;
  284. HashNode *node;
  285. hashValue = hashTable->entryHashFunction(reference);
  286. node = hashTable->hashBuckets[hashValue % hashTable->numBuckets];
  287. while (node != NULL)
  288. {
  289. TRY_LOCK(&node->lock);
  290. if (hashTable->matchFunctions[keyType](reference, node->data))
  291. {
  292. UNLOCK(&node->lock);
  293. return IX_ETH_DB_SUCCESS;
  294. }
  295. else
  296. {
  297. UNLOCK(&node->lock);
  298. node = node->next;
  299. }
  300. }
  301. /* not found */
  302. return IX_ETH_DB_NO_SUCH_ADDR;
  303. }
  304. /**
  305. * @brief releases the write access lock
  306. *
  307. * @pre the node should have been obtained via @ref ixEthDBSearchHashEntry()
  308. *
  309. * @see ixEthDBSearchHashEntry()
  310. *
  311. * @internal
  312. */
  313. void ixEthDBReleaseHashNode(HashNode *node)
  314. {
  315. UNLOCK(&node->lock);
  316. }
  317. /**
  318. * @brief initializes a hash iterator
  319. *
  320. * @param hashTable hash table to be iterated
  321. * @param iterator iterator object
  322. *
  323. * If the initialization is successful the iterator will point to the
  324. * first hash table record (if any).
  325. * Testing if the iterator has not passed the end of the table should be
  326. * done using the IS_ITERATOR_VALID(iteratorPtr) macro.
  327. * An implicit write access lock is granted on the entry pointed by the iterator.
  328. * The access is automatically revoked when the iterator is incremented.
  329. * If the caller decides to terminate the iteration before the end of the table is
  330. * passed then the manual access release method, @ref ixEthDBReleaseHashIterator,
  331. * must be called.
  332. *
  333. * @see ixEthDBReleaseHashIterator()
  334. *
  335. * @retval IX_ETH_DB_SUCCESS if initialization was successful and the iterator points
  336. * to the first valid table node
  337. * @retval IX_ETH_DB_FAIL if the table is empty
  338. * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller
  339. * should retry
  340. *
  341. * @warning do not use ixEthDBReleaseHashNode() on entries pointed by the iterator, as this
  342. * might place the database in a permanent invalid lock state
  343. *
  344. * @internal
  345. */
  346. IxEthDBStatus ixEthDBInitHashIterator(HashTable *hashTable, HashIterator *iterator)
  347. {
  348. iterator->bucketIndex = 0;
  349. iterator->node = NULL;
  350. iterator->previousNode = NULL;
  351. return ixEthDBIncrementHashIterator(hashTable, iterator);
  352. }
  353. /**
  354. * @brief releases the write access locks of the iterator nodes
  355. *
  356. * @warning use of this function is required only when the caller terminates an iteration
  357. * before reaching the end of the table
  358. *
  359. * @see ixEthDBInitHashIterator()
  360. * @see ixEthDBIncrementHashIterator()
  361. *
  362. * @param iterator iterator whose node(s) should be unlocked
  363. *
  364. * @internal
  365. */
  366. void ixEthDBReleaseHashIterator(HashIterator *iterator)
  367. {
  368. if (iterator->previousNode != NULL)
  369. {
  370. UNLOCK(&iterator->previousNode->lock);
  371. }
  372. if (iterator->node != NULL)
  373. {
  374. UNLOCK(&iterator->node->lock);
  375. }
  376. }
  377. /**
  378. * @brief incremenents an iterator so that it points to the next valid entry of the table
  379. * (if any)
  380. *
  381. * @param hashTable hash table to iterate
  382. * @param iterator iterator object
  383. *
  384. * @pre the iterator object must be initialized using @ref ixEthDBInitHashIterator()
  385. *
  386. * If the increment operation is successful the iterator will point to the
  387. * next hash table record (if any).
  388. * Testing if the iterator has not passed the end of the table should be
  389. * done using the IS_ITERATOR_VALID(iteratorPtr) macro.
  390. * An implicit write access lock is granted on the entry pointed by the iterator.
  391. * The access is automatically revoked when the iterator is re-incremented.
  392. * If the caller decides to terminate the iteration before the end of the table is
  393. * passed then the manual access release method, @ref ixEthDBReleaseHashIterator,
  394. * must be called.
  395. * Is is guaranteed that no other thread can remove or change the iterated entry until
  396. * the iterator is incremented successfully.
  397. *
  398. * @see ixEthDBReleaseHashIterator()
  399. *
  400. * @retval IX_ETH_DB_SUCCESS if the operation was successful and the iterator points
  401. * to the next valid table node
  402. * @retval IX_ETH_DB_FAIL if the iterator has passed the end of the table
  403. * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller
  404. * should retry
  405. *
  406. * @warning do not use ixEthDBReleaseHashNode() on entries pointed by the iterator, as this
  407. * might place the database in a permanent invalid lock state
  408. *
  409. * @internal
  410. */
  411. IxEthDBStatus ixEthDBIncrementHashIterator(HashTable *hashTable, HashIterator *iterator)
  412. {
  413. /* unless iterator is just initialized... */
  414. if (iterator->node != NULL)
  415. {
  416. /* try next in chain */
  417. if (iterator->node->next != NULL)
  418. {
  419. TRY_LOCK(&iterator->node->next->lock);
  420. if (iterator->previousNode != NULL)
  421. {
  422. UNLOCK(&iterator->previousNode->lock);
  423. }
  424. iterator->previousNode = iterator->node;
  425. iterator->node = iterator->node->next;
  426. return IX_ETH_DB_SUCCESS;
  427. }
  428. else
  429. {
  430. /* last in chain, prepare for next bucket */
  431. iterator->bucketIndex++;
  432. }
  433. }
  434. /* try next used bucket */
  435. for (; iterator->bucketIndex < hashTable->numBuckets ; iterator->bucketIndex++)
  436. {
  437. HashNode **nodePtr = &(hashTable->hashBuckets[iterator->bucketIndex]);
  438. HashNode *node = *nodePtr;
  439. #if (CPU!=SIMSPARCSOLARIS) && !defined (__wince)
  440. if (((iterator->bucketIndex & IX_ETHDB_BUCKET_INDEX_MASK) == 0) &&
  441. (iterator->bucketIndex < (hashTable->numBuckets - IX_ETHDB_BUCKETPTR_AHEAD)))
  442. {
  443. /* preload next cache line (2 cache line ahead) */
  444. nodePtr += IX_ETHDB_BUCKETPTR_AHEAD;
  445. __asm__ ("pld [%0];\n": : "r" (nodePtr));
  446. }
  447. #endif
  448. if (node != NULL)
  449. {
  450. TRY_LOCK(&node->lock);
  451. /* unlock last one or two nodes in the previous chain */
  452. if (iterator->node != NULL)
  453. {
  454. UNLOCK(&iterator->node->lock);
  455. if (iterator->previousNode != NULL)
  456. {
  457. UNLOCK(&iterator->previousNode->lock);
  458. }
  459. }
  460. /* redirect iterator */
  461. iterator->previousNode = NULL;
  462. iterator->node = node;
  463. return IX_ETH_DB_SUCCESS;
  464. }
  465. }
  466. /* could not advance iterator */
  467. if (iterator->node != NULL)
  468. {
  469. UNLOCK(&iterator->node->lock);
  470. if (iterator->previousNode != NULL)
  471. {
  472. UNLOCK(&iterator->previousNode->lock);
  473. }
  474. iterator->node = NULL;
  475. }
  476. return IX_ETH_DB_END;
  477. }
  478. /**
  479. * @brief removes an entry pointed by an iterator
  480. *
  481. * @param hashTable iterated hash table
  482. * @param iterator iterator object
  483. *
  484. * Removes the entry currently pointed by the iterator and repositions the iterator
  485. * on the next valid entry (if any). Handles locking issues automatically and
  486. * implicitely grants write access lock to the new pointed entry.
  487. * Failures due to concurrent threads having write access locks in the same region
  488. * preserve the state of the database and the iterator object, leaving the caller
  489. * free to retry without loss of access. It is guaranteed that only the thread owning
  490. * the iterator can remove the object pointed by the iterator.
  491. *
  492. * @retval IX_ETH_DB_SUCCESS if removal has succeeded
  493. * @retval IX_ETH_DB_BUSY if a locking failure has occured, in which case the caller
  494. * should retry
  495. *
  496. * @internal
  497. */
  498. IxEthDBStatus ixEthDBRemoveEntryAtHashIterator(HashTable *hashTable, HashIterator *iterator)
  499. {
  500. HashIterator nextIteratorPos;
  501. LockStack locks;
  502. INIT_STACK(&locks);
  503. /* set initial bucket index for next position */
  504. nextIteratorPos.bucketIndex = iterator->bucketIndex;
  505. /* compute iterator position before removing anything and lock ahead */
  506. if (iterator->node->next != NULL)
  507. {
  508. PUSH_LOCK(&locks, &iterator->node->next->lock);
  509. /* reposition on the next node in the chain */
  510. nextIteratorPos.node = iterator->node->next;
  511. nextIteratorPos.previousNode = iterator->previousNode;
  512. }
  513. else
  514. {
  515. /* try next chain - don't know yet if we'll find anything */
  516. nextIteratorPos.node = NULL;
  517. /* if we find something it's a chain head */
  518. nextIteratorPos.previousNode = NULL;
  519. /* browse up in the buckets to find a non-null chain */
  520. while (++nextIteratorPos.bucketIndex < hashTable->numBuckets)
  521. {
  522. nextIteratorPos.node = hashTable->hashBuckets[nextIteratorPos.bucketIndex];
  523. if (nextIteratorPos.node != NULL)
  524. {
  525. /* found a non-empty chain, try to lock head */
  526. PUSH_LOCK(&locks, &nextIteratorPos.node->lock);
  527. break;
  528. }
  529. }
  530. }
  531. /* restore links over the to-be-deleted item */
  532. if (iterator->previousNode == NULL)
  533. {
  534. /* first in chain, lock bucket */
  535. PUSH_LOCK(&locks, &hashTable->bucketLocks[iterator->bucketIndex]);
  536. hashTable->hashBuckets[iterator->bucketIndex] = iterator->node->next;
  537. POP_LOCK(&locks);
  538. }
  539. else
  540. {
  541. /* relink */
  542. iterator->previousNode->next = iterator->node->next;
  543. /* unlock last remaining node in current chain when moving between chains */
  544. if (iterator->node->next == NULL)
  545. {
  546. UNLOCK(&iterator->previousNode->lock);
  547. }
  548. }
  549. /* delete entry */
  550. hashTable->freeFunction(iterator->node->data);
  551. ixEthDBFreeHashNode(iterator->node);
  552. /* reposition iterator */
  553. *iterator = nextIteratorPos;
  554. return IX_ETH_DB_SUCCESS;
  555. }
  556. /**
  557. * @}
  558. */