mirror of
https://github.com/reactos/reactos.git
synced 2024-10-31 20:02:55 +00:00
879b1f5754
- 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
295 lines
7.7 KiB
C
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);
|
|
}
|