Author: jgardou Date: Mon Sep 22 17:20:38 2014 New Revision: 64228
URL: http://svn.reactos.org/svn/reactos?rev=64228&view=rev Log: Try to repair catastrophic merge attempt from r64223, part 1 of 2 [NTOS/MM] Commit what I got so far, including: - A very (very) basic balancer for the paged pool, simply sweeping all the pages of it and ageing the pages as they're run over - A page writer thread writing unreferenced pages to the pagefile. - Support for reading from the pagefile in the ARM3 page fault handler - Popping a page from the standby list when running out of free pages - Various fixes here and there Enjoy!
Modified: branches/TransitionPte/ntoskrnl/include/internal/mm.h branches/TransitionPte/ntoskrnl/mm/ARM3/miarm.h branches/TransitionPte/ntoskrnl/mm/ARM3/pagfault.c branches/TransitionPte/ntoskrnl/mm/ARM3/pfnlist.c branches/TransitionPte/ntoskrnl/mm/ARM3/section.c branches/TransitionPte/ntoskrnl/mm/ARM3/virtual.c branches/TransitionPte/ntoskrnl/mm/balance.c branches/TransitionPte/ntoskrnl/mm/mminit.c branches/TransitionPte/ntoskrnl/mm/pagefile.c
Modified: branches/TransitionPte/ntoskrnl/include/internal/mm.h URL: http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/include/i... ============================================================================== --- branches/TransitionPte/ntoskrnl/include/internal/mm.h [iso-8859-1] (original) +++ branches/TransitionPte/ntoskrnl/include/internal/mm.h [iso-8859-1] Mon Sep 22 17:20:38 2014 @@ -373,6 +373,7 @@ extern MMPFNLIST MmFreePageListHead; extern MMPFNLIST MmStandbyPageListHead; extern MMPFNLIST MmModifiedPageListHead; +extern MMPFNLIST MmModifiedPageListByColor[1]; extern MMPFNLIST MmModifiedNoWritePageListHead;
typedef struct _MM_MEMORY_CONSUMER @@ -652,8 +653,27 @@ NTAPI MiReadPageFile( _In_ PFN_NUMBER Page, - _In_ ULONG PageFileIndex, - _In_ ULONG_PTR PageFileOffset); + _In_ const MMPTE* PointerPte +); + +NTSTATUS +NTAPI +MiWritePageFile( + _In_ PFN_NUMBER Page, + _In_ const MMPTE* PointerPte +); + +VOID +NTAPI +MiFreePageFileEntry( + _In_ PMMPTE PointerPte +); + +NTSTATUS +NTAPI +MiReservePageFileEntry( + _Out_ PMMPTE PointerPte +);
/* process.c ****************************************************************/
Modified: branches/TransitionPte/ntoskrnl/mm/ARM3/miarm.h URL: http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/ARM3/m... ============================================================================== --- branches/TransitionPte/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] (original) +++ branches/TransitionPte/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] Mon Sep 22 17:20:38 2014 @@ -687,6 +687,7 @@ extern PKEVENT MiHighPagedPoolEvent; extern PKEVENT MiLowNonPagedPoolEvent; extern PKEVENT MiHighNonPagedPoolEvent; +extern KEVENT MpwThreadEvent; extern PFN_NUMBER MmLowMemoryThreshold; extern PFN_NUMBER MmHighMemoryThreshold; extern PFN_NUMBER MiLowPagedPoolThreshold; @@ -695,6 +696,7 @@ extern PFN_NUMBER MiHighNonPagedPoolThreshold; extern PFN_NUMBER MmMinimumFreePages; extern PFN_NUMBER MmPlentyFreePages; +extern ULONG_PTR MmTotalPagesForPagingFile; extern SIZE_T MmMinimumStackCommitInBytes; extern PFN_COUNT MiExpansionPoolPagesInitialCharge; extern PFN_NUMBER MmResidentAvailablePages;
Modified: branches/TransitionPte/ntoskrnl/mm/ARM3/pagfault.c URL: http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/ARM3/p... ============================================================================== --- branches/TransitionPte/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] (original) +++ branches/TransitionPte/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] Mon Sep 22 17:20:38 2014 @@ -583,7 +583,7 @@ ASSERT(PointerPte->u.Hard.Valid == 0);
/* Assert we have enough pages */ - ASSERT(MmAvailablePages >= 32); + ASSERT(MmAvailablePages != 0);
#if MI_TRACE_PFNS if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE); @@ -688,7 +688,8 @@ IN PMMPTE PointerPte, IN PMMPTE PointerProtoPte, IN KIRQL OldIrql, - IN PMMPFN* LockedProtoPfn) + IN PMMPFN* LockedProtoPfn, + PVOID InPageBlock) { MMPTE TempPte; PMMPTE OriginalPte, PageTablePte; @@ -799,6 +800,10 @@
/* Reset the protection if needed */ if (OriginalProtection) Protection = MM_ZERO_ACCESS; + + /* Let waiters go ahead */ + if (InPageBlock) + KeSetEvent(InPageBlock, IO_NO_INCREMENT, FALSE);
/* Return success */ ASSERT(PointerPte == MiAddressToPte(Address)); @@ -816,26 +821,30 @@ ULONG Color; PFN_NUMBER Page; NTSTATUS Status; - MMPTE TempPte = *PointerPte; - KEVENT Event; + MMPTE PageFilePte = *PointerPte, TempPte; PMMPFN Pfn1; - ULONG PageFileIndex = TempPte.u.Soft.PageFileLow; - ULONG_PTR PageFileOffset = TempPte.u.Soft.PageFileHigh; + + DPRINT1("Serving page fault for PTE %p (%x).\n", PointerPte, PageFilePte.u.Long);
/* Things we don't support yet */ - ASSERT(CurrentProcess > HYDRA_PROCESS); ASSERT(*OldIrql != MM_NOIRQL);
/* We must hold the PFN lock */ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
/* Some sanity checks */ - ASSERT(TempPte.u.Hard.Valid == 0); - ASSERT(TempPte.u.Soft.PageFileHigh != 0); - ASSERT(TempPte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED); + ASSERT(PageFilePte.u.Hard.Valid == 0); + ASSERT(PageFilePte.u.Soft.Transition == 0); + ASSERT(PageFilePte.u.Soft.Prototype == 0); + ASSERT(PageFilePte.u.Soft.PageFileHigh != 0); + ASSERT(PageFilePte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED);
/* Get any page, it will be overwritten */ - Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess); + if (CurrentProcess > HYDRA_PROCESS) + Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess); + else + Color = MI_GET_NEXT_COLOR(); + Page = MiRemoveAnyPage(Color);
/* Initialize this PFN */ @@ -847,14 +856,13 @@ ASSERT(Pfn1->u3.e1.ReadInProgress == 0); ASSERT(Pfn1->u3.e1.WriteInProgress == 0);
- KeInitializeEvent(&Event, NotificationEvent, FALSE); - Pfn1->u1.Event = &Event; + /* Set NULL event */ + Pfn1->u1.Event = NULL; Pfn1->u3.e1.ReadInProgress = 1;
/* We must write the PTE now as the PFN lock will be released while performing the IO operation */ + TempPte.u.Long = 0; TempPte.u.Soft.Transition = 1; - TempPte.u.Soft.PageFileLow = 0; - TempPte.u.Soft.Prototype = 0; TempPte.u.Trans.PageFrameNumber = Page;
MI_WRITE_INVALID_PTE(PointerPte, TempPte); @@ -863,15 +871,25 @@ KeReleaseQueuedSpinLock(LockQueuePfnLock, *OldIrql);
/* Do the paging IO */ - Status = MiReadPageFile(Page, PageFileIndex, PageFileOffset); + Status = MiReadPageFile(Page, &PageFilePte);
/* Lock the PFN database again */ *OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
/* Nobody should have changed that while we were not looking */ - ASSERT(Pfn1->u1.Event == &Event); ASSERT(Pfn1->u3.e1.ReadInProgress == 1); ASSERT(Pfn1->u3.e1.WriteInProgress == 0); + + /* But maybe someone began waiting on us */ + if (Pfn1->u1.Event != NULL) + { + DPRINT1("The page got a waiter!\n"); + /* Waiters gonna wait */ + KeSetEvent(Pfn1->u1.Event, IO_NO_INCREMENT, FALSE); + Pfn1->u1.Event = NULL; + } + + DPRINT1("Pagefile read finished. Status: 0x%08x.\n", Status);
if (!NT_SUCCESS(Status)) { @@ -879,38 +897,60 @@ ASSERT(FALSE); Pfn1->u4.InPageError = 1; Pfn1->u1.ReadStatus = Status; + Status = STATUS_IN_PAGE_ERROR; + } + else + { + /* This is a success status */ + Status = STATUS_PAGE_FAULT_PAGING_FILE; }
/* This is now a nice and normal PFN */ - Pfn1->u1.Event = NULL; Pfn1->u3.e1.ReadInProgress = 0;
- /* And the PTE can finally be valid */ - MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, TempPte.u.Trans.Protection, Page); + /* Check if this is a kernel or user address */ + if (PointerPte <= MiHighestUserPte) + { + /* Build the user PTE */ + MI_MAKE_HARDWARE_PTE_USER(&TempPte, + PointerPte, + PageFilePte.u.Soft.Protection, + Page); + } + else + { + /* Build the kernel PTE */ + MI_MAKE_HARDWARE_PTE(&TempPte, + PointerPte, + PageFilePte.u.Soft.Protection, + Page); + } + + /* Write it */ MI_WRITE_VALID_PTE(PointerPte, TempPte); - - /* Waiters gonna wait */ - KeSetEvent(&Event, IO_NO_INCREMENT, FALSE);
return Status; }
NTSTATUS NTAPI -MiResolveTransitionFault(IN PVOID FaultingAddress, - IN PMMPTE PointerPte, - IN PEPROCESS CurrentProcess, - IN KIRQL OldIrql, - OUT PVOID *InPageBlock) +MiResolveTransitionFault( + _In_ BOOLEAN StoreInstruction, + _In_ PVOID FaultingAddress, + _In_ PMMPTE PointerPte, + _In_ PEPROCESS CurrentProcess, + _In_ KIRQL OldIrql, + _In_ PVOID *InPageBlock) { PFN_NUMBER PageFrameIndex; PMMPFN Pfn1; MMPTE TempPte; PMMPTE PointerToPteForProtoPage; - DPRINT1("Transition fault on 0x%p with PTE 0x%p in process %s\n", - FaultingAddress, PointerPte, CurrentProcess->ImageFileName); - - /* Windowss does this check */ + + DPRINT("Transition fault on 0x%p with PTE 0x%p in process %s\n", + FaultingAddress, PointerPte, CurrentProcess ? CurrentProcess->ImageFileName : "Kernel"); + + /* Windows does this check */ ASSERT(*InPageBlock == NULL);
/* ARM3 doesn't support this path */ @@ -934,25 +974,52 @@ ASSERT(Pfn1->u4.InPageError == 0);
/* See if we should wait before terminating the fault */ - if (Pfn1->u3.e1.ReadInProgress == 1) - { - DPRINT1("The page is currently being read!\n"); - ASSERT(Pfn1->u1.Event != NULL); - *InPageBlock = Pfn1->u1.Event; - if (PointerPte == Pfn1->PteAddress) - { - DPRINT1("And this if for this particular PTE.\n"); - /* The PTE will be made valid by the thread serving the fault */ - return STATUS_SUCCESS; // FIXME: Maybe something more descriptive - } + if ((Pfn1->u3.e1.WriteInProgress == 1) || (Pfn1->u3.e1.ReadInProgress == 1)) + { + KEVENT IoEvent; + DPRINT1("The page is currently being written to or read!\n"); + + /* Both of them, it's not possible */ + ASSERT(((Pfn1->u3.e1.ReadInProgress == 0) && (Pfn1->u3.e1.WriteInProgress == 1)) || + ((Pfn1->u3.e1.ReadInProgress == 1) && (Pfn1->u3.e1.WriteInProgress == 0))); + + KeInitializeEvent(&IoEvent, NotificationEvent, FALSE); + *InPageBlock = Pfn1->u1.Event; + Pfn1->u1.Event = &IoEvent; + + /* + * Mark it as unmodified. + * The page-out thread will put it back in the standby list + * and we will be able to start anew for this PTE + */ + if ((Pfn1->u3.e1.WriteInProgress) && (!StoreInstruction)) + Pfn1->u3.e1.Modified = 0; + + /* Release the lock so the writer can finish */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Wait for it to really finish */ + KeWaitForSingleObject(&IoEvent, WrPageIn, KernelMode, FALSE, NULL); + + /* Get it again */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Read the PTE again */ + TempPte = *PointerPte; + if (TempPte.u.Hard.Valid == 1) + { + /* Someone did it while we were not looking. */ + return STATUS_PAGE_FAULT_TRANSITION; + } + /* Otherwise the state of this PTE must still be the same */ + ASSERT(TempPte.u.Soft.Transition == 1); + ASSERT(TempPte.u.Soft.Prototype == 0); + ASSERT(TempPte.u.Trans.PageFrameNumber == PageFrameIndex); }
/* Windows checks there's some free pages and this isn't an in-page error */ ASSERT(MmAvailablePages > 0); ASSERT(Pfn1->u4.InPageError == 0); - - /* ReactOS checks for this */ - ASSERT(MmAvailablePages > 32);
/* Was this a transition page in the valid list, or free/zero list? */ if (Pfn1->u3.e1.PageLocation == ActiveAndValid) @@ -1066,7 +1133,8 @@ PointerPte, PointerProtoPte, OldIrql, - OutPfn); + OutPfn, + NULL); }
/* Make sure there's some protection mask */ @@ -1125,18 +1193,25 @@ { /* Resolve the transition fault */ ASSERT(OldIrql != MM_NOIRQL); - Status = MiResolveTransitionFault(Address, + Status = MiResolveTransitionFault(StoreInstruction, + Address, PointerProtoPte, Process, OldIrql, &InPageBlock); ASSERT(NT_SUCCESS(Status)); } + else if (TempPte.u.Soft.PageFileHigh != 0) + { + Status = MiResolvePageFileFault(StoreInstruction, + Address, + PointerProtoPte, + Process, + &OldIrql); + ASSERT(NT_SUCCESS(Status)); + } else { - /* We also don't support paged out pages */ - ASSERT(TempPte.u.Soft.PageFileHigh == 0); - /* Resolve the demand zero fault */ Status = MiResolveDemandZeroFault(Address, PointerProtoPte, @@ -1152,7 +1227,8 @@ PointerPte, PointerProtoPte, OldIrql, - OutPfn); + OutPfn, + InPageBlock); }
NTSTATUS @@ -1173,9 +1249,8 @@ PMMPFN Pfn1, OutPfn = NULL; PFN_NUMBER PageFrameIndex; PFN_COUNT PteCount, ProcessedPtes; - DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n", - Address, - Process); + DPRINT("ARM3 Page Fault Dispatcher for address: %p (PTE %p) in process: %p\n", + Address, PointerPte, Process);
/* Make sure the addresses are ok */ ASSERT(PointerPte == MiAddressToPte(Address)); @@ -1283,8 +1358,49 @@ ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
/* Should not yet happen in ReactOS */ - ASSERT(Pfn1->u3.e1.ReadInProgress == 0); ASSERT(Pfn1->u4.InPageError == 0); + + if ((Pfn1->u3.e1.ReadInProgress == 1) || (Pfn1->u3.e1.WriteInProgress == 1)) + { + KEVENT IoEvent; + PKEVENT PageEvent; + DPRINT1("The page is currently being written to or read!\n"); + + /* Both of them, it's not possible */ + ASSERT(((Pfn1->u3.e1.ReadInProgress == 0) && (Pfn1->u3.e1.WriteInProgress == 1)) || + ((Pfn1->u3.e1.ReadInProgress == 1) && (Pfn1->u3.e1.WriteInProgress == 0))); + + KeInitializeEvent(&IoEvent, NotificationEvent, FALSE); + PageEvent = Pfn1->u1.Event; + Pfn1->u1.Event = &IoEvent; + + /* + * Mark it as unmodified. + * The page-out thread will put it back in the standby list + * and we will be able to start anew for this PTE + */ + if (Pfn1->u3.e1.WriteInProgress) + Pfn1->u3.e1.Modified = 0; + + /* Release the lock so the writer can finish */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql); + + /* Wait for it to really finish */ + KeWaitForSingleObject(&IoEvent, WrPageIn, KernelMode, FALSE, NULL); + + /* Get it again */ + LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Read the PTE again */ + TempPte = *PointerProtoPte; + + /* Maybe someone else was also waiting */ + if (PageEvent) + KeSetEvent(PageEvent, IO_NO_INCREMENT, FALSE); + + /* And re-loop */ + continue; + }
/* Get the page */ MiUnlinkPageFromList(Pfn1); @@ -1337,7 +1453,8 @@ PointerPte, PointerProtoPte, LockIrql, - &OutPfn); + &OutPfn, + NULL);
/* THIS RELEASES THE PFN LOCK! */ break; @@ -1390,14 +1507,14 @@ { /* We had a locked PFN, so acquire the PFN lock to dereference it */ ASSERT(PointerProtoPte != NULL); - OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
/* Dereference the locked PFN */ MiDereferencePfnAndDropLockCount(OutPfn); ASSERT(OutPfn->u3.e2.ReferenceCount >= 1);
/* And now release the lock */ - KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql); }
/* Complete this as a transition fault */ @@ -1416,7 +1533,12 @@ LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
/* Resolve */ - Status = MiResolveTransitionFault(Address, PointerPte, Process, LockIrql, &InPageBlock); + Status = MiResolveTransitionFault(StoreInstruction, + Address, + PointerPte, + Process, + LockIrql, + &InPageBlock);
NT_ASSERT(NT_SUCCESS(Status));
@@ -1425,8 +1547,8 @@
if (InPageBlock != NULL) { - /* The page is being paged in by another process */ - KeWaitForSingleObject(InPageBlock, WrPageIn, KernelMode, FALSE, NULL); + /* We must wake the blocked threads */ + KeSetEvent(InPageBlock, IO_NO_INCREMENT, FALSE); }
ASSERT(OldIrql == KeGetCurrentIrql()); @@ -1821,9 +1943,6 @@ } else { - /* We don't implement transition PTEs */ - ASSERT(TempPte.u.Soft.Transition == 0); - /* Check for no-access PTE */ if (TempPte.u.Soft.Protection == MM_NOACCESS) { @@ -2090,7 +2209,7 @@ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
/* Make sure we have enough pages */ - ASSERT(MmAvailablePages >= 32); + ASSERT(MmAvailablePages != 0);
/* Try to get a zero page */ MI_SET_USAGE(MI_USAGE_PEB_TEB);
Modified: branches/TransitionPte/ntoskrnl/mm/ARM3/pfnlist.c URL: http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/ARM3/p... ============================================================================== --- branches/TransitionPte/ntoskrnl/mm/ARM3/pfnlist.c [iso-8859-1] (original) +++ branches/TransitionPte/ntoskrnl/mm/ARM3/pfnlist.c [iso-8859-1] Mon Sep 22 17:20:38 2014 @@ -36,7 +36,7 @@ ULONG MmSystemPageColor;
ULONG MmTransitionSharedPages; -ULONG MmTotalPagesForPagingFile; +ULONG_PTR MmTotalPagesForPagingFile;
MMPFNLIST MmZeroedPageListHead = {0, ZeroedPageList, LIST_HEAD, LIST_HEAD}; MMPFNLIST MmFreePageListHead = {0, FreePageList, LIST_HEAD, LIST_HEAD}; @@ -266,7 +266,6 @@ }
/* Decrease transition page counter */ - ASSERT(Pfn->u3.e1.PrototypePte == 1); /* Only supported ARM3 case */ MmTransitionSharedPages--;
/* One less page */ @@ -287,6 +286,9 @@ /* Decrement the counters */ ListHead->Total--; MmTotalPagesForPagingFile--; + + if (MmTotalPagesForPagingFile == 0) + KeClearEvent(&MpwThreadEvent);
/* Pick the correct colored list */ ListHead = &MmModifiedPageListByColor[0]; @@ -477,12 +479,89 @@ return PageIndex; }
+static +PFN_NUMBER +NTAPI +MiPopPageFromStandbyList(void) +{ + unsigned i; + PFN_NUMBER PageFrameIndex; + PMMPFN Pfn1; + PMMPTE PointerPte, SysPte, PteTable; + MMPTE PdePte; + + /* Make sure PFN lock is held and we have pages */ + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + ASSERT(MmAvailablePages != 0); + + /* This should be called in last resort if there are no more free pages */ + ASSERT(MmZeroedPageListHead.Total == 0); + ASSERT(MmFreePageListHead.Total == 0); + + /* Go by priority */ + for (i = 0; i < 8; i++) + { + /* Get the oldest one */ + PageFrameIndex = MmStandbyPageListByPriority[i].Blink; + if (PageFrameIndex != LIST_HEAD) + break; + } + + /* There are no free pages but still available ones, so this must be here */ + ASSERT(PageFrameIndex != LIST_HEAD); + + /* Get the PFN */ + Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); + + DPRINT1("Popping page %x from standbylist (PTE %p).\n", + PageFrameIndex, Pfn1->PteAddress); + + /* Sanity checks */ + ASSERT(Pfn1->u3.e1.PageLocation == StandbyPageList); + ASSERT(Pfn1->u3.e1.Modified == 0); + + /* Get a PTE to map the page directory */ + SysPte = MiReserveSystemPtes(1, SystemPteSpace); + ASSERT(SysPte != NULL); + + /* Build it */ + MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte, + SysPte, + MM_READWRITE, + Pfn1->u4.PteFrame); + /* And map it */ + MI_WRITE_VALID_PTE(SysPte, PdePte); + PteTable = MiPteToAddress(SysPte); + + /* Finally get a pointer to the PTE this page represents */ + PointerPte = &PteTable[MiAddressToPteOffset(MiPteToAddress(Pfn1->PteAddress))]; + + /* Other sanity checks */ + ASSERT(PointerPte->u.Hard.Valid == 0); + ASSERT(PointerPte->u.Soft.Transition == 1); + + /* Restore the PTE to its original value */ + *PointerPte = Pfn1->OriginalPte; + + /* We can get rid of this */ + MiReleaseSystemPtes(SysPte, 1, SystemPteSpace); + + /* And dereference the page table frame */ + MiDecrementShareCount(MI_PFN_ELEMENT(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame); + + /* Unlink */ + MiUnlinkPageFromList(Pfn1); + + return PageFrameIndex; +} + PFN_NUMBER NTAPI MiRemoveAnyPage(IN ULONG Color) { PFN_NUMBER PageIndex; PMMPFN Pfn1; + BOOLEAN FromStandbyList = FALSE;
/* Make sure PFN lock is held and we have pages */ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); @@ -507,23 +586,27 @@ ASSERT_LIST_INVARIANT(&MmZeroedPageListHead); PageIndex = MmZeroedPageListHead.Flink; Color = PageIndex & MmSecondaryColorMask; - ASSERT(PageIndex != LIST_HEAD); if (PageIndex == LIST_HEAD) { - /* FIXME: Should check the standby list */ ASSERT(MmZeroedPageListHead.Total == 0); + /* Pop one page from the standby list and try again */ + PageIndex = MiPopPageFromStandbyList(); + FromStandbyList = TRUE; + ASSERT(PageIndex != LIST_HEAD); } } } }
/* Remove the page from its list */ - PageIndex = MiRemovePageByColor(PageIndex, Color); + if (!FromStandbyList) + PageIndex = MiRemovePageByColor(PageIndex, Color);
/* Sanity checks */ Pfn1 = MI_PFN_ELEMENT(PageIndex); ASSERT((Pfn1->u3.e1.PageLocation == FreePageList) || - (Pfn1->u3.e1.PageLocation == ZeroedPageList)); + (Pfn1->u3.e1.PageLocation == ZeroedPageList) || + (Pfn1->u3.e1.PageLocation == StandbyPageList)); ASSERT(Pfn1->u3.e2.ReferenceCount == 0); ASSERT(Pfn1->u2.ShareCount == 0); ASSERT_LIST_INVARIANT(&MmFreePageListHead); @@ -540,6 +623,7 @@ PFN_NUMBER PageIndex; PMMPFN Pfn1; BOOLEAN Zero = FALSE; + BOOLEAN FromStandbyList = FALSE;
/* Make sure PFN lock is held and we have pages */ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); @@ -567,11 +651,13 @@ ASSERT_LIST_INVARIANT(&MmFreePageListHead); PageIndex = MmFreePageListHead.Flink; Color = PageIndex & MmSecondaryColorMask; - ASSERT(PageIndex != LIST_HEAD); if (PageIndex == LIST_HEAD) { - /* FIXME: Should check the standby list */ ASSERT(MmZeroedPageListHead.Total == 0); + /* Pop one page from the standby list and try again */ + PageIndex = MiPopPageFromStandbyList(); + FromStandbyList = TRUE; + ASSERT(PageIndex != LIST_HEAD); } } } @@ -584,10 +670,12 @@ /* Sanity checks */ Pfn1 = MI_PFN_ELEMENT(PageIndex); ASSERT((Pfn1->u3.e1.PageLocation == FreePageList) || - (Pfn1->u3.e1.PageLocation == ZeroedPageList)); + (Pfn1->u3.e1.PageLocation == ZeroedPageList)|| + (Pfn1->u3.e1.PageLocation == StandbyPageList));
/* Remove the page from its list */ - PageIndex = MiRemovePageByColor(PageIndex, Color); + if (!FromStandbyList) + PageIndex = MiRemovePageByColor(PageIndex, Color); ASSERT(Pfn1 == MI_PFN_ELEMENT(PageIndex));
/* Zero it, if needed */ @@ -744,7 +832,6 @@ Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); ASSERT(Pfn1->u4.MustBeCached == 0); ASSERT(Pfn1->u3.e2.ReferenceCount == 0); - ASSERT(Pfn1->u3.e1.PrototypePte == 1); ASSERT(Pfn1->u3.e1.Rom != 1);
/* One more transition page on a list */ @@ -979,9 +1066,8 @@ } else if (ListName == ModifiedPageList) { - /* In ARM3, page must be destined for page file, and not yet written out */ + /* In ARM3, page must be destined for page file */ ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0); - ASSERT(Pfn1->OriginalPte.u.Soft.PageFileHigh == 0);
/* One more transition page */ MmTransitionSharedPages++; @@ -989,7 +1075,8 @@ /* Increment the number of per-process modified pages */ PsGetCurrentProcess()->ModifiedPageCount++;
- /* FIXME: Wake up modified page writer if there are not enough free pages */ + /* Wake the page writer thread */ + KeSetEvent(&MpwThreadEvent, IO_NO_INCREMENT, FALSE); } else if (ListName == ModifiedNoWritePageList) { @@ -1059,6 +1146,9 @@ PageFrameIndex = PFN_FROM_PTE(PointerPtePte); ASSERT(PageFrameIndex != 0); Pfn1->u4.PteFrame = PageFrameIndex; + + /* HACK until we get working sets rolling */ + Pfn1->Wsle.u1.e1.Age = 0;
/* Increase its share count so we don't get rid of it */ Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); @@ -1270,32 +1360,8 @@ /* PFN lock must be held */ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
- if (Pfn1->u3.e2.ReferenceCount == 1) - { - /* Is there still a PFN for this page? */ - if (MI_IS_PFN_DELETED(Pfn1) == TRUE) - { - /* Clear the last reference */ - Pfn1->u3.e2.ReferenceCount = 0; - ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0); - - /* Mark the page temporarily as valid, we're going to make it free soon */ - Pfn1->u3.e1.PageLocation = ActiveAndValid; - - /* Bring it back into the free list */ - MiInsertPageInFreeList(PageFrameIndex); - } - else - { - /* PFN not yet deleted, drop a ref count */ - MiDecrementReferenceCount(Pfn1, PageFrameIndex); - } - } - else - { - /* Otherwise, just drop the reference count */ - InterlockedDecrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount); - } + /* Drop a ref count */ + MiDecrementReferenceCount(Pfn1, PageFrameIndex); } }
@@ -1338,6 +1404,13 @@ /* Did someone set the delete flag? */ if (MI_IS_PFN_DELETED(Pfn1)) { + /* Did we have a Pagefile entry for it */ + if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) + && (Pfn1->OriginalPte.u.Soft.PageFileHigh != 0)) + { + /* Yes. Set it free */ + MiFreePageFileEntry(&Pfn1->OriginalPte); + } /* Insert it into the free list, there's nothing left to do */ MiInsertPageInFreeList(PageFrameIndex); return;
Modified: branches/TransitionPte/ntoskrnl/mm/ARM3/section.c URL: http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/ARM3/s... ============================================================================== --- branches/TransitionPte/ntoskrnl/mm/ARM3/section.c [iso-8859-1] (original) +++ branches/TransitionPte/ntoskrnl/mm/ARM3/section.c [iso-8859-1] Mon Sep 22 17:20:38 2014 @@ -680,25 +680,55 @@ PageFrameIndex = PFN_FROM_PTE(&TempPte); Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
- /* As this is a paged-backed section, nobody should reference it anymore (no cache or whatever) */ - ASSERT(Pfn1->u3.ReferenceCount == 0); - - /* And it should be in standby or modified list */ - ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList)); - - /* Unlink it and put it back in free list */ - MiUnlinkPageFromList(Pfn1); - - /* Temporarily mark this as active and make it free again */ - Pfn1->u3.e1.PageLocation = ActiveAndValid; - MI_SET_PFN_DELETED(Pfn1); - - MiInsertPageInFreeList(PageFrameIndex); + if (Pfn1->u3.e1.WriteInProgress == 1) + { + KEVENT WriteEvent; + + DPRINT1("Setting the page free while it is being paged out!\n"); + + /* Only the page writer thread should hold a reference to it */ + ASSERT(Pfn1->u3.ReferenceCount == 1); + + /* Set the page event */ + KeInitializeEvent(&WriteEvent, NotificationEvent, FALSE); + /* We must be the only ones waiting */ + ASSERT(Pfn1->u1.Event == NULL); + Pfn1->u1.Event = &WriteEvent; + + /* Tell the page-out thread to abort */ + Pfn1->u3.e1.WriteInProgress = 0; + /* We canceled the write operation, so the page-write-thread will simply dereference + * the Pfn. It has no way to know whether the page will be re-used again (page fault case) + * or if it should be made free. So mark it as deleted here */ + MI_SET_PFN_DELETED(Pfn1); + + /* Let the writer thread finish and go along */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + KeWaitForSingleObject(&WriteEvent, FreePage, KernelMode, FALSE, NULL); + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + } + else + { + /* As this is a paged-backed section, nobody should reference it anymore (no cache or whatever) */ + ASSERT(Pfn1->u3.ReferenceCount == 0); + + /* And it should be in standby or modified list */ + ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || + (Pfn1->u3.e1.PageLocation == StandbyPageList)); + + /* Unlink it and temporarily take a reference */ + MiUnlinkPageFromList(Pfn1); + Pfn1->u3.e2.ReferenceCount++; + + /* This will put it back in free list and clean up properly */ + MI_SET_PFN_DELETED(Pfn1); + MiDecrementReferenceCount(Pfn1, PageFrameIndex); + } } else if (TempPte.u.Soft.PageFileHigh != 0) { - /* Should not happen for now */ - ASSERT(FALSE); + /* There isn't much to do */ + MiFreePageFileEntry(&TempPte); } } else
Modified: branches/TransitionPte/ntoskrnl/mm/ARM3/virtual.c URL: http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/ARM3/v... ============================================================================== --- branches/TransitionPte/ntoskrnl/mm/ARM3/virtual.c [iso-8859-1] (original) +++ branches/TransitionPte/ntoskrnl/mm/ARM3/virtual.c [iso-8859-1] Mon Sep 22 17:20:38 2014 @@ -317,7 +317,6 @@ { /* As always, only handle current ARM3 scenarios */ ASSERT(PointerPte->u.Soft.Prototype == 0); - ASSERT(PointerPte->u.Soft.Transition == 0);
/* Normally this is one possibility -- freeing a valid page */ if (PointerPte->u.Hard.Valid) @@ -345,6 +344,57 @@
/* Decrement the page table too */ MiDecrementShareCount(Pfn2, PageTableIndex); + + /* Release the PFN database */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Destroy the PTE */ + MI_ERASE_PTE(PointerPte); + } + else if (PointerPte->u.Soft.Transition == 1) + { + /* Get the page PFN */ + PageFrameIndex = PFN_FROM_PTE(PointerPte); + Pfn1 = MiGetPfnEntry(PageFrameIndex); + + /* The page should be out of any working set */ + ASSERT(Pfn1->u1.WsIndex == 0); + + /* Get the page table entry */ + PageTableIndex = Pfn1->u4.PteFrame; + Pfn2 = MiGetPfnEntry(PageTableIndex); + + /* Lock the PFN database */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Delete the page */ + MI_SET_PFN_DELETED(Pfn1); + + /* Decrement the page table */ + MiDecrementShareCount(Pfn2, PageTableIndex); + + ASSERT(Pfn1->u3.e2.ReferenceCount == 0); + /* Unlink it and temporarily take a reference */ + MiUnlinkPageFromList(Pfn1); + Pfn1->u3.e2.ReferenceCount++; + + /* This will put it back in free list and clean properly up */ + MI_SET_PFN_DELETED(Pfn1); + MiDecrementReferenceCount(Pfn1, PageFrameIndex); + + /* Release the PFN database */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Destroy the PTE */ + MI_ERASE_PTE(PointerPte); + } + else if (PointerPte->u.Soft.PageFileHigh != 0) + { + /* The PFN lock also protects the page files */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Pretty easy: free the page file slot and go look elsewhere */ + MiFreePageFileEntry(PointerPte);
/* Release the PFN database */ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); @@ -358,10 +408,7 @@ * The only other ARM3 possibility is a demand zero page, which would * mean freeing some of the paged pool pages that haven't even been * touched yet, as part of a larger allocation. - * - * Right now, we shouldn't expect any page file information in the PTE */ - ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
/* Destroy the PTE */ MI_ERASE_PTE(PointerPte); @@ -407,9 +454,8 @@ /* See if the PTE is valid */ if (TempPte.u.Hard.Valid == 0) { - /* Prototype and paged out PTEs not supported yet */ + /* Prototype PTEs not supported yet */ ASSERT(TempPte.u.Soft.Prototype == 0); - ASSERT((TempPte.u.Soft.PageFileHigh == 0) || (TempPte.u.Soft.Transition == 1));
if (TempPte.u.Soft.Transition) { @@ -435,15 +481,24 @@ /* And it should be in standby or modified list */ ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList));
- /* Unlink it and temporarily mark it as active */ + /* Unlink it and temporarily take a reference */ MiUnlinkPageFromList(Pfn1); Pfn1->u3.e2.ReferenceCount++; - Pfn1->u3.e1.PageLocation = ActiveAndValid;
/* This will put it back in free list and clean properly up */ MI_SET_PFN_DELETED(Pfn1); MiDecrementReferenceCount(Pfn1, PageFrameIndex); } + return; + } + + if (TempPte.u.Soft.PageFileHigh != 0) + { + /* Release the page file entry */ + MiFreePageFileEntry(&TempPte); + + /* And that's pretty much it */ + MI_ERASE_PTE(PointerPte); return; } } @@ -475,7 +530,6 @@ } #endif /* Drop the share count on the page table */ - PointerPde = MiPteToPde(PointerPte); MiDecrementShareCount(MiGetPfnEntry(PointerPde->u.Hard.PageFrameNumber), PointerPde->u.Hard.PageFrameNumber);
Modified: branches/TransitionPte/ntoskrnl/mm/balance.c URL: http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/balanc... ============================================================================== --- branches/TransitionPte/ntoskrnl/mm/balance.c [iso-8859-1] (original) +++ branches/TransitionPte/ntoskrnl/mm/balance.c [iso-8859-1] Mon Sep 22 17:20:38 2014 @@ -22,22 +22,14 @@ #pragma alloc_text(INIT, MiInitBalancerThread) #endif
- -/* TYPES ********************************************************************/ -typedef struct _MM_ALLOCATION_REQUEST -{ - PFN_NUMBER Page; - LIST_ENTRY ListEntry; - KEVENT Event; -} -MM_ALLOCATION_REQUEST, *PMM_ALLOCATION_REQUEST; +#define TEST_PAGING + + /* GLOBALS ******************************************************************/
MM_MEMORY_CONSUMER MiMemoryConsumers[MC_MAXIMUM]; static ULONG MiMinimumAvailablePages; static ULONG MiNrTotalPages; -static LIST_ENTRY AllocationListHead; -static KSPIN_LOCK AllocationListLock; static ULONG MiMinimumPagesPerRun;
static CLIENT_ID MiBalancerThreadId; @@ -53,8 +45,6 @@ MmInitializeBalancer(ULONG NrAvailablePages, ULONG NrSystemPages) { memset(MiMemoryConsumers, 0, sizeof(MiMemoryConsumers)); - InitializeListHead(&AllocationListHead); - KeInitializeSpinLock(&AllocationListLock);
MiNrTotalPages = NrAvailablePages;
@@ -86,18 +76,10 @@ MiMemoryConsumers[Consumer].Trim = Trim; }
-VOID -NTAPI -MiZeroPhysicalPage( - IN PFN_NUMBER PageFrameIndex -); - NTSTATUS NTAPI MmReleasePageMemoryConsumer(ULONG Consumer, PFN_NUMBER Page) { - PMM_ALLOCATION_REQUEST Request; - PLIST_ENTRY Entry; KIRQL OldIrql;
if (Page == 0) @@ -110,26 +92,11 @@ { if(Consumer == MC_USER) MmRemoveLRUUserPage(Page); (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed); - if ((Entry = ExInterlockedRemoveHeadList(&AllocationListHead, &AllocationListLock)) == NULL) - { - OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); - MmDereferencePage(Page); - KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); - } - else - { - Request = CONTAINING_RECORD(Entry, MM_ALLOCATION_REQUEST, ListEntry); - MiZeroPhysicalPage(Page); - Request->Page = Page; - KeSetEvent(&Request->Event, IO_NO_INCREMENT, FALSE); - } } - else - { - OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); - MmDereferencePage(Page); - KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); - } + + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + MmDereferencePage(Page); + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
return(STATUS_SUCCESS); } @@ -149,10 +116,13 @@ return InitialTarget; }
- if (MiMemoryConsumers[Consumer].PagesUsed > MiMemoryConsumers[Consumer].PagesTarget) - { - /* Consumer page limit exceeded */ - Target = max(Target, MiMemoryConsumers[Consumer].PagesUsed - MiMemoryConsumers[Consumer].PagesTarget); + // if (MiFreeSwapPages == 0) + { + if (MiMemoryConsumers[Consumer].PagesUsed > MiMemoryConsumers[Consumer].PagesTarget) + { + /* Consumer page limit exceeded */ + Target = max(Target, MiMemoryConsumers[Consumer].PagesUsed - MiMemoryConsumers[Consumer].PagesTarget); + } } if (MmAvailablePages < MiMinimumAvailablePages) { @@ -162,13 +132,6 @@
if (Target) { - if (!InitialTarget) - { - /* If there was no initial target, - * swap at least MiMinimumPagesPerRun */ - Target = max(Target, MiMinimumPagesPerRun); - } - /* Now swap the pages out */ Status = MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
@@ -265,8 +228,9 @@ * Make sure we don't exceed our individual target. */ PagesUsed = InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed); - if (PagesUsed > MiMemoryConsumers[Consumer].PagesTarget && - !MiIsBalancerThread()) + if ((PagesUsed > MiMemoryConsumers[Consumer].PagesTarget) && + !MiIsBalancerThread() && + (MmNumberOfPagingFiles == 0)) { MmRebalanceMemoryConsumers(); } @@ -285,52 +249,6 @@ } if (Consumer == MC_USER) MmInsertLRULastUserPage(Page); *AllocatedPage = Page; - if (MmAvailablePages < MiMinimumAvailablePages) - MmRebalanceMemoryConsumers(); - return(STATUS_SUCCESS); - } - - /* - * Make sure we don't exceed global targets. - */ - if (MmAvailablePages < MiMinimumAvailablePages) - { - MM_ALLOCATION_REQUEST Request; - - if (!CanWait) - { - (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed); - MmRebalanceMemoryConsumers(); - return(STATUS_NO_MEMORY); - } - - /* Insert an allocation request. */ - Request.Page = 0; - KeInitializeEvent(&Request.Event, NotificationEvent, FALSE); - - ExInterlockedInsertTailList(&AllocationListHead, &Request.ListEntry, &AllocationListLock); - MmRebalanceMemoryConsumers(); - - KeWaitForSingleObject(&Request.Event, - 0, - KernelMode, - FALSE, - NULL); - - Page = Request.Page; - if (Page == 0) - { - KeBugCheck(NO_PAGES_AVAILABLE); - } - - if(Consumer == MC_USER) MmInsertLRULastUserPage(Page); - *AllocatedPage = Page; - - if (MmAvailablePages < MiMinimumAvailablePages) - { - MmRebalanceMemoryConsumers(); - } - return(STATUS_SUCCESS); }
@@ -347,91 +265,246 @@ if(Consumer == MC_USER) MmInsertLRULastUserPage(Page); *AllocatedPage = Page;
- if (MmAvailablePages < MiMinimumAvailablePages) - { - MmRebalanceMemoryConsumers(); - } - return(STATUS_SUCCESS); }
+/* Old implementation of the balancer thread */ +static +VOID +NTAPI +MiRosBalancerThread(VOID) +{ + ULONG InitialTarget = 0; + + DPRINT1("Legacy MM balancer in action!\n"); + +#if (_MI_PAGING_LEVELS == 2) + if (!MiIsBalancerThread()) + { + /* Clean up the unused PDEs */ + ULONG_PTR Address; + PEPROCESS Process = PsGetCurrentProcess(); + + /* Acquire PFN lock */ + KIRQL OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + PMMPDE pointerPde; + for (Address = (ULONG_PTR) MI_LOWEST_VAD_ADDRESS ; + Address < (ULONG_PTR) MM_HIGHEST_VAD_ADDRESS; Address += (PAGE_SIZE * PTE_COUNT)) + { + if (MiQueryPageTableReferences((PVOID) Address) == 0) + { + pointerPde = MiAddressToPde(Address); + if (pointerPde->u.Hard.Valid) + MiDeletePte(pointerPde, MiPdeToPte(pointerPde), Process, NULL); + ASSERT(pointerPde->u.Hard.Valid == 0); + } + } + /* Release lock */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + } +#endif + do + { + ULONG OldTarget = InitialTarget; + ULONG i; + + /* Trim each consumer */ + for (i = 0; i < MC_MAXIMUM; i++) + { + InitialTarget = MiTrimMemoryConsumer(i, InitialTarget); + } + + /* No pages left to swap! */ + if (InitialTarget != 0 && InitialTarget == OldTarget) + { + /* Game over */ + KeBugCheck(NO_PAGES_AVAILABLE); + } + } while (InitialTarget != 0); +}
VOID NTAPI MiBalancerThread(PVOID Unused) { - PVOID WaitObjects[2]; - NTSTATUS Status; - ULONG i; - - WaitObjects[0] = &MiBalancerEvent; - WaitObjects[1] = &MiBalancerTimer; - - while (1) - { - Status = KeWaitForMultipleObjects(2, - WaitObjects, - WaitAny, - Executive, - KernelMode, - FALSE, - NULL, - NULL); - - if (Status == STATUS_WAIT_0 || Status == STATUS_WAIT_1) - { - ULONG InitialTarget = 0; - -#if (_MI_PAGING_LEVELS == 2) - if (!MiIsBalancerThread()) - { - /* Clean up the unused PDEs */ - ULONG_PTR Address; - PEPROCESS Process = PsGetCurrentProcess(); - - /* Acquire PFN lock */ - KIRQL OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); - PMMPDE pointerPde; - for (Address = (ULONG_PTR)MI_LOWEST_VAD_ADDRESS; - Address < (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS; - Address += (PAGE_SIZE * PTE_COUNT)) - { - if (MiQueryPageTableReferences((PVOID)Address) == 0) + PVOID WaitObjects[2]; + NTSTATUS Status; + ULONG i; + PVOID LastPagedPoolAddress = MmPagedPoolStart; +#ifdef TEST_PAGING + PULONG PagingTestBuffer; + BOOLEAN TestPage = FALSE; +#endif + + WaitObjects[0] = &MiBalancerTimer; + WaitObjects[1] = &MiBalancerEvent; + +#ifdef TEST_PAGING + PagingTestBuffer = ExAllocatePoolWithTag(PagedPool, PAGE_SIZE, 'tseT'); + DPRINT1("PagingTestBuffer: %p, PTE %p\n", PagingTestBuffer, MiAddressToPte(PagingTestBuffer)); + for (i = 0; i < (PAGE_SIZE / sizeof(ULONG)); i++) + PagingTestBuffer[i] = i; +#endif + + while (1) + { + /* For now, we only age the paged pool, with 100 pages per run. */ + LONG Count = 100; + PVOID Address = LastPagedPoolAddress; + KIRQL OldIrql; + PMMPTE PointerPte; + PMMPDE PointerPde; + ULONG PdeIndex; + MMPTE TempPte; + PMMPFN Pfn1; + BOOLEAN Flush; + + Status = KeWaitForMultipleObjects( + 2, + WaitObjects, + WaitAny, + Executive, + KernelMode, + FALSE, + NULL, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("KeWaitForMultipleObjects failed, status = %x\n", Status); + KeBugCheck(MEMORY_MANAGEMENT); + } + + DPRINT("MM Balancer: Starting to loop.\n"); + + while (Count-- > 0) + { + Address = (PVOID)((ULONG_PTR) Address + PAGE_SIZE); + + /* Acquire PFN lock while we are cooking */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + if (Address > MmPagedPoolEnd) + { + Address = MmPagedPoolStart; + } + + PointerPde = MiAddressToPde(Address); + PointerPte = MiAddressToPte(Address); + + PdeIndex = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE); + + if (MmSystemPagePtes[PdeIndex].u.Hard.Valid == 0) + TempPte.u.Long = 0; + else + TempPte = *PointerPte; + + if (TempPte.u.Hard.Valid == 0) + { + /* Bad luck, not a paged-in address */ +#ifdef TEST_PAGING + if (PointerPte == MiAddressToPte(PagingTestBuffer)) { - pointerPde = MiAddressToPde(Address); - if (pointerPde->u.Hard.Valid) - MiDeletePte(pointerPde, MiPdeToPte(pointerPde), Process, NULL); - ASSERT(pointerPde->u.Hard.Valid == 0); + /* Of course it should not magically become a prototype pte */ + ASSERT(TempPte.u.Soft.Prototype == 0); + if (TempPte.u.Soft.Transition == 0) + { + /* It was paged out! */ + ASSERT(TempPte.u.Soft.PageFileHigh != 0); + TestPage = TRUE; + } } - } - /* Release lock */ +#endif + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + continue; + } + + Flush = FALSE; + + /* Get the Pfn */ + Pfn1 = MI_PFN_ELEMENT(PFN_FROM_PTE(&TempPte)); + ASSERT(Pfn1->PteAddress == PointerPte); + ASSERT(Pfn1->u3.e1.PrototypePte == 0); + + /* First check if it was written to */ + if (TempPte.u.Hard.Dirty) + Pfn1->u3.e1.Modified = 1; + + /* See if it was accessed since the last time we looked */ + if (TempPte.u.Hard.Accessed) + { + /* Yes! Mark the page as young and fresh again */ + Pfn1->Wsle.u1.e1.Age = 0; + /* Tell the CPU we want to know for the next time */ + TempPte.u.Hard.Accessed = 0; + MI_UPDATE_VALID_PTE(PointerPte, TempPte); + Flush = TRUE; + } + else if (Pfn1->Wsle.u1.e1.Age == 3) + { + /* Page is getting old: Mark the PTE as transition */ + DPRINT1("MM Balancer: putting %p (page %x) as transition.\n", PointerPte, + PFN_FROM_PTE(&TempPte)); + TempPte.u.Hard.Valid = 0; + TempPte.u.Soft.Transition = 1; + TempPte.u.Trans.Protection = MM_READWRITE; + MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&TempPte)); + MI_WRITE_INVALID_PTE(PointerPte, TempPte); + Flush = TRUE; + } + else + { + /* The page is just getting older */ + Pfn1->Wsle.u1.e1.Age++; + } + + /* Flush the TLB entry if we have to */ + if (Flush) + { +#ifdef CONFIG_SMP + // FIXME: Should invalidate entry in every CPU TLB + ASSERT(FALSE); +#endif + KeInvalidateTlbEntry(Address); + } + + /* Unlock, we're done for this address */ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); } -#endif - do - { - ULONG OldTarget = InitialTarget; - - /* Trim each consumer */ - for (i = 0; i < MC_MAXIMUM; i++) - { - InitialTarget = MiTrimMemoryConsumer(i, InitialTarget); - } - - /* No pages left to swap! */ - if (InitialTarget != 0 && - InitialTarget == OldTarget) - { - /* Game over */ - KeBugCheck(NO_PAGES_AVAILABLE); - } - } while (InitialTarget != 0); - } - else - { - DPRINT1("KeWaitForMultipleObjects failed, status = %x\n", Status); - KeBugCheck(MEMORY_MANAGEMENT); - } - } + + DPRINT("MM Balancer: End of the loop.\n"); + +#ifdef TEST_PAGING + if (TestPage) + { + BOOLEAN Passed = TRUE; + DPRINT1("Verifying data integrity of paged out data!\n"); + for (i = 0; i < (PAGE_SIZE / sizeof(ULONG)); i++) + { + if (PagingTestBuffer[i] != i) + Passed = FALSE; + } + if (Passed) + DPRINT1("PASSED! \o/\n"); + else + DPRINT1("FAILED /o\\n"); + TestPage = FALSE; + } +#endif + /* Remember this for the next run */ + LastPagedPoolAddress = Address; + + if (Status == STATUS_WAIT_1) + { + /* Balancer thread was woken up by legacy MM + * This means we are really starving. Let's see if this still a problem */ + if ((MmAvailablePages >= MmMinimumFreePages) && (MmNumberOfPagingFiles != 0)) + { + /* Yay! No need to trust the old balancer */ + continue; + } + + MiRosBalancerThread(); + } + } }
VOID
Modified: branches/TransitionPte/ntoskrnl/mm/mminit.c URL: http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/mminit... ============================================================================== --- branches/TransitionPte/ntoskrnl/mm/mminit.c [iso-8859-1] (original) +++ branches/TransitionPte/ntoskrnl/mm/mminit.c [iso-8859-1] Mon Sep 22 17:20:38 2014 @@ -299,36 +299,179 @@ NTAPI MmMpwThreadMain(PVOID Parameter) { - NTSTATUS Status; - ULONG PagesWritten; - LARGE_INTEGER Timeout; - - UNREFERENCED_PARAMETER(Parameter); - - Timeout.QuadPart = -50000000; - - for(;;) - { - Status = KeWaitForSingleObject(&MpwThreadEvent, - 0, - KernelMode, - FALSE, - &Timeout); - if (!NT_SUCCESS(Status)) - { - DbgPrint("MpwThread: Wait failed\n"); - KeBugCheck(MEMORY_MANAGEMENT); - return; - } - - PagesWritten = 0; - -#ifndef NEWCC - // XXX arty -- we flush when evicting pages or destorying cache - // sections. - CcRosFlushDirtyPages(128, &PagesWritten, FALSE); -#endif - } + NTSTATUS Status; + PMMPTE SysPte, PteTable; + + UNREFERENCED_PARAMETER(Parameter); + + /* Reserve a PTE for the page table */ + SysPte = MiReserveSystemPtes(1, SystemPteSpace); + ASSERT(SysPte != NULL); + PteTable = MiPteToAddress(SysPte); + + for(;;) + { + KIRQL OldIrql; + PFN_NUMBER PageFrameIndex; + PMMPFN Pfn; + MMPTE TempPte, PdePte; + PMMPTE PointerPte; + BOOLEAN PageFileEntryFromPage = FALSE; + /* + * To start working, we wait for two conditions: + * - there are pages to be paged out. + * - We are in a low memory situation. + */ + Status = KeWaitForSingleObject( + &MpwThreadEvent, + WrPageOut, + KernelMode, + FALSE, + NULL); + + DPRINT("THE KRAKEN WAS RELEASED AND WILL PAGE YOUR ASS OUT!\n"); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("MpwThread: Wait failed\n"); + KeBugCheck(MEMORY_MANAGEMENT); + return; + } + + /* Lock the PFN database */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* The true main loop */ + while ((MmTotalPagesForPagingFile != 0) && (MiFreeSwapPages != 0)) + { + /* Get the first page from the list */ + PageFrameIndex = MmModifiedPageListByColor[0].Flink; + + /* Get The Pfn */ + Pfn = MI_PFN_ELEMENT(PageFrameIndex); + + /* Some things which must always hold */ + ASSERT(Pfn->OriginalPte.u.Soft.Transition == 0); + ASSERT(Pfn->u3.ReferenceCount == 0); + + /* And some that are not yet supported */ + ASSERT(Pfn->OriginalPte.u.Soft.Prototype == 0); + + /* Maybe this is not the first time */ + if ((Pfn->OriginalPte.u.Soft.Prototype == 0) && + (Pfn->OriginalPte.u.Soft.PageFileHigh != 0)) + { + /* Use that */ + TempPte = Pfn->OriginalPte; + PageFileEntryFromPage = TRUE; + } + else + { + MiReservePageFileEntry(&TempPte); + } + + /* Get it out of the list and reference it */ + MiUnlinkPageFromList(Pfn); + MiReferenceUnusedPageAndBumpLockCount(Pfn); + + ASSERT(Pfn->u3.e1.PageLocation == ModifiedPageList); + + /* Mark it as write in progress */ + Pfn->u3.e1.WriteInProgress = 1; + Pfn->u1.Event = NULL; + + /* Release the PFN lock while we are writing */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Do the actual write */ + Status = MiWritePageFile(PageFrameIndex, &TempPte); + + /* Get the lock again */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Get a mapping to the page table */ + MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte, + SysPte, + MM_READWRITE, + Pfn->u4.PteFrame); + MI_WRITE_VALID_PTE(SysPte, PdePte); + + /* Finally get a pointer to the PTE this page represents */ + PointerPte = &PteTable[MiAddressToPteOffset(MiPteToAddress(Pfn->PteAddress))]; + + /* Get relevant values from the original PTE */ + TempPte.u.Soft.Protection = PointerPte->u.Soft.Protection; + + /* Maybe someone aborted the operation */ + if (Pfn->u3.e1.WriteInProgress == 0) + { + DPRINT1("Someone aborted the page-out operation!\n"); + /* Just set the event and let the waiter go along */ + ASSERT(Pfn->u1.Event != NULL); + KeSetEvent(Pfn->u1.Event, IO_NO_INCREMENT, FALSE); + Pfn->u1.Event = NULL; + /* This is now useless */ + if (!PageFileEntryFromPage) + MiFreePageFileEntry(&TempPte); + } + /* Maybe someone tried to access the page while we were not looking */ + else if (Pfn->u1.Event != NULL) + { + DPRINT1("Page fault occured while we were paging out!\n"); + + /* So the page fault handler marked the page as unmodified */ + ASSERT(Pfn->u3.e1.Modified == 0); + + /* Save the pagefile entry for this page */ + Pfn->OriginalPte = TempPte; + KeSetEvent(Pfn->u1.Event, IO_NO_INCREMENT, FALSE); + Pfn->u1.Event = NULL; + } + else if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to write page to pagefile!\n"); + /* This is now useless */ + if (!PageFileEntryFromPage) + MiFreePageFileEntry(&TempPte); + /* MiDereferencePfnAndDropLockCount will put it back on the tail of the list */ + } + else + { + DPRINT1("Page %x successfully paged out. PTE pointer %p (-> %x)\n", + PageFrameIndex, Pfn->PteAddress, TempPte.u.Long); + + /* Of course it must already be invalid */ + ASSERT(PointerPte->u.Hard.Valid == 0); + + /* And be in transition */ + ASSERT(PointerPte->u.Soft.Transition == 1); + + /* So the PTE is now officially paged out */ + MI_WRITE_INVALID_PTE(PointerPte, TempPte); + + /* And the pagefile entry belongs to the PTE, not to the page! */ + Pfn->OriginalPte.u.Long = 0; + + /* And dereference the page table frame */ + MiDecrementShareCount(MI_PFN_ELEMENT(Pfn->u4.PteFrame), Pfn->u4.PteFrame); + /* We can finally make it available for real */ + MI_SET_PFN_DELETED(Pfn); + } + + /* We're done with this */ + Pfn->u3.e1.WriteInProgress = 0; + + /* This will put it back in the free list */ + MiDereferencePfnAndDropLockCount(Pfn); + + /* Unmap the Page Table */ + MI_ERASE_PTE(SysPte); + KeInvalidateTlbEntry(PteTable); + } + + /* We're done for this run */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + } }
NTSTATUS
Modified: branches/TransitionPte/ntoskrnl/mm/pagefile.c URL: http://svn.reactos.org/svn/reactos/branches/TransitionPte/ntoskrnl/mm/pagefi... ============================================================================== --- branches/TransitionPte/ntoskrnl/mm/pagefile.c [iso-8859-1] (original) +++ branches/TransitionPte/ntoskrnl/mm/pagefile.c [iso-8859-1] Mon Sep 22 17:20:38 2014 @@ -46,13 +46,14 @@ { LIST_ENTRY PagingFileListEntry; PFILE_OBJECT FileObject; - LARGE_INTEGER MaximumSize; - LARGE_INTEGER CurrentSize; + ULONG_PTR MaximumSize; + ULONG_PTR CurrentSize; PFN_NUMBER FreePages; PFN_NUMBER UsedPages; - PULONG AllocMap; + PVOID AllocMap; + RTL_BITMAP AllocBitMap; + ULONG_PTR AllocBitMapHint; KSPIN_LOCK AllocMapLock; - ULONG AllocMapSize; PRETRIEVAL_POINTERS_BUFFER RetrievalPointers; } PAGINGFILE, *PPAGINGFILE; @@ -72,9 +73,6 @@
/* List of paging files, both used and free */ static PPAGINGFILE PagingFileList[MAX_PAGING_FILES]; - -/* Lock for examining the list of paging files */ -static KSPIN_LOCK PagingFileListLock;
/* Number of paging files */ ULONG MmNumberOfPagingFiles; @@ -221,30 +219,62 @@ NTAPI MmWriteToSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page) { - ULONG i; - ULONG_PTR offset; - LARGE_INTEGER file_offset; + MMPTE TempPte; + + TempPte.u.Long = 0; + TempPte.u.Soft.PageFileHigh = OFFSET_FROM_ENTRY(SwapEntry); + TempPte.u.Soft.PageFileLow = FILE_FROM_ENTRY(SwapEntry); + return MiWritePageFile(Page, &TempPte); +} + + +NTSTATUS +NTAPI +MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page) +{ + MMPTE TempPte; + + TempPte.u.Long = 0; + TempPte.u.Soft.PageFileHigh = OFFSET_FROM_ENTRY(SwapEntry); + TempPte.u.Soft.PageFileLow = FILE_FROM_ENTRY(SwapEntry); + return MiReadPageFile(Page, &TempPte); +} + +NTSTATUS +NTAPI +MiReadPageFile( + _In_ PFN_NUMBER Page, + _In_ const MMPTE* PointerPte +) +{ + LARGE_INTEGER FileOffset; IO_STATUS_BLOCK Iosb; + ULONG PageFileIndex = PointerPte->u.Soft.PageFileLow; + ULONG_PTR PageFileOffset = PointerPte->u.Soft.PageFileHigh; NTSTATUS Status; KEVENT Event; - UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)]; + UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)]; PMDL Mdl = (PMDL)MdlBase; - - DPRINT("MmWriteToSwapPage\n"); - - if (SwapEntry == 0) + PPAGINGFILE PagingFile; + + DPRINT("MiReadPageFile\n"); + + if (PageFileOffset == 0) { KeBugCheck(MEMORY_MANAGEMENT); return(STATUS_UNSUCCESSFUL); }
- i = FILE_FROM_ENTRY(SwapEntry); - offset = OFFSET_FROM_ENTRY(SwapEntry); - - if (PagingFileList[i]->FileObject == NULL || - PagingFileList[i]->FileObject->DeviceObject == NULL) - { - DPRINT1("Bad paging file 0x%.8X\n", SwapEntry); + /* Normalize it */ + PageFileOffset--; + + ASSERT(PageFileIndex < MAX_PAGING_FILES); + + PagingFile = PagingFileList[PageFileIndex]; + + if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject == NULL) + { + DPRINT1("Bad paging file %u\n", PageFileIndex); KeBugCheck(MEMORY_MANAGEMENT); }
@@ -252,80 +282,13 @@ MmBuildMdlFromPages(Mdl, &Page); Mdl->MdlFlags |= MDL_PAGES_LOCKED;
- file_offset.QuadPart = offset * PAGE_SIZE; - file_offset = MmGetOffsetPageFile(PagingFileList[i]->RetrievalPointers, file_offset); - - KeInitializeEvent(&Event, NotificationEvent, FALSE); - Status = IoSynchronousPageWrite(PagingFileList[i]->FileObject, - Mdl, - &file_offset, - &Event, - &Iosb); - if (Status == STATUS_PENDING) - { - KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); - Status = Iosb.Status; - } - - if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) - { - MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl); - } - return(Status); -} - - -NTSTATUS -NTAPI -MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page) -{ - return MiReadPageFile(Page, FILE_FROM_ENTRY(SwapEntry), OFFSET_FROM_ENTRY(SwapEntry)); -} - -NTSTATUS -NTAPI -MiReadPageFile( - _In_ PFN_NUMBER Page, - _In_ ULONG PageFileIndex, - _In_ ULONG_PTR PageFileOffset) -{ - LARGE_INTEGER file_offset; - IO_STATUS_BLOCK Iosb; - NTSTATUS Status; - KEVENT Event; - UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)]; - PMDL Mdl = (PMDL)MdlBase; - PPAGINGFILE PagingFile; - - DPRINT("MiReadSwapFile\n"); - - if (PageFileOffset == 0) - { - KeBugCheck(MEMORY_MANAGEMENT); - return(STATUS_UNSUCCESSFUL); - } - - ASSERT(PageFileIndex < MAX_PAGING_FILES); - - PagingFile = PagingFileList[PageFileIndex]; - - if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject == NULL) - { - DPRINT1("Bad paging file %u\n", PageFileIndex); - KeBugCheck(MEMORY_MANAGEMENT); - } - - MmInitializeMdl(Mdl, NULL, PAGE_SIZE); - MmBuildMdlFromPages(Mdl, &Page); - Mdl->MdlFlags |= MDL_PAGES_LOCKED; - - file_offset.QuadPart = PageFileOffset * PAGE_SIZE; - file_offset = MmGetOffsetPageFile(PagingFile->RetrievalPointers, file_offset); + FileOffset.QuadPart = PageFileOffset * PAGE_SIZE; + FileOffset = MmGetOffsetPageFile(PagingFile->RetrievalPointers, FileOffset);
KeInitializeEvent(&Event, NotificationEvent, FALSE); Status = IoPageRead(PagingFile->FileObject, Mdl, - &file_offset, + &FileOffset, &Event, &Iosb); if (Status == STATUS_PENDING) @@ -340,14 +303,182 @@ return(Status); }
+NTSTATUS +NTAPI +MiWritePageFile( + _In_ PFN_NUMBER Page, + _In_ const MMPTE* PointerPte +) +{ + ULONG PageFileIndex = PointerPte->u.Soft.PageFileLow; + ULONG_PTR PageFileOffset = PointerPte->u.Soft.PageFileHigh; + LARGE_INTEGER FileOffset; + IO_STATUS_BLOCK Iosb; + NTSTATUS Status; + KEVENT Event; + UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)]; + PMDL Mdl = (PMDL) MdlBase; + PPAGINGFILE PagingFile; + + /* The PTE must already be setup to point to a pagefile entry */ + ASSERT(PointerPte->u.Hard.Valid == 0); + ASSERT(PointerPte->u.Soft.Transition == 0); + ASSERT(PointerPte->u.Soft.Prototype == 0); + + DPRINT("MiWriteSwapFile\n"); + + if (PageFileOffset == 0) + { + KeBugCheck(MEMORY_MANAGEMENT); + return (STATUS_UNSUCCESSFUL); + } + + /* Normalize it */ + PageFileOffset--; + + ASSERT(PageFileIndex < MAX_PAGING_FILES); + + PagingFile = PagingFileList[PageFileIndex]; + + if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject == NULL) + { + DPRINT1("Bad paging file %u\n", PageFileIndex); + KeBugCheck(MEMORY_MANAGEMENT); + } + + MmInitializeMdl(Mdl, NULL, PAGE_SIZE); + MmBuildMdlFromPages(Mdl, &Page); + Mdl->MdlFlags |= MDL_PAGES_LOCKED; + + FileOffset.QuadPart = PageFileOffset * PAGE_SIZE; + FileOffset = MmGetOffsetPageFile(PagingFile->RetrievalPointers, FileOffset); + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + Status = IoSynchronousPageWrite(PagingFile->FileObject, + Mdl, + &FileOffset, + &Event, + &Iosb); + if (Status == STATUS_PENDING) + { + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); + Status = Iosb.Status; + } + if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) + { + MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl); + } + return Status; +} + +VOID +NTAPI +MiFreePageFileEntry( + _In_ PMMPTE PointerPte +) +{ + ULONG PageFileIndex; + ULONG_PTR PageFileOffset; + PPAGINGFILE PageFile; + + /* Some sanity checks */ + ASSERT(PointerPte->u.Hard.Valid == 0); + ASSERT(PointerPte->u.Soft.Transition == 0); + ASSERT(PointerPte->u.Soft.Prototype == 0); + + /* We must hold the PFN lock */ + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + PageFileIndex = PointerPte->u.Soft.PageFileLow; + PageFileOffset = PointerPte->u.Soft.PageFileHigh; + + /* Fix up the value */ + ASSERT(PageFileOffset != 0); + PageFileOffset--; + + DPRINT1("Releasing page file entry %u, %lu\n", PageFileIndex, PageFileOffset); + + ASSERT(PageFileIndex < MAX_PAGING_FILES); + + PageFile = PagingFileList[PageFileIndex]; + + /* And also lock this pagefile */ + KeAcquireSpinLockAtDpcLevel(&PageFile->AllocMapLock); + + /* It must be already in use */ + ASSERT(RtlTestBit(&PageFile->AllocBitMap, PageFileOffset)); + RtlClearBit(&PageFile->AllocBitMap, PageFileOffset); + + /* One more */ + PageFile->FreePages++; + PageFile->UsedPages--; + + MiFreeSwapPages++; + MiUsedSwapPages--; + + /* Done */ + KeReleaseSpinLockFromDpcLevel(&PageFile->AllocMapLock); +} + +NTSTATUS +NTAPI +MiReservePageFileEntry( + _Out_ PMMPTE PointerPte +) +{ + ULONG PageFileIndex; + ULONG_PTR PageFileOffset; + PPAGINGFILE PageFile; + + /* We must hold the PFN lock */ + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + for (PageFileIndex = 0; PageFileIndex < MAX_PAGING_FILES; PageFileIndex++) + { + if (PagingFileList[PageFileIndex] == NULL) + continue; + if (PagingFileList[PageFileIndex]->FreePages != 0) + break; + } + + if (PageFileIndex == MAX_PAGING_FILES) + { + DPRINT1("Pagefiles are full!\n"); + return STATUS_PAGEFILE_QUOTA; + } + + PointerPte->u.Long = 0; + + PageFile = PagingFileList[PageFileIndex]; + + /* And also lock this page file */ + KeAcquireSpinLockAtDpcLevel(&PageFile->AllocMapLock); + + /* Find a free entry in this page file */ + PageFileOffset = RtlFindClearBitsAndSet(&PageFile->AllocBitMap, 1, PageFile->AllocBitMapHint); + + /* We must have found something as the file was marked as having free entries */ + ASSERT(PageFileOffset != ~((ULONG_PTR)0)); + PageFile->AllocBitMapHint = PageFileOffset & ~((PageFile->MaximumSize / PAGE_SIZE) - 1); + + DPRINT1("Reserving page file entry %lu, %Iu\n", PageFileIndex, PageFileOffset); + + /* Done */ + KeReleaseSpinLockFromDpcLevel(&PageFile->AllocMapLock); + + /* We rely on PageFileHigh being != 0 */ + PointerPte->u.Soft.PageFileHigh = PageFileOffset + 1; + PointerPte->u.Soft.PageFileLow = PageFileIndex; + + return STATUS_SUCCESS; +} + VOID INIT_FUNCTION NTAPI MmInitPagingFile(VOID) { ULONG i; - - KeInitializeSpinLock(&PagingFileListLock);
MiFreeSwapPages = 0; MiUsedSwapPages = 0; @@ -360,104 +491,41 @@ MmNumberOfPagingFiles = 0; }
-static ULONG -MiAllocPageFromPagingFile(PPAGINGFILE PagingFile) -{ - KIRQL oldIrql; - ULONG i, j; - - KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql); - - for (i = 0; i < PagingFile->AllocMapSize; i++) - { - for (j = 0; j < 32; j++) - { - if (!(PagingFile->AllocMap[i] & (1 << j))) - { - PagingFile->AllocMap[i] |= (1 << j); - PagingFile->UsedPages++; - PagingFile->FreePages--; - KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql); - return((i * 32) + j); - } - } - } - - KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql); - return(0xFFFFFFFF); -} - VOID NTAPI MmFreeSwapPage(SWAPENTRY Entry) { - ULONG i; - ULONG_PTR off; - KIRQL oldIrql; - - i = FILE_FROM_ENTRY(Entry); - off = OFFSET_FROM_ENTRY(Entry); - - KeAcquireSpinLock(&PagingFileListLock, &oldIrql); - if (PagingFileList[i] == NULL) - { - KeBugCheck(MEMORY_MANAGEMENT); - } - KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock); - - PagingFileList[i]->AllocMap[off >> 5] &= (~(1 << (off % 32))); - - PagingFileList[i]->FreePages++; - PagingFileList[i]->UsedPages--; - - MiFreeSwapPages++; - MiUsedSwapPages--; - - KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock); - KeReleaseSpinLock(&PagingFileListLock, oldIrql); + MMPTE TempPte; + KIRQL OldIrql; + + TempPte.u.Long = 0; + TempPte.u.Soft.PageFileLow = FILE_FROM_ENTRY(Entry); + TempPte.u.Soft.PageFileHigh = OFFSET_FROM_ENTRY(Entry); + + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + MiFreePageFileEntry(&TempPte); + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); }
SWAPENTRY NTAPI MmAllocSwapPage(VOID) { - KIRQL oldIrql; - ULONG i; - ULONG off; - SWAPENTRY entry; - - KeAcquireSpinLock(&PagingFileListLock, &oldIrql); - - if (MiFreeSwapPages == 0) - { - KeReleaseSpinLock(&PagingFileListLock, oldIrql); - return(0); - } - - for (i = 0; i < MAX_PAGING_FILES; i++) - { - if (PagingFileList[i] != NULL && - PagingFileList[i]->FreePages >= 1) - { - off = MiAllocPageFromPagingFile(PagingFileList[i]); - if (off == 0xFFFFFFFF) - { - KeBugCheck(MEMORY_MANAGEMENT); - KeReleaseSpinLock(&PagingFileListLock, oldIrql); - return(STATUS_UNSUCCESSFUL); - } - MiUsedSwapPages++; - MiFreeSwapPages--; - KeReleaseSpinLock(&PagingFileListLock, oldIrql); - - entry = ENTRY_FROM_FILE_OFFSET(i, off); - return(entry); - } - } - - KeReleaseSpinLock(&PagingFileListLock, oldIrql); - KeBugCheck(MEMORY_MANAGEMENT); - return(0); + MMPTE TempPte; + SWAPENTRY entry; + NTSTATUS Status; + KIRQL OldIrql; + + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + Status = MiReservePageFileEntry(&TempPte); + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + if (!NT_SUCCESS(Status)) + return 0; + + entry = ENTRY_FROM_FILE_OFFSET(TempPte.u.Soft.PageFileLow, TempPte.u.Soft.PageFileHigh); + + return entry; }
static PRETRIEVEL_DESCRIPTOR_LIST FASTCALL @@ -488,7 +556,7 @@ IO_STATUS_BLOCK IoStatus; PFILE_OBJECT FileObject; PPAGINGFILE PagingFile; - KIRQL oldIrql; + KIRQL OldIrql; ULONG AllocMapSize; FILE_FS_SIZE_INFORMATION FsSizeInformation; PRETRIEVEL_DESCRIPTOR_LIST RetDescList; @@ -646,7 +714,6 @@ #if defined(__GNUC__) Vcn.QuadPart = 0LL; #else - Vcn.QuadPart = 0; #endif
@@ -718,16 +785,15 @@ RtlZeroMemory(PagingFile, sizeof(*PagingFile));
PagingFile->FileObject = FileObject; - PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart; - PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart; - PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE); + PagingFile->MaximumSize = SafeMaximumSize.LowPart; + PagingFile->CurrentSize = SafeInitialSize.LowPart; + PagingFile->FreePages = (ULONG)(SafeMaximumSize.LowPart / PAGE_SIZE); PagingFile->UsedPages = 0; KeInitializeSpinLock(&PagingFile->AllocMapLock);
AllocMapSize = (PagingFile->FreePages / 32) + 1; PagingFile->AllocMap = ExAllocatePool(NonPagedPool, AllocMapSize * sizeof(ULONG)); - PagingFile->AllocMapSize = AllocMapSize;
if (PagingFile->AllocMap == NULL) { @@ -760,8 +826,10 @@ return(STATUS_NO_MEMORY); }
- RtlZeroMemory(PagingFile->AllocMap, AllocMapSize * sizeof(ULONG)); RtlZeroMemory(PagingFile->RetrievalPointers, Size); + + RtlInitializeBitMap(&PagingFile->AllocBitMap, PagingFile->AllocMap, AllocMapSize * 32); + RtlClearAllBits(&PagingFile->AllocBitMap);
Count = 0; PagingFile->RetrievalPointers->ExtentCount = ExtentCount; @@ -799,7 +867,7 @@ PagingFile->RetrievalPointers->Extents[i].NextVcn.QuadPart *= BytesPerAllocationUnit; }
- KeAcquireSpinLock(&PagingFileListLock, &oldIrql); + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); for (i = 0; i < MAX_PAGING_FILES; i++) { if (PagingFileList[i] == NULL) @@ -810,7 +878,7 @@ } MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages; MmNumberOfPagingFiles++; - KeReleaseSpinLock(&PagingFileListLock, oldIrql); + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
ZwClose(FileHandle);