Source code of Windows XP (NT5)
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.

547 lines
14 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. memory.c
  5. Abstract:
  6. This module contains routines for handling memory management within the SAC.
  7. Currently the SAC allocates a chunk of memory up front and then does all local allocations
  8. from this, growing it as necessary.
  9. Author:
  10. Sean Selitrennikoff (v-seans) - Jan 11, 1999
  11. Revision History:
  12. --*/
  13. #include "sac.h"
  14. //
  15. // These are useful for finding memory leaks.
  16. //
  17. LONG TotalAllocations = 0;
  18. LONG TotalFrees = 0;
  19. LARGE_INTEGER TotalBytesAllocated;
  20. LARGE_INTEGER TotalBytesFreed;
  21. #define GLOBAL_MEMORY_SIGNATURE 0x44414548
  22. #define LOCAL_MEMORY_SIGNATURE 0x5353454C
  23. //
  24. // Structure for holding all allocations from the system
  25. //
  26. typedef struct _GLOBAL_MEMORY_DESCRIPTOR {
  27. #if DBG
  28. ULONG Signature;
  29. #endif
  30. PVOID Memory;
  31. ULONG Size;
  32. struct _GLOBAL_MEMORY_DESCRIPTOR *NextDescriptor;
  33. } GLOBAL_MEMORY_DESCRIPTOR, *PGLOBAL_MEMORY_DESCRIPTOR;
  34. typedef struct _LOCAL_MEMORY_DESCRIPTOR {
  35. #if DBG
  36. #if defined (_IA64_)
  37. //
  38. // We must make sure that allocated memory falls on mod-8 boundaries.
  39. // To do this, we must make sure that this structure is of size mod-8.
  40. //
  41. ULONG Filler;
  42. #endif
  43. ULONG Signature;
  44. #endif
  45. ULONG Tag;
  46. ULONG Size;
  47. } LOCAL_MEMORY_DESCRIPTOR, *PLOCAL_MEMORY_DESCRIPTOR;
  48. //
  49. // Variable for holding our memory together.
  50. //
  51. PGLOBAL_MEMORY_DESCRIPTOR GlobalMemoryList;
  52. KSPIN_LOCK MemoryLock;
  53. //
  54. // Constants used to manipulate size growth
  55. //
  56. #define MEMORY_ALLOCATION_SIZE PAGE_SIZE
  57. #define INITIAL_MEMORY_BLOCK_SIZE 0x100000
  58. //
  59. // Functions
  60. //
  61. BOOLEAN
  62. InitializeMemoryManagement(
  63. VOID
  64. )
  65. /*++
  66. Routine Description:
  67. This routine initializes the internal memory management system.
  68. Arguments:
  69. None.
  70. Return Value:
  71. TRUE if successful, else FALSE
  72. --*/
  73. {
  74. PLOCAL_MEMORY_DESCRIPTOR LocalDescriptor;
  75. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeMem: Entering\n")));
  76. GlobalMemoryList = (PGLOBAL_MEMORY_DESCRIPTOR)ExAllocatePoolWithTagPriority(NonPagedPool,
  77. INITIAL_MEMORY_BLOCK_SIZE,
  78. INITIAL_POOL_TAG,
  79. HighPoolPriority
  80. );
  81. if (GlobalMemoryList == NULL) {
  82. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE,
  83. KdPrint(("SAC InitializeMem: Exiting with FALSE. No pool.\n")));
  84. return FALSE;
  85. }
  86. KeInitializeSpinLock(&MemoryLock);
  87. #if DBG
  88. GlobalMemoryList->Signature = GLOBAL_MEMORY_SIGNATURE;
  89. #endif
  90. GlobalMemoryList->Memory = (PVOID)(GlobalMemoryList + 1);
  91. GlobalMemoryList->Size = INITIAL_MEMORY_BLOCK_SIZE - sizeof(GLOBAL_MEMORY_DESCRIPTOR);
  92. GlobalMemoryList->NextDescriptor = NULL;
  93. LocalDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(GlobalMemoryList->Memory);
  94. #if DBG
  95. LocalDescriptor->Signature = LOCAL_MEMORY_SIGNATURE;
  96. #endif
  97. LocalDescriptor->Tag = FREE_POOL_TAG;
  98. LocalDescriptor->Size = GlobalMemoryList->Size - sizeof(LOCAL_MEMORY_DESCRIPTOR);
  99. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeMem: Exiting with TRUE.\n")));
  100. return TRUE;
  101. } // InitializeMemoryManagement
  102. VOID
  103. FreeMemoryManagement(
  104. VOID
  105. )
  106. /*++
  107. Routine Description:
  108. This routine frees the internal memory management system.
  109. Arguments:
  110. None.
  111. Return Value:
  112. TRUE if successful, else FALSE
  113. --*/
  114. {
  115. KIRQL OldIrql;
  116. PGLOBAL_MEMORY_DESCRIPTOR NextDescriptor;
  117. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC FreeMem: Entering\n")));
  118. KeAcquireSpinLock(&MemoryLock, &OldIrql);
  119. //
  120. // Check if the memory allocation fits in a current block anywhere
  121. //
  122. while (GlobalMemoryList != NULL) {
  123. #if DBG
  124. ASSERT( GlobalMemoryList->Signature == GLOBAL_MEMORY_SIGNATURE );
  125. #endif
  126. NextDescriptor = GlobalMemoryList->NextDescriptor;
  127. KeReleaseSpinLock(&MemoryLock, OldIrql);
  128. ExFreePool(GlobalMemoryList);
  129. KeAcquireSpinLock(&MemoryLock, &OldIrql);
  130. GlobalMemoryList = NextDescriptor;
  131. }
  132. KeReleaseSpinLock(&MemoryLock, OldIrql);
  133. IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC FreeMem: Exiting\n")));
  134. }
  135. PVOID
  136. MyAllocatePool(
  137. IN SIZE_T NumberOfBytes,
  138. IN ULONG Tag,
  139. IN PCHAR FileName,
  140. IN ULONG LineNumber
  141. )
  142. /*++
  143. Routine Description:
  144. This routine allocates memory from our internal structures, and if needed, gets more pool.
  145. Arguments:
  146. NumberOfBytes - Number of bytes the client wants.
  147. Tag - A tag to place on the memory.
  148. FileName - The file name this request is coming from.
  149. LineNumber - Line number within the file that is making this request.
  150. Return Value:
  151. A pointer to the allocated block if successful, else NULL
  152. --*/
  153. {
  154. KIRQL OldIrql;
  155. PGLOBAL_MEMORY_DESCRIPTOR GlobalDescriptor;
  156. PGLOBAL_MEMORY_DESCRIPTOR NewDescriptor;
  157. PLOCAL_MEMORY_DESCRIPTOR LocalDescriptor;
  158. PLOCAL_MEMORY_DESCRIPTOR NextDescriptor;
  159. ULONG ThisBlockSize;
  160. ULONG BytesToAllocate;
  161. UNREFERENCED_PARAMETER(FileName);
  162. UNREFERENCED_PARAMETER(LineNumber);
  163. ASSERT(Tag != FREE_POOL_TAG);
  164. IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyAllocPool: Entering.\n")));
  165. KeAcquireSpinLock(&MemoryLock, &OldIrql);
  166. //
  167. // Always allocate on mod-8 boundaries
  168. //
  169. if( NumberOfBytes & 0x7 ) {
  170. NumberOfBytes += 8 - (NumberOfBytes & 0x7);
  171. }
  172. //
  173. // Check if the memory allocation fits in a current block anywhere
  174. //
  175. GlobalDescriptor = GlobalMemoryList;
  176. while (GlobalDescriptor != NULL) {
  177. #if DBG
  178. ASSERT( GlobalDescriptor->Signature == GLOBAL_MEMORY_SIGNATURE );
  179. #endif
  180. LocalDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(GlobalDescriptor->Memory);
  181. ThisBlockSize = GlobalDescriptor->Size;
  182. while (ThisBlockSize != 0) {
  183. #if DBG
  184. ASSERT( LocalDescriptor->Signature == LOCAL_MEMORY_SIGNATURE );
  185. #endif
  186. if ((LocalDescriptor->Tag == FREE_POOL_TAG) &&
  187. (LocalDescriptor->Size >= NumberOfBytes)) {
  188. IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyAllocPool: Found a good sized block.\n")));
  189. goto FoundBlock;
  190. }
  191. ThisBlockSize -= (LocalDescriptor->Size + sizeof(LOCAL_MEMORY_DESCRIPTOR));
  192. LocalDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(((PUCHAR)LocalDescriptor) +
  193. LocalDescriptor->Size +
  194. sizeof(LOCAL_MEMORY_DESCRIPTOR)
  195. );
  196. }
  197. GlobalDescriptor = GlobalDescriptor->NextDescriptor;
  198. }
  199. KeReleaseSpinLock(&MemoryLock, OldIrql);
  200. //
  201. // There is no memory block big enough to hold the request.
  202. //
  203. //
  204. // Now check if the request is larger than the normal allocation unit we use.
  205. //
  206. if (NumberOfBytes >
  207. (MEMORY_ALLOCATION_SIZE - sizeof(GLOBAL_MEMORY_DESCRIPTOR) - sizeof(LOCAL_MEMORY_DESCRIPTOR))) {
  208. BytesToAllocate = (ULONG)(NumberOfBytes + sizeof(GLOBAL_MEMORY_DESCRIPTOR) + sizeof(LOCAL_MEMORY_DESCRIPTOR));
  209. } else {
  210. BytesToAllocate = MEMORY_ALLOCATION_SIZE;
  211. }
  212. IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyAllocPool: Allocating new space.\n")));
  213. NewDescriptor = (PGLOBAL_MEMORY_DESCRIPTOR)ExAllocatePoolWithTagPriority(NonPagedPool,
  214. BytesToAllocate,
  215. ALLOC_POOL_TAG,
  216. HighPoolPriority
  217. );
  218. if (NewDescriptor == NULL) {
  219. IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyAllocPool: No more memory, returning NULL.\n")));
  220. return NULL;
  221. }
  222. KeAcquireSpinLock(&MemoryLock, &OldIrql);
  223. #if DBG
  224. NewDescriptor->Signature = GLOBAL_MEMORY_SIGNATURE;
  225. #endif
  226. NewDescriptor->Memory = (PVOID)(NewDescriptor + 1);
  227. NewDescriptor->Size = BytesToAllocate - sizeof(GLOBAL_MEMORY_DESCRIPTOR);
  228. NewDescriptor->NextDescriptor = GlobalMemoryList;
  229. GlobalMemoryList = NewDescriptor;
  230. LocalDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(GlobalMemoryList->Memory);
  231. #if DBG
  232. LocalDescriptor->Signature = LOCAL_MEMORY_SIGNATURE;
  233. #endif
  234. LocalDescriptor->Tag = FREE_POOL_TAG;
  235. LocalDescriptor->Size = GlobalMemoryList->Size - sizeof(LOCAL_MEMORY_DESCRIPTOR);
  236. FoundBlock:
  237. //
  238. // Jump to here when a memory descriptor of the right size has been found. It is expected that
  239. // LocalDescriptor points to the correct block.
  240. //
  241. ASSERT(LocalDescriptor != NULL);
  242. ASSERT(LocalDescriptor->Tag == FREE_POOL_TAG);
  243. #if DBG
  244. ASSERT(LocalDescriptor->Signature == LOCAL_MEMORY_SIGNATURE );
  245. #endif
  246. if (LocalDescriptor->Size > NumberOfBytes + sizeof(LOCAL_MEMORY_DESCRIPTOR)) {
  247. //
  248. // Make a descriptor of the left over parts of this block.
  249. //
  250. NextDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(((PUCHAR)LocalDescriptor) +
  251. sizeof(LOCAL_MEMORY_DESCRIPTOR) +
  252. NumberOfBytes
  253. );
  254. #if DBG
  255. NextDescriptor->Signature = LOCAL_MEMORY_SIGNATURE;
  256. #endif
  257. NextDescriptor->Tag = FREE_POOL_TAG;
  258. NextDescriptor->Size = (ULONG)(LocalDescriptor->Size - NumberOfBytes - sizeof(LOCAL_MEMORY_DESCRIPTOR));
  259. LocalDescriptor->Size = (ULONG)NumberOfBytes;
  260. }
  261. LocalDescriptor->Tag = Tag;
  262. KeReleaseSpinLock(&MemoryLock, OldIrql);
  263. InterlockedIncrement(
  264. &TotalAllocations
  265. );
  266. ExInterlockedAddLargeStatistic(
  267. &TotalBytesAllocated,
  268. (CLONG)LocalDescriptor->Size // Sundown - FIX
  269. );
  270. IF_SAC_DEBUG(SAC_DEBUG_MEM,
  271. KdPrint(("SAC MyAllocPool: Returning block 0x%X.\n", LocalDescriptor)));
  272. RtlZeroMemory( (LocalDescriptor+1), NumberOfBytes );
  273. return (PVOID)(LocalDescriptor + 1);
  274. } // MyAllocatePool
  275. VOID
  276. MyFreePool(
  277. IN PVOID *Pointer
  278. )
  279. /*++
  280. Routine Description:
  281. This routine frees a block previously allocated from the internal memory management system.
  282. Arguments:
  283. Pointer - A pointer to the pointer to free.
  284. Return Value:
  285. Pointer is set to NULL if successful, else it is left alone.
  286. --*/
  287. {
  288. KIRQL OldIrql;
  289. ULONG ThisBlockSize;
  290. PGLOBAL_MEMORY_DESCRIPTOR GlobalDescriptor;
  291. PLOCAL_MEMORY_DESCRIPTOR LocalDescriptor;
  292. PLOCAL_MEMORY_DESCRIPTOR PrevDescriptor;
  293. PLOCAL_MEMORY_DESCRIPTOR ThisDescriptor;
  294. LocalDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(((PUCHAR)(*Pointer)) - sizeof(LOCAL_MEMORY_DESCRIPTOR));
  295. IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyFreePool: Entering with block 0x%X.\n", LocalDescriptor)));
  296. ASSERT (LocalDescriptor->Size > 0);
  297. #if DBG
  298. ASSERT (LocalDescriptor->Signature == LOCAL_MEMORY_SIGNATURE);
  299. #endif
  300. InterlockedIncrement(
  301. &TotalFrees
  302. );
  303. ExInterlockedAddLargeStatistic(
  304. &TotalBytesFreed,
  305. (CLONG)LocalDescriptor->Size
  306. );
  307. //
  308. // Find the memory block in the global list
  309. //
  310. KeAcquireSpinLock(&MemoryLock, &OldIrql);
  311. GlobalDescriptor = GlobalMemoryList;
  312. while (GlobalDescriptor != NULL) {
  313. #if DBG
  314. ASSERT(GlobalDescriptor->Signature == GLOBAL_MEMORY_SIGNATURE);
  315. #endif
  316. PrevDescriptor = NULL;
  317. ThisDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(GlobalDescriptor->Memory);
  318. ThisBlockSize = GlobalDescriptor->Size;
  319. while (ThisBlockSize != 0) {
  320. #if DBG
  321. ASSERT (ThisDescriptor->Signature == LOCAL_MEMORY_SIGNATURE);
  322. #endif
  323. if (ThisDescriptor == LocalDescriptor) {
  324. goto FoundBlock;
  325. }
  326. ThisBlockSize -= (ThisDescriptor->Size + sizeof(LOCAL_MEMORY_DESCRIPTOR));
  327. PrevDescriptor = ThisDescriptor;
  328. ThisDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(((PUCHAR)ThisDescriptor) +
  329. ThisDescriptor->Size +
  330. sizeof(LOCAL_MEMORY_DESCRIPTOR)
  331. );
  332. }
  333. GlobalDescriptor = GlobalDescriptor->NextDescriptor;
  334. }
  335. KeReleaseSpinLock(&MemoryLock, OldIrql);
  336. IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyFreePool: Could not find block.\n")));
  337. ASSERT(FALSE);
  338. return;
  339. FoundBlock:
  340. //
  341. // Jump to here when the proper memory descriptor has been found.
  342. //
  343. #if DBG
  344. ASSERT (ThisDescriptor->Signature == LOCAL_MEMORY_SIGNATURE);
  345. #endif
  346. if (LocalDescriptor->Tag == FREE_POOL_TAG) {
  347. //
  348. // Ouch! We tried to free something twice, skip it before bad things happen.
  349. //
  350. KeReleaseSpinLock(&MemoryLock, OldIrql);
  351. IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyFreePool: Attempted to free something twice.\n")));
  352. ASSERT(FALSE);
  353. return;
  354. }
  355. LocalDescriptor->Tag = FREE_POOL_TAG;
  356. //
  357. // If possible, merge this memory block with the next one.
  358. //
  359. if (ThisBlockSize > (LocalDescriptor->Size + sizeof(LOCAL_MEMORY_DESCRIPTOR))) {
  360. ThisDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(((PUCHAR)LocalDescriptor) +
  361. LocalDescriptor->Size +
  362. sizeof(LOCAL_MEMORY_DESCRIPTOR)
  363. );
  364. if (ThisDescriptor->Tag == FREE_POOL_TAG) {
  365. ThisDescriptor->Tag = 0;
  366. #if DBG
  367. ThisDescriptor->Signature = 0;
  368. #endif
  369. LocalDescriptor->Size += ThisDescriptor->Size + sizeof(LOCAL_MEMORY_DESCRIPTOR);
  370. }
  371. }
  372. //
  373. // Now see if we can merge this block with a previous block.
  374. //
  375. if ((PrevDescriptor != NULL) && (PrevDescriptor->Tag == FREE_POOL_TAG)) {
  376. #if DBG
  377. LocalDescriptor->Signature = 0;
  378. #endif
  379. LocalDescriptor->Tag = 0;
  380. PrevDescriptor->Size += LocalDescriptor->Size + sizeof(LOCAL_MEMORY_DESCRIPTOR);
  381. }
  382. KeReleaseSpinLock(&MemoryLock, OldIrql);
  383. *Pointer = NULL;
  384. IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyFreePool: exiting.\n")));
  385. } // MyFreePool