Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

792 lines
22 KiB

  1. /*++
  2. Copyright (c) 1998-2002 Microsoft Corporation
  3. Module Name:
  4. opaqueid.c
  5. Abstract:
  6. This module implements the opaque ID table. The ID table is
  7. implemented as a two-level array.
  8. The first level is an array of pointers to the second-level arrays.
  9. This first-level array is not growable, but its size is controlled
  10. by a registry key.
  11. The second level is an array of ID_TABLE_ENTRY structures. These
  12. structures contain a cyclic (to detect stale IDs) and caller-supplied
  13. context value.
  14. The data structures may be diagrammed as follows:
  15. g_FirstLevelTable[i]
  16. |
  17. | +-----+
  18. | | | +-----+-----+-----+----+...--+-----+-----+
  19. | | | | ID_ | ID_ | | ID_ |
  20. +-->| *-------->| TABLE_ | TABLE_ | | TABLE_ |
  21. | | | ENTRY | ENTRY | | ENTRY |
  22. | | +-----+-----+-----+----+--...+-----+-----+
  23. +-----+
  24. | | +-----+-----+-----+----+...--+-----+-----+
  25. | | | ID_ | ID_ | | ID_ |
  26. | *-------->| TABLE_ | TABLE_ | | TABLE_ |
  27. | | | ENTRY | ENTRY | | ENTRY |
  28. | | +-----+-----+-----+----+--...+-----+-----+
  29. +-----+
  30. | .
  31. | .
  32. . .
  33. . |
  34. . |
  35. +-----+
  36. | |
  37. | |
  38. | / |
  39. | |
  40. | |
  41. +-----+
  42. | |
  43. | |
  44. | / |
  45. | |
  46. | |
  47. +-----+
  48. Because the lock protecting the single, global table of opaque IDs
  49. turned out to be a major scalability bottleneck on SMP machines, we
  50. now maintain per-processor subtables of opaque IDs. In addition, each
  51. ID_TABLE_ENTRY itself has a small lock that protects the fields inside
  52. it. This means we usually don't need to take the per-table spinlock.
  53. The per-table lock is only used when we grow the second-level table,
  54. in which case we have to protect the first-level table index and its
  55. pointer to the new second-level table.
  56. Note that all free ID_TABLE_ENTRY structures are kept on a single
  57. (global) free list. Whenever a new ID needs to be allocated, the free
  58. list is consulted. If it's not empty, an item is popped from the list
  59. and used. If the list is empty, then new space must be allocated. This
  60. will involve the allocation of a new second-level array.
  61. A HTTP_OPAQUE_ID is opaque at user-mode. Internally, it consists of 5
  62. fields:
  63. 1) A processor number the ID was allocated on. This tells which
  64. per-processor table to free the ID.
  65. 2) An index into the first-level array.
  66. 3) An index into the second-level array referenced by the
  67. first-level index.
  68. 4) A cyclic for the ID, used to detect stale IDs.
  69. 5) An opaque ID type, used to guard against misuse of opaque IDs.
  70. See the OPAQUE_ID_INTERNAL structure definition (opaqueidp.h) for details.
  71. Note that most of the routines in this module assume they are called
  72. at PASSIVE_LEVEL.
  73. Author:
  74. Keith Moore (keithmo) 05-Aug-1998
  75. Revision History:
  76. --*/
  77. #include "precomp.h"
  78. //
  79. // Private globals.
  80. //
  81. DECLSPEC_ALIGN(UL_CACHE_LINE)
  82. UL_ALIGNED_OPAQUE_ID_TABLE g_UlOpaqueIdTable[MAXIMUM_PROCESSORS];
  83. #ifdef OPAQUE_ID_INSTRUMENTATION
  84. LONGLONG g_NumberOfTotalGets = 0;
  85. LONGLONG g_NumberOfSuccessfulGets = 0;
  86. #endif
  87. #ifdef ALLOC_PRAGMA
  88. #pragma alloc_text( INIT, UlInitializeOpaqueIdTable )
  89. #pragma alloc_text( PAGE, UlTerminateOpaqueIdTable )
  90. #endif // ALLOC_PRAGMA
  91. #if 0
  92. NOT PAGEABLE -- UlAllocateOpaqueId
  93. NOT PAGEABLE -- UlFreeOpaqueId
  94. NOT PAGEABLE -- UlGetObjectFromOpaqueId
  95. #endif
  96. //
  97. // Public functions.
  98. //
  99. /***************************************************************************++
  100. Routine Description:
  101. Performs global initialization of the opaque ID package.
  102. Arguments:
  103. None
  104. Return Value:
  105. NTSTATUS - Completion status.
  106. --***************************************************************************/
  107. NTSTATUS
  108. UlInitializeOpaqueIdTable(
  109. VOID
  110. )
  111. {
  112. PUL_OPAQUE_ID_TABLE pOpaqueIdTable;
  113. LONG i;
  114. //
  115. // Allocate the first-level opaque ID table arrry.
  116. //
  117. for (i = 0; i < (LONG)g_UlNumberOfProcessors; i++)
  118. {
  119. pOpaqueIdTable = &g_UlOpaqueIdTable[i].OpaqueIdTable;
  120. RtlZeroMemory(
  121. pOpaqueIdTable,
  122. sizeof(UL_OPAQUE_ID_TABLE)
  123. );
  124. pOpaqueIdTable->FirstLevelTable = UL_ALLOCATE_ARRAY(
  125. NonPagedPool,
  126. PUL_OPAQUE_ID_TABLE_ENTRY,
  127. g_UlOpaqueIdTableSize,
  128. UL_OPAQUE_ID_TABLE_POOL_TAG
  129. );
  130. if (pOpaqueIdTable->FirstLevelTable != NULL)
  131. {
  132. //
  133. // Initialization.
  134. //
  135. InitializeSListHead( &pOpaqueIdTable->FreeOpaqueIdSListHead );
  136. UlInitializeSpinLock( &pOpaqueIdTable->Lock, "OpaqueIdTableLock" );
  137. pOpaqueIdTable->FirstLevelTableSize = g_UlOpaqueIdTableSize;
  138. pOpaqueIdTable->FirstLevelTableInUse = 0;
  139. pOpaqueIdTable->Processor = (UCHAR)i;
  140. //
  141. // Zero out the first-level table.
  142. //
  143. RtlZeroMemory(
  144. pOpaqueIdTable->FirstLevelTable,
  145. g_UlOpaqueIdTableSize * sizeof(PUL_OPAQUE_ID_TABLE_ENTRY)
  146. );
  147. }
  148. else
  149. {
  150. while (--i >= 0)
  151. {
  152. pOpaqueIdTable = &g_UlOpaqueIdTable[i].OpaqueIdTable;
  153. UL_FREE_POOL(
  154. pOpaqueIdTable->FirstLevelTable,
  155. UL_OPAQUE_ID_TABLE_POOL_TAG
  156. );
  157. pOpaqueIdTable->FirstLevelTable = NULL;
  158. }
  159. return STATUS_NO_MEMORY;
  160. }
  161. }
  162. return STATUS_SUCCESS;
  163. }
  164. /***************************************************************************++
  165. Routine Description:
  166. Performs global termination of the opaque ID package.
  167. Arguments:
  168. None
  169. Return Value:
  170. None
  171. --***************************************************************************/
  172. VOID
  173. UlTerminateOpaqueIdTable(
  174. VOID
  175. )
  176. {
  177. PUL_OPAQUE_ID_TABLE pOpaqueIdTable;
  178. ULONG i, j;
  179. for (i = 0; i < g_UlNumberOfProcessors; i++)
  180. {
  181. pOpaqueIdTable = &g_UlOpaqueIdTable[i].OpaqueIdTable;
  182. #ifdef OPAQUE_ID_INSTRUMENTATION
  183. ASSERT( pOpaqueIdTable->NumberOfAllocations ==
  184. pOpaqueIdTable->NumberOfFrees );
  185. #endif
  186. //
  187. // Free all allocated second-level tables.
  188. //
  189. for (j = 0; j < pOpaqueIdTable->FirstLevelTableInUse; j++)
  190. {
  191. ASSERT( pOpaqueIdTable->FirstLevelTable[j] != NULL );
  192. UL_FREE_POOL(
  193. pOpaqueIdTable->FirstLevelTable[j],
  194. UL_OPAQUE_ID_TABLE_POOL_TAG
  195. );
  196. }
  197. //
  198. // Free the first-level table.
  199. //
  200. if (pOpaqueIdTable->FirstLevelTable != NULL)
  201. {
  202. UL_FREE_POOL(
  203. pOpaqueIdTable->FirstLevelTable,
  204. UL_OPAQUE_ID_TABLE_POOL_TAG
  205. );
  206. }
  207. }
  208. }
  209. /***************************************************************************++
  210. Routine Description:
  211. Allocates a new opaque ID and associates it with the specified
  212. context. A new opaque ID takes a new slot in the opaque ID table.
  213. Arguments:
  214. pOpaqueId - Receives the newly allocated opaque ID if successful.
  215. OpaqueIdType - Supplies the opaque ID type to be associated with
  216. the opaque ID and associated object.
  217. pContext - Supplies the context to associate with the new opaque ID.
  218. Return Value:
  219. NTSTATUS - Completion status.
  220. --***************************************************************************/
  221. NTSTATUS
  222. UlAllocateOpaqueId(
  223. OUT PHTTP_OPAQUE_ID pOpaqueId,
  224. IN UL_OPAQUE_ID_TYPE OpaqueIdType,
  225. IN PVOID pContext
  226. )
  227. {
  228. PUL_OPAQUE_ID_TABLE pOpaqueIdTable;
  229. PUL_OPAQUE_ID_TABLE_ENTRY pEntry;
  230. PSLIST_ENTRY pListEntry;
  231. PUL_OPAQUE_ID_INTERNAL pInternalId;
  232. ULONG CurrentProcessor;
  233. ULONG CapturedFirstTableInUse;
  234. NTSTATUS Status = STATUS_SUCCESS;
  235. KIRQL OldIrql;
  236. //
  237. // Allocate a new opaque ID from the current processor table. We need
  238. // a new entry for each ID.
  239. //
  240. CurrentProcessor = KeGetCurrentProcessorNumber();
  241. pOpaqueIdTable = &g_UlOpaqueIdTable[CurrentProcessor].OpaqueIdTable;
  242. //
  243. // Loop, trying to allocate an item from the table.
  244. //
  245. do
  246. {
  247. //
  248. // Remember the first-level table index if we need to expand later.
  249. //
  250. CapturedFirstTableInUse =
  251. *((volatile LONG *) &pOpaqueIdTable->FirstLevelTableInUse);
  252. pListEntry = InterlockedPopEntrySList(
  253. &pOpaqueIdTable->FreeOpaqueIdSListHead
  254. );
  255. if (pListEntry != NULL)
  256. {
  257. //
  258. // The free list isn't empty, so we can just use this
  259. // entry. We'll calculate the indices for this entry
  260. // and initialize the entry.
  261. //
  262. pEntry = CONTAINING_RECORD(
  263. pListEntry,
  264. UL_OPAQUE_ID_TABLE_ENTRY,
  265. FreeListEntry
  266. );
  267. //
  268. // If the entry popped up is not ready, loop and try again.
  269. // This can happen if we get thread switched right after we
  270. // obtained the current processor number, and the remaining
  271. // of the code got executed on another processor. This would
  272. // mean we can't assume UlpExpandOpaqueIdTable will be completed
  273. // first before we issue the pop so we could end up having
  274. // entries that are not yet fully initialized.
  275. //
  276. if (pEntry->FirstIndex >= CapturedFirstTableInUse)
  277. {
  278. InterlockedPushEntrySList(
  279. &pOpaqueIdTable->FreeOpaqueIdSListHead,
  280. &pEntry->FreeListEntry
  281. );
  282. continue;
  283. }
  284. pInternalId = (PUL_OPAQUE_ID_INTERNAL) pOpaqueId;
  285. UlpAcquireOpaqueIdLock( &pEntry->Lock, &OldIrql );
  286. //
  287. // Processor and FirstIndex are ready to use.
  288. //
  289. pInternalId->Index = pEntry->Index;
  290. //
  291. // Re-compute SecondIndex because its corresponding field has
  292. // been overwritten by Cyclic and OpaqueIdType when the entry
  293. // is in use.
  294. //
  295. pInternalId->SecondIndex
  296. = (UCHAR) (pEntry - pOpaqueIdTable->FirstLevelTable[pEntry->FirstIndex]);
  297. //
  298. // Set the context associated with this entry.
  299. //
  300. pEntry->pContext = pContext;
  301. //
  302. // Update the cyclic and ID type of the entry.
  303. //
  304. pEntry->OpaqueIdCyclic = ++pEntry->EntryOpaqueIdCyclic;
  305. pEntry->OpaqueIdType = OpaqueIdType;
  306. pInternalId->Cyclic = pEntry->Cyclic;
  307. UlpReleaseOpaqueIdLock( &pEntry->Lock, OldIrql );
  308. #ifdef OPAQUE_ID_INSTRUMENTATION
  309. UlInterlockedIncrement64( &pOpaqueIdTable->NumberOfAllocations );
  310. #endif
  311. Status = STATUS_SUCCESS;
  312. break;
  313. }
  314. //
  315. // We only make it to this point if the free list is empty,
  316. // meaning we need to do some memory allocations before
  317. // we can continue. We'll put this off into a separate routine
  318. // to keep this one small (to avoid cache thrash). The realloc
  319. // routine returns STATUS_SUCCESS if it (or another thread)
  320. // managed to successfully reallocate the tables. Otherwise, it
  321. // returns a failure code.
  322. //
  323. Status = UlpExpandOpaqueIdTable(
  324. pOpaqueIdTable,
  325. CapturedFirstTableInUse
  326. );
  327. } while ( Status == STATUS_SUCCESS );
  328. return Status;
  329. }
  330. /***************************************************************************++
  331. Routine Description:
  332. Frees the specified opaque ID. This frees up the slot in the ID
  333. table as well.
  334. Arguments:
  335. OpaqueId - Supplies the opaque ID to free.
  336. OpaqueIdType - Supplies the opaque ID type associated with the opaque ID.
  337. Return Value:
  338. None
  339. --***************************************************************************/
  340. VOID
  341. UlFreeOpaqueId(
  342. IN HTTP_OPAQUE_ID OpaqueId,
  343. IN UL_OPAQUE_ID_TYPE OpaqueIdType
  344. )
  345. {
  346. PUL_OPAQUE_ID_TABLE pOpaqueIdTable;
  347. PUL_OPAQUE_ID_TABLE_ENTRY pEntry;
  348. ULONG Processor;
  349. ULONG FirstIndex = ULONG_MAX;
  350. ULONG SecondIndex;
  351. BOOLEAN Result;
  352. KIRQL OldIrql;
  353. #if !DBG
  354. UNREFERENCED_PARAMETER( OpaqueIdType );
  355. #endif
  356. //
  357. // Obtain the global opaque ID table and the entry associated with the
  358. // opaque ID passed in.
  359. //
  360. Result = UlpExtractIndexFromOpaqueId(
  361. OpaqueId,
  362. &Processor,
  363. &FirstIndex,
  364. &SecondIndex
  365. );
  366. ASSERT( Result );
  367. pOpaqueIdTable = &g_UlOpaqueIdTable[Processor].OpaqueIdTable;
  368. pEntry = pOpaqueIdTable->FirstLevelTable[FirstIndex] + SecondIndex;
  369. UlpAcquireOpaqueIdLock( &pEntry->Lock, &OldIrql );
  370. ASSERT( pEntry->OpaqueIdType != UlOpaqueIdTypeInvalid );
  371. ASSERT( (UL_OPAQUE_ID_TYPE) pEntry->OpaqueIdType == OpaqueIdType );
  372. ASSERT( pEntry->pContext != NULL );
  373. ASSERT( pEntry->OpaqueIdCyclic ==
  374. ((PUL_OPAQUE_ID_INTERNAL)&OpaqueId)->OpaqueIdCyclic );
  375. //
  376. // Restore the processor and first-level index but set the ID type
  377. // to invalid. This ensures subsequent mapping attempts on the stale
  378. // opaque ID entry will fail.
  379. //
  380. pEntry->Processor = Processor;
  381. pEntry->FirstIndex = FirstIndex;
  382. //
  383. // Setting OpaqueIdType to UlOpaqueIdTypeInvalid means the entry is freed.
  384. //
  385. pEntry->OpaqueIdType = UlOpaqueIdTypeInvalid;
  386. UlpReleaseOpaqueIdLock( &pEntry->Lock, OldIrql );
  387. InterlockedPushEntrySList(
  388. &pOpaqueIdTable->FreeOpaqueIdSListHead,
  389. &pEntry->FreeListEntry
  390. );
  391. #ifdef OPAQUE_ID_INSTRUMENTATION
  392. UlInterlockedIncrement64( &pOpaqueIdTable->NumberOfFrees );
  393. #endif
  394. }
  395. /***************************************************************************++
  396. Routine Description:
  397. Maps the specified opaque ID to the corresponding context value.
  398. Arguments:
  399. OpaqueId - Supplies the opaque ID to map.
  400. OpaqueIdType - Supplies the opaque ID type associated with the opaque ID.
  401. pReferenceRoutine - Supplies the reference routine to call on the mapped
  402. context if there is a match.
  403. Return Value:
  404. PVOID - Returns the original context associated with the opaqued ID.
  405. --***************************************************************************/
  406. PVOID
  407. UlGetObjectFromOpaqueId(
  408. IN HTTP_OPAQUE_ID OpaqueId,
  409. IN UL_OPAQUE_ID_TYPE OpaqueIdType,
  410. IN PUL_OPAQUE_ID_OBJECT_REFERENCE pReferenceRoutine
  411. )
  412. {
  413. PUL_OPAQUE_ID_TABLE pOpaqueIdTable = NULL;
  414. PUL_OPAQUE_ID_TABLE_ENTRY pEntry;
  415. UL_OPAQUE_ID_INTERNAL InternalId;
  416. ULONG Processor;
  417. ULONG FirstIndex;
  418. ULONG SecondIndex;
  419. PVOID pContext = NULL;
  420. BOOLEAN Result;
  421. KIRQL OldIrql;
  422. //
  423. // Sanity check.
  424. //
  425. ASSERT( OpaqueIdType != UlOpaqueIdTypeInvalid );
  426. ASSERT( pReferenceRoutine != NULL );
  427. #ifdef OPAQUE_ID_INSTRUMENTATION
  428. UlInterlockedIncrement64( &g_NumberOfTotalGets );
  429. #endif
  430. InternalId.OpaqueId = OpaqueId;
  431. //
  432. // Preliminary checking.
  433. //
  434. if ((UL_OPAQUE_ID_TYPE) InternalId.OpaqueIdType != OpaqueIdType)
  435. {
  436. return pContext;
  437. }
  438. //
  439. // Obtain a matching ID table entry. If we get one, this means the
  440. // processor, first-level table index and second-level table index of
  441. // the ID passed in are valid.
  442. //
  443. Result = UlpExtractIndexFromOpaqueId(
  444. OpaqueId,
  445. &Processor,
  446. &FirstIndex,
  447. &SecondIndex
  448. );
  449. if (Result)
  450. {
  451. pOpaqueIdTable = &g_UlOpaqueIdTable[Processor].OpaqueIdTable;
  452. pEntry = pOpaqueIdTable->FirstLevelTable[FirstIndex] + SecondIndex;
  453. }
  454. else
  455. {
  456. pEntry = NULL;
  457. }
  458. if (pEntry != NULL)
  459. {
  460. #ifdef OPAQUE_ID_INSTRUMENTATION
  461. ASSERT( pOpaqueIdTable );
  462. UlInterlockedIncrement64( &pOpaqueIdTable->NumberOfTotalGets );
  463. #endif
  464. //
  465. // Check other things inside the lock.
  466. //
  467. UlpAcquireOpaqueIdLock( &pEntry->Lock, &OldIrql );
  468. if ((UL_OPAQUE_ID_TYPE) pEntry->OpaqueIdType == OpaqueIdType &&
  469. pEntry->OpaqueIdCyclic == InternalId.OpaqueIdCyclic)
  470. {
  471. ASSERT( pEntry->pContext != NULL );
  472. //
  473. // All matched so we set pContext.
  474. //
  475. pContext = pEntry->pContext;
  476. //
  477. // Invoke the caller's reference routine with the lock held.
  478. //
  479. (pReferenceRoutine)(
  480. pContext
  481. REFERENCE_DEBUG_ACTUAL_PARAMS
  482. );
  483. #ifdef OPAQUE_ID_INSTRUMENTATION
  484. UlInterlockedIncrement64( &pOpaqueIdTable->NumberOfSuccessfulGets );
  485. UlInterlockedIncrement64( &g_NumberOfSuccessfulGets );
  486. #endif
  487. }
  488. UlpReleaseOpaqueIdLock( &pEntry->Lock, OldIrql );
  489. }
  490. return pContext;
  491. }
  492. //
  493. // Private functions.
  494. //
  495. /***************************************************************************++
  496. Routine Description:
  497. Allocates a new second-level table.
  498. Arguments:
  499. pOpaqueIdTable - Supplies the per-processor opaque ID table that we need
  500. to grow the second-level table.
  501. CapturedFirstTableInUse - Supplies the size of the first-level table as
  502. captured before InterlockedPopEntrySList. If this changes, it
  503. would mean another thread has allocated a new second-level table
  504. already and we return success right away in that case.
  505. Return Value:
  506. NTSTATUS - Completion status.
  507. --***************************************************************************/
  508. NTSTATUS
  509. UlpExpandOpaqueIdTable(
  510. IN PUL_OPAQUE_ID_TABLE pOpaqueIdTable,
  511. IN LONG CapturedFirstTableInUse
  512. )
  513. {
  514. PUL_OPAQUE_ID_TABLE_ENTRY pNewTable;
  515. PUL_OPAQUE_ID_TABLE_ENTRY pEntry;
  516. LONG FirstIndex;
  517. LONG Processor;
  518. NTSTATUS Status;
  519. KIRQL OldIrql;
  520. LONG i;
  521. //
  522. // Acquire the lock when expanding the table. This protects the
  523. // FirstLevelTableInUse and its associated first-level table.
  524. //
  525. UlAcquireSpinLock( &pOpaqueIdTable->Lock, &OldIrql );
  526. //
  527. // Bail out if FirstLevelTableInUse has changed. This means, though
  528. // unlikely, another thread has expanded the table for us.
  529. //
  530. if (CapturedFirstTableInUse < (LONG)(pOpaqueIdTable->FirstLevelTableInUse))
  531. {
  532. Status = STATUS_SUCCESS;
  533. goto end;
  534. }
  535. //
  536. // Fail the expansion if we reach the limit.
  537. //
  538. if (pOpaqueIdTable->FirstLevelTableInUse >=
  539. pOpaqueIdTable->FirstLevelTableSize)
  540. {
  541. Status = STATUS_ALLOTTED_SPACE_EXCEEDED;
  542. goto end;
  543. }
  544. //
  545. // Allocate a new second-level table.
  546. //
  547. pNewTable = UL_ALLOCATE_ARRAY(
  548. NonPagedPool,
  549. UL_OPAQUE_ID_TABLE_ENTRY,
  550. SECOND_LEVEL_TABLE_SIZE,
  551. UL_OPAQUE_ID_TABLE_POOL_TAG
  552. );
  553. if (pNewTable == NULL)
  554. {
  555. Status = STATUS_NO_MEMORY;
  556. goto end;
  557. }
  558. //
  559. // Initialize each table entry and push them to the global table's
  560. // free list.
  561. //
  562. RtlZeroMemory(
  563. pNewTable,
  564. sizeof(UL_OPAQUE_ID_TABLE_ENTRY) * SECOND_LEVEL_TABLE_SIZE
  565. );
  566. Processor = pOpaqueIdTable->Processor;
  567. FirstIndex = pOpaqueIdTable->FirstLevelTableInUse;
  568. for (i = 0, pEntry = pNewTable; i < SECOND_LEVEL_TABLE_SIZE; i++, pEntry++)
  569. {
  570. pEntry->Processor = Processor;
  571. pEntry->FirstIndex = FirstIndex;
  572. UlpInitializeOpaqueIdLock( &pEntry->Lock );
  573. InterlockedPushEntrySList(
  574. &pOpaqueIdTable->FreeOpaqueIdSListHead,
  575. &pEntry->FreeListEntry
  576. );
  577. }
  578. //
  579. // Adjust the first-level index forward. Do this only after all entries
  580. // have been pushed to the global list so the IDs only become valid when
  581. // they indeed exist. Because we have raised IRQL to DISPATCH level by
  582. // acquiring a spinlock, it is impossible for another thread to get in
  583. // and allocate an opaque ID from the current processor and its assoicated
  584. // global ID table. All the map attempts on the IDs being pushed will
  585. // duely fail because we haven't moved first-level index forward during
  586. // the push.
  587. //
  588. ASSERT( pOpaqueIdTable->FirstLevelTable[FirstIndex] == NULL );
  589. pOpaqueIdTable->FirstLevelTable[FirstIndex] = pNewTable;
  590. pOpaqueIdTable->FirstLevelTableInUse++;
  591. Status = STATUS_SUCCESS;
  592. end:
  593. UlReleaseSpinLock( &pOpaqueIdTable->Lock, OldIrql );
  594. return Status;
  595. }