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.

624 lines
16 KiB

  1. #include <nt.h>
  2. #include <ntrtl.h>
  3. #include <nturtl.h>
  4. #include <windows.h>
  5. #include <windef.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <stddef.h>
  9. #include "dfsgeneric.hxx"
  10. #include "dfsinit.hxx"
  11. #include "dsgetdc.h"
  12. #include "lm.h"
  13. #include <dsrole.h>
  14. #include <DfsReferralData.h>
  15. #include <DfsReferral.hxx>
  16. #include <dfsheader.h>
  17. #include <Dfsumr.h>
  18. #include <winsock2.h>
  19. #include <DfsSiteCostCache.hxx>
  20. #include <DfsSite.hxx>
  21. #include "DfsSiteCostCache.tmh"
  22. //
  23. // Create a DFS_SITE_COST_DATA structure that is ready to
  24. // get inserted in to the SiteCost Cache.
  25. //
  26. DFSSTATUS
  27. DfsSiteCostCache::CreateCostData (
  28. DfsSite *pDestinationSite,
  29. ULONG Cost,
  30. DFSSTATUS ValidityStatus,
  31. PDFS_SITE_COST_DATA *ppNewData)
  32. {
  33. DFSSTATUS Status = ERROR_SUCCESS;
  34. PDFS_SITE_COST_DATA pData = NULL;
  35. pData = (PDFS_SITE_COST_DATA) DfsAllocateHashData( sizeof( DFS_SITE_COST_DATA ));
  36. if (pData != NULL)
  37. {
  38. RtlZeroMemory(pData, sizeof(DFS_SITE_COST_DATA));
  39. pData->Header.RefCount = 1;
  40. pData->Header.pData = (PVOID)pData;
  41. pData->ValidityStatus = ValidityStatus;
  42. pData->Cost = Cost;
  43. pData->AccessTime = GetTickCount();
  44. //
  45. // Key is the referenced pointer to the destination site itself.
  46. //
  47. pDestinationSite->AcquireReference();
  48. pData->pDestinationSite = pDestinationSite;
  49. pData->Header.pvKey = (PVOID)pData->pDestinationSite;
  50. }
  51. else
  52. {
  53. Status = ERROR_NOT_ENOUGH_MEMORY;
  54. }
  55. *ppNewData = pData;
  56. return Status;
  57. }
  58. DFSSTATUS
  59. DfsSiteCostCache::Initialize(void)
  60. {
  61. DFSSTATUS Status = ERROR_SUCCESS;
  62. NTSTATUS NtStatus = STATUS_SUCCESS;
  63. SHASH_FUNCTABLE FunctionTable;
  64. ZeroMemory(&FunctionTable, sizeof(FunctionTable));
  65. FunctionTable.NumBuckets = DFS_DEFAULT_SITE_COST_NUM_BUCKETS;
  66. FunctionTable.CompareFunc = DfsCompareDfsSites;
  67. FunctionTable.HashFunc = DfsHashDfsSite;
  68. FunctionTable.AllocFunc = DfsAllocateHashData;
  69. FunctionTable.FreeFunc = DfsDeallocateHashData;
  70. FunctionTable.AllocHashEntryFunc = DfsAllocateHashData;
  71. FunctionTable.FreeHashEntryFunc = DfsSiteCostCache::DfsDeallocateSiteCostData;
  72. NtStatus = ShashInitHashTable(&_pSiteCostTable, &FunctionTable);
  73. Status = RtlNtStatusToDosError(NtStatus);
  74. return Status;
  75. }
  76. // Just delete the cache data entry.
  77. VOID
  78. DfsSiteCostCache::DfsDeallocateSiteCostData(PVOID pPointer )
  79. {
  80. PDFS_SITE_COST_DATA pSiteStructure = (PDFS_SITE_COST_DATA)pPointer;
  81. if (pSiteStructure)
  82. {
  83. if (pSiteStructure->pDestinationSite != NULL)
  84. {
  85. pSiteStructure->pDestinationSite->ReleaseReference();
  86. }
  87. delete [] (PBYTE)pSiteStructure;
  88. }
  89. }
  90. //
  91. // Create the hash entry and insert it in the hash table.
  92. //
  93. DFSSTATUS
  94. DfsSiteCostCache::SetCost(
  95. DfsSite *pDestinationSite,
  96. ULONG Cost,
  97. DWORD ValidityStatus)
  98. {
  99. DFSSTATUS Status = ERROR_SUCCESS;
  100. PDFS_SITE_COST_DATA pCostData = NULL;
  101. Status = CreateCostData( pDestinationSite,
  102. Cost,
  103. ValidityStatus,
  104. &pCostData );
  105. if (Status != ERROR_SUCCESS)
  106. {
  107. return Status;
  108. }
  109. Status = SetCostData( pDestinationSite,
  110. pCostData );
  111. return Status;
  112. }
  113. //-------------------------------------------------------------------------
  114. // GetCost
  115. //
  116. // Given a target site, return it's cost. We know that the Source and the Destination
  117. // sites are NOT the same at this point.
  118. //
  119. // This returns ERROR_SUCCESS if it finds a valid cost.
  120. // ERROR_NOT_FOUND if the caller needs to GenerateCostMatrix.
  121. //
  122. //
  123. //-------------------------------------------------------------------------
  124. DFSSTATUS
  125. DfsSiteCostCache::GetCost (
  126. DfsSite *pDestinationSite,
  127. PULONG pCost)
  128. {
  129. DFSSTATUS Status = ERROR_NOT_FOUND;
  130. PDFS_SITE_COST_DATA pData = NULL;
  131. *pCost = DFS_MAX_COST;
  132. pData = (PDFS_SITE_COST_DATA)SHashLookupKeyEx(_pSiteCostTable,
  133. (PVOID)pDestinationSite);
  134. if (pData != NULL)
  135. {
  136. // See if the entry has expired.
  137. if (IsTimeToRefresh( pData ))
  138. {
  139. // Ask the caller to retry and replace this entry.
  140. ASSERT(Status == ERROR_NOT_FOUND);
  141. NOTHING;
  142. }
  143. // See if DS had returned an errorneous entry.
  144. else if (pData->ValidityStatus == ERROR_SUCCESS)
  145. {
  146. *pCost = pData->Cost;
  147. Status = ERROR_SUCCESS;
  148. }
  149. // See if it's time for us to try getting this entry again.
  150. else if (IsTimeToRetry( pData ))
  151. {
  152. //
  153. // Return ERROR_NOT_FOUND because we'd like the caller to retry.
  154. //
  155. ASSERT(Status == ERROR_NOT_FOUND);
  156. NOTHING;
  157. }
  158. else
  159. {
  160. //
  161. // We know at this point that we don't want the caller to retry or refresh.
  162. // We also know that we need to fallback on using the default max cost.
  163. //
  164. *pCost = DFS_MAX_COST;
  165. Status = ERROR_SUCCESS;
  166. }
  167. }
  168. return Status;
  169. }
  170. DFSSTATUS
  171. DfsSiteCostCache::RemoveCost(
  172. DfsSite *pSite)
  173. {
  174. NTSTATUS NtStatus;
  175. NtStatus = SHashRemoveKey(_pSiteCostTable,
  176. pSite,
  177. NULL );
  178. return RtlNtStatusToDosError( NtStatus );
  179. }
  180. VOID
  181. DfsSiteCostCache::InvalidateCache(VOID)
  182. {
  183. SHASH_ITERATOR Iter;
  184. DfsSite *pSite = NULL;
  185. pSite = StartSiteEnumerate( &Iter );
  186. while (pSite != NULL)
  187. {
  188. //
  189. // Remove this item. There's nothing we can do if we hit errors
  190. // except to keep going.
  191. //
  192. (VOID)RemoveCost( pSite );
  193. pSite = NextSiteEnumerate( &Iter );
  194. }
  195. FinishSiteEnumerate( &Iter );
  196. DFS_TRACE_LOW( REFERRAL, "SiteCostCache %p: invalidate cache done\n", this);
  197. }
  198. //------------------------------
  199. // DfsSiteCostSupport
  200. //
  201. // Static constructor
  202. //------------------------------
  203. DFSSTATUS
  204. DfsSiteCostSupport::DfsCreateSiteCostSupport(
  205. DfsSiteCostSupport **ppSup )
  206. {
  207. DFSSTATUS Status = ERROR_SUCCESS;
  208. DfsSiteCostSupport *pSup = NULL;
  209. *ppSup = NULL;
  210. do {
  211. pSup = new DfsSiteCostSupport;
  212. if (pSup == NULL)
  213. {
  214. Status = ERROR_NOT_ENOUGH_MEMORY;
  215. break;
  216. }
  217. // This doesn't create the hashtable yet. That's done
  218. // later as needed.
  219. Status = pSup->Initialize();
  220. if (Status != ERROR_SUCCESS)
  221. {
  222. break;
  223. }
  224. *ppSup = pSup;
  225. } while (FALSE);
  226. // Error path
  227. if (Status != ERROR_SUCCESS)
  228. {
  229. if (pSup != NULL)
  230. {
  231. delete pSup;
  232. pSup = NULL;
  233. }
  234. }
  235. return Status;
  236. }
  237. //
  238. // Return a referenced site cache for either lookups or
  239. // inserts. This guarantees
  240. DFSSTATUS
  241. DfsSiteCostSupport::Acquire(
  242. DfsSiteCostCache **ppCache )
  243. {
  244. DFSSTATUS Status = ERROR_SUCCESS;
  245. ULONG CachesToTrim = 0;
  246. *ppCache = NULL;
  247. //
  248. // Return a referenced SiteCostCache.
  249. //
  250. EnterCriticalSection( &_SiteCostLock );
  251. {
  252. do {
  253. if (_pCostTable != NULL)
  254. {
  255. // Put this at the head of the table.
  256. MoveToFrontOfMruList();
  257. break;
  258. }
  259. _pCostTable = new DfsSiteCostCache;
  260. if (_pCostTable == NULL)
  261. {
  262. Status = ERROR_NOT_ENOUGH_MEMORY;
  263. break;
  264. }
  265. //
  266. // Initialize the hashtable of sitenames-to-cost mappings.
  267. // We'll need to populate this later.
  268. //
  269. Status = _pCostTable->Initialize();
  270. if (Status != ERROR_SUCCESS)
  271. {
  272. delete _pCostTable;
  273. _pCostTable = NULL;
  274. break;
  275. }
  276. _InUseCount = 1;
  277. //
  278. // Add ourselves to the MRU list because we are a real table now.
  279. //
  280. CachesToTrim = InsertInMruList();
  281. } while (FALSE);
  282. //
  283. // We are already in the critical section.
  284. // No need to do atomic increments.
  285. //
  286. if (Status == ERROR_SUCCESS)
  287. {
  288. _InUseCount++;
  289. *ppCache = _pCostTable;
  290. // We mark the time at acquire time, as opposed to individual lookups and inserts
  291. // that happen on the table. This assumes that the current reference taken
  292. // on this cache is not long lived. That indeed is a
  293. // primary assumption behind this container class.
  294. _LastAccessTime = GetTickCount();
  295. }
  296. ASSERT( Status == ERROR_SUCCESS || _pCostTable == NULL );
  297. }
  298. LeaveCriticalSection( &_SiteCostLock );
  299. //
  300. // In a real degenerate case, the following may even throw out what we've just
  301. // generated above. We are safe though, because we hold a reference
  302. // until the referral is done.
  303. //
  304. if (CachesToTrim)
  305. {
  306. TrimSiteCostCaches( CachesToTrim );
  307. }
  308. return Status;
  309. }
  310. //
  311. // Callers are expected to pair all Acquires above with this Release.
  312. // This decrements the in use count, and if it reaches zero, which
  313. // only happens when it's earmarked for deletion, swaps the cost table
  314. // with a NULL. The actual deletion will then proceed unsynchronized.
  315. //
  316. VOID
  317. DfsSiteCostSupport::Release( VOID )
  318. {
  319. DfsSiteCostCache *pOldTable = NULL;
  320. EnterCriticalSection( &_SiteCostLock );
  321. {
  322. //
  323. // If this is the last reference, get rid of
  324. // the cache completely. This will only happen
  325. // if we are actually trying to get rid this to
  326. // trim the down the total number of caches
  327. // sitting around. (See TrimSiteCaches)
  328. //
  329. if (_pCostTable != NULL)
  330. {
  331. _InUseCount--;
  332. if (_InUseCount == 0)
  333. {
  334. // ASSERT( InMruList == FALSE ); //(unsafe)
  335. pOldTable = _pCostTable;
  336. _pCostTable = NULL;
  337. DfsServerGlobalData.NumSiteCostTables--;
  338. _LastAccessTime = 0;
  339. }
  340. }
  341. }
  342. LeaveCriticalSection( &_SiteCostLock );
  343. //
  344. // Delete the site cost table altogether.
  345. //
  346. if (pOldTable != NULL)
  347. {
  348. pOldTable->ReleaseReference();
  349. }
  350. return;
  351. }
  352. //
  353. // This starts the deletion proceeding of a SiteCostCache.
  354. // It is safe to call this multiple times, because it'll only
  355. // take the table off the MRU list only once.
  356. // The eventual deletion will happen in Release above
  357. // quite possibly at a later point.
  358. //
  359. // DfsSiteNameSupport calls this.
  360. //
  361. VOID
  362. DfsSiteCostSupport::MarkForDeletion( VOID )
  363. {
  364. BOOLEAN Delete = FALSE;
  365. EnterCriticalSection( &_SiteCostLock );
  366. {
  367. if (_pCostTable != NULL)
  368. {
  369. Delete = RemoveFromMruList();
  370. }
  371. }
  372. LeaveCriticalSection( &_SiteCostLock );
  373. //
  374. // We needed to get out of the critical section to call Release
  375. // which will mark it for deletion deletion.
  376. //
  377. if (Delete)
  378. {
  379. Release();
  380. }
  381. return;
  382. }
  383. //
  384. // The entry is too old to live. Prescribe euthanasia.
  385. //
  386. BOOLEAN
  387. DfsSiteCostSupport::IsExpired( VOID )
  388. {
  389. DWORD Interval = DfsServerGlobalData.SiteSupportRefreshInterval;
  390. BOOLEAN Expired = FALSE;
  391. EnterCriticalSection( &_SiteCostLock );
  392. {
  393. //
  394. // No point in expiring a table that doesnt
  395. // exist.
  396. //
  397. if (_pCostTable != NULL)
  398. {
  399. Expired = DfsSiteCostCache::IsTime( _LastAccessTime, Interval );
  400. }
  401. }
  402. LeaveCriticalSection( &_SiteCostLock );
  403. return Expired;
  404. }
  405. /*-------------------------------------------------------
  406. MRU list handling functions of the SiteCostSupport objects.
  407. The MRU list is guarded by the GlobalDataLock.
  408. --------------------------------------------------------*/
  409. ULONG
  410. DfsSiteCostSupport::InsertInMruList(VOID)
  411. {
  412. ULONG CachesToTrim = 0;
  413. DfsAcquireGlobalDataLock();
  414. {
  415. InsertHeadList( &DfsServerGlobalData.SiteCostTableMruList, &MruListEntry );
  416. InMruList = TRUE;
  417. DfsServerGlobalData.NumSiteCostTablesOnMruList++;
  418. DfsServerGlobalData.NumSiteCostTables++;
  419. //
  420. // This is iust a convenient time to check this.
  421. //
  422. if (DfsServerGlobalData.NumSiteCostTables > DFS_MAX_SITE_COST_CACHES)
  423. {
  424. CachesToTrim = DfsServerGlobalData.NumSiteCostTables - DFS_MAX_SITE_COST_CACHES;
  425. }
  426. }
  427. DfsReleaseGlobalDataLock();
  428. return CachesToTrim;
  429. }
  430. VOID
  431. DfsSiteCostSupport::MoveToFrontOfMruList( VOID )
  432. {
  433. DfsAcquireGlobalDataLock();
  434. {
  435. if (InMruList == TRUE)
  436. {
  437. RemoveEntryList( &MruListEntry );
  438. InsertHeadList( &DfsServerGlobalData.SiteCostTableMruList, &MruListEntry );
  439. }
  440. }
  441. DfsReleaseGlobalDataLock();
  442. return;
  443. }
  444. //
  445. // Returns TRUE if the table was successfully removed
  446. // from the MRU list.
  447. //
  448. BOOLEAN
  449. DfsSiteCostSupport::RemoveFromMruList( VOID )
  450. {
  451. BOOLEAN Delete = FALSE;
  452. DfsAcquireGlobalDataLock();
  453. {
  454. //
  455. // If the cache is not on the MRU, then there's
  456. // nothing to delete.
  457. //
  458. if (InMruList == TRUE)
  459. {
  460. RemoveEntryList( &MruListEntry );
  461. DfsServerGlobalData.NumSiteCostTablesOnMruList--;
  462. InMruList = FALSE;
  463. Delete = TRUE;
  464. }
  465. }
  466. DfsReleaseGlobalDataLock();
  467. return Delete;
  468. }
  469. DFSSTATUS
  470. DfsSiteCostSupport::PopLastTableFromMruList(
  471. DfsSiteCostSupport **pTableToRemove)
  472. {
  473. DFSSTATUS Status = ERROR_SUCCESS;
  474. DfsSiteCostSupport *pTable = NULL;
  475. PLIST_ENTRY pEntry;
  476. *pTableToRemove = NULL;
  477. DfsAcquireGlobalDataLock();
  478. do {
  479. // Nothing to do if the MRU list is empty.
  480. if (IsListEmpty( &DfsServerGlobalData.SiteCostTableMruList ))
  481. {
  482. Status = ERROR_NO_MORE_ITEMS;
  483. break;
  484. }
  485. // Pop the LRU entry off the list
  486. pEntry = RemoveTailList( &DfsServerGlobalData.SiteCostTableMruList );
  487. DfsServerGlobalData.NumSiteCostTablesOnMruList--;
  488. pTable = CONTAINING_RECORD( pEntry,
  489. DfsSiteCostSupport,
  490. MruListEntry );
  491. pTable->InMruList = FALSE;
  492. *pTableToRemove = pTable;
  493. } while (FALSE);
  494. DfsReleaseGlobalDataLock();
  495. return Status;
  496. }
  497. //
  498. // This is called when we detect that the total number of site cost
  499. // tables has exceeded its threshold. Currently we check to see if
  500. // that's the case only when we add a new table (see Acquire).
  501. //
  502. ULONG
  503. DfsSiteCostSupport::TrimSiteCostCaches(
  504. ULONG MaxCachesToTrim)
  505. {
  506. DfsSiteCostSupport *pCacheSup;
  507. ULONG NumTrims = 0;
  508. while (NumTrims < MaxCachesToTrim)
  509. {
  510. pCacheSup = NULL;
  511. (VOID) PopLastTableFromMruList( &pCacheSup );
  512. //
  513. // This just means we have no more items on the MRU list.
  514. // We must have raced with another because only the tables
  515. // that are initialized (non-empty) are on the MRU.
  516. //
  517. if (pCacheSup == NULL)
  518. {
  519. break;
  520. }
  521. //
  522. // This will start the deletion process.
  523. //
  524. pCacheSup->Release();
  525. NumTrims++;
  526. // unsafe, but this isn't an exact science
  527. if (DfsServerGlobalData.NumSiteCostTablesOnMruList <= DFS_MAX_SITE_COST_CACHES)
  528. {
  529. break;
  530. }
  531. }
  532. return NumTrims;
  533. }