https://git.reactos.org/?p=reactos.git;a=commitdiff;h=589016ddb9b818345f8a1e...
commit 589016ddb9b818345f8a1e730685957f2d137a98 Author: Jérôme Gardou jerome.gardou@reactos.org AuthorDate: Fri Aug 6 18:14:10 2021 +0200 Commit: Jérôme Gardou zefklop@users.noreply.github.com CommitDate: Sat Aug 7 09:34:58 2021 +0200
[NTOS:MM] Implement MmFlushImageSection(MmFlushForWrite) --- ntoskrnl/mm/section.c | 155 +++++++++++++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 60 deletions(-)
diff --git a/ntoskrnl/mm/section.c b/ntoskrnl/mm/section.c index c4b89125f06..e106e0a66d1 100644 --- a/ntoskrnl/mm/section.c +++ b/ntoskrnl/mm/section.c @@ -1011,11 +1011,11 @@ MmDereferenceSegmentWithLock(PMM_SECTION_SEGMENT Segment, KIRQL OldIrql) }
*Segment->Flags |= MM_SEGMENT_INDELETE; - MiReleasePfnLock(OldIrql);
/* Flush the segment */ if (*Segment->Flags & MM_DATAFILE_SEGMENT) { + MiReleasePfnLock(OldIrql); /* Free the page table. This will flush any remaining dirty data */ MmFreePageTablesSectionSegment(Segment, FreeSegmentPage);
@@ -1036,7 +1036,6 @@ MmDereferenceSegmentWithLock(PMM_SECTION_SEGMENT Segment, KIRQL OldIrql) ULONG NrSegments; ULONG i;
- OldIrql = MiAcquirePfnLock(); /* Delete the pointer on the file */ ASSERT(ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject == ImageSectionObject); ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject = NULL; @@ -1098,6 +1097,7 @@ MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea, ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset); PFN_NUMBER Page = PFN_FROM_SSE(Entry); BOOLEAN IsDataMap = BooleanFlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT); + SWAPENTRY SwapEntry;
if (Entry == 0) { @@ -1134,12 +1134,12 @@ MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea, return FALSE; }
- if (!BooleanFlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED)) + if (!FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED)) { - /* So this must have been a read-only page. Keep it ! */ ASSERT(Segment->WriteCopy); ASSERT(!IS_DIRTY_SSE(Entry)); ASSERT(MmGetSavedSwapEntryPage(Page) == 0); + /* So this must have been a read-only page. Keep it ! */ MmSetPageEntrySectionSegment(Segment, Offset, Entry); return FALSE; } @@ -1148,7 +1148,7 @@ MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea, * So this is a page for a shared section of a DLL. * We can keep it if it is not dirty. */ - SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page); + SwapEntry = MmGetSavedSwapEntryPage(Page); if ((SwapEntry == 0) && !IS_DIRTY_SSE(Entry)) { MmSetPageEntrySectionSegment(Segment, Offset, Entry); @@ -1194,7 +1194,8 @@ MmMakeSegmentResident( _In_ PMM_SECTION_SEGMENT Segment, _In_ LONGLONG Offset, _In_ ULONG Length, - _In_opt_ PLARGE_INTEGER ValidDataLength) + _In_opt_ PLARGE_INTEGER ValidDataLength, + _In_ BOOLEAN SetDirty) { /* Let's use a 64K granularity. */ LONGLONG RangeStart, RangeEnd; @@ -1261,7 +1262,9 @@ MmMakeSegmentResident(
if (Entry != 0) { - /* There is a page here. Or a swap entry. Or whatever... */ + /* Dirtify it if it's a resident page and we're asked to */ + if (SetDirty && !IS_SWAP_FROM_SSE(Entry)) + MmSetPageEntrySectionSegment(Segment, &CurrentOffset, DIRTY_SSE(Entry)); continue; }
@@ -1409,12 +1412,16 @@ AssignPagesToSegment:
for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++) { + ULONG_PTR Entry = MAKE_SSE(Pages[i] << PAGE_SHIFT, 0); LARGE_INTEGER CurrentOffset; CurrentOffset.QuadPart = ChunkOffset + (i * PAGE_SIZE);
ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
- MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SSE(Pages[i] << PAGE_SHIFT, 0)); + if (SetDirty) + Entry = DIRTY_SSE(Entry); + + MmSetPageEntrySectionSegment(Segment, &CurrentOffset, Entry); }
MmUnlockSectionSegment(Segment); @@ -1755,7 +1762,7 @@ MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext;
- Status = MmMakeSegmentResident(Segment, Offset.QuadPart, PAGE_SIZE, &FcbHeader->ValidDataLength); + Status = MmMakeSegmentResident(Segment, Offset.QuadPart, PAGE_SIZE, &FcbHeader->ValidDataLength, FALSE);
FsRtlReleaseFile(Segment->FileObject);
@@ -3621,6 +3628,7 @@ MiRosUnmapViewOfSection(IN PEPROCESS Process, ASSERT(NT_SUCCESS(Status)); } } + DPRINT("One mapping less for %p\n", ImageSectionObject->FileObject->SectionObjectPointer); InterlockedDecrement(&ImageSectionObject->MapCount); } else @@ -4046,6 +4054,8 @@ MmMapViewOfSection(IN PVOID SectionObject, *BaseAddress = (PVOID)ImageBase; *ViewSize = ImageSize;
+ DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer); + /* One more map */ InterlockedIncrement(&ImageSectionObject->MapCount); } @@ -4184,6 +4194,50 @@ MmCanFileBeTruncated (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, return Ret; }
+static +BOOLEAN +MiPurgeImageSegment(PMM_SECTION_SEGMENT Segment) +{ + PCACHE_SECTION_PAGE_TABLE PageTable; + + MmLockSectionSegment(Segment); + + /* Loop over all entries */ + for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE); + PageTable != NULL; + PageTable = RtlEnumerateGenericTable(&Segment->PageTable, FALSE)) + { + for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++) + { + ULONG_PTR Entry = PageTable->PageEntries[i]; + LARGE_INTEGER Offset; + + if (!Entry) + continue; + + if (IS_SWAP_FROM_SSE(Entry) || (SHARE_COUNT_FROM_SSE(Entry) > 0)) + { + /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */ + MmUnlockSectionSegment(Segment); + return FALSE; + } + + /* Regular entry */ + ASSERT(!IS_WRITE_SSE(Entry)); + ASSERT(MmGetSavedSwapEntryPage(PFN_FROM_SSE(Entry)) == 0); + + /* Properly remove using the used API */ + Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT); + MmSetPageEntrySectionSegment(Segment, &Offset, 0); + MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry)); + } + } + + MmUnlockSectionSegment(Segment); + + return TRUE; +} + /* * @implemented */ @@ -4194,12 +4248,34 @@ MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, switch(FlushType) { case MmFlushForDelete: + { + /* + * FIXME: Check for outstanding write probes on Data section. + * How do we do that ? + */ + } + /* Fall-through */ + case MmFlushForWrite: { KIRQL OldIrql = MiAcquirePfnLock(); PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject;
- if (!ImageSectionObject || (ImageSectionObject->SegFlags & MM_SEGMENT_INDELETE)) + DPRINT("Deleting or modifying %p\n", SectionObjectPointer); + + /* Wait for concurrent creation or deletion of image to be done */ + ImageSectionObject = SectionObjectPointer->ImageSectionObject; + while (ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE))) { + MiReleasePfnLock(OldIrql); + KeDelayExecutionThread(KernelMode, FALSE, &TinyTime); + OldIrql = MiAcquirePfnLock(); + ImageSectionObject = SectionObjectPointer->ImageSectionObject; + } + + if (!ImageSectionObject) + { + DPRINT("No image section object. Accepting\n"); + /* Nothing to do */ MiReleasePfnLock(OldIrql); return TRUE; } @@ -4209,6 +4285,7 @@ MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, { /* We do. No way to delete it */ MiReleasePfnLock(OldIrql); + DPRINT("Denying. There are mappings open\n"); return FALSE; }
@@ -4217,39 +4294,12 @@ MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, InterlockedIncrement64(&ImageSectionObject->RefCount); MiReleasePfnLock(OldIrql);
+ DPRINT("Purging\n"); + for (ULONG i = 0; i < ImageSectionObject->NrSegments; i++) { - PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i]; - LONGLONG Length; - - MmLockSectionSegment(Segment); - /* Loop over all entries */ - LARGE_INTEGER Offset; - Offset.QuadPart = 0; - - Length = Segment->Length.QuadPart; - if (Length < Segment->RawLength.QuadPart) - Length = Segment->RawLength.QuadPart; - - while (Offset.QuadPart < Length) - { - ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &Offset); - - /* Shared data must already be discarded, and nobody should be reading it. */ - ASSERT(!IS_SWAP_FROM_SSE(Entry)); - if (Entry != 0) - { - DPRINT1("Freeing page %lx for image section %p\n", PFN_FROM_SSE(Entry), ImageSectionObject); - /* Release the page */ - ASSERT(SHARE_COUNT_FROM_SSE(Entry) == 0); - ASSERT(!IS_WRITE_SSE(Entry)); - ASSERT(MmGetSavedSwapEntryPage(PFN_FROM_SSE(Entry)) == 0); - MmSetPageEntrySectionSegment(Segment, &Offset, 0); - MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry)); - } - Offset.QuadPart += PAGE_SIZE; - } - MmUnlockSectionSegment(Segment); + if (!MiPurgeImageSegment(&ImageSectionObject->Segments[i])) + break; }
/* Grab lock again */ @@ -4274,21 +4324,6 @@ MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer, MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql); return TRUE; } - case MmFlushForWrite: - { - BOOLEAN Ret = TRUE; - KIRQL OldIrql = MiAcquirePfnLock(); - - if (SectionObjectPointer->ImageSectionObject) - { - PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject; - if (!(ImageSectionObject->SegFlags & MM_SEGMENT_INDELETE)) - Ret = FALSE; - } - - MiReleasePfnLock(OldIrql); - return Ret; - } } return FALSE; } @@ -4782,7 +4817,7 @@ MmMakeDataSectionResident( /* There must be a segment for this call */ ASSERT(Segment);
- NTSTATUS Status = MmMakeSegmentResident(Segment, Offset, Length, ValidDataLength); + NTSTATUS Status = MmMakeSegmentResident(Segment, Offset, Length, ValidDataLength, FALSE);
MmDereferenceSegment(Segment);
@@ -4906,8 +4941,8 @@ MmCheckDirtySegment( * We got a dirty entry. This path is for the shared data, * be-it regular file maps or shared sections of DLLs */ - ASSERT(!Segment->WriteCopy); - ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT) || FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED)); + ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT) || + FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
/* Insert the cleaned entry back. Mark it as write in progress, and clear the dirty bit. */ Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1); @@ -4936,7 +4971,7 @@ MmCheckDirtySegment( ASSERT(PageOut);
/* And this must be for a shared section in a DLL */ - ASSERT(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED); + ASSERT(FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page); if (!SwapEntry)