431 lines
14 KiB
C#
431 lines
14 KiB
C#
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: MemoryReservations.cs - Reservations and tracking of memory for
|
|
// Heap, Stacks, SIP's, and Kernel.
|
|
//
|
|
// Note:
|
|
//
|
|
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using System.GCs;
|
|
|
|
using Microsoft.Singularity;
|
|
|
|
namespace Microsoft.Singularity.Memory
|
|
{
|
|
[NoCCtor]
|
|
[CLSCompliant(false)]
|
|
public class MemoryReservations {
|
|
|
|
//
|
|
// This class provides one place for memory reservation policy
|
|
// and is used by the different memory managers in the system
|
|
// (FlatPages, Paging allocator, etc.)
|
|
//
|
|
// Memory reservation policy is covered by SDN42 - Handling OutOfMemory
|
|
//
|
|
// Summary of the policy:
|
|
//
|
|
// We ensure there is enough memory for kernel stacks by implementing a per
|
|
// thread kernel stack reservation. We receive notifications of thread create
|
|
// and destroy so this reservation may be tracked in real time.
|
|
//
|
|
// A basic reservation is available for the kernel heap.
|
|
//
|
|
// Allocations for SIP's that exceed the reservations provided above, fail.
|
|
//
|
|
// The reservation policy ensures that we can reliably "fail-fast" the SIP
|
|
// without crashing the kernel due to OOM for stack or heap.
|
|
//
|
|
// The memory managers must live under some strict constraints in regards to
|
|
// allocations themselves, stack linking, no access to managed pointers that
|
|
// could mutate, etc. The methods in this class
|
|
// must adhere to these constraints.
|
|
//
|
|
|
|
/////////////////////////////////////
|
|
// CONSTANTS
|
|
/////////////////////////////////////
|
|
|
|
// Note: These should be passed in from memory manager during initialize()
|
|
private const ulong kernelStackReservationPerThread = (4096*4); // 16k
|
|
|
|
private const ulong kernelStackInitialThreads = 10;
|
|
|
|
// Set enough stack reservation for an initial 10 threads
|
|
private const ulong initialKernelStackReservation = kernelStackInitialThreads * kernelStackReservationPerThread;
|
|
|
|
private const ulong initialKernelHeapReservation = 1024*512;
|
|
|
|
private const ulong initialUserStackReservation = 1024*128;
|
|
|
|
|
|
/////////////////////////////////////
|
|
// STATIC FIELDS
|
|
/////////////////////////////////////
|
|
|
|
//
|
|
// These counters serve two purposes:
|
|
//
|
|
// 1: Provide resource usage monitoring information
|
|
//
|
|
// 2: Provide for memory reservations. In order to provide
|
|
// for memory reservations reliably in an MP environment, they
|
|
// are only manipulated while holding the pageLock.
|
|
//
|
|
|
|
//
|
|
// This represents total memory allocated and freed
|
|
//
|
|
private static ulong allocatedBytes;
|
|
private static ulong allocatedCount;
|
|
private static ulong freedBytes;
|
|
private static ulong freedCount;
|
|
|
|
//
|
|
// This represents the real time count of user (SIP) allocated
|
|
// memory
|
|
//
|
|
private static ulong userAllocatedBytes;
|
|
private static ulong userAllocatedCount;
|
|
private static ulong userFreedBytes;
|
|
private static ulong userFreedCount;
|
|
|
|
//
|
|
// These represent kernel and SIP stack usage
|
|
//
|
|
private static ulong stackAllocatedBytes;
|
|
private static ulong stackAllocatedCount;
|
|
private static ulong stackFreedBytes;
|
|
private static ulong stackFreedCount;
|
|
|
|
private static ulong userStackAllocatedBytes;
|
|
private static ulong userStackAllocatedCount;
|
|
private static ulong userStackFreedBytes;
|
|
private static ulong userStackFreedCount;
|
|
|
|
//
|
|
// This value is updated as threads are created and destroyed
|
|
// and represents the amount that available memory can not
|
|
// drop below without possibly running into a kernel stack OOM.
|
|
//
|
|
private static ulong kernelStackReservation;
|
|
|
|
//
|
|
// This represents a kernel heap reservation
|
|
//
|
|
private static ulong kernelHeapReservation;
|
|
|
|
//
|
|
// This represents a reservation to allow SIP stacks
|
|
// to be allocated once SIP heap reservations have been
|
|
// exceeded, but not kernel stack+heap reservations.
|
|
//
|
|
// This allows a SIP to continue executing after a SIP
|
|
// allocations are failed to allow the SIP to exit and cleanup.
|
|
//
|
|
// (Ideally, we should not need to rely on allowing further SIP
|
|
// thread execution for cleanup, but this is a later stage
|
|
// )
|
|
//
|
|
private static ulong userStackReservation;
|
|
|
|
/////////////////////////////////////
|
|
// PUBLIC METHODS
|
|
/////////////////////////////////////
|
|
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void Initialize()
|
|
{
|
|
allocatedBytes = 0;
|
|
allocatedCount = 0;
|
|
freedBytes = 0;
|
|
freedCount = 0;
|
|
|
|
userAllocatedBytes = 0;
|
|
userAllocatedCount = 0;
|
|
userFreedBytes = 0;
|
|
userFreedCount = 0;
|
|
|
|
stackAllocatedBytes = 0;
|
|
stackAllocatedCount = 0;
|
|
stackFreedBytes = 0;
|
|
stackFreedCount = 0;
|
|
|
|
userStackAllocatedBytes = 0;
|
|
userStackAllocatedCount = 0;
|
|
userStackFreedBytes = 0;
|
|
userStackFreedCount = 0;
|
|
|
|
kernelStackReservation = initialKernelStackReservation;
|
|
kernelHeapReservation = initialKernelHeapReservation;
|
|
|
|
userStackReservation = initialUserStackReservation;
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// This returns true if an allocation should fail based on the memory
|
|
// reservation policy and currently available memory.
|
|
//
|
|
// This is called under the lock of the calling memory manager to ensure
|
|
// a consistent view in a multithreaded environment.
|
|
//
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static bool MemoryReservationExceeded(
|
|
ulong availableMemory,
|
|
uint tag,
|
|
bool kernelAllocation,
|
|
UIntPtr size)
|
|
{
|
|
//
|
|
// The owner information is not reliable for determining kernel
|
|
// or SIP allocation when the page type is stack, so the logic
|
|
// is a little extra complicated here.
|
|
//
|
|
if ((tag & MemoryManager.TypeMask) == (uint)PageType.Stack) {
|
|
|
|
//
|
|
// Stack pages use the kernelAllocation flag to determine
|
|
// whether the allocation is for the SIP or the kernel stack
|
|
//
|
|
if (kernelAllocation) {
|
|
return false;
|
|
}
|
|
else {
|
|
if (availableMemory < (kernelStackReservation + kernelHeapReservation + size)) {
|
|
//DebugStub.Break();
|
|
//DebugStub.WriteLine("CheckMemoryReservation: Failing allocation request for SIP stack due to reservations\n");
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if ((tag & MemoryManager.ProcessPageMask) != MemoryManager.KernelPage) {
|
|
|
|
// process tag is reliable for non-stack allocations
|
|
|
|
//
|
|
// User heap allocation must be under any outstanding reservations
|
|
// for the kernel, or SIP stack.
|
|
//
|
|
if (availableMemory < (kernelStackReservation + kernelHeapReservation + userStackReservation + size)) {
|
|
//DebugStub.Break();
|
|
//DebugStub.WriteLine("CheckMemoryReservation: Failing allocation request for SIP heap due to reservations\n");
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
// Kernel heap allocation
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// These are called by the memory manager in order to keep an accurate view
|
|
// of memory reservations in real time. They are called under the memory
|
|
// managers lock.
|
|
//
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void ThreadCreateNotification()
|
|
{
|
|
kernelStackReservation += kernelStackReservationPerThread;
|
|
}
|
|
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void ThreadDestroyNotification()
|
|
{
|
|
kernelStackReservation -= kernelStackReservationPerThread;
|
|
}
|
|
|
|
//
|
|
// Allocation tracking
|
|
//
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void AllocationForHeap(ulong size)
|
|
{
|
|
allocatedBytes += size;
|
|
allocatedCount++;
|
|
}
|
|
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void AllocationForStack(ulong size)
|
|
{
|
|
stackAllocatedBytes += size;
|
|
stackAllocatedCount++;
|
|
|
|
allocatedBytes += size;
|
|
allocatedCount++;
|
|
}
|
|
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void SIPAllocationForHeap(ulong size)
|
|
{
|
|
userAllocatedBytes += size;
|
|
userAllocatedCount++;
|
|
|
|
allocatedBytes += size;
|
|
allocatedCount++;
|
|
}
|
|
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void SIPAllocationForStack(ulong size)
|
|
{
|
|
userStackAllocatedBytes += size;
|
|
userStackAllocatedCount++;
|
|
|
|
allocatedBytes += size;
|
|
allocatedCount++;
|
|
}
|
|
|
|
//
|
|
// Free tracking
|
|
//
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void FreeForHeap(ulong size)
|
|
{
|
|
freedBytes += size;
|
|
freedCount++;
|
|
}
|
|
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void FreeForStack(ulong size)
|
|
{
|
|
stackFreedBytes += size;
|
|
stackFreedCount++;
|
|
|
|
freedBytes += size;
|
|
freedCount++;
|
|
}
|
|
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void SIPFreeForHeap(ulong size)
|
|
{
|
|
userFreedBytes += size;
|
|
userFreedCount++;
|
|
|
|
freedBytes += size;
|
|
freedCount++;
|
|
}
|
|
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void SIPFreeForStack(ulong size)
|
|
{
|
|
userStackFreedBytes += size;
|
|
userStackFreedCount++;
|
|
|
|
freedBytes += size;
|
|
freedCount++;
|
|
}
|
|
|
|
//
|
|
// Statistics gathering methods
|
|
//
|
|
// Calling under a lock is not required unless you want an accurate view
|
|
//
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void GetUsageStatistics(out ulong allocatedCount,
|
|
out ulong allocatedBytes,
|
|
out ulong freedCount,
|
|
out ulong freedBytes,
|
|
out ulong kernelHeapReservationArg)
|
|
{
|
|
allocatedCount = MemoryReservations.allocatedCount;
|
|
allocatedBytes = MemoryReservations.allocatedBytes;
|
|
freedCount = MemoryReservations.freedCount;
|
|
freedBytes = MemoryReservations.freedBytes;
|
|
kernelHeapReservationArg = MemoryReservations.kernelHeapReservation;
|
|
}
|
|
|
|
//
|
|
// Return user (SIP) memory statistics
|
|
//
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void GetUserStatistics(out ulong allocatedCountArg,
|
|
out ulong allocatedBytesArg,
|
|
out ulong freedCountArg,
|
|
out ulong freedBytesArg)
|
|
{
|
|
allocatedCountArg = userAllocatedCount;
|
|
allocatedBytesArg = userAllocatedBytes;
|
|
freedCountArg = userFreedCount;
|
|
freedBytesArg = userFreedBytes;
|
|
}
|
|
|
|
//
|
|
// Return stack usage as raw counters
|
|
//
|
|
[NoHeapAllocation]
|
|
[NoStackLinkCheck]
|
|
internal static void GetStackUsage(
|
|
out ulong kernelStackCount,
|
|
out ulong kernelStackReturnCount,
|
|
out ulong kernelStackBytes,
|
|
out ulong kernelStackReturnBytes,
|
|
out ulong kernelStackReservationArg,
|
|
out ulong userStackCount,
|
|
out ulong userStackReturnCount,
|
|
out ulong userStackBytes,
|
|
out ulong userStackReturnBytes,
|
|
out ulong userStackReservation
|
|
)
|
|
{
|
|
kernelStackCount = stackAllocatedCount;
|
|
kernelStackReturnCount = stackFreedCount;
|
|
|
|
kernelStackBytes = stackAllocatedBytes;
|
|
kernelStackReturnBytes = stackFreedBytes;
|
|
|
|
kernelStackReservationArg = kernelStackReservation;
|
|
|
|
userStackCount = userStackAllocatedCount;
|
|
userStackReturnCount = userStackFreedCount;
|
|
|
|
userStackBytes = userStackAllocatedBytes;
|
|
userStackReturnBytes = userStackFreedBytes;
|
|
|
|
userStackReservation = 0;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|