#ifndef _DFS_SITE_COST_CACHE_HXX__ #define _DFS_SITE_COST_CACHE_HXX__ #include #include 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