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.

766 lines
20 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. namcache.c
  5. Abstract:
  6. The following functions are provided to support name cache management in
  7. mini-rdrs. See namcache.h for a more complete description of how a mini-rdr
  8. could use name caches to help eliminate trips to the server.
  9. Author:
  10. David Orbits [davidor] 9-Sep-1996
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. #include "prefix.h"
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE, RxNameCacheInitialize)
  18. #pragma alloc_text(PAGE, RxNameCacheCreateEntry)
  19. #pragma alloc_text(PAGE, RxNameCacheFetchEntry)
  20. #pragma alloc_text(PAGE, RxNameCacheCheckEntry)
  21. #pragma alloc_text(PAGE, RxNameCacheActivateEntry)
  22. #pragma alloc_text(PAGE, RxNameCacheExpireEntry)
  23. #pragma alloc_text(PAGE, RxNameCacheFreeEntry)
  24. #pragma alloc_text(PAGE, RxNameCacheFinalize)
  25. #pragma alloc_text(PAGE, RxNameCacheExpireEntryWithShortName)
  26. #endif
  27. #define Dbg (DEBUG_TRACE_NAMECACHE)
  28. VOID
  29. RxNameCacheInitialize(
  30. IN PNAME_CACHE_CONTROL NameCacheCtl,
  31. IN ULONG MRxNameCacheSize,
  32. IN ULONG MaximumEntries
  33. )
  34. /*++
  35. Routine Description:
  36. This routine initializes a NAME_CACHE structure.
  37. Arguments:
  38. NameCacheCtl - pointer to the NAME_CACHE_CONTROL from which to
  39. allocate the entry.
  40. MRxNameCacheSize - The size in bytes of the mini-rdr portion of the name
  41. cache entry.
  42. MaximumEntries - The maximum number of entries that will ever be
  43. allocated. E.g. This prevents an errant program which
  44. opens tons of files with bad names from chewing up
  45. paged pool.
  46. Return Value:
  47. None.
  48. --*/
  49. {
  50. PAGED_CODE();
  51. ExInitializeFastMutex(&NameCacheCtl->NameCacheLock);
  52. InitializeListHead(&NameCacheCtl->ActiveList);
  53. InitializeListHead(&NameCacheCtl->FreeList);
  54. NameCacheCtl->NumberActivates = 0;
  55. NameCacheCtl->NumberChecks = 0;
  56. NameCacheCtl->NumberNameHits = 0;
  57. NameCacheCtl->NumberNetOpsSaved = 0;
  58. NameCacheCtl->EntryCount = 0;
  59. NameCacheCtl->MaximumEntries = MaximumEntries;
  60. NameCacheCtl->MRxNameCacheSize = MRxNameCacheSize;
  61. return;
  62. }
  63. PNAME_CACHE
  64. RxNameCacheCreateEntry (
  65. IN PNAME_CACHE_CONTROL NameCacheCtl,
  66. IN PUNICODE_STRING Name,
  67. IN BOOLEAN CaseInsensitive
  68. )
  69. /*++
  70. Routine Description:
  71. This routine allocates and initializes a NAME_CACHE structure with the
  72. given name string, Lifetime (in seconds) and MRxContext.
  73. It returns a pointer to the name cache structure or NULL if no entry was
  74. available. It is expected that the caller will then initialize any
  75. additional mini-rdr portion of the name cache context and then put the
  76. entry on the name cache active list by calling RxNameCacheActivateEntry().
  77. Arguments:
  78. NameCacheCtl - pointer to the NAME_CACHE_CONTROL from which to
  79. allocate the entry.
  80. Name - A pointer to the unicode name string to initialize the
  81. the entry with.
  82. CaseInsensitive - True if need case insensitive compare on name.
  83. Return Value:
  84. PNAME_CACHE - returns a pointer to the newly allocated NAME_CACHE struct
  85. or NULL if allocation fails.
  86. --*/
  87. {
  88. LONG i;
  89. PNAME_CACHE *NameCacheArray;
  90. PNAME_CACHE NameCache;
  91. ULONG NameCacheSize;
  92. PAGED_CODE();
  93. RxDbgTrace( +1, Dbg, ("RxNameCacheCreateEntry: %wZ\n", Name ));
  94. //
  95. // Grab an entry off the free list.
  96. //
  97. ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
  98. if (!IsListEmpty(&NameCacheCtl->FreeList)) {
  99. NameCache = (PNAME_CACHE) RemoveHeadList(&NameCacheCtl->FreeList);
  100. } else {
  101. NameCache = NULL;
  102. }
  103. ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
  104. if (NameCache != NULL) {
  105. NameCache = CONTAINING_RECORD(NameCache, NAME_CACHE, Link);
  106. RxDbgTrace(0, Dbg, ("took from free list\n"));
  107. } else {
  108. //
  109. // Didn't get an entry off the free list, allocate one.
  110. // Don't exceed Max but we could go over a little if multiple threads
  111. // are allocating.
  112. //
  113. if (NameCacheCtl->EntryCount < NameCacheCtl->MaximumEntries) {
  114. NameCacheSize = QuadAlign(sizeof(NAME_CACHE)) +
  115. QuadAlign(NameCacheCtl->MRxNameCacheSize);
  116. NameCache = RxAllocatePoolWithTag(
  117. PagedPool,
  118. NameCacheSize,
  119. RX_NAME_CACHE_POOLTAG);
  120. if (NameCache != NULL) {
  121. //
  122. // Init standard header fields, bump entry count & setup
  123. // mini-rdr context extension.
  124. //
  125. ZeroAndInitializeNodeType(
  126. NameCache,
  127. RDBSS_NTC_STORAGE_TYPE_UNKNOWN,
  128. (NODE_BYTE_SIZE) NameCacheSize);
  129. InterlockedIncrement(&NameCacheCtl->EntryCount);
  130. NameCache->Name.Buffer = NULL;
  131. NameCache->Name.Length = 0;
  132. NameCache->Name.MaximumLength = 0;
  133. if (NameCacheCtl->MRxNameCacheSize > 0) {
  134. NameCache->ContextExtension = (PBYTE)NameCache +
  135. QuadAlign(sizeof(NAME_CACHE));
  136. RxDbgTrace(0, Dbg, ("allocated new entry\n"));
  137. }
  138. }
  139. }
  140. //
  141. // If still no entry then bag it.
  142. //
  143. if (NameCache == NULL) {
  144. RxDbgTrace(-1, Dbg, ("Fail no entry allocated!\n"));
  145. return NULL;
  146. }
  147. }
  148. //
  149. // If name won't fit in current string, free it and allocate new string.
  150. //
  151. if (Name->Length > NameCache->Name.MaximumLength) {
  152. if (NameCache->Name.Buffer != NULL) {
  153. RxFreePool(NameCache->Name.Buffer);
  154. }
  155. if (Name->Length > 0) {
  156. NameCache->Name.Buffer = RxAllocatePoolWithTag(
  157. PagedPool,
  158. (ULONG) Name->Length,
  159. RX_NAME_CACHE_POOLTAG);
  160. } else {
  161. NameCache->Name.Buffer = NULL;
  162. }
  163. if (Name->Length > 0 &&
  164. NameCache->Name.Buffer == NULL) {
  165. //
  166. // if didn't get the storage. Zero the string length and put entry
  167. // back on the free list. Otherwise save allocation in max length.
  168. //
  169. NameCache->Name.Length = 0;
  170. NameCache->Name.MaximumLength = 0;
  171. ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
  172. InsertHeadList(&NameCacheCtl->FreeList, &NameCache->Link);
  173. ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
  174. RxDbgTrace(-1, Dbg, ("Fail no pool for name!\n"));
  175. return NULL;
  176. } else {
  177. NameCache->Name.MaximumLength = Name->Length;
  178. }
  179. }
  180. //
  181. // Save the name & length. Set the case matching flag. Set the hash field.
  182. //
  183. NameCache->Name.Length = Name->Length;
  184. NameCache->CaseInsensitive = CaseInsensitive;
  185. if (Name->Length > 0) {
  186. RtlMoveMemory(NameCache->Name.Buffer, Name->Buffer, Name->Length);
  187. NameCache->HashValue = RxTableComputeHashValue(&NameCache->Name);
  188. }else {
  189. NameCache->HashValue = 0;
  190. }
  191. RxDbgTrace(-1, Dbg, ("Success!\n"));
  192. return NameCache;
  193. }
  194. PNAME_CACHE
  195. RxNameCacheFetchEntry (
  196. IN PNAME_CACHE_CONTROL NameCacheCtl,
  197. IN PUNICODE_STRING Name
  198. )
  199. /*++
  200. Routine Description:
  201. This routine looks for a match in the name cache for Name.
  202. If found the entry is removed from the Name Cache active list and
  203. a pointer to the NAME_CACHE struct is returned. Otherwise NULL is returned.
  204. The entry is removed to avoid problems with another thread trying to
  205. update the same entry or observing that it expired and putting it on the
  206. free list. We could get multiple entries with the same name by different
  207. threads but eventually they will expire.
  208. If a matching entry is found no check is made for expiration. That is left
  209. to the caller since it is likely the caller would want to take a special
  210. action.
  211. Arguments:
  212. NameCacheCtl - pointer to the NAME_CACHE_CONTROL to scan.
  213. Name - A pointer to the unicode name string to scan for.
  214. Return Value:
  215. PNAME_CACHE - returns a pointer to the NAME_CACHE struct if found or NULL.
  216. Side Effects:
  217. As the active list is scanned any non-matching entries that have expired are
  218. put on the free list.
  219. --*/
  220. {
  221. PNAME_CACHE NameCache = NULL;
  222. PLIST_ENTRY pListEntry;
  223. PLIST_ENTRY ExpiredEntry;
  224. ULONG HashValue;
  225. LARGE_INTEGER CurrentTime;
  226. PAGED_CODE();
  227. RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Lookup %wZ\n", Name ));
  228. if (Name->Length > 0) {
  229. HashValue = RxTableComputeHashValue(Name);
  230. } else {
  231. HashValue = 0;
  232. }
  233. KeQueryTickCount( &CurrentTime );
  234. NameCacheCtl->NumberChecks += 1;
  235. //
  236. // Get the lock and scan the active list.
  237. //
  238. ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
  239. pListEntry = NameCacheCtl->ActiveList.Flink;
  240. while (pListEntry != &NameCacheCtl->ActiveList) {
  241. NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
  242. //
  243. // Do initial match on the hash value and the length. Then do full string.
  244. //
  245. if ((NameCache->HashValue == HashValue) &&
  246. (Name->Length == NameCache->Name.Length)) {
  247. if (Name->Length == 0 ||
  248. RtlEqualUnicodeString(
  249. Name,
  250. &NameCache->Name,
  251. NameCache->CaseInsensitive) ) {
  252. //
  253. // Found a match.
  254. //
  255. NameCacheCtl->NumberNameHits += 1;
  256. break;
  257. }
  258. }
  259. //
  260. // No match. If the entry is expired, put it on the free list.
  261. //
  262. ExpiredEntry = pListEntry;
  263. pListEntry = pListEntry->Flink;
  264. if (CurrentTime.QuadPart >= NameCache->ExpireTime.QuadPart) {
  265. RemoveEntryList(ExpiredEntry);
  266. InsertHeadList(&NameCacheCtl->FreeList, ExpiredEntry);
  267. RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Entry expired %wZ\n", &NameCache->Name ));
  268. }
  269. NameCache = NULL;
  270. }
  271. //
  272. // If we found something pull it off the active list and give it to caller.
  273. //
  274. if (NameCache != NULL) {
  275. RemoveEntryList(pListEntry);
  276. }
  277. ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
  278. if (NameCache != NULL) {
  279. RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Entry found %wZ\n", &NameCache->Name ));
  280. }
  281. return NameCache;
  282. }
  283. RX_NC_CHECK_STATUS
  284. RxNameCacheCheckEntry (
  285. IN PNAME_CACHE NameCache,
  286. IN ULONG MRxContext
  287. )
  288. /*++
  289. Routine Description:
  290. This routine checks a name cache entry for validity. A valid entry
  291. means that the lifetime has not expired and the MRxContext passes
  292. the equality check.
  293. Arguments:
  294. NameCache - pointer to NAME_CACHE struct to check.
  295. MRxContext - A ULONG worth of mini-rdr supplied context for
  296. equality checking when making a valid entry check.
  297. Return Value:
  298. RX_NC_CHECK_STATUS: RX_NC_SUCCESS - The entry is valid
  299. RX_NC_TIME_EXPIRED - The Lifetime on the entry expired
  300. RX_NC_MRXCTX_FAIL - The MRxContext equality test failed
  301. --*/
  302. {
  303. LARGE_INTEGER CurrentTime;
  304. PAGED_CODE();
  305. //
  306. // Check for Mini-rdr context equality.
  307. //
  308. if (NameCache->Context != MRxContext) {
  309. RxDbgTrace( 0, Dbg, ("RxNameCacheCheckEntry: MRxContext_Fail %08lx,%08lx %wZ\n",
  310. NameCache->Context,
  311. MRxContext,
  312. &NameCache->Name ));
  313. return RX_NC_MRXCTX_FAIL;
  314. }
  315. //
  316. // Check for lifetime expired.
  317. //
  318. KeQueryTickCount( &CurrentTime );
  319. if (CurrentTime.QuadPart >= NameCache->ExpireTime.QuadPart) {
  320. RxDbgTrace( 0, Dbg, ("RxNameCacheCheckEntry: Expired %wZ\n", &NameCache->Name ));
  321. return RX_NC_TIME_EXPIRED;
  322. }
  323. RxDbgTrace( 0, Dbg, ("RxNameCacheCheckEntry: Success %wZ\n", &NameCache->Name ));
  324. return RX_NC_SUCCESS;
  325. }
  326. VOID
  327. RxNameCacheExpireEntry(
  328. IN PNAME_CACHE_CONTROL NameCacheCtl,
  329. IN PNAME_CACHE NameCache
  330. )
  331. /*++
  332. Routine Description:
  333. This routine puts the entry on the free list.
  334. Arguments:
  335. NameCacheCtl - pointer to the NAME_CACHE_CONTROL on which to
  336. activate the entry.
  337. NameCache - pointer to NAME_CACHE struct to activate.
  338. Return Value:
  339. None.
  340. Assumes:
  341. The name cache entry is not on either the free or active list.
  342. --*/
  343. {
  344. PAGED_CODE();
  345. RxDbgTrace( 0, Dbg, ("RxNameCacheExpireEntry: %wZ\n", &NameCache->Name ));
  346. //
  347. // Put the entry on free list for recycle.
  348. //
  349. ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
  350. InsertHeadList(&NameCacheCtl->FreeList, &NameCache->Link);
  351. ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
  352. //
  353. // Update stats.
  354. //
  355. NameCacheCtl->NumberActivates -= 1;
  356. return;
  357. }
  358. VOID
  359. RxNameCacheExpireEntryWithShortName (
  360. IN PNAME_CACHE_CONTROL NameCacheCtl,
  361. IN PUNICODE_STRING Name
  362. )
  363. /*++
  364. Routine Description:
  365. This routine expires all the name cache whose name prefix matches the given short file name.
  366. Arguments:
  367. NameCacheCtl - pointer to the NAME_CACHE_CONTROL to scan.
  368. Name - A pointer to the unicode name string to scan for.
  369. Return Value:
  370. PNAME_CACHE - returns a pointer to the NAME_CACHE struct if found or NULL.
  371. Side Effects:
  372. As the active list is scanned any non-matching entries that have expired are
  373. put on the free list.
  374. --*/
  375. {
  376. PNAME_CACHE NameCache = NULL;
  377. PLIST_ENTRY pListEntry;
  378. PLIST_ENTRY ExpiredEntry;
  379. ULONG HashValue;
  380. LARGE_INTEGER CurrentTime;
  381. PAGED_CODE();
  382. RxDbgTrace( 0, Dbg, ("RxNameCacheFetchEntry: Lookup %wZ\n", Name ));
  383. KeQueryTickCount( &CurrentTime );
  384. NameCacheCtl->NumberChecks += 1;
  385. //
  386. // Get the lock and scan the active list.
  387. //
  388. ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
  389. pListEntry = NameCacheCtl->ActiveList.Flink;
  390. while (pListEntry != &NameCacheCtl->ActiveList) {
  391. USHORT SavedNameLength;
  392. NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
  393. ExpiredEntry = pListEntry;
  394. pListEntry = pListEntry->Flink;
  395. //
  396. // Do initial match on the hash value and the length. Then do full string.
  397. //
  398. if (Name->Length <= NameCache->Name.Length) {
  399. SavedNameLength = NameCache->Name.Length;
  400. NameCache->Name.Length = Name->Length;
  401. if (Name->Length == 0 ||
  402. RtlEqualUnicodeString(
  403. Name,
  404. &NameCache->Name,
  405. NameCache->CaseInsensitive) ) {
  406. //
  407. // Found a match.
  408. //
  409. RemoveEntryList(ExpiredEntry);
  410. InsertHeadList(&NameCacheCtl->FreeList, ExpiredEntry);
  411. RxDbgTrace( 0, Dbg, ("RxNameCacheExpireEntryWithShortName: Entry expired %wZ\n", &NameCache->Name ));
  412. continue;
  413. }
  414. NameCache->Name.Length = SavedNameLength;
  415. }
  416. }
  417. ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
  418. }
  419. VOID
  420. RxNameCacheActivateEntry (
  421. IN PNAME_CACHE_CONTROL NameCacheCtl,
  422. IN PNAME_CACHE NameCache,
  423. IN ULONG LifeTime,
  424. IN ULONG MRxContext
  425. )
  426. /*++
  427. Routine Description:
  428. This routine takes a name cache entry and updates the expiration time and
  429. the mini rdr context. It then puts the entry on the active list.
  430. Arguments:
  431. NameCacheCtl - pointer to the NAME_CACHE_CONTROL on which to
  432. activate the entry.
  433. NameCache - pointer to NAME_CACHE struct to activate.
  434. LifeTime - The valid lifetime of the cache entry (in seconds).
  435. A lifetime of zero means leave current value unchanged.
  436. This is for reactivations after a match where you
  437. want the original lifetime preserved.
  438. MRxContext - A ULONG worth of mini-rdr supplied context for
  439. equality checking when making a valid entry check.
  440. An MRxContext of zero means leave current value unchanged.
  441. This is for reactivations after a match where you
  442. want the original MRxContext preserved.
  443. Return Value:
  444. None.
  445. Assumes:
  446. The name cache entry is not on either the free or active list.
  447. --*/
  448. {
  449. LARGE_INTEGER CurrentTime;
  450. PAGED_CODE();
  451. RxDbgTrace( 0, Dbg, ("RxNameCacheActivateEntry: %wZ\n", &NameCache->Name ));
  452. //
  453. // Set new expiration time on the entry and save the mini-rdr context.
  454. // A lifetime of zero or a MRxContext of zero implies leave value unchanged.
  455. //
  456. if (LifeTime != 0) {
  457. KeQueryTickCount( &CurrentTime );
  458. NameCache->ExpireTime.QuadPart = CurrentTime.QuadPart +
  459. (LONGLONG) ((LifeTime * 10*1000*1000) / KeQueryTimeIncrement());
  460. }
  461. if (MRxContext != 0) {
  462. NameCache->Context = MRxContext;
  463. }
  464. //
  465. // Put the entry on active list.
  466. //
  467. ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
  468. InsertHeadList(&NameCacheCtl->ActiveList, &NameCache->Link);
  469. ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
  470. //
  471. // Update stats.
  472. //
  473. NameCacheCtl->NumberActivates += 1;
  474. return;
  475. }
  476. VOID
  477. RxNameCacheFreeEntry (
  478. IN PNAME_CACHE_CONTROL NameCacheCtl,
  479. IN PNAME_CACHE NameCache
  480. )
  481. /*++
  482. Routine Description:
  483. This routine releases the storage for a name cache entry and decrements the
  484. count of name cache entries for this name cache.
  485. Arguments:
  486. NameCacheCtl - pointer to the NAME_CACHE_CONTROL for the name cache.
  487. NameCache - pointer to the NAME_CACHE struct to free.
  488. Return Value:
  489. None.
  490. Assumes:
  491. The name cache entry is not on either the free or active list.
  492. --*/
  493. {
  494. PAGED_CODE();
  495. RxDbgTrace( 0, Dbg, ("RxNameCacheFreeEntry: %wZ\n", &NameCache->Name ));
  496. //
  497. // Release storage for name
  498. //
  499. if (NameCache->Name.Buffer != NULL) {
  500. RxFreePool(NameCache->Name.Buffer);
  501. }
  502. //
  503. // Release storage for NAME_CACHE entry (includes context ext., if any)
  504. //
  505. RxFreePool(NameCache);
  506. InterlockedDecrement(&NameCacheCtl->EntryCount);
  507. return;
  508. }
  509. VOID
  510. RxNameCacheFinalize (
  511. IN PNAME_CACHE_CONTROL NameCacheCtl
  512. )
  513. /*++
  514. Routine Description:
  515. This routine releases the storage for all the name cache entries.
  516. Arguments:
  517. NameCacheCtl - pointer to the NAME_CACHE_CONTROL for the name cache.
  518. Return Value:
  519. None.
  520. --*/
  521. {
  522. PNAME_CACHE NameCache;
  523. PLIST_ENTRY pListEntry;
  524. PAGED_CODE();
  525. //
  526. // Get the lock and remove entries from the active list.
  527. //
  528. ExAcquireFastMutex(&NameCacheCtl->NameCacheLock);
  529. while (!IsListEmpty(&NameCacheCtl->ActiveList)) {
  530. pListEntry = RemoveHeadList(&NameCacheCtl->ActiveList);
  531. NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
  532. RxNameCacheFreeEntry(NameCacheCtl, NameCache);
  533. }
  534. //
  535. // scan free list and remove entries.
  536. //
  537. while (!IsListEmpty(&NameCacheCtl->FreeList)) {
  538. pListEntry = RemoveHeadList(&NameCacheCtl->FreeList);
  539. NameCache = (PNAME_CACHE) CONTAINING_RECORD(pListEntry, NAME_CACHE, Link);
  540. RxNameCacheFreeEntry(NameCacheCtl, NameCache);
  541. }
  542. ExReleaseFastMutex(&NameCacheCtl->NameCacheLock);
  543. //
  544. // At this point the entry count should be zero. If not then there is
  545. // a memory leak since someone didn't call free.
  546. //
  547. ASSERT(NameCacheCtl->EntryCount == 0);
  548. return;
  549. }
  550.