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.

670 lines
18 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. heapmgr.c
  5. Abstract:
  6. This module contains initialization and termination routines for
  7. server FSP heap, as well as debug routines for memory tracking.
  8. --*/
  9. #include "precomp.h"
  10. #include "heapmgr.tmh"
  11. #pragma hdrstop
  12. // Make the retry time 15 milli-seconds
  13. #define SRV_LOW_PRIORITY_RETRY_TIME -1*1000*10*15
  14. #ifdef POOL_TAGGING
  15. //
  16. // Array correlating BlockType numbers to pool tags.
  17. //
  18. // *** This array must be maintained in concert with the BlockType
  19. // definitions in srvblock.h!
  20. //
  21. ULONG SrvPoolTags[BlockTypeMax-1] = {
  22. 'fbSL', // BlockTypeBuffer
  23. 'ncSL', // BlockTypeConnection
  24. 'peSL', // BlockTypeEndpoint
  25. 'flSL', // BlockTypeLfcb
  26. 'fmSL', // BlockTypeMfcb
  27. 'frSL', // BlockTypeRfcb
  28. 'rsSL', // BlockTypeSearch
  29. 'csSL', // BlockTypeSearchCore
  30. 'lbSL', // BlockTypeByteRangeLock for persistent handles
  31. 'ssSL', // BlockTypeSession
  32. 'hsSL', // BlockTypeShare
  33. 'rtSL', // BlockTypeTransaction
  34. 'ctSL', // BlockTypeTreeConnect
  35. 'poSL', // BlockTypeOplockBreak
  36. 'dcSL', // BlockTypeCommDevice
  37. 'iwSL', // BlockTypeWorkContextInitial
  38. 'nwSL', // BlockTypeWorkContextNormal
  39. 'rwSL', // BlockTypeWorkContextRaw
  40. 'swSL', // BlockTypeWorkContextSpecial
  41. 'dcSL', // BlockTypeCachedDirectory
  42. 'bdSL', // BlockTypeDataBuffer
  43. 'btSL', // BlockTypeTable
  44. 'hnSL', // BlockTypeNonpagedHeader
  45. 'cpSL', // BlockTypePagedConnection
  46. 'rpSL', // BlockTypePagedRfcb
  47. 'mpSL', // BlockTypePagedMfcb
  48. 'itSL', // BlockTypeTimer
  49. 'caSL', // BlockTypeAdminCheck
  50. 'qwSL', // BlockTypeWorkQueue
  51. 'fsSL', // BlockTypeDfs
  52. 'rlSL', // BlockTypeLargeReadX
  53. 'saSL', // BlockTypeAdapterStatus
  54. 'rsSL', // BlockTypeShareRemark
  55. 'dsSL', // BlockTypeShareSecurityDescriptor
  56. 'ivSL', // BlockTypeVolumeInformation
  57. 'nfSL', // BlockTypeFSName
  58. 'inSL', // BlockTypeNameInfo
  59. 'idSL', // BlockTypeDirectoryInfo
  60. 'cdSL', // BlockTypeDirCache
  61. 'imSL', // BlockTypeMisc
  62. 'nsSL', // BlockTypeSnapShot
  63. 'esSL', // BlockTypeSecurityContext
  64. };
  65. #endif // def POOL_TAGGING
  66. #ifdef ALLOC_PRAGMA
  67. #pragma alloc_text( PAGE, SrvAllocatePagedPool )
  68. #pragma alloc_text( PAGE, SrvFreePagedPool )
  69. #pragma alloc_text( PAGE, SrvClearLookAsideList )
  70. #endif
  71. #if 0
  72. NOT PAGEABLE -- SrvAllocateNonPagedPool
  73. NOT PAGEABLE -- SrvFreeNonPagedPool
  74. #endif
  75. extern LONG SrvMemoryAllocationRetries;
  76. extern LONG SrvMemoryAllocationRetriesSuccessful;
  77. PVOID SRVFASTCALL
  78. SrvInterlockedAllocate( PLOOK_ASIDE_LIST l, ULONG NumberOfBytes, PLONG statistics )
  79. {
  80. PPOOL_HEADER newPool;
  81. PPOOL_HEADER *pentry = NumberOfBytes > LOOK_ASIDE_SWITCHOVER ?
  82. l->LargeFreeList : l->SmallFreeList;
  83. PPOOL_HEADER *pend = pentry + LOOK_ASIDE_MAX_ELEMENTS;
  84. do {
  85. //
  86. // Exchange with the lookaside spot and see if we get anything
  87. //
  88. newPool = NULL;
  89. newPool = (PPOOL_HEADER)InterlockedExchangePointer( pentry, newPool );
  90. if( newPool == NULL ) {
  91. continue;
  92. }
  93. if( newPool->RequestedSize >= NumberOfBytes ) {
  94. //
  95. // The one we got is big enough! Return it.
  96. //
  97. ++(l->AllocHit);
  98. return newPool + 1;
  99. }
  100. //
  101. // It wasn't big enough, so put it back.
  102. //
  103. newPool = (PPOOL_HEADER)InterlockedExchangePointer( pentry, newPool );
  104. if( newPool == NULL ) {
  105. continue;
  106. }
  107. //
  108. // Oops, somebody else freed some memory to this spot. Can we use it?
  109. //
  110. if( newPool->RequestedSize >= NumberOfBytes ) {
  111. //
  112. // We can use it!
  113. //
  114. ++(l->AllocHit);
  115. return newPool + 1;
  116. }
  117. //
  118. // Can't use the memory -- so really free it and keep looking
  119. //
  120. if( statistics ) {
  121. InterlockedExchangeAdd(
  122. statistics,
  123. -(LONG)newPool->RequestedSize
  124. );
  125. }
  126. ExFreePool( newPool );
  127. } while( ++pentry < pend );
  128. ++(l->AllocMiss);
  129. return NULL;
  130. }
  131. PPOOL_HEADER SRVFASTCALL
  132. SrvInterlockedFree( PPOOL_HEADER block )
  133. {
  134. PPOOL_HEADER *pentry = block->FreeList;
  135. PPOOL_HEADER *pend = pentry + LOOK_ASIDE_MAX_ELEMENTS;
  136. do {
  137. block = (PPOOL_HEADER)InterlockedExchangePointer( pentry, block );
  138. } while( block != NULL && ++pentry < pend );
  139. return block;
  140. }
  141. VOID SRVFASTCALL
  142. SrvClearLookAsideList( PLOOK_ASIDE_LIST l, VOID (SRVFASTCALL *FreeRoutine )( PVOID ) )
  143. {
  144. PPOOL_HEADER *pentry, *pend, block;
  145. PAGED_CODE();
  146. //
  147. // Clear out the list of large chunks
  148. //
  149. pentry = l->LargeFreeList;
  150. pend = pentry + LOOK_ASIDE_MAX_ELEMENTS;
  151. do {
  152. block = NULL;
  153. block = (PPOOL_HEADER)InterlockedExchangePointer( pentry, block );
  154. if( block != NULL ) {
  155. block->FreeList = NULL;
  156. FreeRoutine( block + 1 );
  157. }
  158. } while( ++pentry < pend );
  159. //
  160. // Clear out the list of small chunks
  161. //
  162. pentry = l->SmallFreeList;
  163. pend = pentry + LOOK_ASIDE_MAX_ELEMENTS;
  164. do {
  165. block = NULL;
  166. block = (PPOOL_HEADER)InterlockedExchangePointer( pentry, block );
  167. if( block != NULL ) {
  168. block->FreeList = NULL;
  169. FreeRoutine( block + 1 );
  170. }
  171. } while( ++pentry < pend );
  172. }
  173. PVOID SRVFASTCALL
  174. SrvAllocateNonPagedPool (
  175. IN CLONG NumberOfBytes
  176. #ifdef POOL_TAGGING
  177. , IN CLONG BlockType
  178. #endif
  179. )
  180. /*++
  181. Routine Description:
  182. This routine allocates nonpaged pool in the server. A check is
  183. made to ensure that the server's total nonpaged pool usage is below
  184. the configurable limit.
  185. Arguments:
  186. NumberOfBytes - the number of bytes to allocate.
  187. BlockType - the type of block (used to pass pool tag to allocator)
  188. Return Value:
  189. PVOID - a pointer to the allocated memory or NULL if the memory could
  190. not be allocated.
  191. --*/
  192. {
  193. PPOOL_HEADER newPool;
  194. PPOOL_HEADER *FreeList = NULL;
  195. ULONG newUsage;
  196. BOOLEAN IsLowPriority = FALSE;
  197. LARGE_INTEGER interval;
  198. #ifdef POOL_TAGGING
  199. ASSERT( BlockType > 0 && BlockType < BlockTypeMax );
  200. #endif
  201. //
  202. // Pull this allocation off the per-processor free list if we can
  203. //
  204. if( SrvWorkQueues ) {
  205. PWORK_QUEUE queue = PROCESSOR_TO_QUEUE();
  206. if( NumberOfBytes <= queue->NonPagedPoolLookAsideList.MaxSize ) {
  207. newPool = SrvInterlockedAllocate(
  208. &queue->NonPagedPoolLookAsideList,
  209. NumberOfBytes,
  210. (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage
  211. );
  212. if( newPool != NULL ) {
  213. return newPool;
  214. }
  215. FreeList = NumberOfBytes > LOOK_ASIDE_SWITCHOVER ?
  216. queue->NonPagedPoolLookAsideList.LargeFreeList :
  217. queue->NonPagedPoolLookAsideList.SmallFreeList ;
  218. }
  219. }
  220. //
  221. // Account for this allocation in the statistics database and make
  222. // sure that this allocation will not put us over the limit of
  223. // nonpaged pool that we can allocate.
  224. //
  225. newUsage = InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
  226. (LONG)NumberOfBytes );
  227. newUsage += NumberOfBytes;
  228. if ( newUsage > SrvMaxNonPagedPoolUsage ) {
  229. //
  230. // Count the failure, but do NOT log an event. The scavenger
  231. // will log an event when it next wakes up. This keeps us from
  232. // flooding the event log.
  233. //
  234. SrvNonPagedPoolLimitHitCount++;
  235. SrvStatistics.NonPagedPoolFailures++;
  236. InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
  237. -(LONG)NumberOfBytes );
  238. return NULL;
  239. }
  240. if (SrvStatistics.CurrentNonPagedPoolUsage > SrvStatistics.PeakNonPagedPoolUsage) {
  241. SrvStatistics.PeakNonPagedPoolUsage = SrvStatistics.CurrentNonPagedPoolUsage;
  242. }
  243. //
  244. // Do the actual memory allocation. Allocate extra space so that we
  245. // can store the size of the allocation for the free routine.
  246. //
  247. if( NumberOfBytes > 2*4096 )
  248. {
  249. IsLowPriority = TRUE;
  250. }
  251. newPool = ExAllocatePoolWithTagPriority(
  252. NonPagedPool,
  253. NumberOfBytes + sizeof(POOL_HEADER),
  254. TAG_FROM_TYPE(BlockType),
  255. IsLowPriority ? LowPoolPriority : NormalPoolPriority
  256. );
  257. if( (newPool == NULL) && IsLowPriority && (KeGetCurrentIrql() <= APC_LEVEL) )
  258. {
  259. interval.QuadPart = SRV_LOW_PRIORITY_RETRY_TIME;
  260. InterlockedIncrement( &SrvMemoryAllocationRetries );
  261. // Wait and try again
  262. KeDelayExecutionThread( KernelMode, FALSE, &interval );
  263. newPool = ExAllocatePoolWithTagPriority(
  264. NonPagedPool,
  265. NumberOfBytes + sizeof(POOL_HEADER),
  266. TAG_FROM_TYPE(BlockType),
  267. LowPoolPriority
  268. );
  269. if( newPool )
  270. {
  271. InterlockedIncrement( &SrvMemoryAllocationRetriesSuccessful );
  272. }
  273. }
  274. //
  275. // If the system couldn't satisfy the request, return NULL.
  276. //
  277. if ( newPool != NULL ) {
  278. //
  279. // Save the size of this block in the extra space we allocated.
  280. //
  281. newPool->RequestedSize = NumberOfBytes;
  282. newPool->FreeList = FreeList;
  283. //
  284. // Return a pointer to the memory after the size longword.
  285. //
  286. return (PVOID)( newPool + 1 );
  287. }
  288. //
  289. // Count the failure, but do NOT log an event. The scavenger
  290. // will log an event when it next wakes up. This keeps us from
  291. // flooding the event log.
  292. //
  293. SrvStatistics.NonPagedPoolFailures++;
  294. InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
  295. -(LONG)NumberOfBytes );
  296. return NULL;
  297. } // SrvAllocateNonPagedPool
  298. VOID SRVFASTCALL
  299. SrvFreeNonPagedPool (
  300. IN PVOID Address
  301. )
  302. /*++
  303. Routine Description:
  304. Frees the memory allocated by a call to SrvAllocateNonPagedPool.
  305. The statistics database is updated to reflect the current nonpaged
  306. pool usage.
  307. Arguments:
  308. Address - the address of allocated memory returned by
  309. SrvAllocateNonPagedPool.
  310. Return Value:
  311. None.
  312. --*/
  313. {
  314. PPOOL_HEADER actualBlock = (PPOOL_HEADER)Address - 1;
  315. //
  316. // See if we can stash this bit of memory away in the NonPagedPoolFreeList
  317. //
  318. if( actualBlock->FreeList ) {
  319. actualBlock = SrvInterlockedFree( actualBlock );
  320. }
  321. if( actualBlock != NULL ) {
  322. //
  323. // Update the nonpaged pool usage statistic.
  324. //
  325. InterlockedExchangeAdd(
  326. (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
  327. -(LONG)actualBlock->RequestedSize
  328. );
  329. //
  330. // Free the pool and return.
  331. //
  332. ExFreePool( actualBlock );
  333. }
  334. } // SrvFreeNonPagedPool
  335. PVOID SRVFASTCALL
  336. SrvAllocatePagedPool (
  337. IN POOL_TYPE PoolType,
  338. IN CLONG NumberOfBytes
  339. #ifdef POOL_TAGGING
  340. , IN CLONG BlockType
  341. #endif
  342. )
  343. /*++
  344. Routine Description:
  345. This routine allocates Paged pool in the server. A check is
  346. made to ensure that the server's total Paged pool usage is below
  347. the configurable limit.
  348. Arguments:
  349. NumberOfBytes - the number of bytes to allocate.
  350. BlockType - the type of block (used to pass pool tag to allocator)
  351. Return Value:
  352. PVOID - a pointer to the allocated memory or NULL if the memory could
  353. not be allocated.
  354. --*/
  355. {
  356. PPOOL_HEADER newPool;
  357. PPOOL_HEADER *FreeList = NULL;
  358. ULONG newUsage;
  359. BOOLEAN IsLowPriority = FALSE;
  360. LARGE_INTEGER interval;
  361. PAGED_CODE();
  362. #ifdef POOL_TAGGING
  363. ASSERT( BlockType > 0 && BlockType < BlockTypeMax );
  364. #endif
  365. //
  366. // Pull this allocation off the per-processor free list if we can
  367. //
  368. if( SrvWorkQueues ) {
  369. PWORK_QUEUE queue = PROCESSOR_TO_QUEUE();
  370. if( NumberOfBytes <= queue->PagedPoolLookAsideList.MaxSize ) {
  371. newPool = SrvInterlockedAllocate(
  372. &queue->PagedPoolLookAsideList,
  373. NumberOfBytes,
  374. (PLONG)&SrvStatistics.CurrentPagedPoolUsage
  375. );
  376. if( newPool != NULL ) {
  377. return newPool;
  378. }
  379. FreeList = NumberOfBytes > LOOK_ASIDE_SWITCHOVER ?
  380. queue->PagedPoolLookAsideList.LargeFreeList :
  381. queue->PagedPoolLookAsideList.SmallFreeList ;
  382. }
  383. }
  384. //
  385. // Account for this allocation in the statistics database and make
  386. // sure that this allocation will not put us over the limit of
  387. // nonpaged pool that we can allocate.
  388. //
  389. newUsage = InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
  390. (LONG)NumberOfBytes );
  391. newUsage += NumberOfBytes;
  392. if ( newUsage > SrvMaxPagedPoolUsage ) {
  393. //
  394. // Count the failure, but do NOT log an event. The scavenger
  395. // will log an event when it next wakes up. This keeps us from
  396. // flooding the event log.
  397. //
  398. SrvPagedPoolLimitHitCount++;
  399. SrvStatistics.PagedPoolFailures++;
  400. InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
  401. -(LONG)NumberOfBytes );
  402. return NULL;
  403. }
  404. if (SrvStatistics.CurrentPagedPoolUsage > SrvStatistics.PeakPagedPoolUsage ) {
  405. SrvStatistics.PeakPagedPoolUsage = SrvStatistics.CurrentPagedPoolUsage;
  406. }
  407. //
  408. // Do the actual memory allocation. Allocate extra space so that we
  409. // can store the size of the allocation for the free routine.
  410. //
  411. if( NumberOfBytes > 2*4096 )
  412. {
  413. IsLowPriority = TRUE;
  414. }
  415. newPool = ExAllocatePoolWithTagPriority(
  416. PoolType,
  417. NumberOfBytes + sizeof(POOL_HEADER),
  418. TAG_FROM_TYPE(BlockType),
  419. IsLowPriority ? LowPoolPriority : NormalPoolPriority
  420. );
  421. if( (newPool == NULL) && IsLowPriority && (KeGetCurrentIrql() <= APC_LEVEL) )
  422. {
  423. interval.QuadPart = SRV_LOW_PRIORITY_RETRY_TIME;
  424. InterlockedIncrement( &SrvMemoryAllocationRetries );
  425. // Wait and try again
  426. KeDelayExecutionThread( KernelMode, FALSE, &interval );
  427. newPool = ExAllocatePoolWithTagPriority(
  428. PoolType,
  429. NumberOfBytes + sizeof(POOL_HEADER),
  430. TAG_FROM_TYPE(BlockType),
  431. LowPoolPriority
  432. );
  433. if( newPool )
  434. {
  435. InterlockedIncrement( &SrvMemoryAllocationRetriesSuccessful );
  436. }
  437. }
  438. if( newPool != NULL ) {
  439. newPool->FreeList = FreeList;
  440. newPool->RequestedSize = NumberOfBytes;
  441. //
  442. // Return a pointer to the memory after the POOL_HEADER
  443. //
  444. return newPool + 1;
  445. }
  446. //
  447. // If the system couldn't satisfy the request, return NULL.
  448. //
  449. // Count the failure, but do NOT log an event. The scavenger
  450. // will log an event when it next wakes up. This keeps us from
  451. // flooding the event log.
  452. //
  453. SrvStatistics.PagedPoolFailures++;
  454. InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
  455. -(LONG)NumberOfBytes );
  456. return NULL;
  457. } // SrvAllocatePagedPool
  458. VOID SRVFASTCALL
  459. SrvFreePagedPool (
  460. IN PVOID Address
  461. )
  462. /*++
  463. Routine Description:
  464. Frees the memory allocated by a call to SrvAllocatePagedPool.
  465. The statistics database is updated to reflect the current Paged
  466. pool usage. If this routine is change, look at scavengr.c
  467. Arguments:
  468. Address - the address of allocated memory returned by
  469. SrvAllocatePagedPool.
  470. Return Value:
  471. None.
  472. --*/
  473. {
  474. PPOOL_HEADER actualBlock = (PPOOL_HEADER)Address - 1;
  475. PAGED_CODE();
  476. ASSERT( actualBlock != NULL );
  477. //
  478. // See if we can stash this bit of memory away in the PagedPoolFreeList
  479. //
  480. if( actualBlock->FreeList ) {
  481. actualBlock = SrvInterlockedFree( actualBlock );
  482. }
  483. if( actualBlock != NULL ) {
  484. //
  485. // Update the Paged pool usage statistic.
  486. //
  487. ASSERT( SrvStatistics.CurrentPagedPoolUsage >= actualBlock->RequestedSize );
  488. InterlockedExchangeAdd(
  489. (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
  490. -(LONG)actualBlock->RequestedSize
  491. );
  492. ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
  493. //
  494. // Free the pool and return.
  495. //
  496. ExFreePool( actualBlock );
  497. }
  498. } // SrvFreePagedPool