Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417/* * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section) * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * PROJECT: ReactOS kernel * FILE: ntoskrnl/cache/section/sptab.c * PURPOSE: Section object page tables * * PROGRAMMERS: arty */
/*
This file implements the section page table. It relies on rtl generic tablefunctionality to provide access to 256-page chunks. Calls toMiSetPageEntrySectionSegment and MiGetPageEntrySectionSegment must besynchronized by holding the segment lock.
Each page table entry is a ULONG as in x86.
Bit 1 is used as a swap entry indication as in the main page table.Bit 2 is used as a dirty indication. A dirty page will eventually be writtenback to the file.Bits 3-11 are used as a map count in the legacy mm code, Note that zero isillegal, as the legacy code does not take advantage of segment rmaps.Therefore, every segment page is mapped in at least one address space, andMmUnsharePageEntry is quite complicated. In addition, the page may also beowned by the legacy cache manager, giving an implied additional reference.Upper bits are a PFN_NUMBER.
These functions, in addition to maintaining the segment page table alsoautomatically maintain the segment rmap by calling MmSetSectionAssociationand MmDeleteSectionAssociation. Segment rmaps are discussed in rmap.c. Theupshot is that it is impossible to have a page properly registered in a segmentpage table and not also found in a segment rmap that can be found from thepaging machinery.
*/
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>#include "newmm.h"#define NDEBUG#include <debug.h>
#define DPRINTC DPRINT
/* TYPES *********************************************************************/
extern KSPIN_LOCK MiSectionPageTableLock;
_Function_class_(RTL_GENERIC_ALLOCATE_ROUTINE)staticPVOIDNTAPIMiSectionPageTableAllocate(PRTL_GENERIC_TABLE Table, CLONG Bytes){ PVOID Result; Result = ExAllocatePoolWithTag(NonPagedPool, Bytes, 'tPmM'); //DPRINT("MiSectionPageTableAllocate(%d) => %p\n", Bytes, Result); return Result;}
_Function_class_(RTL_GENERIC_FREE_ROUTINE)staticVOIDNTAPIMiSectionPageTableFree(PRTL_GENERIC_TABLE Table, PVOID Data){ //DPRINT("MiSectionPageTableFree(%p)\n", Data); ExFreePoolWithTag(Data, 'tPmM');}
_Function_class_(RTL_GENERIC_COMPARE_ROUTINE)staticRTL_GENERIC_COMPARE_RESULTSNTAPIMiSectionPageTableCompare(PRTL_GENERIC_TABLE Table, PVOID PtrA, PVOID PtrB){ PLARGE_INTEGER A = PtrA, B = PtrB; BOOLEAN Result = (A->QuadPart < B->QuadPart) ? GenericLessThan : (A->QuadPart == B->QuadPart) ? GenericEqual : GenericGreaterThan;
#if 0 DPRINT ("Compare: %08x%08x vs %08x%08x => %s\n", A->u.HighPart, A->u.LowPart, B->u.HighPart, B->u.LowPart, Result == GenericLessThan ? "GenericLessThan" : Result == GenericGreaterThan ? "GenericGreaterThan" : "GenericEqual");#endif
return Result;}
staticPCACHE_SECTION_PAGE_TABLENTAPIMiSectionPageTableGet(PRTL_GENERIC_TABLE Table, PLARGE_INTEGER FileOffset){ LARGE_INTEGER SearchFileOffset; PCACHE_SECTION_PAGE_TABLE PageTable; SearchFileOffset.QuadPart = ROUND_DOWN(FileOffset->QuadPart, ENTRIES_PER_ELEMENT * PAGE_SIZE); PageTable = RtlLookupElementGenericTable(Table, &SearchFileOffset);
DPRINT("MiSectionPageTableGet(%p,%I64x)\n", Table, FileOffset->QuadPart);
return PageTable;}
staticPCACHE_SECTION_PAGE_TABLENTAPIMiSectionPageTableGetOrAllocate(PRTL_GENERIC_TABLE Table, PLARGE_INTEGER FileOffset){ LARGE_INTEGER SearchFileOffset; CACHE_SECTION_PAGE_TABLE SectionZeroPageTable; PCACHE_SECTION_PAGE_TABLE PageTableSlice = MiSectionPageTableGet(Table, FileOffset); /* Please zero memory when taking away zero initialization. */ RtlZeroMemory(&SectionZeroPageTable, sizeof(CACHE_SECTION_PAGE_TABLE)); if (!PageTableSlice) { SearchFileOffset.QuadPart = ROUND_DOWN(FileOffset->QuadPart, ENTRIES_PER_ELEMENT * PAGE_SIZE); SectionZeroPageTable.FileOffset = SearchFileOffset; SectionZeroPageTable.Refcount = 1; PageTableSlice = RtlInsertElementGenericTable(Table, &SectionZeroPageTable, sizeof(SectionZeroPageTable), NULL); if (!PageTableSlice) return NULL; DPRINT("Allocate page table %p (%I64x)\n", PageTableSlice, PageTableSlice->FileOffset.QuadPart); } return PageTableSlice;}
VOIDNTAPIMiInitializeSectionPageTable(PMM_SECTION_SEGMENT Segment){ RtlInitializeGenericTable(&Segment->PageTable, MiSectionPageTableCompare, MiSectionPageTableAllocate, MiSectionPageTableFree, NULL);
DPRINT("MiInitializeSectionPageTable(%p)\n", &Segment->PageTable);}
NTSTATUSNTAPI_MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset, ULONG_PTR Entry, const char *file, int line){ ULONG_PTR PageIndex, OldEntry; PCACHE_SECTION_PAGE_TABLE PageTable;
ASSERT(Segment->Locked); ASSERT(!IS_SWAP_FROM_SSE(Entry) || !IS_DIRTY_SSE(Entry));
PageTable = MiSectionPageTableGetOrAllocate(&Segment->PageTable, Offset);
if (!PageTable) return STATUS_NO_MEMORY;
ASSERT(MiSectionPageTableGet(&Segment->PageTable, Offset));
PageTable->Segment = Segment; PageIndex = (ULONG_PTR)((Offset->QuadPart - PageTable->FileOffset.QuadPart) / PAGE_SIZE); OldEntry = PageTable->PageEntries[PageIndex];
DPRINT("MiSetPageEntrySectionSegment(%p,%08x%08x,%x=>%x)\n", Segment, Offset->u.HighPart, Offset->u.LowPart, OldEntry, Entry);
/* Manage ref on segment */ if (Entry && !OldEntry) { InterlockedIncrement64(Segment->ReferenceCount); } if (OldEntry && !Entry) { MmDereferenceSegment(Segment); }
if (Entry && !IS_SWAP_FROM_SSE(Entry)) { /* We have a valid entry. See if we must do something */ if (OldEntry && !IS_SWAP_FROM_SSE(OldEntry)) { /* The previous entry was valid. Shall we swap the Rmaps ? */ if (PFN_FROM_SSE(Entry) != PFN_FROM_SSE(OldEntry)) { MmDeleteSectionAssociation(PFN_FROM_SSE(OldEntry));
/* This has to be done before setting the new section association to prevent a race condition with the paging out path */ PageTable->PageEntries[PageIndex] = Entry;
MmSetSectionAssociation(PFN_FROM_SSE(Entry), Segment, Offset); } else { PageTable->PageEntries[PageIndex] = Entry; } } else { /* * We're switching to a valid entry from an invalid one. * Add the Rmap and take a ref on the segment. */ PageTable->PageEntries[PageIndex] = Entry; MmSetSectionAssociation(PFN_FROM_SSE(Entry), Segment, Offset);
if (Offset->QuadPart >= (Segment->LastPage << PAGE_SHIFT)) Segment->LastPage = (Offset->QuadPart >> PAGE_SHIFT) + 1; } } else if (OldEntry && !IS_SWAP_FROM_SSE(OldEntry)) { /* We're switching to an invalid entry from a valid one */ MmDeleteSectionAssociation(PFN_FROM_SSE(OldEntry)); PageTable->PageEntries[PageIndex] = Entry;
if (Offset->QuadPart == ((Segment->LastPage - 1ULL) << PAGE_SHIFT)) { /* We are unsetting the last page */ while (--Segment->LastPage) { LARGE_INTEGER CheckOffset; CheckOffset.QuadPart = (Segment->LastPage - 1) << PAGE_SHIFT; ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &CheckOffset); if ((Entry != 0) && !IS_SWAP_FROM_SSE(Entry)) break; } } } else { PageTable->PageEntries[PageIndex] = Entry; }
return STATUS_SUCCESS;}
ULONG_PTRNTAPI_MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset, const char *file, int line){ LARGE_INTEGER FileOffset; ULONG_PTR PageIndex, Result; PCACHE_SECTION_PAGE_TABLE PageTable;
ASSERT(Segment->Locked); FileOffset.QuadPart = ROUND_DOWN(Offset->QuadPart, ENTRIES_PER_ELEMENT * PAGE_SIZE); PageTable = MiSectionPageTableGet(&Segment->PageTable, &FileOffset); if (!PageTable) return 0; PageIndex = (ULONG_PTR)((Offset->QuadPart - PageTable->FileOffset.QuadPart) / PAGE_SIZE); Result = PageTable->PageEntries[PageIndex];#if 0 DPRINTC ("MiGetPageEntrySectionSegment(%p,%08x%08x) => %x %s:%d\n", Segment, FileOffset.u.HighPart, FileOffset.u.LowPart + PageIndex * PAGE_SIZE, Result, file, line);#endif return Result;}
/*
Destroy the rtl generic table that serves as the section's page table. Callthe FreePage function for each non-zero entry in the section page table aswe go. Note that the page table is still techinally valid until after allpages are destroyed, as we don't finally destroy the table until we've freeeach slice. There is no order guarantee for deletion of individual elementsalthough it's in-order as written now.
*/
VOIDNTAPIMmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment, FREE_SECTION_PAGE_FUN FreePage){ PCACHE_SECTION_PAGE_TABLE Element; DPRINT("MiFreePageTablesSectionSegment(%p)\n", &Segment->PageTable); while ((Element = RtlGetElementGenericTable(&Segment->PageTable, 0))) { DPRINT("Delete table for <%wZ> %p -> %I64x\n", Segment->FileObject ? &Segment->FileObject->FileName : NULL, Segment, Element->FileOffset.QuadPart); if (FreePage) { ULONG i; for (i = 0; i < ENTRIES_PER_ELEMENT; i++) { ULONG_PTR Entry; LARGE_INTEGER Offset; Offset.QuadPart = Element->FileOffset.QuadPart + i * PAGE_SIZE; Entry = Element->PageEntries[i]; if (Entry && !IS_SWAP_FROM_SSE(Entry)) { DPRINT("Freeing page %p:%Ix @ %I64x\n", Segment, Entry, Offset.QuadPart);
FreePage(Segment, &Offset); } } } DPRINT("Remove memory\n"); RtlDeleteElementGenericTable(&Segment->PageTable, Element); } DPRINT("Done\n");}
/*
Retrieves the MM_SECTION_SEGMENT and fills in the LARGE_INTEGER Offset givenby the caller that corresponds to the page specified. This usesMmGetSegmentRmap to find the rmap belonging to the segment itself, and usesthe result as a pointer to a 256-entry page table structure. The rmap alsoincludes 8 bits of offset information indication one of 256 page entries thatthe rmap corresponds to. This information together gives us an exact offsetinto the file, as well as the MM_SECTION_SEGMENT pointer stored in the pagetable slice.
NULL is returned is there is no segment rmap for the page.
*/
PMM_SECTION_SEGMENTNTAPIMmGetSectionAssociation(PFN_NUMBER Page, PLARGE_INTEGER Offset){ ULONG RawOffset; PMM_SECTION_SEGMENT Segment = NULL; PCACHE_SECTION_PAGE_TABLE PageTable;
KIRQL OldIrql = MiAcquirePfnLock();
PageTable = MmGetSegmentRmap(Page, &RawOffset); if (PageTable) { Segment = PageTable->Segment; Offset->QuadPart = PageTable->FileOffset.QuadPart + ((ULONG64)RawOffset << PAGE_SHIFT); ASSERT(PFN_FROM_SSE(PageTable->PageEntries[RawOffset]) == Page); InterlockedIncrement64(Segment->ReferenceCount); }
MiReleasePfnLock(OldIrql);
return Segment;}
NTSTATUSNTAPIMmSetSectionAssociation(PFN_NUMBER Page, PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset){ PCACHE_SECTION_PAGE_TABLE PageTable; ULONG ActualOffset;
PageTable = MiSectionPageTableGet(&Segment->PageTable, Offset); ASSERT(PageTable);
ActualOffset = (ULONG)(Offset->QuadPart - PageTable->FileOffset.QuadPart); MmInsertRmap(Page, (PEPROCESS)PageTable, (PVOID)(RMAP_SEGMENT_MASK | (ActualOffset >> PAGE_SHIFT)));
return STATUS_SUCCESS;}