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.
348 lines
9.2 KiB
348 lines
9.2 KiB
/*++
|
|
|
|
Copyright (c) 2000-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
largemem.c
|
|
|
|
Abstract:
|
|
|
|
The implementation of large memory allocator interfaces.
|
|
|
|
Author:
|
|
|
|
George V. Reilly (GeorgeRe) 10-Nov-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "largemem.h"
|
|
|
|
// Magic constant for use with MmAllocatePagesForMdl
|
|
#define LOWEST_USABLE_PHYSICAL_ADDRESS (16 * 1024 * 1024)
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
LONG g_LargeMemInitialized;
|
|
volatile SIZE_T g_LargeMemPagesMaxLimit; // " " pages " " "
|
|
volatile ULONG g_LargeMemPagesCurrent; // #pages currently used
|
|
volatile ULONG g_LargeMemPagesMaxEverUsed; // max #pages ever used
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( INIT, UlLargeMemInitialize )
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
#if 0
|
|
NOT PAGEABLE -- UlLargeMemTerminate
|
|
#endif
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initialize global state for LargeMem
|
|
|
|
Arguments:
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlLargeMemInitialize(
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
g_LargeMemPagesCurrent = 0;
|
|
g_LargeMemPagesMaxEverUsed = 0;
|
|
|
|
// Set the upper bound on the amount of memory that we'll ever use
|
|
|
|
// Set it to size of physical memory. We wont actually use this much
|
|
// because the scavenger thread will get a low memory notification and
|
|
// trim the cache
|
|
|
|
g_LargeMemPagesMaxLimit = MEGABYTES_TO_PAGES(g_UlTotalPhysicalMemMB);
|
|
|
|
UlTraceVerbose(LARGE_MEM,
|
|
("Http!UlLargeMemInitialize: "
|
|
"g_UlTotalPhysicalMemMB=%dMB, "
|
|
"\t g_LargeMemPagesMaxLimit=%I64u.\n",
|
|
g_UlTotalPhysicalMemMB,
|
|
g_LargeMemPagesMaxLimit));
|
|
|
|
g_LargeMemInitialized = TRUE;
|
|
|
|
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.
|
|
//
|
|
|
|
g_LargeMemInitialized = FALSE;
|
|
}
|
|
|
|
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:
|
|
|
|
Allocate a MDL from PAE memory
|
|
|
|
--***************************************************************************/
|
|
PMDL
|
|
UlLargeMemAllocate(
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
PMDL pMdl;
|
|
PHYSICAL_ADDRESS LowAddress, HighAddress, SkipBytes;
|
|
LONG PrevPagesUsed;
|
|
LONG NewMaxUsed;
|
|
|
|
LONG RoundUpBytes = (LONG) ROUND_TO_PAGES(Length);
|
|
LONG NewPages = RoundUpBytes >> PAGE_SHIFT;
|
|
|
|
PrevPagesUsed =
|
|
InterlockedExchangeAdd((PLONG) &g_LargeMemPagesCurrent, NewPages);
|
|
|
|
if (PrevPagesUsed + NewPages > (LONG)g_LargeMemPagesMaxLimit) {
|
|
|
|
// overshot g_LargeMemPagesMaxLimit
|
|
UlTrace(LARGE_MEM,
|
|
("http!UlLargeMemAllocate: "
|
|
"overshot g_LargeMemPagesMaxLimit=%I64u pages. "
|
|
"Releasing %d pages\n",
|
|
g_LargeMemPagesMaxLimit, NewPages
|
|
));
|
|
|
|
// 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.
|
|
|
|
//
|
|
// Start the scavenger
|
|
//
|
|
UlSetScavengerLimitEvent();
|
|
|
|
// Fail the allocation. The cache miss path will be taken
|
|
|
|
InterlockedExchangeAdd((PLONG) &g_LargeMemPagesCurrent, -NewPages);
|
|
return NULL;
|
|
}
|
|
|
|
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
|
|
));
|
|
|
|
InterlockedExchangeAdd((PLONG) &g_LargeMemPagesCurrent, -NewPages);
|
|
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);
|
|
|
|
InterlockedExchangeAdd((PLONG) &g_LargeMemPagesCurrent, -NewPages);
|
|
return NULL;
|
|
}
|
|
|
|
UlTrace(LARGE_MEM,
|
|
("http!UlLargeMemAllocate: %u->%u, mdl=%p, %d pages.\n",
|
|
Length, pMdl->ByteCount, pMdl, NewPages
|
|
));
|
|
|
|
ASSERT(pMdl->MdlFlags & MDL_PAGES_LOCKED);
|
|
|
|
// Hurrah! a successful allocation
|
|
//
|
|
// update g_LargeMemPagesMaxEverUsed in a threadsafe manner
|
|
// using interlocked instructions
|
|
|
|
do
|
|
{
|
|
volatile LONG CurrentPages = g_LargeMemPagesCurrent;
|
|
volatile LONG MaxEver = g_LargeMemPagesMaxEverUsed;
|
|
|
|
NewMaxUsed = max(MaxEver, CurrentPages);
|
|
|
|
if (NewMaxUsed > MaxEver)
|
|
{
|
|
InterlockedCompareExchange(
|
|
(PLONG) &g_LargeMemPagesMaxEverUsed,
|
|
NewMaxUsed,
|
|
MaxEver
|
|
);
|
|
}
|
|
|
|
PAUSE_PROCESSOR;
|
|
} 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;
|
|
|
|
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)
|
|
{
|
|
__try
|
|
{
|
|
RtlCopyMemory (
|
|
pSysAddr + Offset,
|
|
pBuffer,
|
|
Length
|
|
);
|
|
}
|
|
__except(UL_EXCEPTION_FILTER())
|
|
{
|
|
MmUnmapLockedPages (pSysAddr, pMdl);
|
|
return FALSE;
|
|
}
|
|
|
|
MmUnmapLockedPages (pSysAddr, pMdl);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
} // UlLargeMemSetData
|
|
|