xref: /reactos/ntoskrnl/cache/section/data.c (revision 4561998a)
1 /*
2  * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  *
19  * PROJECT:         ReactOS kernel
20  * FILE:            ntoskrnl/cache/section/data.c
21  * PURPOSE:         Implements section objects
22  *
23  * PROGRAMMERS:     Rex Jolliff
24  *                  David Welch
25  *                  Eric Kohl
26  *                  Emanuele Aliberti
27  *                  Eugene Ingerman
28  *                  Casper Hornstrup
29  *                  KJK::Hyperion
30  *                  Guido de Jong
31  *                  Ge van Geldorp
32  *                  Royce Mitchell III
33  *                  Filip Navara
34  *                  Aleksey Bragin
35  *                  Jason Filby
36  *                  Thomas Weidenmueller
37  *                  Gunnar Andre' Dalsnes
38  *                  Mike Nordell
39  *                  Alex Ionescu
40  *                  Gregor Anich
41  *                  Steven Edwards
42  *                  Herve Poussineau
43  */
44 
45 /*
46 
47 A note on this code:
48 
49 Unlike the previous section code, this code does not rely on an active map
50 for a page to exist in a data segment.  Each mapping contains a large integer
51 offset to map at, and the segment always represents the entire section space
52 from zero to the maximum long long.  This allows us to associate one single
53 page map with each file object, and to let each mapping view an offset into
54 the overall mapped file.  Temporarily unmapping the file has no effect on the
55 section membership.
56 
57 This necessitates a change in the section page table implementation, which is
58 now an RtlGenericTable.  This will be elaborated more in sptab.c.  One upshot
59 of this change is that a mapping of a small files takes a bit more than 1/4
60 of the size in nonpaged kernel space as it did previously.
61 
62 When we need other threads that may be competing for the same page fault to
63 wait, we have a mechanism seperate from PageOps for dealing with that, which
64 was suggested by Travis Geiselbrecht after a conversation I had with Alex
65 Ionescu.  That mechanism is the MM_WAIT_ENTRY, which is the all-ones SWAPENTRY.
66 
67 When we wish for other threads to know that we're waiting and will finish
68 handling a page fault, we place the swap entry MM_WAIT_ENTRY in the page table
69 at the fault address (this works on either the section page table or a process
70 address space), perform any blocking operations required, then replace the
71 entry.
72 
73 */
74 
75 /* INCLUDES *****************************************************************/
76 
77 #include <ntoskrnl.h>
78 #include "newmm.h"
79 #include <cache/newcc.h>
80 #define NDEBUG
81 #include <debug.h>
82 #include <mm/ARM3/miarm.h>
83 
84 #define DPRINTC DPRINT
85 
86 LIST_ENTRY MiSegmentList;
87 
88 extern KEVENT MpwThreadEvent;
89 extern KSPIN_LOCK MiSectionPageTableLock;
90 extern PMMWSL MmWorkingSetList;
91 
92 /* FUNCTIONS *****************************************************************/
93 
94 /* Note: Mmsp prefix denotes "Memory Manager Section Private". */
95 
96 VOID
97 NTAPI
98 _MmLockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
99 {
100     //DPRINT("MmLockSectionSegment(%p,%s:%d)\n", Segment, file, line);
101     ExAcquireFastMutex(&Segment->Lock);
102     Segment->Locked = TRUE;
103 }
104 
105 VOID
106 NTAPI
107 _MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
108 {
109     ASSERT(Segment->Locked);
110     Segment->Locked = FALSE;
111     ExReleaseFastMutex(&Segment->Lock);
112     //DPRINT("MmUnlockSectionSegment(%p,%s:%d)\n", Segment, file, line);
113 }
114 
115 /*
116 
117 MiFlushMappedSection
118 
119 Called from cache code to cause dirty pages of a section
120 to be written back.  This doesn't affect the mapping.
121 
122 BaseOffset is the base at which to start writing in file space.
123 FileSize is the length of the file as understood by the cache.
124 
125  */
126 NTSTATUS
127 NTAPI
128 _MiFlushMappedSection(PVOID BaseAddress,
129                       PLARGE_INTEGER BaseOffset,
130                       PLARGE_INTEGER FileSize,
131                       BOOLEAN WriteData,
132                       const char *File,
133                       int Line)
134 {
135     NTSTATUS Status = STATUS_SUCCESS;
136     ULONG_PTR PageAddress;
137     PMMSUPPORT AddressSpace = MmGetKernelAddressSpace();
138     PMEMORY_AREA MemoryArea;
139     PMM_SECTION_SEGMENT Segment;
140     ULONG_PTR BeginningAddress, EndingAddress;
141     LARGE_INTEGER ViewOffset;
142     LARGE_INTEGER FileOffset;
143     PFN_NUMBER Page;
144     PPFN_NUMBER Pages;
145     KIRQL OldIrql;
146 
147     DPRINT("MiFlushMappedSection(%p,%I64x,%I64x,%u,%s:%d)\n",
148            BaseAddress,
149            BaseOffset->QuadPart,
150            FileSize ? FileSize->QuadPart : 0,
151            WriteData,
152            File,
153            Line);
154 
155     MmLockAddressSpace(AddressSpace);
156     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
157     if (!MemoryArea || MemoryArea->Type != MEMORY_AREA_CACHE || MemoryArea->DeleteInProgress)
158     {
159         MmUnlockAddressSpace(AddressSpace);
160         DPRINT("STATUS_NOT_MAPPED_DATA\n");
161         return STATUS_NOT_MAPPED_DATA;
162     }
163     BeginningAddress = PAGE_ROUND_DOWN(MA_GetStartingAddress(MemoryArea));
164     EndingAddress = PAGE_ROUND_UP(MA_GetEndingAddress(MemoryArea));
165     Segment = MemoryArea->Data.SectionData.Segment;
166     ViewOffset.QuadPart = MemoryArea->Data.SectionData.ViewOffset.QuadPart;
167 
168     ASSERT(ViewOffset.QuadPart == BaseOffset->QuadPart);
169 
170     MmLockSectionSegment(Segment);
171 
172     Pages = ExAllocatePool(NonPagedPool,
173                            sizeof(PFN_NUMBER) * ((EndingAddress - BeginningAddress) >> PAGE_SHIFT));
174 
175     if (!Pages)
176     {
177         ASSERT(FALSE);
178     }
179 
180     //DPRINT("Getting pages in range %08x-%08x\n", BeginningAddress, EndingAddress);
181 
182     for (PageAddress = BeginningAddress;
183          PageAddress < EndingAddress;
184          PageAddress += PAGE_SIZE)
185     {
186         ULONG_PTR Entry;
187         FileOffset.QuadPart = ViewOffset.QuadPart + PageAddress - BeginningAddress;
188         Entry = MmGetPageEntrySectionSegment(MemoryArea->Data.SectionData.Segment,
189                                              &FileOffset);
190         Page = PFN_FROM_SSE(Entry);
191         if (Entry != 0 && !IS_SWAP_FROM_SSE(Entry) &&
192             (MmIsDirtyPageRmap(Page) || IS_DIRTY_SSE(Entry)) &&
193             FileOffset.QuadPart < FileSize->QuadPart)
194         {
195             OldIrql = MiAcquirePfnLock();
196             MmReferencePage(Page);
197             MiReleasePfnLock(OldIrql);
198             Pages[(PageAddress - BeginningAddress) >> PAGE_SHIFT] = Entry;
199         }
200         else
201         {
202             Pages[(PageAddress - BeginningAddress) >> PAGE_SHIFT] = 0;
203         }
204     }
205 
206     MmUnlockSectionSegment(Segment);
207     MmUnlockAddressSpace(AddressSpace);
208 
209     for (PageAddress = BeginningAddress;
210          PageAddress < EndingAddress;
211          PageAddress += PAGE_SIZE)
212     {
213         ULONG_PTR Entry;
214         FileOffset.QuadPart = ViewOffset.QuadPart + PageAddress - BeginningAddress;
215         Entry = Pages[(PageAddress - BeginningAddress) >> PAGE_SHIFT];
216         Page = PFN_FROM_SSE(Entry);
217         if (Page)
218         {
219             if (WriteData) {
220                 //DPRINT("MiWriteBackPage(%wZ,addr %x,%08x%08x)\n", &Segment->FileObject->FileName, PageAddress, FileOffset.u.HighPart, FileOffset.u.LowPart);
221                 Status = MiWriteBackPage(Segment->FileObject, &FileOffset, PAGE_SIZE, Page);
222             } else
223                 Status = STATUS_SUCCESS;
224 
225             if (NT_SUCCESS(Status)) {
226                 MmLockAddressSpace(AddressSpace);
227                 MmSetCleanAllRmaps(Page);
228 
229                 MmSetPageProtect(MmGetAddressSpaceOwner(AddressSpace),
230                                  (PVOID)PageAddress,
231                                  PAGE_READONLY);
232 
233                 MmLockSectionSegment(Segment);
234                 Entry = MmGetPageEntrySectionSegment(Segment, &FileOffset);
235 
236                 if (Entry && !IS_SWAP_FROM_SSE(Entry) && PFN_FROM_SSE(Entry) == Page)
237                     MmSetPageEntrySectionSegment(Segment, &FileOffset, CLEAN_SSE(Entry));
238 
239                 MmUnlockSectionSegment(Segment);
240                 MmUnlockAddressSpace(AddressSpace);
241             } else {
242                 DPRINT("Writeback from section flush %08x%08x (%x) %x@%x (%08x%08x:%wZ) failed %x\n",
243                        FileOffset.u.HighPart,
244                        FileOffset.u.LowPart,
245                        (ULONG)(FileSize->QuadPart - FileOffset.QuadPart),
246                        PageAddress,
247                        Page,
248                        FileSize->u.HighPart,
249                        FileSize->u.LowPart,
250                        &Segment->FileObject->FileName,
251                        Status);
252             }
253             MmReleasePageMemoryConsumer(MC_CACHE, Page);
254         }
255     }
256 
257     ExFreePool(Pages);
258 
259     return Status;
260 }
261 
262 /*
263 
264 This deletes a segment entirely including its page map.
265 It must have been unmapped in every address space.
266 
267  */
268 
269 VOID
270 NTAPI
271 MmFinalizeSegment(PMM_SECTION_SEGMENT Segment)
272 {
273     KIRQL OldIrql = 0;
274 
275     DPRINT("Finalize segment %p\n", Segment);
276 
277     MmLockSectionSegment(Segment);
278     RemoveEntryList(&Segment->ListOfSegments);
279     if (Segment->Flags & MM_DATAFILE_SEGMENT) {
280         KeAcquireSpinLock(&Segment->FileObject->IrpListLock, &OldIrql);
281         if (Segment->Flags & MM_SEGMENT_FINALIZE) {
282             KeReleaseSpinLock(&Segment->FileObject->IrpListLock, OldIrql);
283             MmUnlockSectionSegment(Segment);
284             return;
285         }
286         Segment->Flags |= MM_SEGMENT_FINALIZE;
287         DPRINTC("Finalizing data file segment %p\n", Segment);
288 
289         Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
290         KeReleaseSpinLock(&Segment->FileObject->IrpListLock, OldIrql);
291         MmFreePageTablesSectionSegment(Segment, MiFreeSegmentPage);
292         MmUnlockSectionSegment(Segment);
293         DPRINT("Dereference file object %wZ\n", &Segment->FileObject->FileName);
294         ObDereferenceObject(Segment->FileObject);
295         DPRINT("Done with %wZ\n", &Segment->FileObject->FileName);
296         Segment->FileObject = NULL;
297     } else {
298         DPRINTC("Finalizing segment %p\n", Segment);
299         MmFreePageTablesSectionSegment(Segment, MiFreeSegmentPage);
300         MmUnlockSectionSegment(Segment);
301     }
302     DPRINTC("Segment %p destroy\n", Segment);
303     ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
304 }
305 
306 NTSTATUS
307 NTAPI
308 MmCreateCacheSection(PROS_SECTION_OBJECT *SectionObject,
309                      ACCESS_MASK DesiredAccess,
310                      POBJECT_ATTRIBUTES ObjectAttributes,
311                      PLARGE_INTEGER UMaximumSize,
312                      ULONG SectionPageProtection,
313                      ULONG AllocationAttributes,
314                      PFILE_OBJECT FileObject)
315 /*
316  * Create a section backed by a data file.
317  */
318 {
319     PROS_SECTION_OBJECT Section;
320     NTSTATUS Status;
321     LARGE_INTEGER MaximumSize;
322     PMM_SECTION_SEGMENT Segment;
323     IO_STATUS_BLOCK Iosb;
324     CC_FILE_SIZES FileSizes;
325     FILE_STANDARD_INFORMATION FileInfo;
326     KIRQL OldIrql;
327 
328     DPRINT("MmCreateDataFileSection\n");
329 
330     /* Create the section */
331     Status = ObCreateObject(ExGetPreviousMode(),
332                             MmSectionObjectType,
333                             ObjectAttributes,
334                             ExGetPreviousMode(),
335                             NULL,
336                             sizeof(ROS_SECTION_OBJECT),
337                             0,
338                             0,
339                             (PVOID*)(PVOID)&Section);
340     if (!NT_SUCCESS(Status))
341     {
342         DPRINT("Failed: %x\n", Status);
343         ObDereferenceObject(FileObject);
344         return Status;
345     }
346 
347     /* Initialize it */
348     RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
349     Section->Type = 'SC';
350     Section->Size = 'TN';
351     Section->SectionPageProtection = SectionPageProtection;
352     Section->AllocationAttributes = AllocationAttributes;
353     Section->Segment = NULL;
354 
355     Section->FileObject = FileObject;
356 
357     DPRINT("Getting original file size\n");
358     /* A hack: If we're cached, we can overcome deadlocking with the upper
359     * layer filesystem call by retriving the object sizes from the cache
360     * which is made to keep track.  If I had to guess, they were figuring
361     * out a similar problem.
362     */
363     if (!CcGetFileSizes(FileObject, &FileSizes))
364     {
365         ULONG Information;
366         /*
367         * FIXME: This is propably not entirely correct. We can't look into
368         * the standard FCB header because it might not be initialized yet
369         * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
370         * standard file information is filled on first request).
371         */
372         DPRINT("Querying info\n");
373         Status = IoQueryFileInformation(FileObject,
374                                         FileStandardInformation,
375                                         sizeof(FILE_STANDARD_INFORMATION),
376                                         &FileInfo,
377                                         &Information);
378         Iosb.Information = Information;
379         DPRINT("Query => %x\n", Status);
380         DBG_UNREFERENCED_LOCAL_VARIABLE(Iosb);
381 
382         if (!NT_SUCCESS(Status))
383         {
384             DPRINT("Status %x\n", Status);
385             ObDereferenceObject(Section);
386             ObDereferenceObject(FileObject);
387             return Status;
388         }
389         ASSERT(Status != STATUS_PENDING);
390 
391         FileSizes.ValidDataLength = FileInfo.EndOfFile;
392         FileSizes.FileSize = FileInfo.EndOfFile;
393     }
394     DPRINT("Got %I64x\n", FileSizes.ValidDataLength.QuadPart);
395 
396     /*
397     * FIXME: Revise this once a locking order for file size changes is
398     * decided
399     *
400     * We're handed down a maximum size in every case.  Should we still check at all?
401     */
402     if (UMaximumSize != NULL && UMaximumSize->QuadPart)
403     {
404         DPRINT("Taking maximum %I64x\n", UMaximumSize->QuadPart);
405         MaximumSize.QuadPart = UMaximumSize->QuadPart;
406     }
407     else
408     {
409         DPRINT("Got file size %I64x\n", FileSizes.FileSize.QuadPart);
410         MaximumSize.QuadPart = FileSizes.FileSize.QuadPart;
411     }
412 
413     /* Mapping zero-sized files isn't allowed. */
414     if (MaximumSize.QuadPart == 0)
415     {
416         DPRINT("Zero size file\n");
417         ObDereferenceObject(Section);
418         ObDereferenceObject(FileObject);
419         return STATUS_FILE_INVALID;
420     }
421 
422     Segment = ExAllocatePoolWithTag(NonPagedPool,
423                                     sizeof(MM_SECTION_SEGMENT),
424                                     TAG_MM_SECTION_SEGMENT);
425     if (Segment == NULL)
426     {
427         DPRINT("Failed: STATUS_NO_MEMORY\n");
428         ObDereferenceObject(Section);
429         ObDereferenceObject(FileObject);
430         return STATUS_NO_MEMORY;
431     }
432 
433     DPRINT("Zeroing %p\n", Segment);
434     RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
435     ExInitializeFastMutex(&Segment->Lock);
436 
437     Segment->ReferenceCount = 1;
438     Segment->Locked = TRUE;
439     RtlZeroMemory(&Segment->Image, sizeof(Segment->Image));
440     Section->Segment = Segment;
441 
442     KeAcquireSpinLock(&FileObject->IrpListLock, &OldIrql);
443     /*
444     * If this file hasn't been mapped as a data file before then allocate a
445     * section segment to describe the data file mapping
446     */
447     if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
448     {
449         FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
450         KeReleaseSpinLock(&FileObject->IrpListLock, OldIrql);
451 
452         /*
453         * Set the lock before assigning the segment to the file object
454         */
455         ExAcquireFastMutex(&Segment->Lock);
456 
457         DPRINT("Filling out Segment info (No previous data section)\n");
458         ObReferenceObject(FileObject);
459         Segment->FileObject = FileObject;
460         Segment->Protection = SectionPageProtection;
461         Segment->Flags = MM_DATAFILE_SEGMENT;
462         memset(&Segment->Image, 0, sizeof(Segment->Image));
463         Segment->WriteCopy = FALSE;
464 
465         if (AllocationAttributes & SEC_RESERVE)
466         {
467             Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
468         }
469         else
470         {
471             Segment->RawLength.QuadPart = MaximumSize.QuadPart;
472             Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
473         }
474         MiInitializeSectionPageTable(Segment);
475         InsertHeadList(&MiSegmentList, &Segment->ListOfSegments);
476     }
477     else
478     {
479         KeReleaseSpinLock(&FileObject->IrpListLock, OldIrql);
480         DPRINTC("Free Segment %p\n", Segment);
481         ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
482 
483         DPRINT("Filling out Segment info (previous data section)\n");
484 
485         /*
486         * If the file is already mapped as a data file then we may need
487         * to extend it
488         */
489         Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->DataSectionObject;
490         Section->Segment = Segment;
491         (void)InterlockedIncrementUL(&Segment->ReferenceCount);
492 
493         MmLockSectionSegment(Segment);
494 
495         if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
496             !(AllocationAttributes & SEC_RESERVE))
497         {
498             Segment->RawLength.QuadPart = MaximumSize.QuadPart;
499             Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
500         }
501     }
502 
503     MmUnlockSectionSegment(Segment);
504 
505     Section->MaximumSize.QuadPart = MaximumSize.QuadPart;
506 
507     /* Extend file if section is longer */
508     DPRINT("MaximumSize %I64x ValidDataLength %I64x\n",
509            MaximumSize.QuadPart,
510            FileSizes.ValidDataLength.QuadPart);
511     if (MaximumSize.QuadPart > FileSizes.ValidDataLength.QuadPart)
512     {
513         DPRINT("Changing file size to %I64x, segment %p\n",
514                MaximumSize.QuadPart,
515                Segment);
516 
517         Status = IoSetInformation(FileObject,
518                                   FileEndOfFileInformation,
519                                   sizeof(LARGE_INTEGER),
520                                   &MaximumSize);
521 
522         DPRINT("Change: Status %x\n", Status);
523         if (!NT_SUCCESS(Status))
524         {
525             DPRINT("Could not expand section\n");
526             ObDereferenceObject(Section);
527             return Status;
528         }
529     }
530 
531     DPRINTC("Segment %p created (%x)\n", Segment, Segment->Flags);
532 
533     *SectionObject = Section;
534     return STATUS_SUCCESS;
535 }
536 
537 NTSTATUS
538 NTAPI
539 _MiMapViewOfSegment(PMMSUPPORT AddressSpace,
540                     PMM_SECTION_SEGMENT Segment,
541                     PVOID* BaseAddress,
542                     SIZE_T ViewSize,
543                     ULONG Protect,
544                     PLARGE_INTEGER ViewOffset,
545                     ULONG AllocationType,
546                     const char *file,
547                     int line)
548 {
549     PMEMORY_AREA MArea;
550     NTSTATUS Status;
551 
552     Status = MmCreateMemoryArea(AddressSpace,
553                                 MEMORY_AREA_CACHE,
554                                 BaseAddress,
555                                 ViewSize,
556                                 Protect,
557                                 &MArea,
558                                 AllocationType,
559                                 *BaseAddress ?
560                                 PAGE_SIZE : MM_ALLOCATION_GRANULARITY);
561 
562     if (!NT_SUCCESS(Status))
563     {
564         DPRINT("Mapping between 0x%p and 0x%p failed (%X).\n",
565                (*BaseAddress),
566                (char*)(*BaseAddress) + ViewSize,
567                Status);
568 
569         return Status;
570     }
571 
572     DPRINTC("MiMapViewOfSegment %p %p %p %I64x %Ix %wZ %s:%d\n",
573             MmGetAddressSpaceOwner(AddressSpace),
574             *BaseAddress,
575             Segment,
576             ViewOffset ? ViewOffset->QuadPart : 0,
577             ViewSize,
578             Segment->FileObject ? &Segment->FileObject->FileName : NULL,
579             file,
580             line);
581 
582     MArea->Data.SectionData.Segment = Segment;
583     if (ViewOffset)
584         MArea->Data.SectionData.ViewOffset = *ViewOffset;
585     else
586         MArea->Data.SectionData.ViewOffset.QuadPart = 0;
587 
588 #if 0
589     MArea->NotPresent = MmNotPresentFaultPageFile;
590     MArea->AccessFault = MiCowSectionPage;
591     MArea->PageOut = MmPageOutPageFileView;
592 #endif
593 
594     MmInitializeRegion(&MArea->Data.SectionData.RegionListHead,
595                        ViewSize,
596                        0,
597                        Protect);
598 
599     DPRINTC("MiMapViewOfSegment(P %p, A %p, T %x)\n",
600             MmGetAddressSpaceOwner(AddressSpace),
601             *BaseAddress,
602             MArea->Type);
603 
604     return STATUS_SUCCESS;
605 }
606 
607 /*
608 
609 Completely remove the page at FileOffset in Segment.  The page must not
610 be mapped.
611 
612 */
613 
614 VOID
615 NTAPI
616 MiFreeSegmentPage(PMM_SECTION_SEGMENT Segment,
617                   PLARGE_INTEGER FileOffset)
618 {
619     ULONG_PTR Entry;
620     PFILE_OBJECT FileObject = Segment->FileObject;
621 
622     Entry = MmGetPageEntrySectionSegment(Segment, FileOffset);
623     DPRINTC("MiFreeSegmentPage(%p:%I64x -> Entry %Ix\n",
624             Segment,
625             FileOffset->QuadPart,
626             Entry);
627 
628     if (Entry && !IS_SWAP_FROM_SSE(Entry))
629     {
630         // The segment is carrying a dirty page.
631         PFN_NUMBER OldPage = PFN_FROM_SSE(Entry);
632         if (IS_DIRTY_SSE(Entry) && FileObject)
633         {
634             DPRINT("MiWriteBackPage(%p,%wZ,%I64x)\n",
635                    Segment,
636                    &FileObject->FileName,
637                    FileOffset->QuadPart);
638 
639             MiWriteBackPage(FileObject, FileOffset, PAGE_SIZE, OldPage);
640         }
641         DPRINTC("Free page %Ix (off %I64x from %p) (ref ct %lu, ent %Ix, dirty? %s)\n",
642                 OldPage,
643                 FileOffset->QuadPart,
644                 Segment,
645                 MmGetReferenceCountPage(OldPage),
646                 Entry,
647                 IS_DIRTY_SSE(Entry) ? "true" : "false");
648 
649         MmSetPageEntrySectionSegment(Segment, FileOffset, 0);
650         MmReleasePageMemoryConsumer(MC_CACHE, OldPage);
651     }
652     else if (IS_SWAP_FROM_SSE(Entry))
653     {
654         DPRINT("Free swap\n");
655         MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
656     }
657 
658     DPRINT("Done\n");
659 }
660 
661 VOID
662 MmFreeCacheSectionPage(PVOID Context,
663                        MEMORY_AREA* MemoryArea,
664                        PVOID Address,
665                        PFN_NUMBER Page,
666                        SWAPENTRY SwapEntry,
667                        BOOLEAN Dirty)
668 {
669     ULONG_PTR Entry;
670     PVOID *ContextData = Context;
671     PMMSUPPORT AddressSpace;
672     PEPROCESS Process;
673     PMM_SECTION_SEGMENT Segment;
674     LARGE_INTEGER Offset;
675 
676     DPRINT("MmFreeSectionPage(%p,%p,%Ix,%Ix,%u)\n",
677            MmGetAddressSpaceOwner(ContextData[0]),
678            Address,
679            Page,
680            SwapEntry,
681            Dirty);
682 
683     AddressSpace = ContextData[0];
684     Process = MmGetAddressSpaceOwner(AddressSpace);
685     Address = (PVOID)PAGE_ROUND_DOWN(Address);
686     Segment = ContextData[1];
687     Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea) +
688                       MemoryArea->Data.SectionData.ViewOffset.QuadPart;
689 
690     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
691 
692     if (Page != 0 && PFN_FROM_SSE(Entry) == Page && Dirty)
693     {
694         DPRINT("Freeing section page %p:%I64x -> %Ix\n", Segment, Offset.QuadPart, Entry);
695         MmSetPageEntrySectionSegment(Segment, &Offset, DIRTY_SSE(Entry));
696     }
697     if (Page)
698     {
699         DPRINT("Removing page %p:%I64x -> %x\n", Segment, Offset.QuadPart, Entry);
700         MmSetSavedSwapEntryPage(Page, 0);
701         MmDeleteRmap(Page, Process, Address);
702         MmDeleteVirtualMapping(Process, Address, NULL, NULL);
703         MmReleasePageMemoryConsumer(MC_CACHE, Page);
704     }
705     if (SwapEntry != 0)
706     {
707         MmFreeSwapPage(SwapEntry);
708     }
709 }
710 
711 NTSTATUS
712 NTAPI
713 MmUnmapViewOfCacheSegment(PMMSUPPORT AddressSpace,
714                           PVOID BaseAddress)
715 {
716     PVOID Context[2];
717     PMEMORY_AREA MemoryArea;
718     PMM_SECTION_SEGMENT Segment;
719 
720     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
721     if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
722     {
723         ASSERT(MemoryArea);
724         return STATUS_UNSUCCESSFUL;
725     }
726 
727     MemoryArea->DeleteInProgress = TRUE;
728     Segment = MemoryArea->Data.SectionData.Segment;
729     MemoryArea->Data.SectionData.Segment = NULL;
730 
731     MmLockSectionSegment(Segment);
732 
733     Context[0] = AddressSpace;
734     Context[1] = Segment;
735 
736     DPRINT("MmFreeMemoryArea(%p,%p)\n",
737            MmGetAddressSpaceOwner(AddressSpace),
738            MA_GetStartingAddress(MemoryArea));
739 
740     MmLockAddressSpace(AddressSpace);
741 
742     MmFreeMemoryArea(AddressSpace, MemoryArea, MmFreeCacheSectionPage, Context);
743 
744     MmUnlockAddressSpace(AddressSpace);
745 
746     MmUnlockSectionSegment(Segment);
747 
748     DPRINTC("MiUnmapViewOfSegment %p %p %p\n",
749             MmGetAddressSpaceOwner(AddressSpace),
750             BaseAddress,
751             Segment);
752 
753     return STATUS_SUCCESS;
754 }
755 
756 NTSTATUS
757 NTAPI
758 MmExtendCacheSection(PROS_SECTION_OBJECT Section,
759                      PLARGE_INTEGER NewSize,
760                      BOOLEAN ExtendFile)
761 {
762     LARGE_INTEGER OldSize;
763     PMM_SECTION_SEGMENT Segment = Section->Segment;
764     DPRINT("Extend Segment %p\n", Segment);
765 
766     MmLockSectionSegment(Segment);
767     OldSize.QuadPart = Segment->RawLength.QuadPart;
768     MmUnlockSectionSegment(Segment);
769 
770     DPRINT("OldSize 0x%I64x NewSize 0x%I64x\n",
771            OldSize.QuadPart,
772            NewSize->QuadPart);
773 
774     if (ExtendFile && OldSize.QuadPart < NewSize->QuadPart)
775     {
776         NTSTATUS Status;
777 
778         Status = IoSetInformation(Segment->FileObject,
779                                   FileEndOfFileInformation,
780                                   sizeof(LARGE_INTEGER),
781                                   NewSize);
782 
783         if (!NT_SUCCESS(Status)) return Status;
784     }
785 
786     MmLockSectionSegment(Segment);
787     Segment->RawLength.QuadPart = NewSize->QuadPart;
788     Segment->Length.QuadPart = MAX(Segment->Length.QuadPart,
789                                    (LONG64)PAGE_ROUND_UP(Segment->RawLength.QuadPart));
790     MmUnlockSectionSegment(Segment);
791     return STATUS_SUCCESS;
792 }
793 
794 NTSTATUS
795 NTAPI
796 MmMapCacheViewInSystemSpaceAtOffset(IN PMM_SECTION_SEGMENT Segment,
797                                     OUT PVOID *MappedBase,
798                                     PLARGE_INTEGER FileOffset,
799                                     IN OUT PULONG ViewSize)
800 {
801     PMMSUPPORT AddressSpace;
802     NTSTATUS Status;
803 
804     DPRINT("MmMapViewInSystemSpaceAtOffset() called offset 0x%I64x\n",
805            FileOffset->QuadPart);
806 
807     AddressSpace = MmGetKernelAddressSpace();
808 
809     MmLockAddressSpace(AddressSpace);
810     MmLockSectionSegment(Segment);
811 
812     Status = MiMapViewOfSegment(AddressSpace,
813                                 Segment,
814                                 MappedBase,
815                                 *ViewSize,
816                                 PAGE_READWRITE,
817                                 FileOffset,
818                                 0);
819 
820     MmUnlockSectionSegment(Segment);
821     MmUnlockAddressSpace(AddressSpace);
822 
823     return Status;
824 }
825 
826 /*
827  * @implemented
828  */
829 NTSTATUS NTAPI
830 MmUnmapCacheViewInSystemSpace (IN PVOID MappedBase)
831 {
832     PMMSUPPORT AddressSpace;
833     NTSTATUS Status;
834 
835     DPRINT("MmUnmapViewInSystemSpace() called\n");
836 
837     AddressSpace = MmGetKernelAddressSpace();
838 
839     Status = MmUnmapViewOfCacheSegment(AddressSpace, MappedBase);
840 
841     return Status;
842 }
843 
844 /* EOF */
845