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.

578 lines
15 KiB

  1. /*++
  2. Copyright (c) 2002-2002 Microsoft Corporation
  3. Module Name:
  4. scavenger.c
  5. Abstract:
  6. The cache scavenger implementation
  7. Author:
  8. Karthik Mahesh (KarthikM) Feb-2002
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "scavenger.h"
  13. #include "scavengerp.h"
  14. // MB to trim at a time
  15. SIZE_T g_UlScavengerTrimMB = DEFAULT_SCAVENGER_TRIM_MB;
  16. // Min Interval between 2 scavenger events
  17. ULONG g_UlMinScavengerInterval = DEFAULT_MIN_SCAVENGER_INTERVAL;
  18. // Pages to trim on a low memory event.
  19. ULONG_PTR g_ScavengerTrimPages;
  20. volatile BOOLEAN g_ScavengerThreadStarted;
  21. HANDLE g_ScavengerLowMemHandle;
  22. HANDLE g_ScavengerThreadHandle;
  23. KEVENT g_ScavengerLimitExceededEvent;
  24. KEVENT g_ScavengerTerminateThreadEvent;
  25. KEVENT g_ScavengerTimerEvent;
  26. KTIMER g_ScavengerTimer;
  27. KDPC g_ScavengerTimerDpc;
  28. // Event Array for Scavenger Thread
  29. PKEVENT g_ScavengerAllEvents[SCAVENGER_NUM_EVENTS];
  30. // Number of scavenger calls since last timer event
  31. ULONG g_ScavengerAge = 0;
  32. #ifdef ALLOC_PRAGMA
  33. #pragma alloc_text( INIT, UlInitializeScavengerThread )
  34. #pragma alloc_text( PAGE, UlTerminateScavengerThread )
  35. #pragma alloc_text( PAGE, UlpSetScavengerTimer )
  36. #pragma alloc_text( PAGE, UlpScavengerThread )
  37. #pragma alloc_text( PAGE, UlpScavengerPeriodicEventHandler )
  38. #pragma alloc_text( PAGE, UlpScavengerLowMemoryEventHandler )
  39. #pragma alloc_text( PAGE, UlpScavengerLimitEventHandler )
  40. #pragma alloc_text( PAGE, UlSetScavengerLimitEvent )
  41. #endif // ALLOC_PRAGMA
  42. #if 0
  43. NOT PAGEABLE -- UlpScavengerDpcRoutine
  44. #endif
  45. /***************************************************************************++
  46. Routine Description:
  47. Initialize the Memory Scavenger
  48. --***************************************************************************/
  49. NTSTATUS
  50. UlInitializeScavengerThread(
  51. VOID
  52. )
  53. {
  54. NTSTATUS Status;
  55. UNICODE_STRING LowMemoryEventName;
  56. OBJECT_ATTRIBUTES ObjAttr;
  57. PKEVENT LowMemoryEventObject;
  58. PAGED_CODE();
  59. // Initialize Trim Size
  60. // If trim size is not set, trim 1M for every 256M
  61. if(g_UlScavengerTrimMB > g_UlTotalPhysicalMemMB) {
  62. g_UlScavengerTrimMB = g_UlTotalPhysicalMemMB;
  63. }
  64. if(g_UlScavengerTrimMB == DEFAULT_SCAVENGER_TRIM_MB) {
  65. g_UlScavengerTrimMB = (g_UlTotalPhysicalMemMB + 255)/256;
  66. }
  67. g_ScavengerTrimPages = MEGABYTES_TO_PAGES(g_UlScavengerTrimMB);
  68. // Open Low Memory Event Object
  69. RtlInitUnicodeString( &LowMemoryEventName, LOW_MEM_EVENT_NAME );
  70. InitializeObjectAttributes( &ObjAttr,
  71. &LowMemoryEventName,
  72. OBJ_KERNEL_HANDLE,
  73. NULL,
  74. NULL );
  75. Status = ZwOpenEvent ( &g_ScavengerLowMemHandle,
  76. EVENT_QUERY_STATE,
  77. &ObjAttr );
  78. if( !NT_SUCCESS(Status) ) {
  79. return Status;
  80. }
  81. Status = ObReferenceObjectByHandle( g_ScavengerLowMemHandle,
  82. EVENT_QUERY_STATE,
  83. NULL,
  84. KernelMode,
  85. (PVOID *) &LowMemoryEventObject,
  86. NULL );
  87. if( !NT_SUCCESS(Status) ) {
  88. ZwClose (g_ScavengerLowMemHandle);
  89. return Status;
  90. }
  91. // Initialize Scavenger Timer DPC object
  92. KeInitializeDpc(
  93. &g_ScavengerTimerDpc,
  94. &UlpScavengerTimerDpcRoutine,
  95. NULL
  96. );
  97. // Initialize Scavenger Timer
  98. KeInitializeTimer(
  99. &g_ScavengerTimer
  100. );
  101. // Initialize other Scavenger Events
  102. KeInitializeEvent ( &g_ScavengerTerminateThreadEvent,
  103. NotificationEvent,
  104. FALSE );
  105. KeInitializeEvent ( &g_ScavengerLimitExceededEvent,
  106. NotificationEvent,
  107. FALSE );
  108. KeInitializeEvent ( &g_ScavengerTimerEvent,
  109. NotificationEvent,
  110. FALSE );
  111. // Initialize Event Array for Scavenger Thread
  112. g_ScavengerAllEvents[SCAVENGER_LOW_MEM_EVENT]
  113. = LowMemoryEventObject;
  114. g_ScavengerAllEvents[SCAVENGER_TERMINATE_THREAD_EVENT]
  115. = &g_ScavengerTerminateThreadEvent;
  116. g_ScavengerAllEvents[SCAVENGER_LIMIT_EXCEEDED_EVENT]
  117. = &g_ScavengerLimitExceededEvent;
  118. g_ScavengerAllEvents[SCAVENGER_TIMER_EVENT]
  119. = &g_ScavengerTimerEvent;
  120. // Start Scavenger Thread
  121. g_ScavengerThreadStarted = TRUE;
  122. InitializeObjectAttributes(&ObjAttr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
  123. Status = PsCreateSystemThread( &g_ScavengerThreadHandle,
  124. THREAD_ALL_ACCESS,
  125. &ObjAttr,
  126. NULL,
  127. NULL,
  128. UlpScavengerThread,
  129. NULL );
  130. if( !NT_SUCCESS(Status) ) {
  131. g_ScavengerThreadStarted = FALSE;
  132. ObDereferenceObject ( LowMemoryEventObject );
  133. ZwClose( g_ScavengerLowMemHandle );
  134. return Status;
  135. }
  136. UlTrace(URI_CACHE, ("UlInitializeScavengerThread: Started Scavenger Thread\n"));
  137. // Kick off periodic scavenger timer
  138. UlpSetScavengerTimer();
  139. return Status;
  140. }
  141. /***************************************************************************++
  142. Routine Description:
  143. Terminate the Memory Scavenger
  144. --***************************************************************************/
  145. VOID
  146. UlTerminateScavengerThread(
  147. VOID
  148. )
  149. {
  150. PETHREAD ThreadObject;
  151. NTSTATUS Status;
  152. PAGED_CODE();
  153. UlTrace(URI_CACHE, ("UlTerminateScavengerThread: Terminating Scavenger Thread\n"));
  154. if(g_ScavengerThreadStarted) {
  155. ASSERT( g_ScavengerThreadHandle );
  156. Status = ObReferenceObjectByHandle( g_ScavengerThreadHandle,
  157. 0,
  158. *PsThreadType,
  159. KernelMode,
  160. (PVOID *) &ThreadObject,
  161. NULL );
  162. ASSERT( NT_SUCCESS(Status) ); // g_ScavengerThreadHandle is a valid thread handle
  163. g_ScavengerThreadStarted = FALSE;
  164. // Set the terminate event
  165. KeSetEvent( &g_ScavengerTerminateThreadEvent, 0, FALSE );
  166. // Wait for thread to terminate
  167. KeWaitForSingleObject( ThreadObject,
  168. Executive,
  169. KernelMode,
  170. FALSE,
  171. NULL );
  172. ObDereferenceObject( ThreadObject );
  173. ZwClose( g_ScavengerThreadHandle );
  174. // Close Low Mem Event handle
  175. ObDereferenceObject(g_ScavengerAllEvents[SCAVENGER_LOW_MEM_EVENT]);
  176. ZwClose( g_ScavengerLowMemHandle );
  177. // Cancel the timer, if it fails it means the Dpc may be running
  178. // In that case, wait for it to finish
  179. if ( !KeCancelTimer( &g_ScavengerTimer ) )
  180. {
  181. KeWaitForSingleObject(
  182. (PVOID)&g_ScavengerTimerEvent,
  183. Executive,
  184. KernelMode,
  185. FALSE,
  186. NULL
  187. );
  188. }
  189. // Clear out any remaining zombies
  190. UlPeriodicCacheScavenger(0);
  191. }
  192. }
  193. /***************************************************************************++
  194. Routine Description:
  195. Figures out the scavenger interval in 100 ns ticks, and sets the timer.
  196. --***************************************************************************/
  197. VOID
  198. UlpSetScavengerTimer(
  199. VOID
  200. )
  201. {
  202. LARGE_INTEGER Interval;
  203. PAGED_CODE();
  204. //
  205. // convert seconds to 100 nanosecond intervals (x * 10^7)
  206. // negative numbers mean relative time
  207. //
  208. Interval.QuadPart= g_UriCacheConfig.ScavengerPeriod
  209. * -C_NS_TICKS_PER_SEC;
  210. UlTrace(URI_CACHE, (
  211. "Http!UlpSetScavengerTimer: %d seconds = %I64d 100ns ticks\n",
  212. g_UriCacheConfig.ScavengerPeriod,
  213. Interval.QuadPart
  214. ));
  215. KeSetTimer(
  216. &g_ScavengerTimer,
  217. Interval,
  218. &g_ScavengerTimerDpc
  219. );
  220. } // UlpSetScavengerTimer
  221. /***************************************************************************++
  222. Routine Description:
  223. Dpc routine to set scavenger timeout event
  224. Arguments:
  225. None.
  226. --***************************************************************************/
  227. VOID
  228. UlpScavengerTimerDpcRoutine(
  229. IN PKDPC Dpc,
  230. IN PVOID DeferredContext,
  231. IN PVOID SystemArgument1,
  232. IN PVOID SystemArgument2
  233. )
  234. {
  235. UNREFERENCED_PARAMETER(Dpc);
  236. UNREFERENCED_PARAMETER(DeferredContext);
  237. UNREFERENCED_PARAMETER(SystemArgument1);
  238. UNREFERENCED_PARAMETER(SystemArgument2);
  239. ASSERT( DeferredContext == NULL );
  240. KeSetEvent( &g_ScavengerTimerEvent, 0, FALSE );
  241. }
  242. /***************************************************************************++
  243. Routine Description:
  244. Wait for memory usage events and recycle when needed
  245. Arguments:
  246. None.
  247. --***************************************************************************/
  248. VOID
  249. UlpScavengerThread(
  250. IN PVOID Context
  251. )
  252. {
  253. NTSTATUS Status;
  254. KWAIT_BLOCK WaitBlockArray[SCAVENGER_NUM_EVENTS];
  255. LARGE_INTEGER MinInterval;
  256. PAGED_CODE();
  257. ASSERT(Context == NULL);
  258. ASSERT(g_ScavengerAllEvents[SCAVENGER_TERMINATE_THREAD_EVENT] != NULL);
  259. ASSERT(g_ScavengerAllEvents[SCAVENGER_TIMER_EVENT] != NULL);
  260. ASSERT(g_ScavengerAllEvents[SCAVENGER_LOW_MEM_EVENT] != NULL);
  261. ASSERT(g_ScavengerAllEvents[SCAVENGER_LIMIT_EXCEEDED_EVENT] != NULL);
  262. UNREFERENCED_PARAMETER(Context);
  263. MinInterval.QuadPart = g_UlMinScavengerInterval * -C_NS_TICKS_PER_SEC;
  264. while(g_ScavengerThreadStarted) {
  265. //
  266. // Pause between successive scavenger calls
  267. //
  268. KeWaitForSingleObject( g_ScavengerAllEvents[SCAVENGER_TERMINATE_THREAD_EVENT],
  269. Executive,
  270. KernelMode,
  271. FALSE,
  272. &MinInterval );
  273. //
  274. // Wait for scavenger events
  275. //
  276. Status = KeWaitForMultipleObjects( SCAVENGER_NUM_EVENTS,
  277. g_ScavengerAllEvents,
  278. WaitAny,
  279. Executive,
  280. KernelMode,
  281. FALSE,
  282. NULL,
  283. WaitBlockArray );
  284. ASSERT( NT_SUCCESS(Status) );
  285. if(KeReadStateEvent( g_ScavengerAllEvents[SCAVENGER_TERMINATE_THREAD_EVENT] )) {
  286. //
  287. // Do Nothing, will exit while loop
  288. //
  289. UlTrace(URI_CACHE, ("UlpScavengerThread: Terminate Event Set\n"));
  290. break;
  291. }
  292. if(KeReadStateEvent( g_ScavengerAllEvents[SCAVENGER_TIMER_EVENT] )) {
  293. UlpScavengerPeriodicEventHandler();
  294. }
  295. if(KeReadStateEvent( g_ScavengerAllEvents[SCAVENGER_LOW_MEM_EVENT] )) {
  296. UlpScavengerLowMemoryEventHandler();
  297. }
  298. if(KeReadStateEvent(g_ScavengerAllEvents[SCAVENGER_LIMIT_EXCEEDED_EVENT] )) {
  299. UlpScavengerLimitEventHandler();
  300. }
  301. } // while(g_ScavengerThreadStarted)
  302. PsTerminateSystemThread( STATUS_SUCCESS );
  303. }
  304. /***************************************************************************++
  305. Routine Description:
  306. Handle the "Cache Size Exceeded Limit" event
  307. Arguments:
  308. None.
  309. --***************************************************************************/
  310. VOID
  311. UlpScavengerPeriodicEventHandler(
  312. VOID
  313. )
  314. {
  315. PAGED_CODE();
  316. KeClearEvent( g_ScavengerAllEvents[SCAVENGER_TIMER_EVENT] );
  317. UlTraceVerbose(URI_CACHE, ("UlpScavengerThread: Calling Periodic Scavenger. Age = %d\n", g_ScavengerAge));
  318. ASSERT(g_ScavengerAge <= SCAVENGER_MAX_AGE);
  319. UlPeriodicCacheScavenger(g_ScavengerAge);
  320. g_ScavengerAge = 0;
  321. //
  322. // Clear the pages exceeded event, hopefully enough memory
  323. // has been reclaimed by the scavenger
  324. // If not, this event will be set again immediately
  325. // on the next cache miss
  326. //
  327. KeClearEvent(g_ScavengerAllEvents[SCAVENGER_LIMIT_EXCEEDED_EVENT]);
  328. //
  329. // Schedule the next periodic scavenger call
  330. //
  331. UlpSetScavengerTimer();
  332. }
  333. /***************************************************************************++
  334. Routine Description:
  335. Handle the "Cache Size Exceeded Limit" event
  336. Arguments:
  337. None.
  338. --***************************************************************************/
  339. VOID
  340. UlpScavengerLowMemoryEventHandler(
  341. VOID
  342. )
  343. {
  344. ULONG_PTR PagesToRecycle;
  345. PAGED_CODE();
  346. UlDisableCache();
  347. ASSERT(g_ScavengerAge <= SCAVENGER_MAX_AGE);
  348. if(g_ScavengerAge < SCAVENGER_MAX_AGE) {
  349. g_ScavengerAge++;
  350. }
  351. UlTrace(URI_CACHE, ("UlpScavengerThread: Low Memory. Age = %d\n", g_ScavengerAge));
  352. do {
  353. //
  354. // Trim up to g_ScavengerTrimPages pages
  355. //
  356. PagesToRecycle = UlGetHashTablePages();
  357. if(PagesToRecycle > g_ScavengerTrimPages){
  358. PagesToRecycle = g_ScavengerTrimPages;
  359. }
  360. if(PagesToRecycle > 0) {
  361. UlTrimCache( PagesToRecycle, g_ScavengerAge );
  362. }
  363. } while(KeReadStateEvent(g_ScavengerAllEvents[SCAVENGER_LOW_MEM_EVENT]) && (PagesToRecycle > 0));
  364. UlEnableCache();
  365. }
  366. /***************************************************************************++
  367. Routine Description:
  368. Handle the "Cache Size Exceeded Limit" event
  369. Arguments:
  370. None.
  371. --***************************************************************************/
  372. VOID
  373. UlpScavengerLimitEventHandler(
  374. VOID
  375. )
  376. {
  377. ULONG_PTR PagesToRecycle;
  378. UlDisableCache();
  379. PagesToRecycle = UlGetHashTablePages() / 8;
  380. if( PagesToRecycle < 1 ) {
  381. PagesToRecycle = UlGetHashTablePages();
  382. }
  383. if(g_ScavengerAge < SCAVENGER_MAX_AGE) {
  384. g_ScavengerAge++;
  385. }
  386. ASSERT(g_ScavengerAge <= SCAVENGER_MAX_AGE);
  387. UlTrace(URI_CACHE, ("UlpScavengerThread: Cache Size Exceeded Limit. Age = %d, Freeing %d pages\n", g_ScavengerAge, PagesToRecycle));
  388. if(PagesToRecycle > 0) {
  389. UlTrimCache( PagesToRecycle, g_ScavengerAge );
  390. }
  391. KeClearEvent(g_ScavengerAllEvents[SCAVENGER_LIMIT_EXCEEDED_EVENT]);
  392. UlEnableCache();
  393. }
  394. /***************************************************************************++
  395. Routine Description:
  396. Set the "Cache Size Exceeded Limit" event
  397. Arguments:
  398. None.
  399. --***************************************************************************/
  400. VOID
  401. UlSetScavengerLimitEvent(
  402. VOID
  403. )
  404. {
  405. PAGED_CODE();
  406. KeSetEvent( g_ScavengerAllEvents[SCAVENGER_LIMIT_EXCEEDED_EVENT],
  407. 0,
  408. FALSE );
  409. }