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.

758 lines
17 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1998 **/
  4. /**********************************************************************/
  5. /*
  6. tlcach.cxx
  7. This module implements the private interface to the two-level cache. This
  8. cache is used to either cache the contents of a file or to copy the
  9. file and cache the handle to it
  10. FILE HISTORY:
  11. BAlam 10-31-1998 Created
  12. */
  13. #include "TsunamiP.Hxx"
  14. #pragma hdrstop
  15. // #define LOCAL_ALLOC 1
  16. #define PRIVATE_HEAP 1
  17. // #define VIRTUAL_ALLOC 1
  18. // #define LOOKASIDE 1
  19. #include "issched.hxx"
  20. #include "tlcach.h"
  21. //
  22. // Globals
  23. //
  24. // Memory Cache size statistics
  25. DWORDLONG g_cbMaxMemCacheSize;
  26. DWORDLONG g_cbMemCacheSize;
  27. DWORDLONG g_cbMaxMemCacheUsed;
  28. DWORD g_cMemCacheElements;
  29. DWORD g_cMaxMemCacheElementsUsed;
  30. // Cache utility
  31. CRITICAL_SECTION g_csMemCache;
  32. DWORD g_dwMemCacheSizeCookie;
  33. DWORD g_cmsecAdjustmentTime = DEFAULT_ADJUSTMENT_TIME;
  34. CHAR g_achTempPath[ MAX_PATH + 1 ];
  35. #if defined(LOCAL_ALLOC)
  36. const char g_szTsunamiAllocator[] = "LocalAlloc";
  37. #elif defined(PRIVATE_HEAP)
  38. const char g_szTsunamiAllocator[] = "PrivateHeap";
  39. HANDLE g_hMemCacheHeap = NULL;
  40. #elif defined(VIRTUAL_ALLOC)
  41. const char g_szTsunamiAllocator[] = "VirtualAlloc";
  42. #elif defined(LOOKASIDE)
  43. const char g_szTsunamiAllocator[] = "LookAside";
  44. enum {
  45. ENORMOUS = 300,
  46. MANY = 200,
  47. LOTS = 100,
  48. SOME = 50,
  49. FEW = 20,
  50. MINIMAL = 4,
  51. KB = 1024,
  52. };
  53. ALLOC_CACHE_CONFIGURATION g_aacc[] = {
  54. { 1, SOME, 128},
  55. { 1, SOME, 256},
  56. { 1, SOME, 512},
  57. { 1, LOTS, 768},
  58. { 1, LOTS, 1 * KB},
  59. { 1, ENORMOUS, 2 * KB},
  60. { 1, ENORMOUS, 3 * KB},
  61. { 1, ENORMOUS, 4 * KB},
  62. { 1, MANY, 5 * KB},
  63. { 1, MANY, 6 * KB},
  64. { 1, MANY, 7 * KB},
  65. { 1, MANY, 8 * KB},
  66. { 1, LOTS, 9 * KB},
  67. { 1, LOTS, 10 * KB},
  68. { 1, LOTS, 11 * KB},
  69. { 1, LOTS, 12 * KB},
  70. { 1, LOTS, 14 * KB},
  71. { 1, SOME, 16 * KB},
  72. { 1, SOME, 20 * KB},
  73. { 1, SOME, 24 * KB},
  74. { 1, SOME, 28 * KB},
  75. { 1, SOME, 32 * KB},
  76. { 1, FEW, 36 * KB},
  77. { 1, FEW, 40 * KB},
  78. { 1, FEW, 44 * KB},
  79. { 1, FEW, 48 * KB},
  80. { 1, FEW, 56 * KB},
  81. { 1, MINIMAL, 64 * KB},
  82. { 1, MINIMAL, 80 * KB},
  83. { 1, MINIMAL, 96 * KB},
  84. { 1, MINIMAL, 128 * KB},
  85. { 1, MINIMAL, 160 * KB},
  86. { 1, MINIMAL, 192 * KB},
  87. { 1, MINIMAL, 256 * KB},
  88. };
  89. CLookAside* g_laMemCache = NULL;
  90. // paacc must be sorted in increasing sizes, it is sorted right now from
  91. // where it is called
  92. CLookAside::CLookAside(
  93. ALLOC_CACHE_CONFIGURATION* paacc,
  94. SIZE_T caacc)
  95. : m_dwSignature(SIGNATURE),
  96. m_apach(NULL),
  97. m_aacc(NULL),
  98. m_cach(0),
  99. m_nMinSize(0),
  100. m_nMaxSize(0)
  101. {
  102. ALLOC_CACHE_HANDLER** apach = new ALLOC_CACHE_HANDLER* [caacc];
  103. if (apach == NULL)
  104. return;
  105. ALLOC_CACHE_CONFIGURATION* paacc2 = new ALLOC_CACHE_CONFIGURATION [caacc];
  106. if (paacc2 == NULL)
  107. return;
  108. for (SIZE_T i = 0; i < caacc; ++i)
  109. {
  110. ALLOC_CACHE_CONFIGURATION acc = paacc[i];
  111. acc.cbSize -= HEAP_PREFIX + HEAP_SUFFIX + ACACHE_OVERHEAD;
  112. paacc2[i] = acc;
  113. if (i == 0)
  114. m_nMinSize = acc.cbSize;
  115. else if (i == caacc-1)
  116. m_nMaxSize = acc.cbSize;
  117. char szName[40];
  118. sprintf(szName, "TsLookAside-%d", acc.cbSize);
  119. apach[i] = new ALLOC_CACHE_HANDLER(szName, &acc);
  120. bool fInOrder = (i == 0 || paacc2[i].cbSize > paacc2[i-1].cbSize);
  121. if (!fInOrder)
  122. {
  123. DBGPRINTF((DBG_CONTEXT, "CLookAside: config array out of order\n"));
  124. }
  125. if (apach[i] == NULL || !fInOrder)
  126. {
  127. for (SIZE_T j = i; j-- > 0; )
  128. {
  129. delete apach[j];
  130. }
  131. m_nMinSize = m_nMaxSize = 0;
  132. return;
  133. }
  134. }
  135. m_apach = apach;
  136. m_aacc = paacc2;
  137. m_cach = caacc;
  138. }
  139. CLookAside::~CLookAside()
  140. {
  141. for (SIZE_T j = m_cach; j-- > 0; )
  142. {
  143. delete m_apach[j];
  144. }
  145. delete [] m_apach;
  146. delete [] m_aacc;
  147. m_dwSignature = SIGNATURE_X;
  148. }
  149. int
  150. CLookAside::_FindAllocator(
  151. IN DWORD cbSize)
  152. {
  153. if (cbSize > m_nMaxSize)
  154. return -1; // too big to cache
  155. else if (cbSize <= m_nMinSize)
  156. return 0;
  157. int l = 0, h = m_cach-1;
  158. do
  159. {
  160. DBG_ASSERT(m_aacc[l].cbSize < cbSize && cbSize <= m_aacc[h].cbSize);
  161. unsigned m = (unsigned) (l + h) >> 1;
  162. DBG_ASSERT(m > 0);
  163. if (m_aacc[m-1].cbSize < cbSize && cbSize <= m_aacc[m].cbSize)
  164. return m;
  165. else if (m_aacc[m].cbSize < cbSize)
  166. l = m+1;
  167. else
  168. h = m-1;
  169. } while (l <= h);
  170. DBG_ASSERT(FALSE);
  171. return -1;
  172. }
  173. LPVOID
  174. CLookAside::Alloc(
  175. IN DWORD cbSize)
  176. {
  177. LPVOID pv = NULL;
  178. int iAllocator = _FindAllocator(cbSize);
  179. if (iAllocator < 0)
  180. {
  181. pv = VirtualAlloc(NULL, cbSize, MEM_COMMIT, PAGE_READWRITE);
  182. }
  183. else
  184. {
  185. DBG_ASSERT(iAllocator < m_cach && cbSize <= m_aacc[iAllocator].cbSize);
  186. pv = m_apach[iAllocator]->Alloc();
  187. }
  188. return pv;
  189. }
  190. BOOL
  191. CLookAside::Free(
  192. IN LPVOID pv,
  193. IN DWORD cbSize)
  194. {
  195. int iAllocator = _FindAllocator(cbSize);
  196. if (iAllocator < 0)
  197. {
  198. VirtualFree(pv, 0, MEM_RELEASE);
  199. }
  200. else
  201. {
  202. DBG_ASSERT(iAllocator < m_cach && cbSize <= m_aacc[iAllocator].cbSize);
  203. m_apach[iAllocator]->Free(pv);
  204. }
  205. return TRUE;
  206. }
  207. #endif // LOCAL_ALLOC
  208. //
  209. // Defines
  210. //
  211. #define MemCacheLock() ( EnterCriticalSection( &g_csMemCache ) )
  212. #define MemCacheUnlock() ( LeaveCriticalSection( &g_csMemCache ) )
  213. //
  214. // Private declarations
  215. //
  216. VOID
  217. WINAPI
  218. I_MemoryCacheSizeAdjustor(
  219. PVOID pContext
  220. );
  221. //
  222. // Global functions
  223. //
  224. DWORD
  225. InitializeTwoLevelCache(
  226. IN DWORDLONG cbMemoryCacheSize
  227. )
  228. /*++
  229. Routine Description:
  230. Initialize memory cache
  231. Arguments:
  232. cbMemoryCacheSize - Size of memory cache (in bytes).
  233. Return Value:
  234. ERROR_SUCCESS if successful, else Win32 Error
  235. --*/
  236. {
  237. DWORD dwError = ERROR_SUCCESS;
  238. if (DisableTsunamiCaching)
  239. return dwError;
  240. INITIALIZE_CRITICAL_SECTION( &g_csMemCache );
  241. if ( cbMemoryCacheSize == (DWORDLONG)-1 )
  242. {
  243. MEMORYSTATUSEX MemoryStatus;
  244. MemoryStatus.dwLength = sizeof MemoryStatus;
  245. //
  246. // Get our own estimate of size of cache
  247. //
  248. GlobalMemoryStatusEx( &MemoryStatus );
  249. g_cbMaxMemCacheSize = min( MemoryStatus.ullAvailPhys,
  250. MemoryStatus.ullTotalVirtual ) / 2;
  251. //
  252. // Schedule a max cache size adjustor
  253. //
  254. g_dwMemCacheSizeCookie = ScheduleWorkItem( I_MemoryCacheSizeAdjustor,
  255. NULL,
  256. g_cmsecAdjustmentTime,
  257. TRUE );
  258. if ( !g_dwMemCacheSizeCookie )
  259. {
  260. dwError = GetLastError();
  261. }
  262. }
  263. else
  264. {
  265. g_cbMaxMemCacheSize = cbMemoryCacheSize;
  266. }
  267. if ( dwError == ERROR_SUCCESS )
  268. {
  269. #if defined(LOCAL_ALLOC)
  270. // no initialization needed
  271. #elif defined(PRIVATE_HEAP)
  272. g_hMemCacheHeap = HeapCreate( 0, 0, 0 );
  273. if (g_hMemCacheHeap == NULL)
  274. dwError = ERROR_NOT_ENOUGH_MEMORY;
  275. #elif defined(VIRTUAL_ALLOC)
  276. // no initialization needed
  277. #elif defined(LOOKASIDE)
  278. g_laMemCache = new CLookAside(g_aacc,
  279. sizeof(g_aacc)/sizeof(g_aacc[0]));
  280. if (g_laMemCache == NULL)
  281. dwError = ERROR_NOT_ENOUGH_MEMORY;
  282. #endif // LOCAL_ALLOC
  283. }
  284. if ( dwError != ERROR_SUCCESS )
  285. {
  286. TerminateTwoLevelCache();
  287. }
  288. return dwError;
  289. }
  290. DWORD
  291. ReadFileIntoMemoryCache(
  292. IN HANDLE hFile,
  293. IN DWORD cbFile,
  294. OUT DWORD * pcbRequired,
  295. OUT VOID ** ppvBuffer
  296. )
  297. /*++
  298. Routine Description:
  299. Read contents of file into a buffer
  300. Arguments:
  301. hFile - Handle to valid file
  302. cbFile - Size of file ( ==> size of buffer )
  303. pcbRequired - Filled in with number of bytes required to be removed from
  304. cache to fit element
  305. ppvBuffer - Filled in with pointer to buffer with file contents. Set
  306. to NULL on failure
  307. Return Value:
  308. ERROR_SUCCESS if successful, else Win32 Error
  309. --*/
  310. {
  311. BOOL bRet;
  312. VOID * pvBuffer = NULL;
  313. DWORD cbRead;
  314. OVERLAPPED Overlapped;
  315. DWORD dwError = ERROR_SUCCESS;
  316. DBG_ASSERT( hFile && ( hFile != INVALID_HANDLE_VALUE ) );
  317. DBG_ASSERT( pcbRequired != NULL );
  318. DBG_ASSERT( ppvBuffer != NULL );
  319. *pcbRequired = 0;
  320. //
  321. // First check whether there will be room in cache for the blob
  322. //
  323. MemCacheLock();
  324. if ( ( g_cbMemCacheSize + cbFile ) > g_cbMaxMemCacheSize )
  325. {
  326. //
  327. // Not enough room for cache
  328. //
  329. MemCacheUnlock();
  330. *pcbRequired = DIFF(( g_cbMemCacheSize + cbFile ) - g_cbMaxMemCacheSize);
  331. dwError = ERROR_NOT_ENOUGH_MEMORY;
  332. goto Finished;
  333. }
  334. g_cbMemCacheSize += cbFile;
  335. g_cbMaxMemCacheUsed = max( g_cbMaxMemCacheUsed, g_cbMemCacheSize );
  336. g_cMemCacheElements++;
  337. g_cMaxMemCacheElementsUsed = max( g_cMaxMemCacheElementsUsed, g_cMemCacheElements );
  338. MemCacheUnlock();
  339. *pcbRequired = 0;
  340. //
  341. // Allocate blob for file
  342. //
  343. #if defined(LOCAL_ALLOC)
  344. pvBuffer = LocalAlloc( LMEM_FIXED, cbFile );
  345. #elif defined(PRIVATE_HEAP)
  346. DBG_ASSERT(g_hMemCacheHeap != NULL);
  347. pvBuffer = HeapAlloc( g_hMemCacheHeap, 0, cbFile );
  348. #elif defined(VIRTUAL_ALLOC)
  349. pvBuffer = VirtualAlloc(NULL, cbFile, MEM_COMMIT, PAGE_READWRITE);
  350. #elif defined(LOOKASIDE)
  351. pvBuffer = g_laMemCache->Alloc(cbFile);
  352. #endif // LOCAL_ALLOC
  353. if ( pvBuffer == NULL )
  354. {
  355. MemCacheLock();
  356. g_cbMemCacheSize -= cbFile;
  357. MemCacheUnlock();
  358. dwError = ERROR_NOT_ENOUGH_MEMORY;
  359. goto Finished;
  360. }
  361. //
  362. // Read file into blob
  363. //
  364. Overlapped.Offset = 0;
  365. Overlapped.OffsetHigh = 0;
  366. Overlapped.hEvent = NULL;
  367. bRet = ReadFile( hFile,
  368. pvBuffer,
  369. cbFile,
  370. &cbRead,
  371. &Overlapped );
  372. if ( !bRet )
  373. {
  374. dwError = GetLastError();
  375. if ( dwError != ERROR_IO_PENDING )
  376. {
  377. //
  378. // Something bad happened
  379. //
  380. goto Finished;
  381. }
  382. else
  383. {
  384. //
  385. // Reset the error lest we confuse ourselves later on cleanup
  386. //
  387. dwError = ERROR_SUCCESS;
  388. //
  389. // Wait for async read to complete
  390. //
  391. bRet = GetOverlappedResult( hFile,
  392. &Overlapped,
  393. &cbRead,
  394. TRUE );
  395. if ( !bRet )
  396. {
  397. //
  398. // Something bad happened
  399. //
  400. dwError = GetLastError();
  401. goto Finished;
  402. }
  403. }
  404. }
  405. //
  406. // Ensure that we read the number of bytes we expected to
  407. //
  408. if ( cbRead != cbFile )
  409. {
  410. dwError = ERROR_INVALID_DATA;
  411. }
  412. Finished:
  413. if ( dwError != ERROR_SUCCESS )
  414. {
  415. if ( pvBuffer != NULL )
  416. {
  417. #if defined(LOCAL_ALLOC)
  418. LocalFree( pvBuffer );
  419. #elif defined(PRIVATE_HEAP)
  420. HeapFree( g_hMemCacheHeap, 0, pvBuffer );
  421. #elif defined(VIRTUAL_ALLOC)
  422. VirtualFree( pvBuffer, 0, MEM_RELEASE );
  423. #elif defined(LOOKASIDE)
  424. g_laMemCache->Free(pvBuffer, cbFile);
  425. #endif // LOCAL_ALLOC
  426. pvBuffer = NULL;
  427. }
  428. }
  429. *ppvBuffer = pvBuffer;
  430. return dwError;
  431. }
  432. DWORD
  433. ReleaseFromMemoryCache(
  434. IN VOID * pvBuffer,
  435. IN DWORD cbBuffer
  436. )
  437. /*++
  438. Routine Description:
  439. Release file content blob from cache
  440. Arguments:
  441. pvBuffer - Buffer to release
  442. cbBuffer - Size of buffer
  443. Return Value:
  444. ERROR_SUCCESS if successful, else Win32 Error
  445. --*/
  446. {
  447. DBG_ASSERT( pvBuffer );
  448. #if defined(LOCAL_ALLOC)
  449. LocalFree( pvBuffer );
  450. #elif defined(PRIVATE_HEAP)
  451. DBG_ASSERT(g_hMemCacheHeap != NULL);
  452. HeapFree( g_hMemCacheHeap, 0, pvBuffer );
  453. #elif defined(VIRTUAL_ALLOC)
  454. VirtualFree( pvBuffer, 0, MEM_RELEASE );
  455. #elif defined(LOOKASIDE)
  456. g_laMemCache->Free(pvBuffer, cbBuffer);
  457. #endif // LOCAL_ALLOC
  458. MemCacheLock();
  459. g_cbMemCacheSize -= cbBuffer;
  460. g_cMemCacheElements--;
  461. MemCacheUnlock();
  462. return ERROR_SUCCESS;
  463. }
  464. DWORD
  465. TerminateTwoLevelCache(
  466. VOID
  467. )
  468. /*++
  469. Routine Description:
  470. Terminate the memory cache
  471. Arguments:
  472. None
  473. Return Value:
  474. ERROR_SUCCESS if successful
  475. ERROR_INVALID_DATA if still elements in cache
  476. --*/
  477. {
  478. if (DisableTsunamiCaching)
  479. return ERROR_SUCCESS;
  480. #if defined(LOCAL_ALLOC)
  481. // no cleanup
  482. #elif defined(PRIVATE_HEAP)
  483. if (g_hMemCacheHeap != NULL)
  484. {
  485. DBG_REQUIRE( HeapDestroy( g_hMemCacheHeap ) );
  486. g_hMemCacheHeap = NULL;
  487. }
  488. #elif defined(VIRTUAL_ALLOC)
  489. // no cleanup
  490. #elif defined(LOOKASIDE)
  491. delete g_laMemCache;
  492. g_laMemCache = NULL;
  493. #endif // LOCAL_ALLOC
  494. if ( g_dwMemCacheSizeCookie != 0 )
  495. {
  496. RemoveWorkItem( g_dwMemCacheSizeCookie );
  497. g_dwMemCacheSizeCookie = 0;
  498. }
  499. DeleteCriticalSection( &g_csMemCache );
  500. return ( g_cbMemCacheSize ) ?
  501. ERROR_INVALID_DATA : ERROR_SUCCESS;
  502. }
  503. VOID
  504. WINAPI
  505. I_MemoryCacheSizeAdjustor(
  506. IN PVOID pContext
  507. )
  508. /*++
  509. Routine Description:
  510. Called to adjust the maximum size of the memory cache
  511. Arguments:
  512. pContext - Context (set to NULL)
  513. Return value:
  514. None
  515. --*/
  516. {
  517. MEMORYSTATUSEX MemoryStatus;
  518. MemoryStatus.dwLength = sizeof MemoryStatus;
  519. GlobalMemoryStatusEx( &MemoryStatus );
  520. MemCacheLock();
  521. g_cbMaxMemCacheSize = min( MemoryStatus.ullAvailPhys + g_cbMemCacheSize,
  522. MemoryStatus.ullTotalVirtual ) / 2;
  523. MemCacheUnlock();
  524. }
  525. DWORD
  526. DumpMemoryCacheToHtml(
  527. IN CHAR * pszBuffer,
  528. IN OUT DWORD * pcbBuffer
  529. )
  530. /*++
  531. Routine Description:
  532. Dump memory cache stats to buffer
  533. Arguments:
  534. pszBuffer - buffer to fill
  535. pcbBuffer - size of buffer
  536. Return value:
  537. ERROR_SUCCESS if successful, else Win32 Error
  538. --*/
  539. {
  540. *pcbBuffer = wsprintf( pszBuffer,
  541. "<table>"
  542. "<tr><td>Current memory cache size</td><td align=right>%I64d</td></tr>"
  543. "<tr><td>Current memory cache limit</td><td align=right>%I64d</td></tr>"
  544. "<tr><td>Number of items in memory cache</td><td align=right>%d</td></tr>"
  545. "<tr><td>Peak memory cache size</td><td align=right>%I64d</td></tr>"
  546. "<tr><td>Peak memory cache element count</td><td align=right>%d</td></tr>"
  547. "</table>",
  548. g_cbMemCacheSize,
  549. g_cbMaxMemCacheSize,
  550. g_cMemCacheElements,
  551. g_cbMaxMemCacheUsed,
  552. g_cMaxMemCacheElementsUsed
  553. );
  554. return TRUE;
  555. }
  556. VOID
  557. QueryMemoryCacheStatistics(
  558. IN INETA_CACHE_STATISTICS * pCacheCtrs,
  559. IN BOOL fClearAll
  560. )
  561. /*++
  562. Routine Description:
  563. Query memory cache perfmon counters
  564. Arguments:
  565. pCacheCtrs - Relevant members of stat structure are filled in
  566. fClearAll - Clear the counters
  567. Return value:
  568. ERROR_SUCCESS if successful, else Win32 Error
  569. --*/
  570. {
  571. DBG_ASSERT( pCacheCtrs );
  572. if ( fClearAll )
  573. {
  574. pCacheCtrs->CurrentFileCacheSize = 0;
  575. pCacheCtrs->MaximumFileCacheSize = 0;
  576. }
  577. else
  578. {
  579. pCacheCtrs->CurrentFileCacheSize = g_cbMemCacheSize;
  580. pCacheCtrs->MaximumFileCacheSize = g_cbMaxMemCacheUsed;
  581. }
  582. }