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.
 
 
 
 
 
 

334 lines
8.3 KiB

#ifndef _DFS_SITE_COST_CACHE_HXX__
#define _DFS_SITE_COST_CACHE_HXX__
#include <shash.h>
#include <limits.h>
class DfsSite;
//
// Minimum and maximum values for inter-site costs.
// These values were 0 and 100 once upon a tender time.
//
#define DFS_MIN_COST 0
#define DFS_MAX_COST ULONG_MAX
#define DFS_DEFAULT_SITE_COST_NUM_BUCKETS 16
#define DFS_MAX_SITE_COST_CACHES 64
//
// When we get a cost that's marked with a ValidityStatus != ERROR_SUCCESS
// this is how soon that entry will expire. If the entry is accessed again after this
// interval, we'll query DS again to see if we have better luck with it.
//
#define DFS_DEFAULT_INVALID_COST_RETRY_INTERVAL 60 * 1000
//
// This is the hash entry that maps
// a destination site to its cost.
//
typedef struct _DFS_SITE_COST_DATA {
SHASH_HEADER Header;
DFSSTATUS ValidityStatus; // to account for possible errors in Cost calculations
ULONG AccessTime; // For aging of individual entries.
ULONG Cost; // Cost from a client site to the following destination
DfsSite *pDestinationSite; // The above cost only applies to this destination.
} DFS_SITE_COST_DATA, *PDFS_SITE_COST_DATA;
//
// This maps sitenames of servers to their corresponding costs.
// There is one of these tables per each unique client DfsSite.
//
class DfsSiteCostCache : public DfsGeneric
{
private:
//
// Hash table mapping destination sites and their corresponding costs.
//
PSHASH_TABLE _pSiteCostTable;
public:
DfsSiteCostCache( VOID )
: DfsGeneric( DFS_OBJECT_TYPE_SITECOST_CACHE )
{
_pSiteCostTable = NULL;
}
~DfsSiteCostCache( VOID )
{
if (_pSiteCostTable != NULL)
{
// we have to throw out individual entries or we'll leak.
InvalidateCache();
ShashTerminateHashTable( _pSiteCostTable );
_pSiteCostTable = NULL;
}
}
DFSSTATUS
Initialize( VOID );
//
// Retry because the entry we have doesn't have a good ValidityStatus.
//
inline BOOLEAN
IsTimeToRetry( PDFS_SITE_COST_DATA pSiteData )
{
DWORD Interval = DFS_DEFAULT_INVALID_COST_RETRY_INTERVAL;
// We should implement a staggered retry here so that we won't
// bombard the DS over and over. BUG:
return IsTime( pSiteData->AccessTime, Interval );
}
//
// Refresh because the entry we have is just plain too old.
//
inline BOOLEAN
IsTimeToRefresh( PDFS_SITE_COST_DATA pSiteData )
{
DWORD Interval = DfsServerGlobalData.SiteSupportRefreshInterval;
return IsTime( pSiteData->AccessTime, Interval );
}
static BOOLEAN
IsTime(
ULONG TimeStamp,
DWORD Interval)
{
DWORD TimeNow = 0;
TimeNow = GetTickCount();
if ((TimeNow > TimeStamp) &&
(TimeNow - TimeStamp) > Interval)
{
return TRUE;
}
if ((TimeNow < TimeStamp) &&
((TimeNow - 0) + (0xFFFFFFFF - TimeStamp) > Interval))
{
return TRUE;
}
return FALSE;
}
DFSSTATUS
GetCost (
DfsSite *pDestinationSite,
PULONG pCost);
DFSSTATUS
SetCost(
DfsSite *pDestinationSite,
ULONG Cost,
DWORD ValidityStatus);
DFSSTATUS
RemoveCost(
DfsSite *pSite);
//
// Given a target site and the hash structure to insert,
// add the tuple to the table.
//
DFSSTATUS
SetCostData (
DfsSite *pDestinationSite,
PDFS_SITE_COST_DATA pSiteCostData)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DFSSTATUS Status = ERROR_SUCCESS;
//
// The destination site is the hash key here.
//
NtStatus = SHashInsertKey(_pSiteCostTable,
pSiteCostData,
pDestinationSite,
SHASH_REPLACE_IFFOUND);
Status = RtlNtStatusToDosError(NtStatus);
return Status;
}
//
// Create a DFS_SITE_COST_DATA structure that is ready to
// get inserted in to the SiteCost Cache.
//
DFSSTATUS
CreateCostData (
DfsSite *pDestinationSite,
ULONG Cost,
DFSSTATUS ValidityStatus,
PDFS_SITE_COST_DATA *ppNewData);
// Just delete the cache data entry.
static VOID
DfsDeallocateSiteCostData(PVOID pPointer );
DfsSite *
StartSiteEnumerate( SHASH_ITERATOR *pIter )
{
PDFS_SITE_COST_DATA pData = NULL;
pData = (PDFS_SITE_COST_DATA)SHashStartEnumerate( pIter, _pSiteCostTable );
if (pData == NULL)
{
return NULL;
}
return pData->pDestinationSite;
}
DfsSite *
NextSiteEnumerate( SHASH_ITERATOR *pIter )
{
PDFS_SITE_COST_DATA pData = NULL;
pData = (PDFS_SITE_COST_DATA)SHashNextEnumerate( pIter, _pSiteCostTable );
if (pData == NULL)
{
return NULL;
}
return pData->pDestinationSite;
}
VOID
FinishSiteEnumerate( SHASH_ITERATOR *pIter )
{
SHashFinishEnumerate( pIter, _pSiteCostTable);
}
//
VOID
InvalidateCache( VOID );
};
//
// DfsSiteCostSupport class provides a much needed layer of indirection
// between DfsSites and the DfsSiteCostCaches the DfsSites have a one-to-one
// mapping with. Since references on DfsSites are long lived (~12hrs+), and we
// may get flooded with DfsSiteCostCaches consuming a lot of memory, we need
// to do some trimming based simply on the total number of caches sitting around.
// For that, we need this extra level of indirection. This solves races
// between trimming and lookup/inserts.
//
class DfsSiteCostSupport
{
private:
DfsSiteCostCache *_pCostTable;
CRITICAL_SECTION _SiteCostLock;
LONG _InUseCount;
// For aging of the entire cache.
ULONG _LastAccessTime;
BOOL _CritInit;
//
// The MRU hangs off the DfsServerGlobalData, and is guarded
// by the GlobalDataLock.
//
LIST_ENTRY MruListEntry;
BOOLEAN InMruList;
DfsSiteCostSupport()
{
_pCostTable = NULL;
_InUseCount = 1;
_LastAccessTime = 0;
InitializeListHead( &MruListEntry );
InMruList = FALSE;
_CritInit = FALSE;
}
ULONG
InsertInMruList(VOID);
VOID
MoveToFrontOfMruList(VOID);
BOOLEAN
RemoveFromMruList(VOID);
DFSSTATUS
PopLastTableFromMruList(
DfsSiteCostSupport **pTableToRemove);
ULONG
TrimSiteCostCaches(
ULONG MaxCachesToTrim);
public:
~DfsSiteCostSupport()
{
// The in-use count is not tied to
// the existence of this structure.
// Rather, the references on this is implicitly the same
// as the unique DfsSite that's pointing to this.
// Hence, there's no need to call its Release() method here.
// That would've happened already, and the CostTable would've already
// gotten deleted.
if(_CritInit)
{
DeleteCriticalSection( &_SiteCostLock );
}
_LastAccessTime = ULONG_MAX; // To help debugging.
}
// Just create the critical section not the table itself.
// Table is created upon demand because the default behavior is
// sitecosting turned off.
DFSSTATUS
Initialize()
{
DFSSTATUS Status = ERROR_SUCCESS;
_CritInit = InitializeCriticalSectionAndSpinCount( &_SiteCostLock, DFS_CRIT_SPIN_COUNT );
if (_CritInit == FALSE)
{
Status = GetLastError();
return Status;
}
return Status;
}
static
DFSSTATUS
DfsCreateSiteCostSupport(
DfsSiteCostSupport **ppSup );
DFSSTATUS
Acquire(
DfsSiteCostCache **ppCache );
VOID
Release( VOID );
VOID
MarkForDeletion( VOID );
BOOLEAN
IsExpired( VOID );
};
#endif