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
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
|
|
|