Windows2003-3790/termsrv/drivers/rdp/rdpdd/nchdisp.c
2020-09-30 16:53:55 +02:00

573 lines
19 KiB
C

/****************************************************************************/
// (C) 1997-2000 Microsoft Corp.
//
// chdisp.c
//
// Cache Handler - display driver portion.
//
// The Cache Handler is a data structure manager that holds hash keys
// generated from original data. CH deals with individual caches. There can
// be multiple caches in the system, e.g. the memblt caches in SBC and the
// cursor cache in CM. Each cache can be searched (CH_SearchCache),
// added to (CH_CacheKey), and removed from (CH_RemoveCacheEntry).
//
// Each cache has associated with it the concept of an MRU/LRU list, where
// incoming cached items to a full cache cause other items to be evicted
// based on recent usage. The cache owner is notified of evicted items via
// a callback.
/****************************************************************************/
#include <precmpdd.h>
#define hdrstop
#define TRC_FILE "nchdisp"
#include <adcg.h>
#include <adcs.h>
#include <nchdisp.h>
#define DC_INCLUDE_DATA
#include <ndddata.c>
#undef DC_INCLUDE_DATA
#include <cbchash.c>
/*
* Calculate number of hash buckets given cache enties
*/
UINT RDPCALL CH_CalculateHashBuckets(UINT NumEntries)
{
UINT NumHashBuckets, Temp, i;
DC_BEGIN_FN("CH_CalculateHashBuckets");
if (NumEntries) {
// Good hash table performance can be created by allocating four times
// the number of hash buckets as there are items. Round this number to
// the next higher power of 2 to make masking the hash key bits
// efficient.
Temp = NumHashBuckets = 4 * NumEntries;
// Find the highest bit set in the hash bucket value.
for (i = 0; Temp > 1; i++)
Temp >>= 1;
// See if the original value was actually a power of two, if so we
// need not waste the extra memory by doubling the number of buckets.
if ((unsigned)(1 << i) != NumHashBuckets)
NumHashBuckets = (1 << (i + 1));
}
else {
NumHashBuckets = 0;
}
DC_END_FN();
return NumHashBuckets;
}
/*
* Calculate cache size in bytes given cache entries
*/
UINT RDPCALL CH_CalculateCacheSize(UINT NumEntries)
{
UINT CacheSize, NumHashBuckets;
DC_BEGIN_FN("CH_CalculateCacheSize");
if (NumEntries) {
NumHashBuckets = CH_CalculateHashBuckets(NumEntries);
CacheSize = sizeof(CHCACHEDATA) +
(NumHashBuckets - 1) * sizeof(LIST_ENTRY) +
NumEntries * sizeof(CHNODE);
}
else {
CacheSize = 0;
}
DC_END_FN();
return CacheSize;
}
/*
* Init a cache in pre-allocated memory. pContext is caller-
* defined information particular to the cache. bNotifyRemoveLRU signals that
* the creator wants to be notified via its cache callback of removal of an
* LRU entry. bQueryRemoveLRU means the creator wants to be queried for whether
* a particular LRU cache entry can be removed. If either of bNotifyRemoveLRU
* or bQueryRemoveLRU is nonzero, a cache callback must be provided.
* Returns TRUE on success.
*/
void RDPCALL CH_InitCache(
PCHCACHEDATA pCacheData,
unsigned NumEntries,
void *pContext,
BOOLEAN bNotifyRemoveLRU,
BOOLEAN bQueryRemoveLRU,
CHCACHECALLBACK pfnCacheCallback)
{
BOOLEAN rc = FALSE;
unsigned i;
unsigned NumHashBuckets;
DC_BEGIN_FN("CH_InitCache");
TRC_ASSERT((NumEntries > 0), (TB, "Must have > 0 cache entries"));
NumHashBuckets = CH_CalculateHashBuckets(NumEntries);
// Initialize the cache. Since the cache mem was not zeroed during
// alloc, be sure to init all members whose initial value matters.
pCacheData->HashKeyMask = NumHashBuckets - 1;
for (i = 0; i < NumHashBuckets; i++)
InitializeListHead(&pCacheData->HashBuckets[i]);
pCacheData->pContext = pContext;
pCacheData->pfnCacheCallback = pfnCacheCallback;
pCacheData->bNotifyRemoveLRU = (bNotifyRemoveLRU ? TRUE : FALSE);
pCacheData->bQueryRemoveLRU = (bQueryRemoveLRU ? TRUE : FALSE);
InitializeListHead(&pCacheData->MRUList);
InitializeListHead(&pCacheData->FreeList);
pCacheData->NumEntries = 0;
#ifdef DC_DEBUG
// Init stat counters.
pCacheData->MaxEntries = NumEntries;
pCacheData->NumSearches = 0;
pCacheData->DeepestSearch = 0;
pCacheData->NumHits = 0;
pCacheData->TotalDepthOnHit = 0;
pCacheData->TotalDepthOnMiss = 0;
memset(&pCacheData->SearchHitDepthHistogram, 0,
sizeof(unsigned) * 8);
memset(&pCacheData->SearchMissDepthHistogram, 0,
sizeof(unsigned) * 8);
#endif // DC_DEBUG
// Add all nodes to the free list.
pCacheData->NodeArray = (CHNODE *)((BYTE *)pCacheData +
sizeof(CHCACHEDATA) + (NumHashBuckets - 1) *
sizeof(LIST_ENTRY));
for (i = 0; i < NumEntries; i++)
InsertTailList(&pCacheData->FreeList,
&pCacheData->NodeArray[i].HashList);
TRC_NRM((TB, "Created %u slot cache (%p), hash buckets = %u",
NumEntries, pCacheData, NumHashBuckets));
DC_END_FN();
}
/*
* Search the cache using the provided key.
* Returns FALSE if the key is not present. If the key is present, returns
* TRUE and fills in *pUserDefined with the UserDefined value associated
* with the key and *piCacheEntry with the cache index of the item.
*/
BOOLEAN RDPCALL CH_SearchCache(
CHCACHEHANDLE hCache,
UINT32 Key1,
UINT32 Key2,
void **pUserDefined,
unsigned *piCacheEntry)
{
PCHCACHEDATA pCacheData;
BOOLEAN rc = FALSE;
CHNODE *pNode;
PLIST_ENTRY pBucketListHead, pCurrentListEntry;
#ifdef DC_DEBUG
unsigned SearchDepth = 0;
#endif
DC_BEGIN_FN("CH_SearchCache");
TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
pCacheData = (CHCACHEDATA *)hCache;
// Find the appropriate hash bucket. Then search the bucket list for the
// right key.
pBucketListHead = &pCacheData->HashBuckets[Key1 & pCacheData->HashKeyMask];
pCurrentListEntry = pBucketListHead->Flink;
while (pCurrentListEntry != pBucketListHead) {
#ifdef DC_DEBUG
SearchDepth++;
#endif
pNode = CONTAINING_RECORD(pCurrentListEntry, CHNODE, HashList);
if (pNode->Key1 == Key1 && pNode->Key2 == Key2) {
// Whenever we search a cache entry we bump it to the top of
// both its bucket list (for perf on real access patterns --
// add an entry then access it a lot) and its MRU list.
RemoveEntryList(pCurrentListEntry);
InsertHeadList(pBucketListHead, pCurrentListEntry);
RemoveEntryList(&pNode->MRUList);
InsertHeadList(&pCacheData->MRUList, &pNode->MRUList);
*pUserDefined = pNode->UserDefined;
*piCacheEntry = (unsigned)(pNode - pCacheData->NodeArray);
rc = TRUE;
break;
}
pCurrentListEntry = pCurrentListEntry->Flink;
}
#ifdef DC_DEBUG
TRC_NRM((TB, "Searched hCache %p, depth count %lu, rc = %d",
hCache, SearchDepth, rc));
// Add search to various search stats.
if (SearchDepth > pCacheData->DeepestSearch) {
pCacheData->DeepestSearch = SearchDepth;
TRC_NRM((TB,"hCache %p: New deepest search depth %u",
hCache, SearchDepth));
}
pCacheData->NumSearches++;
if (SearchDepth > 7)
SearchDepth = 7;
if (rc) {
pCacheData->NumHits++;
pCacheData->TotalDepthOnHit += SearchDepth;
pCacheData->SearchHitDepthHistogram[SearchDepth]++;
}
else {
pCacheData->TotalDepthOnMiss += SearchDepth;
pCacheData->SearchMissDepthHistogram[SearchDepth]++;
}
if ((pCacheData->NumSearches % CH_STAT_DISPLAY_FREQ) == 0)
TRC_NRM((TB,"hCache %p: entr=%u/%u, hits/searches=%u/%u, "
"avg hit srch depth=%u, avg miss srch depth=%u, "
"hit depth hist: %u %u %u %u %u %u %u %u; "
"miss depth hist: %u %u %u %u %u %u %u %u",
hCache,
pCacheData->NumEntries,
pCacheData->MaxEntries,
pCacheData->NumHits,
pCacheData->NumSearches,
((pCacheData->TotalDepthOnHit +
(pCacheData->NumHits / 2)) /
pCacheData->NumHits),
((pCacheData->TotalDepthOnMiss +
((pCacheData->NumSearches -
pCacheData->NumHits) / 2)) /
(pCacheData->NumSearches - pCacheData->NumHits)),
pCacheData->SearchHitDepthHistogram[0],
pCacheData->SearchHitDepthHistogram[1],
pCacheData->SearchHitDepthHistogram[2],
pCacheData->SearchHitDepthHistogram[3],
pCacheData->SearchHitDepthHistogram[4],
pCacheData->SearchHitDepthHistogram[5],
pCacheData->SearchHitDepthHistogram[6],
pCacheData->SearchHitDepthHistogram[7],
pCacheData->SearchMissDepthHistogram[0],
pCacheData->SearchMissDepthHistogram[1],
pCacheData->SearchMissDepthHistogram[2],
pCacheData->SearchMissDepthHistogram[3],
pCacheData->SearchMissDepthHistogram[4],
pCacheData->SearchMissDepthHistogram[5],
pCacheData->SearchMissDepthHistogram[6],
pCacheData->SearchMissDepthHistogram[7]));
#endif // defined(DC_DEBUG)
DC_END_FN();
return rc;
}
/*
* Adds a key to a cache. Returns the index of the new entry within the cache.
* If no entry could be allocated because the cache callback refused all
* requests to evict least-recently-used entries, returns CH_KEY_UNCACHABLE.
*/
unsigned RDPCALL CH_CacheKey(
CHCACHEHANDLE hCache,
UINT32 Key1,
UINT32 Key2,
void *UserDefined)
{
PCHCACHEDATA pCacheData;
PCHNODE pNode;
PLIST_ENTRY pListEntry;
unsigned CacheEntryIndex;
DC_BEGIN_FN("CH_CacheKey");
TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
pCacheData = (CHCACHEDATA *)hCache;
// Get a free cache node, either from the free list or by removing
// the last entry in the MRU list.
if (!IsListEmpty(&pCacheData->FreeList)) {
pListEntry = RemoveHeadList(&pCacheData->FreeList);
pNode = CONTAINING_RECORD(pListEntry, CHNODE, HashList);
CacheEntryIndex = (unsigned)(pNode - pCacheData->NodeArray);
pCacheData->NumEntries++;
}
else {
TRC_ASSERT((!IsListEmpty(&pCacheData->MRUList)),
(TB,"Empty free and MRU lists!"));
// Different code paths depending on whether the cache creator
// wants to be queried for LRU removal.
if (pCacheData->bQueryRemoveLRU) {
// Start iterating from the end of the MRU list, asking
// the caller if the entry can be removed.
pListEntry = pCacheData->MRUList.Blink;
for (;;) {
pNode = CONTAINING_RECORD(pListEntry, CHNODE, MRUList);
CacheEntryIndex = (unsigned)(pNode - pCacheData->NodeArray);
if ((*(pCacheData->pfnCacheCallback))
(hCache, CH_EVT_QUERYREMOVEENTRY, CacheEntryIndex,
pNode->UserDefined)) {
// We can use this entry.
RemoveEntryList(pListEntry);
RemoveEntryList(&pNode->HashList);
break;
}
pListEntry = pListEntry->Blink;
if (pListEntry == &pCacheData->MRUList) {
// We reached the end of the list, no removable entries.
CacheEntryIndex = CH_KEY_UNCACHABLE;
goto EndFunc;
}
}
}
else {
pListEntry = RemoveTailList(&pCacheData->MRUList);
pNode = CONTAINING_RECORD(pListEntry, CHNODE, MRUList);
RemoveEntryList(&pNode->HashList);
CacheEntryIndex = (unsigned)(pNode - pCacheData->NodeArray);
}
// Call the cache callback to inform of the entry removal.
if (pCacheData->bNotifyRemoveLRU)
(*(pCacheData->pfnCacheCallback))
(hCache, CH_EVT_ENTRYREMOVED, CacheEntryIndex,
pNode->UserDefined);
}
// Prepare the node for use.
pNode->Key1 = Key1;
pNode->Key2 = Key2;
pNode->UserDefined = UserDefined;
pNode->hCache = hCache;
// Add the node to the front of its bucket list and the MRU list.
InsertHeadList(&pCacheData->MRUList, &pNode->MRUList);
InsertHeadList(&pCacheData->HashBuckets[Key1 & pCacheData->HashKeyMask],
&pNode->HashList);
TRC_NRM((TB, "Cache %p index %u key1 %lx key2 %lx userdef %p",
pCacheData, CacheEntryIndex, Key1, Key2, UserDefined));
EndFunc:
DC_END_FN();
return CacheEntryIndex;
}
/*
* Used by bitmap cache code for force the internal representation of
* the cache structures to a known initial state. This function does minimal
* checking to make sure of cache integrity -- the cache should be cleared
* before forcing the contents of the cache else some cache nodes may illegally
* remain in the MRU lists.
*/
void RDPCALL CH_ForceCacheKeyAtIndex(
CHCACHEHANDLE hCache,
unsigned CacheEntryIndex,
UINT32 Key1,
UINT32 Key2,
void *UserDefined)
{
PCHCACHEDATA pCacheData;
PCHNODE pNode;
DC_BEGIN_FN("CH_ForceCacheKeyAtIndex");
TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
pCacheData = (CHCACHEDATA *)hCache;
// Find the node. Remove it from the free list.
TRC_ASSERT((CacheEntryIndex <= pCacheData->MaxEntries),
(TB,"Index out of bounds!"));
pNode = &pCacheData->NodeArray[CacheEntryIndex];
RemoveEntryList(&pNode->HashList);
// Prepare the node for use.
pNode->Key1 = Key1;
pNode->Key2 = Key2;
pNode->UserDefined = UserDefined;
pNode->hCache = hCache;
// Add the node to the front of its bucket list and the end of the MRU list.
InsertTailList(&pCacheData->MRUList, &pNode->MRUList);
InsertHeadList(&pCacheData->HashBuckets[Key1 & pCacheData->HashKeyMask],
&pNode->HashList);
pCacheData->NumEntries++;
TRC_NRM((TB, "Cache %p index %u key1 %lx key2 %lx userdef %p",
pCacheData, CacheEntryIndex, Key1, Key2, UserDefined));
DC_END_FN();
}
/*
* Remove an entry by its index number.
*/
void RDPCALL CH_RemoveCacheEntry(
CHCACHEHANDLE hCache,
unsigned CacheEntryIndex)
{
PCHNODE pNode;
PCHCACHEDATA pCacheData;
DC_BEGIN_FN("CH_RemoveCacheEntry");
TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
pCacheData = (CHCACHEDATA *)hCache;
pNode = &pCacheData->NodeArray[CacheEntryIndex];
RemoveEntryList(&pNode->MRUList);
RemoveEntryList(&pNode->HashList);
InsertHeadList(&pCacheData->FreeList, &pNode->HashList);
// Call the cache callback to inform of the entry removal.
if (pCacheData->bNotifyRemoveLRU)
(*(pCacheData->pfnCacheCallback))
(hCache, CH_EVT_ENTRYREMOVED, CacheEntryIndex,
pNode->UserDefined);
pCacheData->NumEntries--;
DC_END_FN();
}
/*
* Clears the cache contents.
*/
void RDPCALL CH_ClearCache(CHCACHEHANDLE hCache)
{
PCHCACHEDATA pCacheData;
PLIST_ENTRY pListEntry;
PCHNODE pNode;
DC_BEGIN_FN("CH_ClearCache");
TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
pCacheData = (CHCACHEDATA *)hCache;
TRC_NRM((TB, "Clear cache %p", pCacheData));
// Remove all entries in the MRU list.
while (!IsListEmpty(&pCacheData->MRUList)) {
pListEntry = RemoveHeadList(&pCacheData->MRUList);
pNode = CONTAINING_RECORD(pListEntry, CHNODE, MRUList);
RemoveEntryList(&pNode->HashList);
InsertHeadList(&pCacheData->FreeList, &pNode->HashList);
// Call the cache callback to inform of the entry removal.
if (pCacheData->bNotifyRemoveLRU)
(*(pCacheData->pfnCacheCallback))
(hCache, CH_EVT_ENTRYREMOVED,
(unsigned)(pNode - pCacheData->NodeArray),
pNode->UserDefined);
}
pCacheData->NumEntries = 0;
#if DC_DEBUG
// Reset stats.
pCacheData->NumSearches = 0;
pCacheData->DeepestSearch = 0;
pCacheData->NumHits = 0;
pCacheData->TotalDepthOnHit = 0;
memset(pCacheData->SearchHitDepthHistogram, 0, sizeof(unsigned) * 8);
pCacheData->TotalDepthOnMiss = 0;
memset(pCacheData->SearchMissDepthHistogram, 0, sizeof(unsigned) * 8);
#endif
DC_END_FN();
}
/*
* Touch the node so that it will be moved to the top of the mru list
*/
void RDPCALL CH_TouchCacheEntry(
CHCACHEHANDLE hCache,
unsigned CacheEntryIndex)
{
PCHCACHEDATA pCacheData;
CHNODE *pNode;
DC_BEGIN_FN("CH_TouchCacheEntry");
TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
pCacheData = (CHCACHEDATA *)hCache;
pNode = pCacheData->NodeArray + CacheEntryIndex;
TRC_ASSERT((pNode != NULL), (TB, "NULL cache node"));
RemoveEntryList(&pNode->MRUList);
InsertHeadList(&pCacheData->MRUList, &pNode->MRUList);
DC_END_FN();
}
/*
* Return the LRU cache entry
*/
unsigned RDPCALL CH_GetLRUCacheEntry(
CHCACHEHANDLE hCache)
{
PCHCACHEDATA pCacheData;
PCHNODE pNode;
PLIST_ENTRY pListEntry;
unsigned CacheEntryIndex;
DC_BEGIN_FN("CH_GetLRUCacheEntry");
TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
pCacheData = (CHCACHEDATA *)hCache;
// Get a free cache node, either from the free list or by removing
// the last entry in the MRU list.
if (!IsListEmpty(&pCacheData->MRUList)) {
pListEntry = (&pCacheData->MRUList)->Blink;
pNode = CONTAINING_RECORD(pListEntry, CHNODE, MRUList);
CacheEntryIndex = (unsigned)(pNode - pCacheData->NodeArray);
}
else {
CacheEntryIndex = CH_KEY_UNCACHABLE;
}
DC_END_FN();
return CacheEntryIndex;
}