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.

572 lines
19 KiB

  1. /****************************************************************************/
  2. // (C) 1997-2000 Microsoft Corp.
  3. //
  4. // chdisp.c
  5. //
  6. // Cache Handler - display driver portion.
  7. //
  8. // The Cache Handler is a data structure manager that holds hash keys
  9. // generated from original data. CH deals with individual caches. There can
  10. // be multiple caches in the system, e.g. the memblt caches in SBC and the
  11. // cursor cache in CM. Each cache can be searched (CH_SearchCache),
  12. // added to (CH_CacheKey), and removed from (CH_RemoveCacheEntry).
  13. //
  14. // Each cache has associated with it the concept of an MRU/LRU list, where
  15. // incoming cached items to a full cache cause other items to be evicted
  16. // based on recent usage. The cache owner is notified of evicted items via
  17. // a callback.
  18. /****************************************************************************/
  19. #include <precmpdd.h>
  20. #define hdrstop
  21. #define TRC_FILE "nchdisp"
  22. #include <adcg.h>
  23. #include <adcs.h>
  24. #include <nchdisp.h>
  25. #define DC_INCLUDE_DATA
  26. #include <ndddata.c>
  27. #undef DC_INCLUDE_DATA
  28. #include <cbchash.c>
  29. /*
  30. * Calculate number of hash buckets given cache enties
  31. */
  32. UINT RDPCALL CH_CalculateHashBuckets(UINT NumEntries)
  33. {
  34. UINT NumHashBuckets, Temp, i;
  35. DC_BEGIN_FN("CH_CalculateHashBuckets");
  36. if (NumEntries) {
  37. // Good hash table performance can be created by allocating four times
  38. // the number of hash buckets as there are items. Round this number to
  39. // the next higher power of 2 to make masking the hash key bits
  40. // efficient.
  41. Temp = NumHashBuckets = 4 * NumEntries;
  42. // Find the highest bit set in the hash bucket value.
  43. for (i = 0; Temp > 1; i++)
  44. Temp >>= 1;
  45. // See if the original value was actually a power of two, if so we
  46. // need not waste the extra memory by doubling the number of buckets.
  47. if ((unsigned)(1 << i) != NumHashBuckets)
  48. NumHashBuckets = (1 << (i + 1));
  49. }
  50. else {
  51. NumHashBuckets = 0;
  52. }
  53. DC_END_FN();
  54. return NumHashBuckets;
  55. }
  56. /*
  57. * Calculate cache size in bytes given cache entries
  58. */
  59. UINT RDPCALL CH_CalculateCacheSize(UINT NumEntries)
  60. {
  61. UINT CacheSize, NumHashBuckets;
  62. DC_BEGIN_FN("CH_CalculateCacheSize");
  63. if (NumEntries) {
  64. NumHashBuckets = CH_CalculateHashBuckets(NumEntries);
  65. CacheSize = sizeof(CHCACHEDATA) +
  66. (NumHashBuckets - 1) * sizeof(LIST_ENTRY) +
  67. NumEntries * sizeof(CHNODE);
  68. }
  69. else {
  70. CacheSize = 0;
  71. }
  72. DC_END_FN();
  73. return CacheSize;
  74. }
  75. /*
  76. * Init a cache in pre-allocated memory. pContext is caller-
  77. * defined information particular to the cache. bNotifyRemoveLRU signals that
  78. * the creator wants to be notified via its cache callback of removal of an
  79. * LRU entry. bQueryRemoveLRU means the creator wants to be queried for whether
  80. * a particular LRU cache entry can be removed. If either of bNotifyRemoveLRU
  81. * or bQueryRemoveLRU is nonzero, a cache callback must be provided.
  82. * Returns TRUE on success.
  83. */
  84. void RDPCALL CH_InitCache(
  85. PCHCACHEDATA pCacheData,
  86. unsigned NumEntries,
  87. void *pContext,
  88. BOOLEAN bNotifyRemoveLRU,
  89. BOOLEAN bQueryRemoveLRU,
  90. CHCACHECALLBACK pfnCacheCallback)
  91. {
  92. BOOLEAN rc = FALSE;
  93. unsigned i;
  94. unsigned NumHashBuckets;
  95. DC_BEGIN_FN("CH_InitCache");
  96. TRC_ASSERT((NumEntries > 0), (TB, "Must have > 0 cache entries"));
  97. NumHashBuckets = CH_CalculateHashBuckets(NumEntries);
  98. // Initialize the cache. Since the cache mem was not zeroed during
  99. // alloc, be sure to init all members whose initial value matters.
  100. pCacheData->HashKeyMask = NumHashBuckets - 1;
  101. for (i = 0; i < NumHashBuckets; i++)
  102. InitializeListHead(&pCacheData->HashBuckets[i]);
  103. pCacheData->pContext = pContext;
  104. pCacheData->pfnCacheCallback = pfnCacheCallback;
  105. pCacheData->bNotifyRemoveLRU = (bNotifyRemoveLRU ? TRUE : FALSE);
  106. pCacheData->bQueryRemoveLRU = (bQueryRemoveLRU ? TRUE : FALSE);
  107. InitializeListHead(&pCacheData->MRUList);
  108. InitializeListHead(&pCacheData->FreeList);
  109. pCacheData->NumEntries = 0;
  110. #ifdef DC_DEBUG
  111. // Init stat counters.
  112. pCacheData->MaxEntries = NumEntries;
  113. pCacheData->NumSearches = 0;
  114. pCacheData->DeepestSearch = 0;
  115. pCacheData->NumHits = 0;
  116. pCacheData->TotalDepthOnHit = 0;
  117. pCacheData->TotalDepthOnMiss = 0;
  118. memset(&pCacheData->SearchHitDepthHistogram, 0,
  119. sizeof(unsigned) * 8);
  120. memset(&pCacheData->SearchMissDepthHistogram, 0,
  121. sizeof(unsigned) * 8);
  122. #endif // DC_DEBUG
  123. // Add all nodes to the free list.
  124. pCacheData->NodeArray = (CHNODE *)((BYTE *)pCacheData +
  125. sizeof(CHCACHEDATA) + (NumHashBuckets - 1) *
  126. sizeof(LIST_ENTRY));
  127. for (i = 0; i < NumEntries; i++)
  128. InsertTailList(&pCacheData->FreeList,
  129. &pCacheData->NodeArray[i].HashList);
  130. TRC_NRM((TB, "Created %u slot cache (%p), hash buckets = %u",
  131. NumEntries, pCacheData, NumHashBuckets));
  132. DC_END_FN();
  133. }
  134. /*
  135. * Search the cache using the provided key.
  136. * Returns FALSE if the key is not present. If the key is present, returns
  137. * TRUE and fills in *pUserDefined with the UserDefined value associated
  138. * with the key and *piCacheEntry with the cache index of the item.
  139. */
  140. BOOLEAN RDPCALL CH_SearchCache(
  141. CHCACHEHANDLE hCache,
  142. UINT32 Key1,
  143. UINT32 Key2,
  144. void **pUserDefined,
  145. unsigned *piCacheEntry)
  146. {
  147. PCHCACHEDATA pCacheData;
  148. BOOLEAN rc = FALSE;
  149. CHNODE *pNode;
  150. PLIST_ENTRY pBucketListHead, pCurrentListEntry;
  151. #ifdef DC_DEBUG
  152. unsigned SearchDepth = 0;
  153. #endif
  154. DC_BEGIN_FN("CH_SearchCache");
  155. TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
  156. pCacheData = (CHCACHEDATA *)hCache;
  157. // Find the appropriate hash bucket. Then search the bucket list for the
  158. // right key.
  159. pBucketListHead = &pCacheData->HashBuckets[Key1 & pCacheData->HashKeyMask];
  160. pCurrentListEntry = pBucketListHead->Flink;
  161. while (pCurrentListEntry != pBucketListHead) {
  162. #ifdef DC_DEBUG
  163. SearchDepth++;
  164. #endif
  165. pNode = CONTAINING_RECORD(pCurrentListEntry, CHNODE, HashList);
  166. if (pNode->Key1 == Key1 && pNode->Key2 == Key2) {
  167. // Whenever we search a cache entry we bump it to the top of
  168. // both its bucket list (for perf on real access patterns --
  169. // add an entry then access it a lot) and its MRU list.
  170. RemoveEntryList(pCurrentListEntry);
  171. InsertHeadList(pBucketListHead, pCurrentListEntry);
  172. RemoveEntryList(&pNode->MRUList);
  173. InsertHeadList(&pCacheData->MRUList, &pNode->MRUList);
  174. *pUserDefined = pNode->UserDefined;
  175. *piCacheEntry = (unsigned)(pNode - pCacheData->NodeArray);
  176. rc = TRUE;
  177. break;
  178. }
  179. pCurrentListEntry = pCurrentListEntry->Flink;
  180. }
  181. #ifdef DC_DEBUG
  182. TRC_NRM((TB, "Searched hCache %p, depth count %lu, rc = %d",
  183. hCache, SearchDepth, rc));
  184. // Add search to various search stats.
  185. if (SearchDepth > pCacheData->DeepestSearch) {
  186. pCacheData->DeepestSearch = SearchDepth;
  187. TRC_NRM((TB,"hCache %p: New deepest search depth %u",
  188. hCache, SearchDepth));
  189. }
  190. pCacheData->NumSearches++;
  191. if (SearchDepth > 7)
  192. SearchDepth = 7;
  193. if (rc) {
  194. pCacheData->NumHits++;
  195. pCacheData->TotalDepthOnHit += SearchDepth;
  196. pCacheData->SearchHitDepthHistogram[SearchDepth]++;
  197. }
  198. else {
  199. pCacheData->TotalDepthOnMiss += SearchDepth;
  200. pCacheData->SearchMissDepthHistogram[SearchDepth]++;
  201. }
  202. if ((pCacheData->NumSearches % CH_STAT_DISPLAY_FREQ) == 0)
  203. TRC_NRM((TB,"hCache %p: entr=%u/%u, hits/searches=%u/%u, "
  204. "avg hit srch depth=%u, avg miss srch depth=%u, "
  205. "hit depth hist: %u %u %u %u %u %u %u %u; "
  206. "miss depth hist: %u %u %u %u %u %u %u %u",
  207. hCache,
  208. pCacheData->NumEntries,
  209. pCacheData->MaxEntries,
  210. pCacheData->NumHits,
  211. pCacheData->NumSearches,
  212. ((pCacheData->TotalDepthOnHit +
  213. (pCacheData->NumHits / 2)) /
  214. pCacheData->NumHits),
  215. ((pCacheData->TotalDepthOnMiss +
  216. ((pCacheData->NumSearches -
  217. pCacheData->NumHits) / 2)) /
  218. (pCacheData->NumSearches - pCacheData->NumHits)),
  219. pCacheData->SearchHitDepthHistogram[0],
  220. pCacheData->SearchHitDepthHistogram[1],
  221. pCacheData->SearchHitDepthHistogram[2],
  222. pCacheData->SearchHitDepthHistogram[3],
  223. pCacheData->SearchHitDepthHistogram[4],
  224. pCacheData->SearchHitDepthHistogram[5],
  225. pCacheData->SearchHitDepthHistogram[6],
  226. pCacheData->SearchHitDepthHistogram[7],
  227. pCacheData->SearchMissDepthHistogram[0],
  228. pCacheData->SearchMissDepthHistogram[1],
  229. pCacheData->SearchMissDepthHistogram[2],
  230. pCacheData->SearchMissDepthHistogram[3],
  231. pCacheData->SearchMissDepthHistogram[4],
  232. pCacheData->SearchMissDepthHistogram[5],
  233. pCacheData->SearchMissDepthHistogram[6],
  234. pCacheData->SearchMissDepthHistogram[7]));
  235. #endif // defined(DC_DEBUG)
  236. DC_END_FN();
  237. return rc;
  238. }
  239. /*
  240. * Adds a key to a cache. Returns the index of the new entry within the cache.
  241. * If no entry could be allocated because the cache callback refused all
  242. * requests to evict least-recently-used entries, returns CH_KEY_UNCACHABLE.
  243. */
  244. unsigned RDPCALL CH_CacheKey(
  245. CHCACHEHANDLE hCache,
  246. UINT32 Key1,
  247. UINT32 Key2,
  248. void *UserDefined)
  249. {
  250. PCHCACHEDATA pCacheData;
  251. PCHNODE pNode;
  252. PLIST_ENTRY pListEntry;
  253. unsigned CacheEntryIndex;
  254. DC_BEGIN_FN("CH_CacheKey");
  255. TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
  256. pCacheData = (CHCACHEDATA *)hCache;
  257. // Get a free cache node, either from the free list or by removing
  258. // the last entry in the MRU list.
  259. if (!IsListEmpty(&pCacheData->FreeList)) {
  260. pListEntry = RemoveHeadList(&pCacheData->FreeList);
  261. pNode = CONTAINING_RECORD(pListEntry, CHNODE, HashList);
  262. CacheEntryIndex = (unsigned)(pNode - pCacheData->NodeArray);
  263. pCacheData->NumEntries++;
  264. }
  265. else {
  266. TRC_ASSERT((!IsListEmpty(&pCacheData->MRUList)),
  267. (TB,"Empty free and MRU lists!"));
  268. // Different code paths depending on whether the cache creator
  269. // wants to be queried for LRU removal.
  270. if (pCacheData->bQueryRemoveLRU) {
  271. // Start iterating from the end of the MRU list, asking
  272. // the caller if the entry can be removed.
  273. pListEntry = pCacheData->MRUList.Blink;
  274. for (;;) {
  275. pNode = CONTAINING_RECORD(pListEntry, CHNODE, MRUList);
  276. CacheEntryIndex = (unsigned)(pNode - pCacheData->NodeArray);
  277. if ((*(pCacheData->pfnCacheCallback))
  278. (hCache, CH_EVT_QUERYREMOVEENTRY, CacheEntryIndex,
  279. pNode->UserDefined)) {
  280. // We can use this entry.
  281. RemoveEntryList(pListEntry);
  282. RemoveEntryList(&pNode->HashList);
  283. break;
  284. }
  285. pListEntry = pListEntry->Blink;
  286. if (pListEntry == &pCacheData->MRUList) {
  287. // We reached the end of the list, no removable entries.
  288. CacheEntryIndex = CH_KEY_UNCACHABLE;
  289. goto EndFunc;
  290. }
  291. }
  292. }
  293. else {
  294. pListEntry = RemoveTailList(&pCacheData->MRUList);
  295. pNode = CONTAINING_RECORD(pListEntry, CHNODE, MRUList);
  296. RemoveEntryList(&pNode->HashList);
  297. CacheEntryIndex = (unsigned)(pNode - pCacheData->NodeArray);
  298. }
  299. // Call the cache callback to inform of the entry removal.
  300. if (pCacheData->bNotifyRemoveLRU)
  301. (*(pCacheData->pfnCacheCallback))
  302. (hCache, CH_EVT_ENTRYREMOVED, CacheEntryIndex,
  303. pNode->UserDefined);
  304. }
  305. // Prepare the node for use.
  306. pNode->Key1 = Key1;
  307. pNode->Key2 = Key2;
  308. pNode->UserDefined = UserDefined;
  309. pNode->hCache = hCache;
  310. // Add the node to the front of its bucket list and the MRU list.
  311. InsertHeadList(&pCacheData->MRUList, &pNode->MRUList);
  312. InsertHeadList(&pCacheData->HashBuckets[Key1 & pCacheData->HashKeyMask],
  313. &pNode->HashList);
  314. TRC_NRM((TB, "Cache %p index %u key1 %lx key2 %lx userdef %p",
  315. pCacheData, CacheEntryIndex, Key1, Key2, UserDefined));
  316. EndFunc:
  317. DC_END_FN();
  318. return CacheEntryIndex;
  319. }
  320. /*
  321. * Used by bitmap cache code for force the internal representation of
  322. * the cache structures to a known initial state. This function does minimal
  323. * checking to make sure of cache integrity -- the cache should be cleared
  324. * before forcing the contents of the cache else some cache nodes may illegally
  325. * remain in the MRU lists.
  326. */
  327. void RDPCALL CH_ForceCacheKeyAtIndex(
  328. CHCACHEHANDLE hCache,
  329. unsigned CacheEntryIndex,
  330. UINT32 Key1,
  331. UINT32 Key2,
  332. void *UserDefined)
  333. {
  334. PCHCACHEDATA pCacheData;
  335. PCHNODE pNode;
  336. DC_BEGIN_FN("CH_ForceCacheKeyAtIndex");
  337. TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
  338. pCacheData = (CHCACHEDATA *)hCache;
  339. // Find the node. Remove it from the free list.
  340. TRC_ASSERT((CacheEntryIndex <= pCacheData->MaxEntries),
  341. (TB,"Index out of bounds!"));
  342. pNode = &pCacheData->NodeArray[CacheEntryIndex];
  343. RemoveEntryList(&pNode->HashList);
  344. // Prepare the node for use.
  345. pNode->Key1 = Key1;
  346. pNode->Key2 = Key2;
  347. pNode->UserDefined = UserDefined;
  348. pNode->hCache = hCache;
  349. // Add the node to the front of its bucket list and the end of the MRU list.
  350. InsertTailList(&pCacheData->MRUList, &pNode->MRUList);
  351. InsertHeadList(&pCacheData->HashBuckets[Key1 & pCacheData->HashKeyMask],
  352. &pNode->HashList);
  353. pCacheData->NumEntries++;
  354. TRC_NRM((TB, "Cache %p index %u key1 %lx key2 %lx userdef %p",
  355. pCacheData, CacheEntryIndex, Key1, Key2, UserDefined));
  356. DC_END_FN();
  357. }
  358. /*
  359. * Remove an entry by its index number.
  360. */
  361. void RDPCALL CH_RemoveCacheEntry(
  362. CHCACHEHANDLE hCache,
  363. unsigned CacheEntryIndex)
  364. {
  365. PCHNODE pNode;
  366. PCHCACHEDATA pCacheData;
  367. DC_BEGIN_FN("CH_RemoveCacheEntry");
  368. TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
  369. pCacheData = (CHCACHEDATA *)hCache;
  370. pNode = &pCacheData->NodeArray[CacheEntryIndex];
  371. RemoveEntryList(&pNode->MRUList);
  372. RemoveEntryList(&pNode->HashList);
  373. InsertHeadList(&pCacheData->FreeList, &pNode->HashList);
  374. // Call the cache callback to inform of the entry removal.
  375. if (pCacheData->bNotifyRemoveLRU)
  376. (*(pCacheData->pfnCacheCallback))
  377. (hCache, CH_EVT_ENTRYREMOVED, CacheEntryIndex,
  378. pNode->UserDefined);
  379. pCacheData->NumEntries--;
  380. DC_END_FN();
  381. }
  382. /*
  383. * Clears the cache contents.
  384. */
  385. void RDPCALL CH_ClearCache(CHCACHEHANDLE hCache)
  386. {
  387. PCHCACHEDATA pCacheData;
  388. PLIST_ENTRY pListEntry;
  389. PCHNODE pNode;
  390. DC_BEGIN_FN("CH_ClearCache");
  391. TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
  392. pCacheData = (CHCACHEDATA *)hCache;
  393. TRC_NRM((TB, "Clear cache %p", pCacheData));
  394. // Remove all entries in the MRU list.
  395. while (!IsListEmpty(&pCacheData->MRUList)) {
  396. pListEntry = RemoveHeadList(&pCacheData->MRUList);
  397. pNode = CONTAINING_RECORD(pListEntry, CHNODE, MRUList);
  398. RemoveEntryList(&pNode->HashList);
  399. InsertHeadList(&pCacheData->FreeList, &pNode->HashList);
  400. // Call the cache callback to inform of the entry removal.
  401. if (pCacheData->bNotifyRemoveLRU)
  402. (*(pCacheData->pfnCacheCallback))
  403. (hCache, CH_EVT_ENTRYREMOVED,
  404. (unsigned)(pNode - pCacheData->NodeArray),
  405. pNode->UserDefined);
  406. }
  407. pCacheData->NumEntries = 0;
  408. #if DC_DEBUG
  409. // Reset stats.
  410. pCacheData->NumSearches = 0;
  411. pCacheData->DeepestSearch = 0;
  412. pCacheData->NumHits = 0;
  413. pCacheData->TotalDepthOnHit = 0;
  414. memset(pCacheData->SearchHitDepthHistogram, 0, sizeof(unsigned) * 8);
  415. pCacheData->TotalDepthOnMiss = 0;
  416. memset(pCacheData->SearchMissDepthHistogram, 0, sizeof(unsigned) * 8);
  417. #endif
  418. DC_END_FN();
  419. }
  420. /*
  421. * Touch the node so that it will be moved to the top of the mru list
  422. */
  423. void RDPCALL CH_TouchCacheEntry(
  424. CHCACHEHANDLE hCache,
  425. unsigned CacheEntryIndex)
  426. {
  427. PCHCACHEDATA pCacheData;
  428. CHNODE *pNode;
  429. DC_BEGIN_FN("CH_TouchCacheEntry");
  430. TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
  431. pCacheData = (CHCACHEDATA *)hCache;
  432. pNode = pCacheData->NodeArray + CacheEntryIndex;
  433. TRC_ASSERT((pNode != NULL), (TB, "NULL cache node"));
  434. RemoveEntryList(&pNode->MRUList);
  435. InsertHeadList(&pCacheData->MRUList, &pNode->MRUList);
  436. DC_END_FN();
  437. }
  438. /*
  439. * Return the LRU cache entry
  440. */
  441. unsigned RDPCALL CH_GetLRUCacheEntry(
  442. CHCACHEHANDLE hCache)
  443. {
  444. PCHCACHEDATA pCacheData;
  445. PCHNODE pNode;
  446. PLIST_ENTRY pListEntry;
  447. unsigned CacheEntryIndex;
  448. DC_BEGIN_FN("CH_GetLRUCacheEntry");
  449. TRC_ASSERT((hCache != NULL), (TB, "NULL cache handle"));
  450. pCacheData = (CHCACHEDATA *)hCache;
  451. // Get a free cache node, either from the free list or by removing
  452. // the last entry in the MRU list.
  453. if (!IsListEmpty(&pCacheData->MRUList)) {
  454. pListEntry = (&pCacheData->MRUList)->Blink;
  455. pNode = CONTAINING_RECORD(pListEntry, CHNODE, MRUList);
  456. CacheEntryIndex = (unsigned)(pNode - pCacheData->NodeArray);
  457. }
  458. else {
  459. CacheEntryIndex = CH_KEY_UNCACHABLE;
  460. }
  461. DC_END_FN();
  462. return CacheEntryIndex;
  463. }