Source code of Windows XP (NT5)
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.

765 lines
20 KiB

  1. /*++
  2. Copyright (c) 1998-2001 Microsoft Corporation
  3. Module Name:
  4. opaqueid.cxx
  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. }
  158. return STATUS_NO_MEMORY;
  159. }
  160. }
  161. return STATUS_SUCCESS;
  162. }
  163. /***************************************************************************++
  164. Routine Description:
  165. Performs global termination of the opaque ID package.
  166. Arguments:
  167. None
  168. Return Value:
  169. None
  170. --***************************************************************************/
  171. VOID
  172. UlTerminateOpaqueIdTable(
  173. VOID
  174. )
  175. {
  176. PUL_OPAQUE_ID_TABLE pOpaqueIdTable;
  177. ULONG i, j;
  178. for (i = 0; i < g_UlNumberOfProcessors; i++)
  179. {
  180. pOpaqueIdTable = &g_UlOpaqueIdTable[i].OpaqueIdTable;
  181. #ifdef OPAQUE_ID_INSTRUMENTATION
  182. ASSERT( pOpaqueIdTable->NumberOfAllocations ==
  183. pOpaqueIdTable->NumberOfFrees );
  184. #endif
  185. //
  186. // Free all allocated second-level tables.
  187. //
  188. for (j = 0; j < pOpaqueIdTable->FirstLevelTableInUse; j++)
  189. {
  190. ASSERT( pOpaqueIdTable->FirstLevelTable[j] != NULL );
  191. UL_FREE_POOL(
  192. pOpaqueIdTable->FirstLevelTable[j],
  193. UL_OPAQUE_ID_TABLE_POOL_TAG
  194. );
  195. }
  196. //
  197. // Free the first-level table.
  198. //
  199. if (pOpaqueIdTable->FirstLevelTable != NULL)
  200. {
  201. UL_FREE_POOL(
  202. pOpaqueIdTable->FirstLevelTable,
  203. UL_OPAQUE_ID_TABLE_POOL_TAG
  204. );
  205. }
  206. }
  207. }
  208. /***************************************************************************++
  209. Routine Description:
  210. Allocates a new opaque ID and associates it with the specified
  211. context. A new opaque ID takes a new slot in the opaque ID table.
  212. Arguments:
  213. pOpaqueId - Receives the newly allocated opaque ID if successful.
  214. OpaqueIdType - Supplies the opaque ID type to be associated with
  215. the opaque ID and associated object.
  216. pContext - Supplies the context to associate with the new opaque ID.
  217. Return Value:
  218. NTSTATUS - Completion status.
  219. --***************************************************************************/
  220. NTSTATUS
  221. UlAllocateOpaqueId(
  222. OUT PHTTP_OPAQUE_ID pOpaqueId,
  223. IN UL_OPAQUE_ID_TYPE OpaqueIdType,
  224. IN PVOID pContext
  225. )
  226. {
  227. PUL_OPAQUE_ID_TABLE pOpaqueIdTable;
  228. PUL_OPAQUE_ID_TABLE_ENTRY pEntry;
  229. PSINGLE_LIST_ENTRY pListEntry;
  230. PUL_OPAQUE_ID_INTERNAL pInternalId;
  231. ULONG CurrentProcessor;
  232. ULONG CapturedFirstTableInUse;
  233. NTSTATUS Status;
  234. KIRQL OldIrql;
  235. //
  236. // Allocate a new opaque ID from the current processor table. We need
  237. // a new entry for each ID.
  238. //
  239. CurrentProcessor = KeGetCurrentProcessorNumber();
  240. pOpaqueIdTable = &g_UlOpaqueIdTable[CurrentProcessor].OpaqueIdTable;
  241. //
  242. // Loop, trying to allocate an item from the table.
  243. //
  244. do
  245. {
  246. //
  247. // Remember the first-level table index if we need to expand later.
  248. //
  249. CapturedFirstTableInUse =
  250. *((volatile LONG *) &pOpaqueIdTable->FirstLevelTableInUse);
  251. pListEntry = InterlockedPopEntrySList(
  252. &pOpaqueIdTable->FreeOpaqueIdSListHead
  253. );
  254. if (pListEntry != NULL)
  255. {
  256. //
  257. // The free list isn't empty, so we can just use this
  258. // entry. We'll calculate the indices for this entry
  259. // and initialize the entry.
  260. //
  261. pEntry = CONTAINING_RECORD(
  262. pListEntry,
  263. UL_OPAQUE_ID_TABLE_ENTRY,
  264. FreeListEntry
  265. );
  266. pInternalId = (PUL_OPAQUE_ID_INTERNAL) pOpaqueId;
  267. UlpAcquireOpaqueIdLock( &pEntry->Lock, &OldIrql );
  268. //
  269. // Processor and FirstIndex are ready to use.
  270. //
  271. pInternalId->Index = pEntry->Index;
  272. //
  273. // Re-compute SecondIndex because its corresponding field has
  274. // been overwritten by Cyclic and OpaqueIdType when the entry
  275. // is in use.
  276. //
  277. pInternalId->SecondIndex =
  278. (pEntry - pOpaqueIdTable->FirstLevelTable[pEntry->FirstIndex]);
  279. //
  280. // Set the context associated with this entry.
  281. //
  282. pEntry->pContext = pContext;
  283. //
  284. // Update the cyclic and ID type of the entry.
  285. //
  286. pEntry->OpaqueIdCyclic = ++pEntry->EntryOpaqueIdCyclic;
  287. pEntry->OpaqueIdType = OpaqueIdType;
  288. pInternalId->Cyclic = pEntry->Cyclic;
  289. UlpReleaseOpaqueIdLock( &pEntry->Lock, OldIrql );
  290. #ifdef OPAQUE_ID_INSTRUMENTATION
  291. UlInterlockedIncrement64( &pOpaqueIdTable->NumberOfAllocations );
  292. #endif
  293. Status = STATUS_SUCCESS;
  294. break;
  295. }
  296. //
  297. // We only make it to this point if the free list is empty,
  298. // meaning we need to do some memory allocations before
  299. // we can continue. We'll put this off into a separate routine
  300. // to keep this one small (to avoid cache thrash). The realloc
  301. // routine returns STATUS_SUCCESS if it (or another thread)
  302. // managed to successfully reallocate the tables. Otherwise, it
  303. // returns a failure code.
  304. //
  305. Status = UlpExpandOpaqueIdTable(
  306. pOpaqueIdTable,
  307. CapturedFirstTableInUse
  308. );
  309. } while ( Status == STATUS_SUCCESS );
  310. return Status;
  311. }
  312. /***************************************************************************++
  313. Routine Description:
  314. Frees the specified opaque ID. This frees up the slot in the ID
  315. table as well.
  316. Arguments:
  317. OpaqueId - Supplies the opaque ID to free.
  318. OpaqueIdType - Supplies the opaque ID type associated with the opaque ID.
  319. Return Value:
  320. None
  321. --***************************************************************************/
  322. VOID
  323. UlFreeOpaqueId(
  324. IN HTTP_OPAQUE_ID OpaqueId,
  325. IN UL_OPAQUE_ID_TYPE OpaqueIdType
  326. )
  327. {
  328. PUL_OPAQUE_ID_TABLE pOpaqueIdTable;
  329. PUL_OPAQUE_ID_TABLE_ENTRY pEntry;
  330. ULONG Processor;
  331. ULONG FirstIndex;
  332. ULONG SecondIndex;
  333. BOOLEAN Result;
  334. KIRQL OldIrql;
  335. //
  336. // Obtain the global opaque ID table and the entry associated with the
  337. // opaque ID passed in.
  338. //
  339. Result = UlpExtractIndexFromOpaqueId(
  340. OpaqueId,
  341. &Processor,
  342. &FirstIndex,
  343. &SecondIndex
  344. );
  345. ASSERT( Result );
  346. pOpaqueIdTable = &g_UlOpaqueIdTable[Processor].OpaqueIdTable;
  347. pEntry = pOpaqueIdTable->FirstLevelTable[FirstIndex] + SecondIndex;
  348. UlpAcquireOpaqueIdLock( &pEntry->Lock, &OldIrql );
  349. ASSERT( pEntry->OpaqueIdType != UlOpaqueIdTypeInvalid );
  350. ASSERT( pEntry->OpaqueIdType == OpaqueIdType );
  351. ASSERT( pEntry->pContext != NULL );
  352. ASSERT( pEntry->OpaqueIdCyclic ==
  353. ((PUL_OPAQUE_ID_INTERNAL)&OpaqueId)->OpaqueIdCyclic );
  354. //
  355. // Restore the processor and first-level index but set the ID type
  356. // to invalid. This ensures subsequent mapping attempts on the stale
  357. // opaque ID entry will fail.
  358. //
  359. pEntry->Processor = Processor;
  360. pEntry->FirstIndex = FirstIndex;
  361. //
  362. // Setting OpaqueIdType to UlOpaqueIdTypeInvalid means the entry is freed.
  363. //
  364. pEntry->OpaqueIdType = UlOpaqueIdTypeInvalid;
  365. UlpReleaseOpaqueIdLock( &pEntry->Lock, OldIrql );
  366. InterlockedPushEntrySList(
  367. &pOpaqueIdTable->FreeOpaqueIdSListHead,
  368. &pEntry->FreeListEntry
  369. );
  370. #ifdef OPAQUE_ID_INSTRUMENTATION
  371. UlInterlockedIncrement64( &pOpaqueIdTable->NumberOfFrees );
  372. #endif
  373. }
  374. /***************************************************************************++
  375. Routine Description:
  376. Maps the specified opaque ID to the corresponding context value.
  377. Arguments:
  378. OpaqueId - Supplies the opaque ID to map.
  379. OpaqueIdType - Supplies the opaque ID type associated with the opaque ID.
  380. pReferenceRoutine - Supplies the reference routine to call on the mapped
  381. context if there is a match.
  382. Return Value:
  383. PVOID - Returns the original context associated with the opaqued ID.
  384. --***************************************************************************/
  385. PVOID
  386. UlGetObjectFromOpaqueId(
  387. IN HTTP_OPAQUE_ID OpaqueId,
  388. IN UL_OPAQUE_ID_TYPE OpaqueIdType,
  389. IN PUL_OPAQUE_ID_OBJECT_REFERENCE pReferenceRoutine
  390. )
  391. {
  392. PUL_OPAQUE_ID_TABLE pOpaqueIdTable;
  393. PUL_OPAQUE_ID_TABLE_ENTRY pEntry;
  394. UL_OPAQUE_ID_INTERNAL InternalId;
  395. ULONG Processor;
  396. ULONG FirstIndex;
  397. ULONG SecondIndex;
  398. PVOID pContext = NULL;
  399. BOOLEAN Result;
  400. KIRQL OldIrql;
  401. //
  402. // Sanity check.
  403. //
  404. ASSERT( OpaqueIdType != UlOpaqueIdTypeInvalid );
  405. ASSERT( pReferenceRoutine != NULL );
  406. #ifdef OPAQUE_ID_INSTRUMENTATION
  407. UlInterlockedIncrement64( &g_NumberOfTotalGets );
  408. #endif
  409. InternalId.OpaqueId = OpaqueId;
  410. //
  411. // Preliminary checking.
  412. //
  413. if (InternalId.OpaqueIdType != OpaqueIdType)
  414. {
  415. return pContext;
  416. }
  417. //
  418. // Obtain a matching ID table entry. If we get one, this means the
  419. // processor, first-level table index and second-level table index of
  420. // the ID passed in are valid.
  421. //
  422. Result = UlpExtractIndexFromOpaqueId(
  423. OpaqueId,
  424. &Processor,
  425. &FirstIndex,
  426. &SecondIndex
  427. );
  428. if (Result)
  429. {
  430. pOpaqueIdTable = &g_UlOpaqueIdTable[Processor].OpaqueIdTable;
  431. pEntry = pOpaqueIdTable->FirstLevelTable[FirstIndex] + SecondIndex;
  432. }
  433. else
  434. {
  435. pEntry = NULL;
  436. }
  437. if (pEntry != NULL)
  438. {
  439. #ifdef OPAQUE_ID_INSTRUMENTATION
  440. UlInterlockedIncrement64( &pOpaqueIdTable->NumberOfTotalGets );
  441. #endif
  442. //
  443. // Check other things inside the lock.
  444. //
  445. UlpAcquireOpaqueIdLock( &pEntry->Lock, &OldIrql );
  446. if (pEntry->OpaqueIdType == OpaqueIdType &&
  447. pEntry->OpaqueIdCyclic == InternalId.OpaqueIdCyclic)
  448. {
  449. ASSERT( pEntry->pContext != NULL );
  450. //
  451. // All matched so we set pContext.
  452. //
  453. pContext = pEntry->pContext;
  454. //
  455. // Invoke the caller's reference routine with the lock held.
  456. //
  457. (pReferenceRoutine)(
  458. pContext
  459. REFERENCE_DEBUG_ACTUAL_PARAMS
  460. );
  461. #ifdef OPAQUE_ID_INSTRUMENTATION
  462. UlInterlockedIncrement64( &pOpaqueIdTable->NumberOfSuccessfulGets );
  463. UlInterlockedIncrement64( &g_NumberOfSuccessfulGets );
  464. #endif
  465. }
  466. UlpReleaseOpaqueIdLock( &pEntry->Lock, OldIrql );
  467. }
  468. return pContext;
  469. }
  470. //
  471. // Private functions.
  472. //
  473. /***************************************************************************++
  474. Routine Description:
  475. Allocates a new second-level table.
  476. Arguments:
  477. pOpaqueIdTable - Supplies the per-processor opaque ID table that we need
  478. to grow the second-level table.
  479. CapturedFirstTableInUse - Supplies the size of the first-level table as
  480. captured before InterlockedPopEntrySList. If this changes, it
  481. would mean another thread has allocated a new second-level table
  482. already and we return success right away in that case.
  483. Return Value:
  484. NTSTATUS - Completion status.
  485. --***************************************************************************/
  486. NTSTATUS
  487. UlpExpandOpaqueIdTable(
  488. IN PUL_OPAQUE_ID_TABLE pOpaqueIdTable,
  489. IN LONG CapturedFirstTableInUse
  490. )
  491. {
  492. PUL_OPAQUE_ID_TABLE_ENTRY pNewTable;
  493. PUL_OPAQUE_ID_TABLE_ENTRY pEntry;
  494. LONG FirstIndex;
  495. LONG Processor;
  496. NTSTATUS Status;
  497. KIRQL OldIrql;
  498. LONG i;
  499. //
  500. // Acquire the lock when expanding the table. This protects the
  501. // FirstLevelTableInUse and its associated first-level table.
  502. //
  503. UlAcquireSpinLock( &pOpaqueIdTable->Lock, &OldIrql );
  504. //
  505. // Bail out if FirstLevelTableInUse has changed. This means, though
  506. // unlikely, another thread has expanded the table for us.
  507. //
  508. if (CapturedFirstTableInUse < (LONG)(pOpaqueIdTable->FirstLevelTableInUse))
  509. {
  510. Status = STATUS_SUCCESS;
  511. goto end;
  512. }
  513. //
  514. // Fail the expansion if we reach the limit.
  515. //
  516. if (pOpaqueIdTable->FirstLevelTableInUse >=
  517. pOpaqueIdTable->FirstLevelTableSize)
  518. {
  519. Status = STATUS_ALLOTTED_SPACE_EXCEEDED;
  520. goto end;
  521. }
  522. //
  523. // Allocate a new second-level table.
  524. //
  525. pNewTable = UL_ALLOCATE_ARRAY(
  526. NonPagedPool,
  527. UL_OPAQUE_ID_TABLE_ENTRY,
  528. SECOND_LEVEL_TABLE_SIZE,
  529. UL_OPAQUE_ID_TABLE_POOL_TAG
  530. );
  531. if (pNewTable == NULL)
  532. {
  533. Status = STATUS_NO_MEMORY;
  534. goto end;
  535. }
  536. //
  537. // Initialize each table entry and push them to the global table's
  538. // free list.
  539. //
  540. RtlZeroMemory(
  541. pNewTable,
  542. sizeof(UL_OPAQUE_ID_TABLE_ENTRY) * SECOND_LEVEL_TABLE_SIZE
  543. );
  544. Processor = pOpaqueIdTable->Processor;
  545. FirstIndex = pOpaqueIdTable->FirstLevelTableInUse;
  546. for (i = 0, pEntry = pNewTable; i < SECOND_LEVEL_TABLE_SIZE; i++, pEntry++)
  547. {
  548. pEntry->Processor = Processor;
  549. pEntry->FirstIndex = FirstIndex;
  550. UlpInitializeOpaqueIdLock( &pEntry->Lock );
  551. InterlockedPushEntrySList(
  552. &pOpaqueIdTable->FreeOpaqueIdSListHead,
  553. &pEntry->FreeListEntry
  554. );
  555. }
  556. //
  557. // Adjust the first-level index forward. Do this only after all entries
  558. // have been pushed to the global list so the IDs only become valid when
  559. // they indeed exist. Because we have raised IRQL to DISPATCH level by
  560. // acquiring a spinlock, it is impossible for another thread to get in
  561. // and allocate an opaque ID from the current processor and its assoicated
  562. // global ID table. All the map attempts on the IDs being pushed will
  563. // duely fail because we haven't moved first-level index forward during
  564. // the push.
  565. //
  566. ASSERT( pOpaqueIdTable->FirstLevelTable[FirstIndex] == NULL );
  567. pOpaqueIdTable->FirstLevelTable[FirstIndex] = pNewTable;
  568. pOpaqueIdTable->FirstLevelTableInUse++;
  569. Status = STATUS_SUCCESS;
  570. end:
  571. UlReleaseSpinLock( &pOpaqueIdTable->Lock, OldIrql );
  572. return Status;
  573. }