mirror of https://github.com/tongzx/nt5src
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
817 lines
22 KiB
/*++
|
|
|
|
Copyright (c) 2000-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
largemem.cxx
|
|
|
|
Abstract:
|
|
|
|
The implementation of large memory allocator interfaces.
|
|
|
|
Author:
|
|
|
|
George V. Reilly (GeorgeRe) 10-Nov-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "largemem.h"
|
|
|
|
#define LOWEST_USABLE_PHYSICAL_ADDRESS (16 * 1024 * 1024)
|
|
|
|
// Periodically snapshot some perf counters so that we can tune
|
|
// memory consumption
|
|
typedef struct _PERF_SNAPSHOT
|
|
{
|
|
SYSTEM_PERFORMANCE_INFORMATION PerfInfo; // for perf counter deltas
|
|
LARGE_INTEGER PerfInfoTime; // to calculate rates
|
|
ULONG AvailMemMB; // Currently available memory, in MB
|
|
} PERF_SNAPSHOT, *PPERF_SNAPSHOT;
|
|
|
|
#define DEFAULT_TUNING_PERIOD 60 // seconds
|
|
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
LONG g_LargeMemInitialized;
|
|
ULONG g_TotalPhysicalMemMB; // total physical memory (MB)
|
|
LONG g_LargeMemMegabytes; // how many MB to use for allocs
|
|
ULONG g_LargeMemPagesHardLimit; // " " pages " " "
|
|
volatile ULONG g_LargeMemPagesMaxLimit; // " " pages " " "
|
|
volatile ULONG g_LargeMemPagesCurrent; // #pages currently used
|
|
volatile ULONG g_LargeMemPagesMaxEverUsed; // max #pages ever used
|
|
|
|
|
|
//
|
|
// Periodic memory tuner
|
|
//
|
|
|
|
UL_SPIN_LOCK g_LargeMemUsageSpinLock;
|
|
KDPC g_LargeMemUsageDpc;
|
|
KTIMER g_LargeMemUsageTimer;
|
|
KEVENT g_LargeMemUsageTerminationEvent;
|
|
UL_WORK_ITEM g_LargeMemUsageWorkItem;
|
|
PERF_SNAPSHOT g_LargeMemPerfSnapshot; // previous value, for deltas
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
#endif // __cplusplus
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
NTSTATUS
|
|
UlpReadPerfSnapshot(
|
|
OUT PPERF_SNAPSHOT pPerfSnapshot);
|
|
|
|
VOID
|
|
UlpLargeMemTuneUsageWorker(
|
|
IN PUL_WORK_ITEM pWorkItem);
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
}; // extern "C"
|
|
#endif // __cplusplus
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( INIT, UlLargeMemInitialize )
|
|
#pragma alloc_text( PAGE, UlpReadPerfSnapshot )
|
|
#pragma alloc_text( PAGE, UlpLargeMemTuneUsageWorker )
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
#if 0
|
|
NOT PAGEABLE -- UlLargeMemTerminate
|
|
NOT PAGEABLE -- UlpSetLargeMemTuneUsageTimer
|
|
NOT PAGEABLE -- UlpLargeMemTuneUsageDpcRoutine
|
|
NOT PAGEABLE -- UlLargeMemUsagePercentage
|
|
#endif
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Read a snapshot of some system performance counters. Used by
|
|
periodic memory usage tuner
|
|
|
|
Arguments:
|
|
|
|
pPerfSnapshot - where to place the snapshot
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpReadPerfSnapshot(
|
|
OUT PPERF_SNAPSHOT pPerfSnapshot)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(NULL != pPerfSnapshot);
|
|
|
|
// NtQuerySystemInformation must be called at passive level
|
|
PAGED_CODE();
|
|
|
|
Status = NtQuerySystemInformation(
|
|
SystemPerformanceInformation,
|
|
&pPerfSnapshot->PerfInfo,
|
|
sizeof(SYSTEM_PERFORMANCE_INFORMATION),
|
|
NULL);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
KeQuerySystemTime(&pPerfSnapshot->PerfInfoTime);
|
|
|
|
pPerfSnapshot->AvailMemMB =
|
|
PAGES_TO_MEGABYTES(pPerfSnapshot->PerfInfo.AvailablePages);
|
|
}
|
|
|
|
return Status;
|
|
} // UlpReadPerfSnapshot
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Set the timer for memory tuning
|
|
|
|
Arguments:
|
|
|
|
TunePeriod - interval until next tuner (in seconds)
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpSetLargeMemTuneUsageTimer(
|
|
IN UINT TunePeriod
|
|
)
|
|
{
|
|
LARGE_INTEGER Interval;
|
|
KIRQL oldIrql;
|
|
|
|
UlAcquireSpinLock(&g_LargeMemUsageSpinLock, &oldIrql);
|
|
|
|
UlTrace(LARGE_MEM, (
|
|
"Http!UlpSetLargeMemTuneUsageTimer: %d seconds\n",
|
|
TunePeriod
|
|
));
|
|
|
|
//
|
|
// Don't want to execute this more often than every couple of seconds.
|
|
// In particular, do not want to execute this every 0 seconds, as the
|
|
// machine will become completely unresponsive.
|
|
//
|
|
|
|
TunePeriod = max(TunePeriod, 2);
|
|
|
|
//
|
|
// convert seconds to 100 nanosecond intervals (x * 10^7)
|
|
// negative numbers mean relative time
|
|
//
|
|
|
|
Interval.QuadPart = TunePeriod * -C_NS_TICKS_PER_SEC;
|
|
|
|
UlTrace(LARGE_MEM, (
|
|
"Http!UlpSetLargeMemTuneUsageTimer: "
|
|
"%d seconds = %I64d 100ns ticks\n",
|
|
TunePeriod, Interval.QuadPart
|
|
));
|
|
|
|
if (g_LargeMemInitialized)
|
|
{
|
|
KeSetTimer(
|
|
&g_LargeMemUsageTimer,
|
|
Interval,
|
|
&g_LargeMemUsageDpc
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Shutdown may have started between the time the timer DPC was
|
|
// called, queuing UlpLargeMemTuneUsageWorker, and the time this
|
|
// routine was actually started, so set the event and quit immediately.
|
|
KeSetEvent(
|
|
&g_LargeMemUsageTerminationEvent,
|
|
0,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
|
|
UlReleaseSpinLock(&g_LargeMemUsageSpinLock, oldIrql);
|
|
|
|
} // UlpSetLargeMemTuneUsageTimer
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Periodically adjust g_LargeMemPagesMaxLimit in response to
|
|
memory pressure. Called at passive level.
|
|
|
|
Arguments:
|
|
|
|
pWorkItem - ignored
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpLargeMemTuneUsageWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
)
|
|
{
|
|
PERF_SNAPSHOT PerfSnapshot;
|
|
UINT TunePeriod = DEFAULT_TUNING_PERIOD;
|
|
ULONG PagesLimit = g_LargeMemPagesMaxLimit;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (! g_LargeMemInitialized)
|
|
{
|
|
// Shutdown may have started between the time the timer DPC was
|
|
// called, queuing this routine, and the time this routine was
|
|
// actually started, so set the event and quit immediately.
|
|
KeSetEvent(
|
|
&g_LargeMemUsageTerminationEvent,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS Status = UlpReadPerfSnapshot(&PerfSnapshot);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
#if 0
|
|
// Needed for rate calculations
|
|
LONGLONG DeltaT = (PerfSnapshot.PerfInfoTime.QuadPart
|
|
- g_LargeMemPerfSnapshot.PerfInfoTime.QuadPart);
|
|
|
|
// DeltaT can be negative if the system clock has moved backwards;
|
|
// e.g., synchronizing with the domain controller.
|
|
// Disable for now, since it's currently unused and we're hitting
|
|
// this assertion.
|
|
ASSERT(DeltaT > 0);
|
|
DeltaT /= C_NS_TICKS_PER_SEC; // convert to seconds
|
|
|
|
// CODEWORK: look at other metrics, such as pagefault rate:
|
|
// (PerfSnapshot.PageFaultCount - g_PerfInfo.PageFaultCount) / DeltaT
|
|
#endif
|
|
|
|
//
|
|
// Adjust g_LargeMemPagesMaxLimit
|
|
//
|
|
|
|
// Is available memory really low?
|
|
if (PerfSnapshot.AvailMemMB <= 8 /* megabytes */)
|
|
{
|
|
// reduce by one-eighth, but don't let go below 4MB
|
|
PagesLimit -= PagesLimit / 8;
|
|
PagesLimit = max(PagesLimit, MEGABYTES_TO_PAGES(4));
|
|
|
|
TunePeriod /= 4; // reschedule quickly
|
|
|
|
UlTrace(LARGE_MEM,
|
|
("Http!UlpLargeMemTuneUsageWorker: "
|
|
"avail mem=%dMB, total=%dMB: "
|
|
"reducing from %d pages (%dMB) to %d pages (%dMB)\n",
|
|
PerfSnapshot.AvailMemMB, g_TotalPhysicalMemMB,
|
|
g_LargeMemPagesMaxLimit,
|
|
PAGES_TO_MEGABYTES(g_LargeMemPagesMaxLimit),
|
|
PagesLimit,
|
|
PAGES_TO_MEGABYTES(PagesLimit)
|
|
));
|
|
}
|
|
|
|
// is at least one-quarter of physical memory available?
|
|
else if (PerfSnapshot.AvailMemMB >= (g_TotalPhysicalMemMB >> 2))
|
|
{
|
|
// raise the limit by one-eighth; clamp at g_LargeMemPagesHardLimit
|
|
PagesLimit += PagesLimit / 8;
|
|
PagesLimit = min(PagesLimit, g_LargeMemPagesHardLimit);
|
|
|
|
UlTrace(LARGE_MEM,
|
|
("Http!UlpLargeMemTuneUsageWorker: "
|
|
"avail mem=%dMB, total=%dMB: "
|
|
"increasing from %d pages (%dMB) to %d pages (%dMB)\n",
|
|
PerfSnapshot.AvailMemMB, g_TotalPhysicalMemMB,
|
|
g_LargeMemPagesMaxLimit,
|
|
PAGES_TO_MEGABYTES(g_LargeMemPagesMaxLimit),
|
|
PagesLimit,
|
|
PAGES_TO_MEGABYTES(PagesLimit)
|
|
));
|
|
}
|
|
|
|
g_LargeMemPagesMaxLimit = PagesLimit;
|
|
|
|
UlTrace(LARGE_MEM,
|
|
("Http!UlpLargeMemTuneUsageWorker: "
|
|
"%d%% of cache memory used: "
|
|
"%d pages (%dMB) / %d pages (%dMB)\n",
|
|
UlLargeMemUsagePercentage(),
|
|
g_LargeMemPagesCurrent,
|
|
PAGES_TO_MEGABYTES(g_LargeMemPagesCurrent),
|
|
g_LargeMemPagesMaxLimit,
|
|
PAGES_TO_MEGABYTES(g_LargeMemPagesMaxLimit)
|
|
));
|
|
|
|
// save g_LargeMemPerfSnapshot for next round
|
|
g_LargeMemPerfSnapshot = PerfSnapshot;
|
|
}
|
|
|
|
// Restart the timer.
|
|
UlpSetLargeMemTuneUsageTimer(TunePeriod);
|
|
|
|
} // UlpLargeMemTuneUsageWorker
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Timer callback to do memory tuning
|
|
|
|
Arguments:
|
|
|
|
ignored
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpLargeMemTuneUsageDpcRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
{
|
|
UlAcquireSpinLockAtDpcLevel(&g_LargeMemUsageSpinLock);
|
|
|
|
if (! g_LargeMemInitialized)
|
|
{
|
|
// We're shutting down, so signal the termination event.
|
|
|
|
KeSetEvent(
|
|
&g_LargeMemUsageTerminationEvent,
|
|
0,
|
|
FALSE
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Do the work at passive level
|
|
|
|
UL_QUEUE_WORK_ITEM(
|
|
&g_LargeMemUsageWorkItem,
|
|
&UlpLargeMemTuneUsageWorker
|
|
);
|
|
}
|
|
|
|
UlReleaseSpinLockFromDpcLevel(&g_LargeMemUsageSpinLock);
|
|
|
|
} // UlpLargeMemTuneUsageDpcRoutine
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initialize global state for LargeMem
|
|
|
|
Arguments:
|
|
|
|
pConfig - default configuration from registry
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlLargeMemInitialize(
|
|
IN PUL_CONFIG pConfig
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
g_LargeMemMegabytes = 0;
|
|
g_LargeMemPagesHardLimit = 0;
|
|
g_LargeMemPagesMaxLimit = 0;
|
|
g_LargeMemPagesCurrent = 0;
|
|
g_LargeMemPagesMaxEverUsed = 0;
|
|
|
|
UlpReadPerfSnapshot(&g_LargeMemPerfSnapshot);
|
|
|
|
g_LargeMemMegabytes = pConfig->LargeMemMegabytes;
|
|
|
|
SYSTEM_BASIC_INFORMATION sbi;
|
|
|
|
Status = NtQuerySystemInformation(
|
|
SystemBasicInformation,
|
|
&sbi,
|
|
sizeof(sbi),
|
|
NULL);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
// Capture total physical memory
|
|
g_TotalPhysicalMemMB = PAGES_TO_MEGABYTES(sbi.NumberOfPhysicalPages);
|
|
|
|
if (DEFAULT_LARGE_MEM_MEGABYTES == g_LargeMemMegabytes)
|
|
{
|
|
if (g_TotalPhysicalMemMB <= 256)
|
|
{
|
|
// <=256MB: set to quarter of physical memory
|
|
g_LargeMemMegabytes = (g_TotalPhysicalMemMB >> 2);
|
|
}
|
|
else if (g_TotalPhysicalMemMB <= 512)
|
|
{
|
|
// 256-512MB: set to half of physical memory
|
|
g_LargeMemMegabytes = (g_TotalPhysicalMemMB >> 1);
|
|
}
|
|
else if (g_TotalPhysicalMemMB <= 2048)
|
|
{
|
|
// 512MB-2GB: set to three-quarters of physical memory
|
|
g_LargeMemMegabytes =
|
|
g_TotalPhysicalMemMB - (g_TotalPhysicalMemMB >> 2);
|
|
}
|
|
else
|
|
{
|
|
// >2GB: set to seven-eighths of physical memory
|
|
g_LargeMemMegabytes =
|
|
g_TotalPhysicalMemMB - (g_TotalPhysicalMemMB >> 3);
|
|
}
|
|
}
|
|
|
|
// Should we clamp this now?
|
|
g_LargeMemMegabytes = min(g_LargeMemMegabytes,
|
|
(LONG)(g_LargeMemPerfSnapshot.AvailMemMB));
|
|
|
|
// We will use at most this many pages of memory
|
|
g_LargeMemPagesHardLimit = MEGABYTES_TO_PAGES(g_LargeMemMegabytes);
|
|
|
|
// g_LargeMemPagesMaxLimit is adjusted in response to memory pressure
|
|
g_LargeMemPagesMaxLimit = g_LargeMemPagesHardLimit;
|
|
|
|
UlTraceVerbose(LARGE_MEM,
|
|
("Http!UlLargeMemInitialize: "
|
|
"g_TotalPhysicalMemMB=%dMB, "
|
|
"AvailMem=%dMB\n"
|
|
"\tg_LargeMemMegabytes=%dMB, g_LargeMemPagesHardLimit=%d.\n",
|
|
g_TotalPhysicalMemMB, g_LargeMemPerfSnapshot.AvailMemMB,
|
|
g_LargeMemMegabytes, g_LargeMemPagesHardLimit));
|
|
|
|
UlInitializeSpinLock(&g_LargeMemUsageSpinLock, "g_LargeMemUsageSpinLock");
|
|
|
|
KeInitializeDpc(
|
|
&g_LargeMemUsageDpc,
|
|
&UlpLargeMemTuneUsageDpcRoutine,
|
|
NULL
|
|
);
|
|
|
|
KeInitializeTimer(&g_LargeMemUsageTimer);
|
|
|
|
KeInitializeEvent(
|
|
&g_LargeMemUsageTerminationEvent,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
|
|
g_LargeMemInitialized = TRUE;
|
|
|
|
UlpSetLargeMemTuneUsageTimer(DEFAULT_TUNING_PERIOD);
|
|
|
|
return Status;
|
|
} // UlLargeMemInitialize
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Cleanup global state for LargeMem
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlLargeMemTerminate(
|
|
VOID
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(0 == g_LargeMemPagesCurrent);
|
|
|
|
if (g_LargeMemInitialized)
|
|
{
|
|
//
|
|
// Clear the "initialized" flag. If the memory tuner runs soon,
|
|
// it will see this flag, set the termination event, and exit
|
|
// quickly.
|
|
//
|
|
KIRQL oldIrql;
|
|
|
|
UlAcquireSpinLock(&g_LargeMemUsageSpinLock, &oldIrql);
|
|
g_LargeMemInitialized = FALSE;
|
|
UlReleaseSpinLock(&g_LargeMemUsageSpinLock, oldIrql);
|
|
|
|
//
|
|
// Cancel the memory tuner timer. If the cancel fails, then the
|
|
// memory tuner is either running or scheduled to run soon. In
|
|
// either case, wait for it to terminate.
|
|
//
|
|
|
|
if (! KeCancelTimer(&g_LargeMemUsageTimer))
|
|
{
|
|
KeWaitForSingleObject(
|
|
&g_LargeMemUsageTerminationEvent,
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
UlTraceVerbose(LARGE_MEM,
|
|
("Http!UlLargeMemTerminate: Memory used: "
|
|
"Current = %d pages = %dMB; MaxEver = %d pages = %dMB.\n",
|
|
g_LargeMemPagesCurrent,
|
|
PAGES_TO_MEGABYTES(g_LargeMemPagesCurrent),
|
|
g_LargeMemPagesMaxEverUsed,
|
|
PAGES_TO_MEGABYTES(g_LargeMemPagesMaxEverUsed)
|
|
));
|
|
} // UlLargeMemTerminate
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Return the percentage of available cache memory that is in use.
|
|
|
|
Return Value:
|
|
|
|
0 < result <= 95: okay
|
|
95 < result <= 100: free up some memory soon
|
|
> 100: free up some memory immediately
|
|
|
|
--***************************************************************************/
|
|
UINT
|
|
UlLargeMemUsagePercentage(
|
|
VOID
|
|
)
|
|
{
|
|
UINT Percentage = (UINT)((((ULONGLONG) g_LargeMemPagesCurrent * 100)
|
|
/ g_LargeMemPagesMaxLimit));
|
|
|
|
return Percentage;
|
|
} // UlLargeMemUsagePercentage
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocate a MDL from PAE memory
|
|
|
|
--***************************************************************************/
|
|
PMDL
|
|
UlLargeMemAllocate(
|
|
IN ULONG Length,
|
|
OUT PBOOLEAN pLongTermCacheable
|
|
)
|
|
{
|
|
PMDL pMdl;
|
|
|
|
// CODEWORK: cap the size of individual allocations
|
|
|
|
LONG RoundUpBytes = (LONG) ROUND_TO_PAGES(Length);
|
|
LONG NewPages = RoundUpBytes >> PAGE_SHIFT;
|
|
|
|
if (g_LargeMemPagesCurrent + NewPages > g_LargeMemPagesMaxLimit)
|
|
{
|
|
UlTrace(LARGE_MEM,
|
|
("http!UlLargeMemAllocate: about to overshoot "
|
|
"g_LargeMemPagesMaxLimit=%d pages. Not allocating %d pages\n",
|
|
g_LargeMemPagesMaxLimit, NewPages
|
|
));
|
|
}
|
|
|
|
PHYSICAL_ADDRESS LowAddress, HighAddress, SkipBytes;
|
|
|
|
LowAddress.QuadPart = LOWEST_USABLE_PHYSICAL_ADDRESS;
|
|
HighAddress.QuadPart = 0xfffffffff; // 64GB
|
|
SkipBytes.QuadPart = 0;
|
|
|
|
pMdl = MmAllocatePagesForMdl(
|
|
LowAddress,
|
|
HighAddress,
|
|
SkipBytes,
|
|
RoundUpBytes
|
|
);
|
|
|
|
// Completely failed to allocate memory
|
|
if (pMdl == NULL)
|
|
{
|
|
UlTrace(LARGE_MEM,
|
|
("http!UlLargeMemAllocate: "
|
|
"Completely failed to allocate %d bytes.\n",
|
|
RoundUpBytes
|
|
));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Couldn't allocate all the memory we asked for. We need all the pages
|
|
// we asked for, so we have to set the state of `this' to invalid.
|
|
// Memory is probably really tight.
|
|
if (MmGetMdlByteCount(pMdl) < Length)
|
|
{
|
|
UlTrace(LARGE_MEM,
|
|
("http!UlLargeMemAllocate: Failed to allocate %d bytes. "
|
|
"Got %d instead.\n",
|
|
RoundUpBytes, MmGetMdlByteCount(pMdl)
|
|
));
|
|
|
|
// Free MDL but don't adjust g_LargeMemPagesCurrent downwards
|
|
MmFreePagesFromMdl(pMdl);
|
|
ExFreePool(pMdl);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
UlTrace(LARGE_MEM,
|
|
("http!UlLargeMemAllocate: %u->%u, mdl=%p, %d pages.\n",
|
|
Length, pMdl->ByteCount, pMdl, NewPages
|
|
));
|
|
|
|
LONG PrevPagesUsed =
|
|
InterlockedExchangeAdd((PLONG) &g_LargeMemPagesCurrent, NewPages);
|
|
|
|
if (PrevPagesUsed + NewPages > (LONG)g_LargeMemPagesMaxLimit)
|
|
{
|
|
// overshot g_LargeMemPagesMaxLimit
|
|
UlTrace(LARGE_MEM,
|
|
("http!UlLargeMemAllocate: "
|
|
"overshot g_LargeMemPagesMaxLimit=%d pages. "
|
|
"Releasing %d pages\n",
|
|
g_LargeMemPagesMaxLimit, NewPages
|
|
));
|
|
|
|
// Don't free up memory. Return the allocated memory to the
|
|
// caller, who is responsible for checking to see if it can be
|
|
// cached for long-term usage, or if it should be freed ASAP.
|
|
|
|
// CODEWORK: This implies that the MRU entries in the cache will
|
|
// be not be cached, which probably leads to poor cache locality.
|
|
// Really ought to free up some LRU cache entries instead.
|
|
|
|
*pLongTermCacheable = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*pLongTermCacheable = TRUE;
|
|
}
|
|
|
|
ASSERT(pMdl->MdlFlags & MDL_PAGES_LOCKED);
|
|
|
|
// Hurrah! a successful allocation
|
|
//
|
|
// update g_LargeMemPagesMaxEverUsed in a threadsafe manner
|
|
// using interlocked instructions
|
|
|
|
LONG NewMaxUsed;
|
|
|
|
do
|
|
{
|
|
LONG CurrentPages = g_LargeMemPagesCurrent;
|
|
LONG MaxEver = g_LargeMemPagesMaxEverUsed;
|
|
|
|
NewMaxUsed = max(MaxEver, CurrentPages);
|
|
|
|
if (NewMaxUsed > MaxEver)
|
|
{
|
|
InterlockedCompareExchange(
|
|
(PLONG) &g_LargeMemPagesMaxEverUsed,
|
|
NewMaxUsed,
|
|
MaxEver
|
|
);
|
|
}
|
|
} while (NewMaxUsed < (LONG)g_LargeMemPagesCurrent);
|
|
|
|
UlTrace(LARGE_MEM,
|
|
("http!UlLargeMemAllocate: "
|
|
"g_LargeMemPagesCurrent=%d pages. "
|
|
"g_LargeMemPagesMaxEverUsed=%d pages.\n",
|
|
g_LargeMemPagesCurrent, NewMaxUsed
|
|
));
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pMdlTraceLog,
|
|
REF_ACTION_ALLOCATE_MDL,
|
|
PtrToLong(pMdl->Next), // bugbug64
|
|
pMdl,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
return pMdl;
|
|
} // UlLargeMemAllocate
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Free a MDL to PAE memory
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlLargeMemFree(
|
|
IN PMDL pMdl
|
|
)
|
|
{
|
|
LONG Pages;
|
|
LONG PrevPagesUsed;
|
|
|
|
ASSERT(ROUND_TO_PAGES(pMdl->ByteCount) == pMdl->ByteCount);
|
|
|
|
Pages = pMdl->ByteCount >> PAGE_SHIFT;
|
|
|
|
MmFreePagesFromMdl(pMdl);
|
|
ExFreePool(pMdl);
|
|
|
|
PrevPagesUsed
|
|
= InterlockedExchangeAdd(
|
|
(PLONG) &g_LargeMemPagesCurrent,
|
|
- Pages);
|
|
|
|
ASSERT(PrevPagesUsed >= Pages);
|
|
} // UlLargeMemFree
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Copy a buffer to the specified MDL starting from Offset.
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
UlLargeMemSetData(
|
|
IN PMDL pMdl,
|
|
IN PUCHAR pBuffer,
|
|
IN ULONG Length,
|
|
IN ULONG Offset
|
|
)
|
|
{
|
|
PUCHAR pSysAddr;
|
|
BOOLEAN Result;
|
|
|
|
ASSERT(Offset <= pMdl->ByteCount);
|
|
ASSERT(Length <= (pMdl->ByteCount - Offset));
|
|
ASSERT(pMdl->MdlFlags & MDL_PAGES_LOCKED);
|
|
|
|
pSysAddr = (PUCHAR) MmMapLockedPagesSpecifyCache (
|
|
pMdl, // MemoryDescriptorList,
|
|
KernelMode, // AccessMode,
|
|
MmCached, // CacheType,
|
|
NULL, // BaseAddress,
|
|
FALSE, // BugCheckOnFailure,
|
|
NormalPagePriority // Priority
|
|
);
|
|
|
|
if (pSysAddr != NULL)
|
|
{
|
|
RtlCopyMemory(
|
|
pSysAddr + Offset,
|
|
pBuffer,
|
|
Length
|
|
);
|
|
|
|
MmUnmapLockedPages(pSysAddr, pMdl);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
} // UlLargeMemSetData
|