reactos/reactos/ntoskrnl/mm/balance.c
KJK::Hyperion 879b1f5754 My biggest commit so far (everything compiles and apparently runs fine):
- replaced DWORD with ULONG in a couple of places
 - replaced some ULONGs with LONGs in the KD GDB stub
 - replaced INITIAL_TEB with USER_STACK, as per Nebbet's book, to support both fixed size and expandable stacks
 - added InterlockedExchangePointer
 - added the ASM_BREAKPOINT macro as the architecture-dependent assembler code to raise a breakpoint exception
 - corrected definitions of INT, LONG, DWORD, UINT, ULONG and ULONG32
 - corrected IoSetCancelRoutine to use InterlockedExchangePointer
 - corrected definition of NtCurrentTeb and NtCurrentPeb
 - corrected DbgBreakPoint and DbgUserBreakPoint not to set up a stack frame (temporary fix with inline assembler - why doesn't GCC understand __declspec(naked)?)
 - corrected various calls to Interlocked* functions to cast OUT operands to LONG *
 - corrected various printf format strings
 - corrected DbgUiIssueRemoteBreakin to use the smallest possible stack  (this is what started everything)
 - removed a DPRINT that accessed pageable memory at non-PASSIVE_LEVEL IRQL
 - beautified CreateProcessA (another temporary fix - all the new functions will be isolated in the upcoming stand-alone RTL)
 - prefixed LdrInitializeThunk with a nop that can be overwritten with a breakpoint for debugging purposes (temporary debugging aid until we have user-mode debugger support). Will add support for this to the breakin utility soon
 - thread creation code rewritten from scratch (some glitches documented inline, but works fine)
 - thread creation code now duplicated just twice, as opposed to five times (temporary fix - three new, non standard functions have been exported from NTDLL.DLL, will fix later)

svn path=/trunk/; revision=4595
2003-04-26 23:13:33 +00:00

295 lines
7.7 KiB
C

/*
* ReactOS kernel
* Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: balance.c,v 1.15 2003/04/26 23:13:32 hyperion Exp $
*
* PROJECT: ReactOS kernel
* FILE: ntoskrnl/mm/balance.c
* PURPOSE: kernel memory managment functions
* PROGRAMMER: David Welch (welch@cwcom.net)
* UPDATE HISTORY:
* Created 27/12/01
*/
/* INCLUDES *****************************************************************/
#include <ddk/ntddk.h>
#include <internal/mm.h>
#include <ntos/minmax.h>
#define NDEBUG
#include <internal/debug.h>
/* TYPES ********************************************************************/
typedef struct _MM_MEMORY_CONSUMER
{
ULONG PagesUsed;
ULONG PagesTarget;
NTSTATUS (*Trim)(ULONG Target, ULONG Priority, PULONG NrFreed);
} MM_MEMORY_CONSUMER, *PMM_MEMORY_CONSUMER;
typedef struct _MM_ALLOCATION_REQUEST
{
PHYSICAL_ADDRESS Page;
LIST_ENTRY ListEntry;
KEVENT Event;
} MM_ALLOCATION_REQUEST, *PMM_ALLOCATION_REQUEST;
/* GLOBALS ******************************************************************/
static MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM];
static ULONG MiMinimumAvailablePages;
static ULONG MiNrAvailablePages;
static ULONG MiNrTotalPages;
static LIST_ENTRY AllocationListHead;
static KSPIN_LOCK AllocationListLock;
static ULONG NrWorkingThreads = 0;
static HANDLE WorkerThreadId;
static ULONG MiPagesRequired = 0;
static ULONG MiMinimumPagesPerRun = 1;
/* FUNCTIONS ****************************************************************/
VOID MmPrintMemoryStatistic(VOID)
{
DbgPrint("MC_CACHE %d, MC_USER %d, MC_PPOOL %d, MC_NPPOOL %d\n",
MiMemoryConsumers[MC_CACHE].PagesUsed,
MiMemoryConsumers[MC_USER].PagesUsed,
MiMemoryConsumers[MC_PPOOL].PagesUsed,
MiMemoryConsumers[MC_NPPOOL].PagesUsed);
}
VOID
MmInitializeBalancer(ULONG NrAvailablePages)
{
memset(MiMemoryConsumers, 0, sizeof(MiMemoryConsumers));
InitializeListHead(&AllocationListHead);
KeInitializeSpinLock(&AllocationListLock);
MiNrAvailablePages = MiNrTotalPages = NrAvailablePages;
/* Set up targets. */
MiMinimumAvailablePages = 64;
MiMemoryConsumers[MC_CACHE].PagesTarget = NrAvailablePages / 2;
MiMemoryConsumers[MC_USER].PagesTarget =
NrAvailablePages - MiMinimumAvailablePages;
MiMemoryConsumers[MC_PPOOL].PagesTarget = NrAvailablePages / 2;
MiMemoryConsumers[MC_NPPOOL].PagesTarget = 0xFFFFFFFF;
}
VOID
MmInitializeMemoryConsumer(ULONG Consumer,
NTSTATUS (*Trim)(ULONG Target, ULONG Priority,
PULONG NrFreed))
{
MiMemoryConsumers[Consumer].Trim = Trim;
}
NTSTATUS
MmReleasePageMemoryConsumer(ULONG Consumer, PHYSICAL_ADDRESS Page)
{
PMM_ALLOCATION_REQUEST Request;
PLIST_ENTRY Entry;
KIRQL oldIrql;
if (Page.QuadPart == 0LL)
{
DPRINT1("Tried to release page zero.\n");
KeBugCheck(0);
}
KeAcquireSpinLock(&AllocationListLock, &oldIrql);
if (MmGetReferenceCountPage(Page) == 1)
{
InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
InterlockedIncrement((LONG *)&MiNrAvailablePages);
InterlockedDecrement((LONG *)&MiPagesRequired);
if (IsListEmpty(&AllocationListHead))
{
KeReleaseSpinLock(&AllocationListLock, oldIrql);
MmDereferencePage(Page);
}
else
{
Entry = RemoveHeadList(&AllocationListHead);
Request = CONTAINING_RECORD(Entry, MM_ALLOCATION_REQUEST, ListEntry);
KeReleaseSpinLock(&AllocationListLock, oldIrql);
Request->Page = Page;
KeSetEvent(&Request->Event, IO_NO_INCREMENT, FALSE);
}
}
else
{
KeReleaseSpinLock(&AllocationListLock, oldIrql);
MmDereferencePage(Page);
}
return(STATUS_SUCCESS);
}
VOID
MiTrimMemoryConsumer(ULONG Consumer)
{
LONG Target;
ULONG NrFreedPages;
Target = MiMemoryConsumers[Consumer].PagesUsed -
MiMemoryConsumers[Consumer].PagesTarget;
if (Target < 0)
{
Target = 1;
}
if (MiMemoryConsumers[Consumer].Trim != NULL)
{
MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
}
}
VOID
MiRebalanceMemoryConsumers(VOID)
{
LONG Target;
ULONG i;
ULONG NrFreedPages;
NTSTATUS Status;
Target = (MiMinimumAvailablePages - MiNrAvailablePages) + MiPagesRequired;
Target = min(Target, (LONG) MiMinimumPagesPerRun);
for (i = 0; i < MC_MAXIMUM && Target > 0; i++)
{
if (MiMemoryConsumers[i].Trim != NULL)
{
Status = MiMemoryConsumers[i].Trim(Target, 0, &NrFreedPages);
if (!NT_SUCCESS(Status))
{
KeBugCheck(0);
}
Target = Target - NrFreedPages;
}
}
if (Target > 0)
{
KeBugCheck(0);
}
}
NTSTATUS
MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
PHYSICAL_ADDRESS* AllocatedPage)
{
ULONG OldUsed;
ULONG OldAvailable;
PHYSICAL_ADDRESS Page;
KIRQL oldIrql;
/*
* Make sure we don't exceed our individual target.
*/
OldUsed = InterlockedIncrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
if (OldUsed >= (MiMemoryConsumers[Consumer].PagesTarget - 1) &&
WorkerThreadId != PsGetCurrentThreadId())
{
if (!CanWait)
{
InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
return(STATUS_NO_MEMORY);
}
MiTrimMemoryConsumer(Consumer);
}
/*
* Make sure we don't exceed global targets.
*/
OldAvailable = InterlockedDecrement((LONG *)&MiNrAvailablePages);
if (OldAvailable < MiMinimumAvailablePages)
{
MM_ALLOCATION_REQUEST Request;
if (!CanWait)
{
InterlockedIncrement((LONG *)&MiNrAvailablePages);
InterlockedDecrement((LONG *)&MiMemoryConsumers[Consumer].PagesUsed);
return(STATUS_NO_MEMORY);
}
/* Insert an allocation request. */
Request.Page.QuadPart = 0LL;
KeInitializeEvent(&Request.Event, NotificationEvent, FALSE);
InterlockedIncrement((LONG *)&MiPagesRequired);
KeAcquireSpinLock(&AllocationListLock, &oldIrql);
if (NrWorkingThreads == 0)
{
InsertTailList(&AllocationListHead, &Request.ListEntry);
NrWorkingThreads++;
KeReleaseSpinLock(&AllocationListLock, oldIrql);
WorkerThreadId = PsGetCurrentThreadId();
MiRebalanceMemoryConsumers();
KeAcquireSpinLock(&AllocationListLock, &oldIrql);
NrWorkingThreads--;
WorkerThreadId = 0;
KeReleaseSpinLock(&AllocationListLock, oldIrql);
}
else
{
if (WorkerThreadId == PsGetCurrentThreadId())
{
Page = MmAllocPage(Consumer, 0);
KeReleaseSpinLock(&AllocationListLock, oldIrql);
if (Page.QuadPart == 0LL)
{
KeBugCheck(0);
}
*AllocatedPage = Page;
return(STATUS_SUCCESS);
}
InsertTailList(&AllocationListHead, &Request.ListEntry);
KeReleaseSpinLock(&AllocationListLock, oldIrql);
}
KeWaitForSingleObject(&Request.Event,
0,
KernelMode,
FALSE,
NULL);
Page = Request.Page;
if (Page.QuadPart == 0LL)
{
KeBugCheck(0);
}
MmTransferOwnershipPage(Page, Consumer);
*AllocatedPage = Page;
return(STATUS_SUCCESS);
}
/*
* Actually allocate the page.
*/
Page = MmAllocPage(Consumer, 0);
if (Page.QuadPart == 0LL)
{
KeBugCheck(0);
}
*AllocatedPage = Page;
return(STATUS_SUCCESS);
}