xref: /reactos/ntoskrnl/cache/section/data.c (revision d2fa434c)
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 #ifdef NEWCC
116 /*
117 
118 MiFlushMappedSection
119 
120 Called from cache code to cause dirty pages of a section
121 to be written back.  This doesn't affect the mapping.
122 
123 BaseOffset is the base at which to start writing in file space.
124 FileSize is the length of the file as understood by the cache.
125 
126  */
127 NTSTATUS
128 NTAPI
129 _MiFlushMappedSection(PVOID BaseAddress,
130                       PLARGE_INTEGER BaseOffset,
131                       PLARGE_INTEGER FileSize,
132                       BOOLEAN WriteData,
133                       const char *File,
134                       int Line)
135 {
136     NTSTATUS Status = STATUS_SUCCESS;
137     ULONG_PTR PageAddress;
138     PMMSUPPORT AddressSpace = MmGetKernelAddressSpace();
139     PMEMORY_AREA MemoryArea;
140     PMM_SECTION_SEGMENT Segment;
141     ULONG_PTR BeginningAddress, EndingAddress;
142     LARGE_INTEGER ViewOffset;
143     LARGE_INTEGER FileOffset;
144     PFN_NUMBER Page;
145     PPFN_NUMBER Pages;
146     KIRQL OldIrql;
147 
148     DPRINT("MiFlushMappedSection(%p,%I64x,%I64x,%u,%s:%d)\n",
149            BaseAddress,
150            BaseOffset->QuadPart,
151            FileSize ? FileSize->QuadPart : 0,
152            WriteData,
153            File,
154            Line);
155 
156     MmLockAddressSpace(AddressSpace);
157     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
158     if (!MemoryArea || MemoryArea->Type != MEMORY_AREA_CACHE || MemoryArea->DeleteInProgress)
159     {
160         MmUnlockAddressSpace(AddressSpace);
161         DPRINT("STATUS_NOT_MAPPED_DATA\n");
162         return STATUS_NOT_MAPPED_DATA;
163     }
164     BeginningAddress = PAGE_ROUND_DOWN(MA_GetStartingAddress(MemoryArea));
165     EndingAddress = PAGE_ROUND_UP(MA_GetEndingAddress(MemoryArea));
166     Segment = MemoryArea->Data.SectionData.Segment;
167     ViewOffset.QuadPart = MemoryArea->Data.SectionData.ViewOffset.QuadPart;
168 
169     ASSERT(ViewOffset.QuadPart == BaseOffset->QuadPart);
170 
171     MmLockSectionSegment(Segment);
172 
173     Pages = ExAllocatePool(NonPagedPool,
174                            sizeof(PFN_NUMBER) * ((EndingAddress - BeginningAddress) >> PAGE_SHIFT));
175 
176     if (!Pages)
177     {
178         ASSERT(FALSE);
179     }
180 
181     //DPRINT("Getting pages in range %08x-%08x\n", BeginningAddress, EndingAddress);
182 
183     for (PageAddress = BeginningAddress;
184          PageAddress < EndingAddress;
185          PageAddress += PAGE_SIZE)
186     {
187         ULONG_PTR Entry;
188         FileOffset.QuadPart = ViewOffset.QuadPart + PageAddress - BeginningAddress;
189         Entry = MmGetPageEntrySectionSegment(MemoryArea->Data.SectionData.Segment,
190                                              &FileOffset);
191         Page = PFN_FROM_SSE(Entry);
192         if (Entry != 0 && !IS_SWAP_FROM_SSE(Entry) &&
193             (MmIsDirtyPageRmap(Page) || IS_DIRTY_SSE(Entry)) &&
194             FileOffset.QuadPart < FileSize->QuadPart)
195         {
196             OldIrql = MiAcquirePfnLock();
197             MmReferencePage(Page);
198             MiReleasePfnLock(OldIrql);
199             Pages[(PageAddress - BeginningAddress) >> PAGE_SHIFT] = Entry;
200         }
201         else
202         {
203             Pages[(PageAddress - BeginningAddress) >> PAGE_SHIFT] = 0;
204         }
205     }
206 
207     MmUnlockSectionSegment(Segment);
208     MmUnlockAddressSpace(AddressSpace);
209 
210     for (PageAddress = BeginningAddress;
211          PageAddress < EndingAddress;
212          PageAddress += PAGE_SIZE)
213     {
214         ULONG_PTR Entry;
215         FileOffset.QuadPart = ViewOffset.QuadPart + PageAddress - BeginningAddress;
216         Entry = Pages[(PageAddress - BeginningAddress) >> PAGE_SHIFT];
217         Page = PFN_FROM_SSE(Entry);
218         if (Page)
219         {
220             if (WriteData) {
221                 //DPRINT("MiWriteBackPage(%wZ,addr %x,%08x%08x)\n", &Segment->FileObject->FileName, PageAddress, FileOffset.u.HighPart, FileOffset.u.LowPart);
222                 Status = MiWriteBackPage(Segment->FileObject, &FileOffset, PAGE_SIZE, Page);
223             } else
224                 Status = STATUS_SUCCESS;
225 
226             if (NT_SUCCESS(Status)) {
227                 MmLockAddressSpace(AddressSpace);
228                 MmSetCleanAllRmaps(Page);
229 
230                 MmSetPageProtect(MmGetAddressSpaceOwner(AddressSpace),
231                                  (PVOID)PageAddress,
232                                  PAGE_READONLY);
233 
234                 MmLockSectionSegment(Segment);
235                 Entry = MmGetPageEntrySectionSegment(Segment, &FileOffset);
236 
237                 if (Entry && !IS_SWAP_FROM_SSE(Entry) && PFN_FROM_SSE(Entry) == Page)
238                     MmSetPageEntrySectionSegment(Segment, &FileOffset, CLEAN_SSE(Entry));
239 
240                 MmUnlockSectionSegment(Segment);
241                 MmUnlockAddressSpace(AddressSpace);
242             } else {
243                 DPRINT("Writeback from section flush %08x%08x (%x) %x@%x (%08x%08x:%wZ) failed %x\n",
244                        FileOffset.u.HighPart,
245                        FileOffset.u.LowPart,
246                        (ULONG)(FileSize->QuadPart - FileOffset.QuadPart),
247                        PageAddress,
248                        Page,
249                        FileSize->u.HighPart,
250                        FileSize->u.LowPart,
251                        &Segment->FileObject->FileName,
252                        Status);
253             }
254             MmReleasePageMemoryConsumer(MC_CACHE, Page);
255         }
256     }
257 
258     ExFreePool(Pages);
259 
260     return Status;
261 }
262 
263 /*
264 
265 This deletes a segment entirely including its page map.
266 It must have been unmapped in every address space.
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 #endif
607 
608 /*
609 
610 Completely remove the page at FileOffset in Segment.  The page must not
611 be mapped.
612 
613 */
614 
615 VOID
616 NTAPI
617 MiFreeSegmentPage(PMM_SECTION_SEGMENT Segment,
618                   PLARGE_INTEGER FileOffset)
619 {
620     ULONG_PTR Entry;
621     PFILE_OBJECT FileObject = Segment->FileObject;
622 
623     Entry = MmGetPageEntrySectionSegment(Segment, FileOffset);
624     DPRINTC("MiFreeSegmentPage(%p:%I64x -> Entry %Ix\n",
625             Segment,
626             FileOffset->QuadPart,
627             Entry);
628 
629     if (Entry && !IS_SWAP_FROM_SSE(Entry))
630     {
631         // The segment is carrying a dirty page.
632         PFN_NUMBER OldPage = PFN_FROM_SSE(Entry);
633         if (IS_DIRTY_SSE(Entry) && FileObject)
634         {
635             DPRINT("MiWriteBackPage(%p,%wZ,%I64x)\n",
636                    Segment,
637                    &FileObject->FileName,
638                    FileOffset->QuadPart);
639 
640             MiWriteBackPage(FileObject, FileOffset, PAGE_SIZE, OldPage);
641         }
642         DPRINTC("Free page %Ix (off %I64x from %p) (ref ct %lu, ent %Ix, dirty? %s)\n",
643                 OldPage,
644                 FileOffset->QuadPart,
645                 Segment,
646                 MmGetReferenceCountPage(OldPage),
647                 Entry,
648                 IS_DIRTY_SSE(Entry) ? "true" : "false");
649 
650         MmSetPageEntrySectionSegment(Segment, FileOffset, 0);
651         MmReleasePageMemoryConsumer(MC_CACHE, OldPage);
652     }
653     else if (IS_SWAP_FROM_SSE(Entry))
654     {
655         DPRINT("Free swap\n");
656         MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
657     }
658 
659     DPRINT("Done\n");
660 }
661 
662 VOID
663 MmFreeCacheSectionPage(PVOID Context,
664                        MEMORY_AREA* MemoryArea,
665                        PVOID Address,
666                        PFN_NUMBER Page,
667                        SWAPENTRY SwapEntry,
668                        BOOLEAN Dirty)
669 {
670     ULONG_PTR Entry;
671     PVOID *ContextData = Context;
672     PMMSUPPORT AddressSpace;
673     PEPROCESS Process;
674     PMM_SECTION_SEGMENT Segment;
675     LARGE_INTEGER Offset;
676 
677     DPRINT("MmFreeSectionPage(%p,%p,%Ix,%Ix,%u)\n",
678            MmGetAddressSpaceOwner(ContextData[0]),
679            Address,
680            Page,
681            SwapEntry,
682            Dirty);
683 
684     AddressSpace = ContextData[0];
685     Process = MmGetAddressSpaceOwner(AddressSpace);
686     Address = (PVOID)PAGE_ROUND_DOWN(Address);
687     Segment = ContextData[1];
688     Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea) +
689                       MemoryArea->Data.SectionData.ViewOffset.QuadPart;
690 
691     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
692 
693     if (Page != 0 && PFN_FROM_SSE(Entry) == Page && Dirty)
694     {
695         DPRINT("Freeing section page %p:%I64x -> %Ix\n", Segment, Offset.QuadPart, Entry);
696         MmSetPageEntrySectionSegment(Segment, &Offset, DIRTY_SSE(Entry));
697     }
698     if (Page)
699     {
700         DPRINT("Removing page %p:%I64x -> %x\n", Segment, Offset.QuadPart, Entry);
701         MmSetSavedSwapEntryPage(Page, 0);
702         MmDeleteRmap(Page, Process, Address);
703         MmDeleteVirtualMapping(Process, Address, NULL, NULL);
704         MmReleasePageMemoryConsumer(MC_CACHE, Page);
705     }
706     if (SwapEntry != 0)
707     {
708         MmFreeSwapPage(SwapEntry);
709     }
710 }
711 
712 #ifdef NEWCC
713 NTSTATUS
714 NTAPI
715 MmUnmapViewOfCacheSegment(PMMSUPPORT AddressSpace,
716                           PVOID BaseAddress)
717 {
718     PVOID Context[2];
719     PMEMORY_AREA MemoryArea;
720     PMM_SECTION_SEGMENT Segment;
721 
722     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
723     if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
724     {
725         ASSERT(MemoryArea);
726         return STATUS_UNSUCCESSFUL;
727     }
728 
729     MemoryArea->DeleteInProgress = TRUE;
730     Segment = MemoryArea->Data.SectionData.Segment;
731     MemoryArea->Data.SectionData.Segment = NULL;
732 
733     MmLockSectionSegment(Segment);
734 
735     Context[0] = AddressSpace;
736     Context[1] = Segment;
737 
738     DPRINT("MmFreeMemoryArea(%p,%p)\n",
739            MmGetAddressSpaceOwner(AddressSpace),
740            MA_GetStartingAddress(MemoryArea));
741 
742     MmLockAddressSpace(AddressSpace);
743 
744     MmFreeMemoryArea(AddressSpace, MemoryArea, MmFreeCacheSectionPage, Context);
745 
746     MmUnlockAddressSpace(AddressSpace);
747 
748     MmUnlockSectionSegment(Segment);
749 
750     DPRINTC("MiUnmapViewOfSegment %p %p %p\n",
751             MmGetAddressSpaceOwner(AddressSpace),
752             BaseAddress,
753             Segment);
754 
755     return STATUS_SUCCESS;
756 }
757 
758 NTSTATUS
759 NTAPI
760 MmExtendCacheSection(PROS_SECTION_OBJECT Section,
761                      PLARGE_INTEGER NewSize,
762                      BOOLEAN ExtendFile)
763 {
764     LARGE_INTEGER OldSize;
765     PMM_SECTION_SEGMENT Segment = Section->Segment;
766     DPRINT("Extend Segment %p\n", Segment);
767 
768     MmLockSectionSegment(Segment);
769     OldSize.QuadPart = Segment->RawLength.QuadPart;
770     MmUnlockSectionSegment(Segment);
771 
772     DPRINT("OldSize 0x%I64x NewSize 0x%I64x\n",
773            OldSize.QuadPart,
774            NewSize->QuadPart);
775 
776     if (ExtendFile && OldSize.QuadPart < NewSize->QuadPart)
777     {
778         NTSTATUS Status;
779 
780         Status = IoSetInformation(Segment->FileObject,
781                                   FileEndOfFileInformation,
782                                   sizeof(LARGE_INTEGER),
783                                   NewSize);
784 
785         if (!NT_SUCCESS(Status)) return Status;
786     }
787 
788     MmLockSectionSegment(Segment);
789     Segment->RawLength.QuadPart = NewSize->QuadPart;
790     Segment->Length.QuadPart = MAX(Segment->Length.QuadPart,
791                                    (LONG64)PAGE_ROUND_UP(Segment->RawLength.QuadPart));
792     MmUnlockSectionSegment(Segment);
793     return STATUS_SUCCESS;
794 }
795 
796 NTSTATUS
797 NTAPI
798 MmMapCacheViewInSystemSpaceAtOffset(IN PMM_SECTION_SEGMENT Segment,
799                                     OUT PVOID *MappedBase,
800                                     PLARGE_INTEGER FileOffset,
801                                     IN OUT PULONG ViewSize)
802 {
803     PMMSUPPORT AddressSpace;
804     NTSTATUS Status;
805 
806     DPRINT("MmMapViewInSystemSpaceAtOffset() called offset 0x%I64x\n",
807            FileOffset->QuadPart);
808 
809     AddressSpace = MmGetKernelAddressSpace();
810 
811     MmLockAddressSpace(AddressSpace);
812     MmLockSectionSegment(Segment);
813 
814     Status = MiMapViewOfSegment(AddressSpace,
815                                 Segment,
816                                 MappedBase,
817                                 *ViewSize,
818                                 PAGE_READWRITE,
819                                 FileOffset,
820                                 0);
821 
822     MmUnlockSectionSegment(Segment);
823     MmUnlockAddressSpace(AddressSpace);
824 
825     return Status;
826 }
827 
828 /*
829  * @implemented
830  */
831 NTSTATUS NTAPI
832 MmUnmapCacheViewInSystemSpace (IN PVOID MappedBase)
833 {
834     PMMSUPPORT AddressSpace;
835     NTSTATUS Status;
836 
837     DPRINT("MmUnmapViewInSystemSpace() called\n");
838 
839     AddressSpace = MmGetKernelAddressSpace();
840 
841     Status = MmUnmapViewOfCacheSegment(AddressSpace, MappedBase);
842 
843     return Status;
844 }
845 #endif /* NEWCC */
846 
847 /* EOF */
848