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.

817 lines
22 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. largemem.cxx
  5. Abstract:
  6. The implementation of large memory allocator interfaces.
  7. Author:
  8. George V. Reilly (GeorgeRe) 10-Nov-2000
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "largemem.h"
  13. #define LOWEST_USABLE_PHYSICAL_ADDRESS (16 * 1024 * 1024)
  14. // Periodically snapshot some perf counters so that we can tune
  15. // memory consumption
  16. typedef struct _PERF_SNAPSHOT
  17. {
  18. SYSTEM_PERFORMANCE_INFORMATION PerfInfo; // for perf counter deltas
  19. LARGE_INTEGER PerfInfoTime; // to calculate rates
  20. ULONG AvailMemMB; // Currently available memory, in MB
  21. } PERF_SNAPSHOT, *PPERF_SNAPSHOT;
  22. #define DEFAULT_TUNING_PERIOD 60 // seconds
  23. //
  24. // Globals
  25. //
  26. LONG g_LargeMemInitialized;
  27. ULONG g_TotalPhysicalMemMB; // total physical memory (MB)
  28. LONG g_LargeMemMegabytes; // how many MB to use for allocs
  29. ULONG g_LargeMemPagesHardLimit; // " " pages " " "
  30. volatile ULONG g_LargeMemPagesMaxLimit; // " " pages " " "
  31. volatile ULONG g_LargeMemPagesCurrent; // #pages currently used
  32. volatile ULONG g_LargeMemPagesMaxEverUsed; // max #pages ever used
  33. //
  34. // Periodic memory tuner
  35. //
  36. UL_SPIN_LOCK g_LargeMemUsageSpinLock;
  37. KDPC g_LargeMemUsageDpc;
  38. KTIMER g_LargeMemUsageTimer;
  39. KEVENT g_LargeMemUsageTerminationEvent;
  40. UL_WORK_ITEM g_LargeMemUsageWorkItem;
  41. PERF_SNAPSHOT g_LargeMemPerfSnapshot; // previous value, for deltas
  42. #ifdef __cplusplus
  43. extern "C" {
  44. #endif // __cplusplus
  45. //
  46. // Private prototypes.
  47. //
  48. NTSTATUS
  49. UlpReadPerfSnapshot(
  50. OUT PPERF_SNAPSHOT pPerfSnapshot);
  51. VOID
  52. UlpLargeMemTuneUsageWorker(
  53. IN PUL_WORK_ITEM pWorkItem);
  54. #ifdef __cplusplus
  55. }; // extern "C"
  56. #endif // __cplusplus
  57. #ifdef ALLOC_PRAGMA
  58. #pragma alloc_text( INIT, UlLargeMemInitialize )
  59. #pragma alloc_text( PAGE, UlpReadPerfSnapshot )
  60. #pragma alloc_text( PAGE, UlpLargeMemTuneUsageWorker )
  61. #endif // ALLOC_PRAGMA
  62. #if 0
  63. NOT PAGEABLE -- UlLargeMemTerminate
  64. NOT PAGEABLE -- UlpSetLargeMemTuneUsageTimer
  65. NOT PAGEABLE -- UlpLargeMemTuneUsageDpcRoutine
  66. NOT PAGEABLE -- UlLargeMemUsagePercentage
  67. #endif
  68. /***************************************************************************++
  69. Routine Description:
  70. Read a snapshot of some system performance counters. Used by
  71. periodic memory usage tuner
  72. Arguments:
  73. pPerfSnapshot - where to place the snapshot
  74. --***************************************************************************/
  75. NTSTATUS
  76. UlpReadPerfSnapshot(
  77. OUT PPERF_SNAPSHOT pPerfSnapshot)
  78. {
  79. NTSTATUS Status;
  80. ASSERT(NULL != pPerfSnapshot);
  81. // NtQuerySystemInformation must be called at passive level
  82. PAGED_CODE();
  83. Status = NtQuerySystemInformation(
  84. SystemPerformanceInformation,
  85. &pPerfSnapshot->PerfInfo,
  86. sizeof(SYSTEM_PERFORMANCE_INFORMATION),
  87. NULL);
  88. ASSERT(NT_SUCCESS(Status));
  89. if (NT_SUCCESS(Status))
  90. {
  91. KeQuerySystemTime(&pPerfSnapshot->PerfInfoTime);
  92. pPerfSnapshot->AvailMemMB =
  93. PAGES_TO_MEGABYTES(pPerfSnapshot->PerfInfo.AvailablePages);
  94. }
  95. return Status;
  96. } // UlpReadPerfSnapshot
  97. /***************************************************************************++
  98. Routine Description:
  99. Set the timer for memory tuning
  100. Arguments:
  101. TunePeriod - interval until next tuner (in seconds)
  102. --***************************************************************************/
  103. VOID
  104. UlpSetLargeMemTuneUsageTimer(
  105. IN UINT TunePeriod
  106. )
  107. {
  108. LARGE_INTEGER Interval;
  109. KIRQL oldIrql;
  110. UlAcquireSpinLock(&g_LargeMemUsageSpinLock, &oldIrql);
  111. UlTrace(LARGE_MEM, (
  112. "Http!UlpSetLargeMemTuneUsageTimer: %d seconds\n",
  113. TunePeriod
  114. ));
  115. //
  116. // Don't want to execute this more often than every couple of seconds.
  117. // In particular, do not want to execute this every 0 seconds, as the
  118. // machine will become completely unresponsive.
  119. //
  120. TunePeriod = max(TunePeriod, 2);
  121. //
  122. // convert seconds to 100 nanosecond intervals (x * 10^7)
  123. // negative numbers mean relative time
  124. //
  125. Interval.QuadPart = TunePeriod * -C_NS_TICKS_PER_SEC;
  126. UlTrace(LARGE_MEM, (
  127. "Http!UlpSetLargeMemTuneUsageTimer: "
  128. "%d seconds = %I64d 100ns ticks\n",
  129. TunePeriod, Interval.QuadPart
  130. ));
  131. if (g_LargeMemInitialized)
  132. {
  133. KeSetTimer(
  134. &g_LargeMemUsageTimer,
  135. Interval,
  136. &g_LargeMemUsageDpc
  137. );
  138. }
  139. else
  140. {
  141. // Shutdown may have started between the time the timer DPC was
  142. // called, queuing UlpLargeMemTuneUsageWorker, and the time this
  143. // routine was actually started, so set the event and quit immediately.
  144. KeSetEvent(
  145. &g_LargeMemUsageTerminationEvent,
  146. 0,
  147. FALSE
  148. );
  149. }
  150. UlReleaseSpinLock(&g_LargeMemUsageSpinLock, oldIrql);
  151. } // UlpSetLargeMemTuneUsageTimer
  152. /***************************************************************************++
  153. Routine Description:
  154. Periodically adjust g_LargeMemPagesMaxLimit in response to
  155. memory pressure. Called at passive level.
  156. Arguments:
  157. pWorkItem - ignored
  158. --***************************************************************************/
  159. VOID
  160. UlpLargeMemTuneUsageWorker(
  161. IN PUL_WORK_ITEM pWorkItem
  162. )
  163. {
  164. PERF_SNAPSHOT PerfSnapshot;
  165. UINT TunePeriod = DEFAULT_TUNING_PERIOD;
  166. ULONG PagesLimit = g_LargeMemPagesMaxLimit;
  167. PAGED_CODE();
  168. if (! g_LargeMemInitialized)
  169. {
  170. // Shutdown may have started between the time the timer DPC was
  171. // called, queuing this routine, and the time this routine was
  172. // actually started, so set the event and quit immediately.
  173. KeSetEvent(
  174. &g_LargeMemUsageTerminationEvent,
  175. 0,
  176. FALSE
  177. );
  178. return;
  179. }
  180. NTSTATUS Status = UlpReadPerfSnapshot(&PerfSnapshot);
  181. ASSERT(NT_SUCCESS(Status));
  182. if (NT_SUCCESS(Status))
  183. {
  184. #if 0
  185. // Needed for rate calculations
  186. LONGLONG DeltaT = (PerfSnapshot.PerfInfoTime.QuadPart
  187. - g_LargeMemPerfSnapshot.PerfInfoTime.QuadPart);
  188. // DeltaT can be negative if the system clock has moved backwards;
  189. // e.g., synchronizing with the domain controller.
  190. // Disable for now, since it's currently unused and we're hitting
  191. // this assertion.
  192. ASSERT(DeltaT > 0);
  193. DeltaT /= C_NS_TICKS_PER_SEC; // convert to seconds
  194. // CODEWORK: look at other metrics, such as pagefault rate:
  195. // (PerfSnapshot.PageFaultCount - g_PerfInfo.PageFaultCount) / DeltaT
  196. #endif
  197. //
  198. // Adjust g_LargeMemPagesMaxLimit
  199. //
  200. // Is available memory really low?
  201. if (PerfSnapshot.AvailMemMB <= 8 /* megabytes */)
  202. {
  203. // reduce by one-eighth, but don't let go below 4MB
  204. PagesLimit -= PagesLimit / 8;
  205. PagesLimit = max(PagesLimit, MEGABYTES_TO_PAGES(4));
  206. TunePeriod /= 4; // reschedule quickly
  207. UlTrace(LARGE_MEM,
  208. ("Http!UlpLargeMemTuneUsageWorker: "
  209. "avail mem=%dMB, total=%dMB: "
  210. "reducing from %d pages (%dMB) to %d pages (%dMB)\n",
  211. PerfSnapshot.AvailMemMB, g_TotalPhysicalMemMB,
  212. g_LargeMemPagesMaxLimit,
  213. PAGES_TO_MEGABYTES(g_LargeMemPagesMaxLimit),
  214. PagesLimit,
  215. PAGES_TO_MEGABYTES(PagesLimit)
  216. ));
  217. }
  218. // is at least one-quarter of physical memory available?
  219. else if (PerfSnapshot.AvailMemMB >= (g_TotalPhysicalMemMB >> 2))
  220. {
  221. // raise the limit by one-eighth; clamp at g_LargeMemPagesHardLimit
  222. PagesLimit += PagesLimit / 8;
  223. PagesLimit = min(PagesLimit, g_LargeMemPagesHardLimit);
  224. UlTrace(LARGE_MEM,
  225. ("Http!UlpLargeMemTuneUsageWorker: "
  226. "avail mem=%dMB, total=%dMB: "
  227. "increasing from %d pages (%dMB) to %d pages (%dMB)\n",
  228. PerfSnapshot.AvailMemMB, g_TotalPhysicalMemMB,
  229. g_LargeMemPagesMaxLimit,
  230. PAGES_TO_MEGABYTES(g_LargeMemPagesMaxLimit),
  231. PagesLimit,
  232. PAGES_TO_MEGABYTES(PagesLimit)
  233. ));
  234. }
  235. g_LargeMemPagesMaxLimit = PagesLimit;
  236. UlTrace(LARGE_MEM,
  237. ("Http!UlpLargeMemTuneUsageWorker: "
  238. "%d%% of cache memory used: "
  239. "%d pages (%dMB) / %d pages (%dMB)\n",
  240. UlLargeMemUsagePercentage(),
  241. g_LargeMemPagesCurrent,
  242. PAGES_TO_MEGABYTES(g_LargeMemPagesCurrent),
  243. g_LargeMemPagesMaxLimit,
  244. PAGES_TO_MEGABYTES(g_LargeMemPagesMaxLimit)
  245. ));
  246. // save g_LargeMemPerfSnapshot for next round
  247. g_LargeMemPerfSnapshot = PerfSnapshot;
  248. }
  249. // Restart the timer.
  250. UlpSetLargeMemTuneUsageTimer(TunePeriod);
  251. } // UlpLargeMemTuneUsageWorker
  252. /***************************************************************************++
  253. Routine Description:
  254. Timer callback to do memory tuning
  255. Arguments:
  256. ignored
  257. --***************************************************************************/
  258. VOID
  259. UlpLargeMemTuneUsageDpcRoutine(
  260. IN PKDPC Dpc,
  261. IN PVOID DeferredContext,
  262. IN PVOID SystemArgument1,
  263. IN PVOID SystemArgument2
  264. )
  265. {
  266. UlAcquireSpinLockAtDpcLevel(&g_LargeMemUsageSpinLock);
  267. if (! g_LargeMemInitialized)
  268. {
  269. // We're shutting down, so signal the termination event.
  270. KeSetEvent(
  271. &g_LargeMemUsageTerminationEvent,
  272. 0,
  273. FALSE
  274. );
  275. }
  276. else
  277. {
  278. // Do the work at passive level
  279. UL_QUEUE_WORK_ITEM(
  280. &g_LargeMemUsageWorkItem,
  281. &UlpLargeMemTuneUsageWorker
  282. );
  283. }
  284. UlReleaseSpinLockFromDpcLevel(&g_LargeMemUsageSpinLock);
  285. } // UlpLargeMemTuneUsageDpcRoutine
  286. /***************************************************************************++
  287. Routine Description:
  288. Initialize global state for LargeMem
  289. Arguments:
  290. pConfig - default configuration from registry
  291. --***************************************************************************/
  292. NTSTATUS
  293. UlLargeMemInitialize(
  294. IN PUL_CONFIG pConfig
  295. )
  296. {
  297. NTSTATUS Status = STATUS_SUCCESS;
  298. PAGED_CODE();
  299. g_LargeMemMegabytes = 0;
  300. g_LargeMemPagesHardLimit = 0;
  301. g_LargeMemPagesMaxLimit = 0;
  302. g_LargeMemPagesCurrent = 0;
  303. g_LargeMemPagesMaxEverUsed = 0;
  304. UlpReadPerfSnapshot(&g_LargeMemPerfSnapshot);
  305. g_LargeMemMegabytes = pConfig->LargeMemMegabytes;
  306. SYSTEM_BASIC_INFORMATION sbi;
  307. Status = NtQuerySystemInformation(
  308. SystemBasicInformation,
  309. &sbi,
  310. sizeof(sbi),
  311. NULL);
  312. ASSERT(NT_SUCCESS(Status));
  313. // Capture total physical memory
  314. g_TotalPhysicalMemMB = PAGES_TO_MEGABYTES(sbi.NumberOfPhysicalPages);
  315. if (DEFAULT_LARGE_MEM_MEGABYTES == g_LargeMemMegabytes)
  316. {
  317. if (g_TotalPhysicalMemMB <= 256)
  318. {
  319. // <=256MB: set to quarter of physical memory
  320. g_LargeMemMegabytes = (g_TotalPhysicalMemMB >> 2);
  321. }
  322. else if (g_TotalPhysicalMemMB <= 512)
  323. {
  324. // 256-512MB: set to half of physical memory
  325. g_LargeMemMegabytes = (g_TotalPhysicalMemMB >> 1);
  326. }
  327. else if (g_TotalPhysicalMemMB <= 2048)
  328. {
  329. // 512MB-2GB: set to three-quarters of physical memory
  330. g_LargeMemMegabytes =
  331. g_TotalPhysicalMemMB - (g_TotalPhysicalMemMB >> 2);
  332. }
  333. else
  334. {
  335. // >2GB: set to seven-eighths of physical memory
  336. g_LargeMemMegabytes =
  337. g_TotalPhysicalMemMB - (g_TotalPhysicalMemMB >> 3);
  338. }
  339. }
  340. // Should we clamp this now?
  341. g_LargeMemMegabytes = min(g_LargeMemMegabytes,
  342. (LONG)(g_LargeMemPerfSnapshot.AvailMemMB));
  343. // We will use at most this many pages of memory
  344. g_LargeMemPagesHardLimit = MEGABYTES_TO_PAGES(g_LargeMemMegabytes);
  345. // g_LargeMemPagesMaxLimit is adjusted in response to memory pressure
  346. g_LargeMemPagesMaxLimit = g_LargeMemPagesHardLimit;
  347. UlTraceVerbose(LARGE_MEM,
  348. ("Http!UlLargeMemInitialize: "
  349. "g_TotalPhysicalMemMB=%dMB, "
  350. "AvailMem=%dMB\n"
  351. "\tg_LargeMemMegabytes=%dMB, g_LargeMemPagesHardLimit=%d.\n",
  352. g_TotalPhysicalMemMB, g_LargeMemPerfSnapshot.AvailMemMB,
  353. g_LargeMemMegabytes, g_LargeMemPagesHardLimit));
  354. UlInitializeSpinLock(&g_LargeMemUsageSpinLock, "g_LargeMemUsageSpinLock");
  355. KeInitializeDpc(
  356. &g_LargeMemUsageDpc,
  357. &UlpLargeMemTuneUsageDpcRoutine,
  358. NULL
  359. );
  360. KeInitializeTimer(&g_LargeMemUsageTimer);
  361. KeInitializeEvent(
  362. &g_LargeMemUsageTerminationEvent,
  363. NotificationEvent,
  364. FALSE
  365. );
  366. g_LargeMemInitialized = TRUE;
  367. UlpSetLargeMemTuneUsageTimer(DEFAULT_TUNING_PERIOD);
  368. return Status;
  369. } // UlLargeMemInitialize
  370. /***************************************************************************++
  371. Routine Description:
  372. Cleanup global state for LargeMem
  373. --***************************************************************************/
  374. VOID
  375. UlLargeMemTerminate(
  376. VOID
  377. )
  378. {
  379. PAGED_CODE();
  380. ASSERT(0 == g_LargeMemPagesCurrent);
  381. if (g_LargeMemInitialized)
  382. {
  383. //
  384. // Clear the "initialized" flag. If the memory tuner runs soon,
  385. // it will see this flag, set the termination event, and exit
  386. // quickly.
  387. //
  388. KIRQL oldIrql;
  389. UlAcquireSpinLock(&g_LargeMemUsageSpinLock, &oldIrql);
  390. g_LargeMemInitialized = FALSE;
  391. UlReleaseSpinLock(&g_LargeMemUsageSpinLock, oldIrql);
  392. //
  393. // Cancel the memory tuner timer. If the cancel fails, then the
  394. // memory tuner is either running or scheduled to run soon. In
  395. // either case, wait for it to terminate.
  396. //
  397. if (! KeCancelTimer(&g_LargeMemUsageTimer))
  398. {
  399. KeWaitForSingleObject(
  400. &g_LargeMemUsageTerminationEvent,
  401. UserRequest,
  402. KernelMode,
  403. FALSE,
  404. NULL
  405. );
  406. }
  407. }
  408. UlTraceVerbose(LARGE_MEM,
  409. ("Http!UlLargeMemTerminate: Memory used: "
  410. "Current = %d pages = %dMB; MaxEver = %d pages = %dMB.\n",
  411. g_LargeMemPagesCurrent,
  412. PAGES_TO_MEGABYTES(g_LargeMemPagesCurrent),
  413. g_LargeMemPagesMaxEverUsed,
  414. PAGES_TO_MEGABYTES(g_LargeMemPagesMaxEverUsed)
  415. ));
  416. } // UlLargeMemTerminate
  417. /***************************************************************************++
  418. Routine Description:
  419. Return the percentage of available cache memory that is in use.
  420. Return Value:
  421. 0 < result <= 95: okay
  422. 95 < result <= 100: free up some memory soon
  423. > 100: free up some memory immediately
  424. --***************************************************************************/
  425. UINT
  426. UlLargeMemUsagePercentage(
  427. VOID
  428. )
  429. {
  430. UINT Percentage = (UINT)((((ULONGLONG) g_LargeMemPagesCurrent * 100)
  431. / g_LargeMemPagesMaxLimit));
  432. return Percentage;
  433. } // UlLargeMemUsagePercentage
  434. /***************************************************************************++
  435. Routine Description:
  436. Allocate a MDL from PAE memory
  437. --***************************************************************************/
  438. PMDL
  439. UlLargeMemAllocate(
  440. IN ULONG Length,
  441. OUT PBOOLEAN pLongTermCacheable
  442. )
  443. {
  444. PMDL pMdl;
  445. // CODEWORK: cap the size of individual allocations
  446. LONG RoundUpBytes = (LONG) ROUND_TO_PAGES(Length);
  447. LONG NewPages = RoundUpBytes >> PAGE_SHIFT;
  448. if (g_LargeMemPagesCurrent + NewPages > g_LargeMemPagesMaxLimit)
  449. {
  450. UlTrace(LARGE_MEM,
  451. ("http!UlLargeMemAllocate: about to overshoot "
  452. "g_LargeMemPagesMaxLimit=%d pages. Not allocating %d pages\n",
  453. g_LargeMemPagesMaxLimit, NewPages
  454. ));
  455. }
  456. PHYSICAL_ADDRESS LowAddress, HighAddress, SkipBytes;
  457. LowAddress.QuadPart = LOWEST_USABLE_PHYSICAL_ADDRESS;
  458. HighAddress.QuadPart = 0xfffffffff; // 64GB
  459. SkipBytes.QuadPart = 0;
  460. pMdl = MmAllocatePagesForMdl(
  461. LowAddress,
  462. HighAddress,
  463. SkipBytes,
  464. RoundUpBytes
  465. );
  466. // Completely failed to allocate memory
  467. if (pMdl == NULL)
  468. {
  469. UlTrace(LARGE_MEM,
  470. ("http!UlLargeMemAllocate: "
  471. "Completely failed to allocate %d bytes.\n",
  472. RoundUpBytes
  473. ));
  474. return NULL;
  475. }
  476. // Couldn't allocate all the memory we asked for. We need all the pages
  477. // we asked for, so we have to set the state of `this' to invalid.
  478. // Memory is probably really tight.
  479. if (MmGetMdlByteCount(pMdl) < Length)
  480. {
  481. UlTrace(LARGE_MEM,
  482. ("http!UlLargeMemAllocate: Failed to allocate %d bytes. "
  483. "Got %d instead.\n",
  484. RoundUpBytes, MmGetMdlByteCount(pMdl)
  485. ));
  486. // Free MDL but don't adjust g_LargeMemPagesCurrent downwards
  487. MmFreePagesFromMdl(pMdl);
  488. ExFreePool(pMdl);
  489. return NULL;
  490. }
  491. UlTrace(LARGE_MEM,
  492. ("http!UlLargeMemAllocate: %u->%u, mdl=%p, %d pages.\n",
  493. Length, pMdl->ByteCount, pMdl, NewPages
  494. ));
  495. LONG PrevPagesUsed =
  496. InterlockedExchangeAdd((PLONG) &g_LargeMemPagesCurrent, NewPages);
  497. if (PrevPagesUsed + NewPages > (LONG)g_LargeMemPagesMaxLimit)
  498. {
  499. // overshot g_LargeMemPagesMaxLimit
  500. UlTrace(LARGE_MEM,
  501. ("http!UlLargeMemAllocate: "
  502. "overshot g_LargeMemPagesMaxLimit=%d pages. "
  503. "Releasing %d pages\n",
  504. g_LargeMemPagesMaxLimit, NewPages
  505. ));
  506. // Don't free up memory. Return the allocated memory to the
  507. // caller, who is responsible for checking to see if it can be
  508. // cached for long-term usage, or if it should be freed ASAP.
  509. // CODEWORK: This implies that the MRU entries in the cache will
  510. // be not be cached, which probably leads to poor cache locality.
  511. // Really ought to free up some LRU cache entries instead.
  512. *pLongTermCacheable = FALSE;
  513. }
  514. else
  515. {
  516. *pLongTermCacheable = TRUE;
  517. }
  518. ASSERT(pMdl->MdlFlags & MDL_PAGES_LOCKED);
  519. // Hurrah! a successful allocation
  520. //
  521. // update g_LargeMemPagesMaxEverUsed in a threadsafe manner
  522. // using interlocked instructions
  523. LONG NewMaxUsed;
  524. do
  525. {
  526. LONG CurrentPages = g_LargeMemPagesCurrent;
  527. LONG MaxEver = g_LargeMemPagesMaxEverUsed;
  528. NewMaxUsed = max(MaxEver, CurrentPages);
  529. if (NewMaxUsed > MaxEver)
  530. {
  531. InterlockedCompareExchange(
  532. (PLONG) &g_LargeMemPagesMaxEverUsed,
  533. NewMaxUsed,
  534. MaxEver
  535. );
  536. }
  537. } while (NewMaxUsed < (LONG)g_LargeMemPagesCurrent);
  538. UlTrace(LARGE_MEM,
  539. ("http!UlLargeMemAllocate: "
  540. "g_LargeMemPagesCurrent=%d pages. "
  541. "g_LargeMemPagesMaxEverUsed=%d pages.\n",
  542. g_LargeMemPagesCurrent, NewMaxUsed
  543. ));
  544. WRITE_REF_TRACE_LOG(
  545. g_pMdlTraceLog,
  546. REF_ACTION_ALLOCATE_MDL,
  547. PtrToLong(pMdl->Next), // bugbug64
  548. pMdl,
  549. __FILE__,
  550. __LINE__
  551. );
  552. return pMdl;
  553. } // UlLargeMemAllocate
  554. /***************************************************************************++
  555. Routine Description:
  556. Free a MDL to PAE memory
  557. --***************************************************************************/
  558. VOID
  559. UlLargeMemFree(
  560. IN PMDL pMdl
  561. )
  562. {
  563. LONG Pages;
  564. LONG PrevPagesUsed;
  565. ASSERT(ROUND_TO_PAGES(pMdl->ByteCount) == pMdl->ByteCount);
  566. Pages = pMdl->ByteCount >> PAGE_SHIFT;
  567. MmFreePagesFromMdl(pMdl);
  568. ExFreePool(pMdl);
  569. PrevPagesUsed
  570. = InterlockedExchangeAdd(
  571. (PLONG) &g_LargeMemPagesCurrent,
  572. - Pages);
  573. ASSERT(PrevPagesUsed >= Pages);
  574. } // UlLargeMemFree
  575. /***************************************************************************++
  576. Routine Description:
  577. Copy a buffer to the specified MDL starting from Offset.
  578. --***************************************************************************/
  579. BOOLEAN
  580. UlLargeMemSetData(
  581. IN PMDL pMdl,
  582. IN PUCHAR pBuffer,
  583. IN ULONG Length,
  584. IN ULONG Offset
  585. )
  586. {
  587. PUCHAR pSysAddr;
  588. BOOLEAN Result;
  589. ASSERT(Offset <= pMdl->ByteCount);
  590. ASSERT(Length <= (pMdl->ByteCount - Offset));
  591. ASSERT(pMdl->MdlFlags & MDL_PAGES_LOCKED);
  592. pSysAddr = (PUCHAR) MmMapLockedPagesSpecifyCache (
  593. pMdl, // MemoryDescriptorList,
  594. KernelMode, // AccessMode,
  595. MmCached, // CacheType,
  596. NULL, // BaseAddress,
  597. FALSE, // BugCheckOnFailure,
  598. NormalPagePriority // Priority
  599. );
  600. if (pSysAddr != NULL)
  601. {
  602. RtlCopyMemory(
  603. pSysAddr + Offset,
  604. pBuffer,
  605. Length
  606. );
  607. MmUnmapLockedPages(pSysAddr, pMdl);
  608. return TRUE;
  609. }
  610. return FALSE;
  611. } // UlLargeMemSetData