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

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <windef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include "dfsgeneric.hxx"
#include "dfsinit.hxx"
#include "dsgetdc.h"
#include "lm.h"
#include <dsrole.h>
#include <DfsReferralData.h>
#include <DfsReferral.hxx>
#include <dfsheader.h>
#include <Dfsumr.h>
#include <winsock2.h>
#include <DfsSiteCostCache.hxx>
#include <DfsSite.hxx>
#include "DfsSiteCostCache.tmh"
//
// Create a DFS_SITE_COST_DATA structure that is ready to
// get inserted in to the SiteCost Cache.
//
DFSSTATUS
DfsSiteCostCache::CreateCostData (
DfsSite *pDestinationSite,
ULONG Cost,
DFSSTATUS ValidityStatus,
PDFS_SITE_COST_DATA *ppNewData)
{
DFSSTATUS Status = ERROR_SUCCESS;
PDFS_SITE_COST_DATA pData = NULL;
pData = (PDFS_SITE_COST_DATA) DfsAllocateHashData( sizeof( DFS_SITE_COST_DATA ));
if (pData != NULL)
{
RtlZeroMemory(pData, sizeof(DFS_SITE_COST_DATA));
pData->Header.RefCount = 1;
pData->Header.pData = (PVOID)pData;
pData->ValidityStatus = ValidityStatus;
pData->Cost = Cost;
pData->AccessTime = GetTickCount();
//
// Key is the referenced pointer to the destination site itself.
//
pDestinationSite->AcquireReference();
pData->pDestinationSite = pDestinationSite;
pData->Header.pvKey = (PVOID)pData->pDestinationSite;
}
else
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
*ppNewData = pData;
return Status;
}
DFSSTATUS
DfsSiteCostCache::Initialize(void)
{
DFSSTATUS Status = ERROR_SUCCESS;
NTSTATUS NtStatus = STATUS_SUCCESS;
SHASH_FUNCTABLE FunctionTable;
ZeroMemory(&FunctionTable, sizeof(FunctionTable));
FunctionTable.NumBuckets = DFS_DEFAULT_SITE_COST_NUM_BUCKETS;
FunctionTable.CompareFunc = DfsCompareDfsSites;
FunctionTable.HashFunc = DfsHashDfsSite;
FunctionTable.AllocFunc = DfsAllocateHashData;
FunctionTable.FreeFunc = DfsDeallocateHashData;
FunctionTable.AllocHashEntryFunc = DfsAllocateHashData;
FunctionTable.FreeHashEntryFunc = DfsSiteCostCache::DfsDeallocateSiteCostData;
NtStatus = ShashInitHashTable(&_pSiteCostTable, &FunctionTable);
Status = RtlNtStatusToDosError(NtStatus);
return Status;
}
// Just delete the cache data entry.
VOID
DfsSiteCostCache::DfsDeallocateSiteCostData(PVOID pPointer )
{
PDFS_SITE_COST_DATA pSiteStructure = (PDFS_SITE_COST_DATA)pPointer;
if (pSiteStructure)
{
if (pSiteStructure->pDestinationSite != NULL)
{
pSiteStructure->pDestinationSite->ReleaseReference();
}
delete [] (PBYTE)pSiteStructure;
}
}
//
// Create the hash entry and insert it in the hash table.
//
DFSSTATUS
DfsSiteCostCache::SetCost(
DfsSite *pDestinationSite,
ULONG Cost,
DWORD ValidityStatus)
{
DFSSTATUS Status = ERROR_SUCCESS;
PDFS_SITE_COST_DATA pCostData = NULL;
Status = CreateCostData( pDestinationSite,
Cost,
ValidityStatus,
&pCostData );
if (Status != ERROR_SUCCESS)
{
return Status;
}
Status = SetCostData( pDestinationSite,
pCostData );
return Status;
}
//-------------------------------------------------------------------------
// GetCost
//
// Given a target site, return it's cost. We know that the Source and the Destination
// sites are NOT the same at this point.
//
// This returns ERROR_SUCCESS if it finds a valid cost.
// ERROR_NOT_FOUND if the caller needs to GenerateCostMatrix.
//
//
//-------------------------------------------------------------------------
DFSSTATUS
DfsSiteCostCache::GetCost (
DfsSite *pDestinationSite,
PULONG pCost)
{
DFSSTATUS Status = ERROR_NOT_FOUND;
PDFS_SITE_COST_DATA pData = NULL;
*pCost = DFS_MAX_COST;
pData = (PDFS_SITE_COST_DATA)SHashLookupKeyEx(_pSiteCostTable,
(PVOID)pDestinationSite);
if (pData != NULL)
{
// See if the entry has expired.
if (IsTimeToRefresh( pData ))
{
// Ask the caller to retry and replace this entry.
ASSERT(Status == ERROR_NOT_FOUND);
NOTHING;
}
// See if DS had returned an errorneous entry.
else if (pData->ValidityStatus == ERROR_SUCCESS)
{
*pCost = pData->Cost;
Status = ERROR_SUCCESS;
}
// See if it's time for us to try getting this entry again.
else if (IsTimeToRetry( pData ))
{
//
// Return ERROR_NOT_FOUND because we'd like the caller to retry.
//
ASSERT(Status == ERROR_NOT_FOUND);
NOTHING;
}
else
{
//
// We know at this point that we don't want the caller to retry or refresh.
// We also know that we need to fallback on using the default max cost.
//
*pCost = DFS_MAX_COST;
Status = ERROR_SUCCESS;
}
}
return Status;
}
DFSSTATUS
DfsSiteCostCache::RemoveCost(
DfsSite *pSite)
{
NTSTATUS NtStatus;
NtStatus = SHashRemoveKey(_pSiteCostTable,
pSite,
NULL );
return RtlNtStatusToDosError( NtStatus );
}
VOID
DfsSiteCostCache::InvalidateCache(VOID)
{
SHASH_ITERATOR Iter;
DfsSite *pSite = NULL;
pSite = StartSiteEnumerate( &Iter );
while (pSite != NULL)
{
//
// Remove this item. There's nothing we can do if we hit errors
// except to keep going.
//
(VOID)RemoveCost( pSite );
pSite = NextSiteEnumerate( &Iter );
}
FinishSiteEnumerate( &Iter );
DFS_TRACE_LOW( REFERRAL, "SiteCostCache %p: invalidate cache done\n", this);
}
//------------------------------
// DfsSiteCostSupport
//
// Static constructor
//------------------------------
DFSSTATUS
DfsSiteCostSupport::DfsCreateSiteCostSupport(
DfsSiteCostSupport **ppSup )
{
DFSSTATUS Status = ERROR_SUCCESS;
DfsSiteCostSupport *pSup = NULL;
*ppSup = NULL;
do {
pSup = new DfsSiteCostSupport;
if (pSup == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// This doesn't create the hashtable yet. That's done
// later as needed.
Status = pSup->Initialize();
if (Status != ERROR_SUCCESS)
{
break;
}
*ppSup = pSup;
} while (FALSE);
// Error path
if (Status != ERROR_SUCCESS)
{
if (pSup != NULL)
{
delete pSup;
pSup = NULL;
}
}
return Status;
}
//
// Return a referenced site cache for either lookups or
// inserts. This guarantees
DFSSTATUS
DfsSiteCostSupport::Acquire(
DfsSiteCostCache **ppCache )
{
DFSSTATUS Status = ERROR_SUCCESS;
ULONG CachesToTrim = 0;
*ppCache = NULL;
//
// Return a referenced SiteCostCache.
//
EnterCriticalSection( &_SiteCostLock );
{
do {
if (_pCostTable != NULL)
{
// Put this at the head of the table.
MoveToFrontOfMruList();
break;
}
_pCostTable = new DfsSiteCostCache;
if (_pCostTable == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
break;
}
//
// Initialize the hashtable of sitenames-to-cost mappings.
// We'll need to populate this later.
//
Status = _pCostTable->Initialize();
if (Status != ERROR_SUCCESS)
{
delete _pCostTable;
_pCostTable = NULL;
break;
}
_InUseCount = 1;
//
// Add ourselves to the MRU list because we are a real table now.
//
CachesToTrim = InsertInMruList();
} while (FALSE);
//
// We are already in the critical section.
// No need to do atomic increments.
//
if (Status == ERROR_SUCCESS)
{
_InUseCount++;
*ppCache = _pCostTable;
// We mark the time at acquire time, as opposed to individual lookups and inserts
// that happen on the table. This assumes that the current reference taken
// on this cache is not long lived. That indeed is a
// primary assumption behind this container class.
_LastAccessTime = GetTickCount();
}
ASSERT( Status == ERROR_SUCCESS || _pCostTable == NULL );
}
LeaveCriticalSection( &_SiteCostLock );
//
// In a real degenerate case, the following may even throw out what we've just
// generated above. We are safe though, because we hold a reference
// until the referral is done.
//
if (CachesToTrim)
{
TrimSiteCostCaches( CachesToTrim );
}
return Status;
}
//
// Callers are expected to pair all Acquires above with this Release.
// This decrements the in use count, and if it reaches zero, which
// only happens when it's earmarked for deletion, swaps the cost table
// with a NULL. The actual deletion will then proceed unsynchronized.
//
VOID
DfsSiteCostSupport::Release( VOID )
{
DfsSiteCostCache *pOldTable = NULL;
EnterCriticalSection( &_SiteCostLock );
{
//
// If this is the last reference, get rid of
// the cache completely. This will only happen
// if we are actually trying to get rid this to
// trim the down the total number of caches
// sitting around. (See TrimSiteCaches)
//
if (_pCostTable != NULL)
{
_InUseCount--;
if (_InUseCount == 0)
{
// ASSERT( InMruList == FALSE ); //(unsafe)
pOldTable = _pCostTable;
_pCostTable = NULL;
DfsServerGlobalData.NumSiteCostTables--;
_LastAccessTime = 0;
}
}
}
LeaveCriticalSection( &_SiteCostLock );
//
// Delete the site cost table altogether.
//
if (pOldTable != NULL)
{
pOldTable->ReleaseReference();
}
return;
}
//
// This starts the deletion proceeding of a SiteCostCache.
// It is safe to call this multiple times, because it'll only
// take the table off the MRU list only once.
// The eventual deletion will happen in Release above
// quite possibly at a later point.
//
// DfsSiteNameSupport calls this.
//
VOID
DfsSiteCostSupport::MarkForDeletion( VOID )
{
BOOLEAN Delete = FALSE;
EnterCriticalSection( &_SiteCostLock );
{
if (_pCostTable != NULL)
{
Delete = RemoveFromMruList();
}
}
LeaveCriticalSection( &_SiteCostLock );
//
// We needed to get out of the critical section to call Release
// which will mark it for deletion deletion.
//
if (Delete)
{
Release();
}
return;
}
//
// The entry is too old to live. Prescribe euthanasia.
//
BOOLEAN
DfsSiteCostSupport::IsExpired( VOID )
{
DWORD Interval = DfsServerGlobalData.SiteSupportRefreshInterval;
BOOLEAN Expired = FALSE;
EnterCriticalSection( &_SiteCostLock );
{
//
// No point in expiring a table that doesnt
// exist.
//
if (_pCostTable != NULL)
{
Expired = DfsSiteCostCache::IsTime( _LastAccessTime, Interval );
}
}
LeaveCriticalSection( &_SiteCostLock );
return Expired;
}
/*-------------------------------------------------------
MRU list handling functions of the SiteCostSupport objects.
The MRU list is guarded by the GlobalDataLock.
--------------------------------------------------------*/
ULONG
DfsSiteCostSupport::InsertInMruList(VOID)
{
ULONG CachesToTrim = 0;
DfsAcquireGlobalDataLock();
{
InsertHeadList( &DfsServerGlobalData.SiteCostTableMruList, &MruListEntry );
InMruList = TRUE;
DfsServerGlobalData.NumSiteCostTablesOnMruList++;
DfsServerGlobalData.NumSiteCostTables++;
//
// This is iust a convenient time to check this.
//
if (DfsServerGlobalData.NumSiteCostTables > DFS_MAX_SITE_COST_CACHES)
{
CachesToTrim = DfsServerGlobalData.NumSiteCostTables - DFS_MAX_SITE_COST_CACHES;
}
}
DfsReleaseGlobalDataLock();
return CachesToTrim;
}
VOID
DfsSiteCostSupport::MoveToFrontOfMruList( VOID )
{
DfsAcquireGlobalDataLock();
{
if (InMruList == TRUE)
{
RemoveEntryList( &MruListEntry );
InsertHeadList( &DfsServerGlobalData.SiteCostTableMruList, &MruListEntry );
}
}
DfsReleaseGlobalDataLock();
return;
}
//
// Returns TRUE if the table was successfully removed
// from the MRU list.
//
BOOLEAN
DfsSiteCostSupport::RemoveFromMruList( VOID )
{
BOOLEAN Delete = FALSE;
DfsAcquireGlobalDataLock();
{
//
// If the cache is not on the MRU, then there's
// nothing to delete.
//
if (InMruList == TRUE)
{
RemoveEntryList( &MruListEntry );
DfsServerGlobalData.NumSiteCostTablesOnMruList--;
InMruList = FALSE;
Delete = TRUE;
}
}
DfsReleaseGlobalDataLock();
return Delete;
}
DFSSTATUS
DfsSiteCostSupport::PopLastTableFromMruList(
DfsSiteCostSupport **pTableToRemove)
{
DFSSTATUS Status = ERROR_SUCCESS;
DfsSiteCostSupport *pTable = NULL;
PLIST_ENTRY pEntry;
*pTableToRemove = NULL;
DfsAcquireGlobalDataLock();
do {
// Nothing to do if the MRU list is empty.
if (IsListEmpty( &DfsServerGlobalData.SiteCostTableMruList ))
{
Status = ERROR_NO_MORE_ITEMS;
break;
}
// Pop the LRU entry off the list
pEntry = RemoveTailList( &DfsServerGlobalData.SiteCostTableMruList );
DfsServerGlobalData.NumSiteCostTablesOnMruList--;
pTable = CONTAINING_RECORD( pEntry,
DfsSiteCostSupport,
MruListEntry );
pTable->InMruList = FALSE;
*pTableToRemove = pTable;
} while (FALSE);
DfsReleaseGlobalDataLock();
return Status;
}
//
// This is called when we detect that the total number of site cost
// tables has exceeded its threshold. Currently we check to see if
// that's the case only when we add a new table (see Acquire).
//
ULONG
DfsSiteCostSupport::TrimSiteCostCaches(
ULONG MaxCachesToTrim)
{
DfsSiteCostSupport *pCacheSup;
ULONG NumTrims = 0;
while (NumTrims < MaxCachesToTrim)
{
pCacheSup = NULL;
(VOID) PopLastTableFromMruList( &pCacheSup );
//
// This just means we have no more items on the MRU list.
// We must have raced with another because only the tables
// that are initialized (non-empty) are on the MRU.
//
if (pCacheSup == NULL)
{
break;
}
//
// This will start the deletion process.
//
pCacheSup->Release();
NumTrims++;
// unsafe, but this isn't an exact science
if (DfsServerGlobalData.NumSiteCostTablesOnMruList <= DFS_MAX_SITE_COST_CACHES)
{
break;
}
}
return NumTrims;
}