xref: /reactos/ntoskrnl/cache/section/data.c (revision 321bcc05)
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 = KeAcquireQueuedSpinLock(LockQueuePfnLock);
196             MmReferencePage(Page);
197             KeReleaseQueuedSpinLock(LockQueuePfnLock, 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         return Status;
344     }
345 
346     /* Initialize it */
347     RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
348     Section->Type = 'SC';
349     Section->Size = 'TN';
350     Section->SectionPageProtection = SectionPageProtection;
351     Section->AllocationAttributes = AllocationAttributes;
352     Section->Segment = NULL;
353 
354     Section->FileObject = FileObject;
355 
356     DPRINT("Getting original file size\n");
357     /* A hack: If we're cached, we can overcome deadlocking with the upper
358     * layer filesystem call by retriving the object sizes from the cache
359     * which is made to keep track.  If I had to guess, they were figuring
360     * out a similar problem.
361     */
362     if (!CcGetFileSizes(FileObject, &FileSizes))
363     {
364         ULONG Information;
365         /*
366         * FIXME: This is propably not entirely correct. We can't look into
367         * the standard FCB header because it might not be initialized yet
368         * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
369         * standard file information is filled on first request).
370         */
371         DPRINT("Querying info\n");
372         Status = IoQueryFileInformation(FileObject,
373                                         FileStandardInformation,
374                                         sizeof(FILE_STANDARD_INFORMATION),
375                                         &FileInfo,
376                                         &Information);
377         Iosb.Information = Information;
378         DPRINT("Query => %x\n", Status);
379         DBG_UNREFERENCED_LOCAL_VARIABLE(Iosb);
380 
381         if (!NT_SUCCESS(Status))
382         {
383             DPRINT("Status %x\n", Status);
384             ObDereferenceObject(Section);
385             return Status;
386         }
387         ASSERT(Status != STATUS_PENDING);
388 
389         FileSizes.ValidDataLength = FileInfo.EndOfFile;
390         FileSizes.FileSize = FileInfo.EndOfFile;
391     }
392     DPRINT("Got %I64x\n", FileSizes.ValidDataLength.QuadPart);
393 
394     /*
395     * FIXME: Revise this once a locking order for file size changes is
396     * decided
397     *
398     * We're handed down a maximum size in every case.  Should we still check at all?
399     */
400     if (UMaximumSize != NULL && UMaximumSize->QuadPart)
401     {
402         DPRINT("Taking maximum %I64x\n", UMaximumSize->QuadPart);
403         MaximumSize.QuadPart = UMaximumSize->QuadPart;
404     }
405     else
406     {
407         DPRINT("Got file size %I64x\n", FileSizes.FileSize.QuadPart);
408         MaximumSize.QuadPart = FileSizes.FileSize.QuadPart;
409     }
410 
411     /* Mapping zero-sized files isn't allowed. */
412     if (MaximumSize.QuadPart == 0)
413     {
414         DPRINT("Zero size file\n");
415         ObDereferenceObject(Section);
416         return STATUS_FILE_INVALID;
417     }
418 
419     Segment = ExAllocatePoolWithTag(NonPagedPool,
420                                     sizeof(MM_SECTION_SEGMENT),
421                                     TAG_MM_SECTION_SEGMENT);
422     if (Segment == NULL)
423     {
424         DPRINT("Failed: STATUS_NO_MEMORY\n");
425         ObDereferenceObject(Section);
426         return STATUS_NO_MEMORY;
427     }
428 
429     DPRINT("Zeroing %p\n", Segment);
430     RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
431     ExInitializeFastMutex(&Segment->Lock);
432 
433     Segment->ReferenceCount = 1;
434     Segment->Locked = TRUE;
435     RtlZeroMemory(&Segment->Image, sizeof(Segment->Image));
436     Section->Segment = Segment;
437 
438     KeAcquireSpinLock(&FileObject->IrpListLock, &OldIrql);
439     /*
440     * If this file hasn't been mapped as a data file before then allocate a
441     * section segment to describe the data file mapping
442     */
443     if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
444     {
445         FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
446         KeReleaseSpinLock(&FileObject->IrpListLock, OldIrql);
447 
448         /*
449         * Set the lock before assigning the segment to the file object
450         */
451         ExAcquireFastMutex(&Segment->Lock);
452 
453         DPRINT("Filling out Segment info (No previous data section)\n");
454         ObReferenceObject(FileObject);
455         Segment->FileObject = FileObject;
456         Segment->Protection = SectionPageProtection;
457         Segment->Flags = MM_DATAFILE_SEGMENT;
458         memset(&Segment->Image, 0, sizeof(Segment->Image));
459         Segment->WriteCopy = FALSE;
460 
461         if (AllocationAttributes & SEC_RESERVE)
462         {
463             Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
464         }
465         else
466         {
467             Segment->RawLength.QuadPart = MaximumSize.QuadPart;
468             Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
469         }
470         MiInitializeSectionPageTable(Segment);
471         InsertHeadList(&MiSegmentList, &Segment->ListOfSegments);
472     }
473     else
474     {
475         KeReleaseSpinLock(&FileObject->IrpListLock, OldIrql);
476         DPRINTC("Free Segment %p\n", Segment);
477         ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
478 
479         DPRINT("Filling out Segment info (previous data section)\n");
480 
481         /*
482         * If the file is already mapped as a data file then we may need
483         * to extend it
484         */
485         Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->DataSectionObject;
486         Section->Segment = Segment;
487         (void)InterlockedIncrementUL(&Segment->ReferenceCount);
488 
489         MmLockSectionSegment(Segment);
490 
491         if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
492             !(AllocationAttributes & SEC_RESERVE))
493         {
494             Segment->RawLength.QuadPart = MaximumSize.QuadPart;
495             Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
496         }
497     }
498 
499     MmUnlockSectionSegment(Segment);
500 
501     Section->MaximumSize.QuadPart = MaximumSize.QuadPart;
502 
503     /* Extend file if section is longer */
504     DPRINT("MaximumSize %I64x ValidDataLength %I64x\n",
505            MaximumSize.QuadPart,
506            FileSizes.ValidDataLength.QuadPart);
507     if (MaximumSize.QuadPart > FileSizes.ValidDataLength.QuadPart)
508     {
509         DPRINT("Changing file size to %I64x, segment %p\n",
510                MaximumSize.QuadPart,
511                Segment);
512 
513         Status = IoSetInformation(FileObject,
514                                   FileEndOfFileInformation,
515                                   sizeof(LARGE_INTEGER),
516                                   &MaximumSize);
517 
518         DPRINT("Change: Status %x\n", Status);
519         if (!NT_SUCCESS(Status))
520         {
521             DPRINT("Could not expand section\n");
522             ObDereferenceObject(Section);
523             return Status;
524         }
525     }
526 
527     DPRINTC("Segment %p created (%x)\n", Segment, Segment->Flags);
528 
529     *SectionObject = Section;
530     return STATUS_SUCCESS;
531 }
532 
533 NTSTATUS
534 NTAPI
535 _MiMapViewOfSegment(PMMSUPPORT AddressSpace,
536                     PMM_SECTION_SEGMENT Segment,
537                     PVOID* BaseAddress,
538                     SIZE_T ViewSize,
539                     ULONG Protect,
540                     PLARGE_INTEGER ViewOffset,
541                     ULONG AllocationType,
542                     const char *file,
543                     int line)
544 {
545     PMEMORY_AREA MArea;
546     NTSTATUS Status;
547 
548     Status = MmCreateMemoryArea(AddressSpace,
549                                 MEMORY_AREA_CACHE,
550                                 BaseAddress,
551                                 ViewSize,
552                                 Protect,
553                                 &MArea,
554                                 AllocationType,
555                                 *BaseAddress ?
556                                 PAGE_SIZE : MM_ALLOCATION_GRANULARITY);
557 
558     if (!NT_SUCCESS(Status))
559     {
560         DPRINT("Mapping between 0x%p and 0x%p failed (%X).\n",
561                (*BaseAddress),
562                (char*)(*BaseAddress) + ViewSize,
563                Status);
564 
565         return Status;
566     }
567 
568     DPRINTC("MiMapViewOfSegment %p %p %p %I64x %Ix %wZ %s:%d\n",
569             MmGetAddressSpaceOwner(AddressSpace),
570             *BaseAddress,
571             Segment,
572             ViewOffset ? ViewOffset->QuadPart : 0,
573             ViewSize,
574             Segment->FileObject ? &Segment->FileObject->FileName : NULL,
575             file,
576             line);
577 
578     MArea->Data.SectionData.Segment = Segment;
579     if (ViewOffset)
580         MArea->Data.SectionData.ViewOffset = *ViewOffset;
581     else
582         MArea->Data.SectionData.ViewOffset.QuadPart = 0;
583 
584 #if 0
585     MArea->NotPresent = MmNotPresentFaultPageFile;
586     MArea->AccessFault = MiCowSectionPage;
587     MArea->PageOut = MmPageOutPageFileView;
588 #endif
589 
590     MmInitializeRegion(&MArea->Data.SectionData.RegionListHead,
591                        ViewSize,
592                        0,
593                        Protect);
594 
595     DPRINTC("MiMapViewOfSegment(P %p, A %p, T %x)\n",
596             MmGetAddressSpaceOwner(AddressSpace),
597             *BaseAddress,
598             MArea->Type);
599 
600     return STATUS_SUCCESS;
601 }
602 
603 /*
604 
605 Completely remove the page at FileOffset in Segment.  The page must not
606 be mapped.
607 
608 */
609 
610 VOID
611 NTAPI
612 MiFreeSegmentPage(PMM_SECTION_SEGMENT Segment,
613                   PLARGE_INTEGER FileOffset)
614 {
615     ULONG_PTR Entry;
616     PFILE_OBJECT FileObject = Segment->FileObject;
617 
618     Entry = MmGetPageEntrySectionSegment(Segment, FileOffset);
619     DPRINTC("MiFreeSegmentPage(%p:%I64x -> Entry %Ix\n",
620             Segment,
621             FileOffset->QuadPart,
622             Entry);
623 
624     if (Entry && !IS_SWAP_FROM_SSE(Entry))
625     {
626         // The segment is carrying a dirty page.
627         PFN_NUMBER OldPage = PFN_FROM_SSE(Entry);
628         if (IS_DIRTY_SSE(Entry) && FileObject)
629         {
630             DPRINT("MiWriteBackPage(%p,%wZ,%I64x)\n",
631                    Segment,
632                    &FileObject->FileName,
633                    FileOffset->QuadPart);
634 
635             MiWriteBackPage(FileObject, FileOffset, PAGE_SIZE, OldPage);
636         }
637         DPRINTC("Free page %Ix (off %I64x from %p) (ref ct %lu, ent %Ix, dirty? %s)\n",
638                 OldPage,
639                 FileOffset->QuadPart,
640                 Segment,
641                 MmGetReferenceCountPage(OldPage),
642                 Entry,
643                 IS_DIRTY_SSE(Entry) ? "true" : "false");
644 
645         MmSetPageEntrySectionSegment(Segment, FileOffset, 0);
646         MmReleasePageMemoryConsumer(MC_CACHE, OldPage);
647     }
648     else if (IS_SWAP_FROM_SSE(Entry))
649     {
650         DPRINT("Free swap\n");
651         MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
652     }
653 
654     DPRINT("Done\n");
655 }
656 
657 VOID
658 MmFreeCacheSectionPage(PVOID Context,
659                        MEMORY_AREA* MemoryArea,
660                        PVOID Address,
661                        PFN_NUMBER Page,
662                        SWAPENTRY SwapEntry,
663                        BOOLEAN Dirty)
664 {
665     ULONG_PTR Entry;
666     PVOID *ContextData = Context;
667     PMMSUPPORT AddressSpace;
668     PEPROCESS Process;
669     PMM_SECTION_SEGMENT Segment;
670     LARGE_INTEGER Offset;
671 
672     DPRINT("MmFreeSectionPage(%p,%p,%Ix,%Ix,%u)\n",
673            MmGetAddressSpaceOwner(ContextData[0]),
674            Address,
675            Page,
676            SwapEntry,
677            Dirty);
678 
679     AddressSpace = ContextData[0];
680     Process = MmGetAddressSpaceOwner(AddressSpace);
681     Address = (PVOID)PAGE_ROUND_DOWN(Address);
682     Segment = ContextData[1];
683     Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea) +
684                       MemoryArea->Data.SectionData.ViewOffset.QuadPart;
685 
686     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
687 
688     if (Page != 0 && PFN_FROM_SSE(Entry) == Page && Dirty)
689     {
690         DPRINT("Freeing section page %p:%I64x -> %Ix\n", Segment, Offset.QuadPart, Entry);
691         MmSetPageEntrySectionSegment(Segment, &Offset, DIRTY_SSE(Entry));
692     }
693     if (Page)
694     {
695         DPRINT("Removing page %p:%I64x -> %x\n", Segment, Offset.QuadPart, Entry);
696         MmSetSavedSwapEntryPage(Page, 0);
697         MmDeleteRmap(Page, Process, Address);
698         MmDeleteVirtualMapping(Process, Address, NULL, NULL);
699         MmReleasePageMemoryConsumer(MC_CACHE, Page);
700     }
701     if (SwapEntry != 0)
702     {
703         MmFreeSwapPage(SwapEntry);
704     }
705 }
706 
707 NTSTATUS
708 NTAPI
709 MmUnmapViewOfCacheSegment(PMMSUPPORT AddressSpace,
710                           PVOID BaseAddress)
711 {
712     PVOID Context[2];
713     PMEMORY_AREA MemoryArea;
714     PMM_SECTION_SEGMENT Segment;
715 
716     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
717     if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
718     {
719         ASSERT(MemoryArea);
720         return STATUS_UNSUCCESSFUL;
721     }
722 
723     MemoryArea->DeleteInProgress = TRUE;
724     Segment = MemoryArea->Data.SectionData.Segment;
725     MemoryArea->Data.SectionData.Segment = NULL;
726 
727     MmLockSectionSegment(Segment);
728 
729     Context[0] = AddressSpace;
730     Context[1] = Segment;
731 
732     DPRINT("MmFreeMemoryArea(%p,%p)\n",
733            MmGetAddressSpaceOwner(AddressSpace),
734            MA_GetStartingAddress(MemoryArea));
735 
736     MmLockAddressSpace(AddressSpace);
737 
738     MmFreeMemoryArea(AddressSpace, MemoryArea, MmFreeCacheSectionPage, Context);
739 
740     MmUnlockAddressSpace(AddressSpace);
741 
742     MmUnlockSectionSegment(Segment);
743 
744     DPRINTC("MiUnmapViewOfSegment %p %p %p\n",
745             MmGetAddressSpaceOwner(AddressSpace),
746             BaseAddress,
747             Segment);
748 
749     return STATUS_SUCCESS;
750 }
751 
752 NTSTATUS
753 NTAPI
754 MmExtendCacheSection(PROS_SECTION_OBJECT Section,
755                      PLARGE_INTEGER NewSize,
756                      BOOLEAN ExtendFile)
757 {
758     LARGE_INTEGER OldSize;
759     PMM_SECTION_SEGMENT Segment = Section->Segment;
760     DPRINT("Extend Segment %p\n", Segment);
761 
762     MmLockSectionSegment(Segment);
763     OldSize.QuadPart = Segment->RawLength.QuadPart;
764     MmUnlockSectionSegment(Segment);
765 
766     DPRINT("OldSize 0x%I64x NewSize 0x%I64x\n",
767            OldSize.QuadPart,
768            NewSize->QuadPart);
769 
770     if (ExtendFile && OldSize.QuadPart < NewSize->QuadPart)
771     {
772         NTSTATUS Status;
773 
774         Status = IoSetInformation(Segment->FileObject,
775                                   FileEndOfFileInformation,
776                                   sizeof(LARGE_INTEGER),
777                                   NewSize);
778 
779         if (!NT_SUCCESS(Status)) return Status;
780     }
781 
782     MmLockSectionSegment(Segment);
783     Segment->RawLength.QuadPart = NewSize->QuadPart;
784     Segment->Length.QuadPart = MAX(Segment->Length.QuadPart,
785                                    (LONG64)PAGE_ROUND_UP(Segment->RawLength.QuadPart));
786     MmUnlockSectionSegment(Segment);
787     return STATUS_SUCCESS;
788 }
789 
790 NTSTATUS
791 NTAPI
792 MmMapCacheViewInSystemSpaceAtOffset(IN PMM_SECTION_SEGMENT Segment,
793                                     OUT PVOID *MappedBase,
794                                     PLARGE_INTEGER FileOffset,
795                                     IN OUT PULONG ViewSize)
796 {
797     PMMSUPPORT AddressSpace;
798     NTSTATUS Status;
799 
800     DPRINT("MmMapViewInSystemSpaceAtOffset() called offset 0x%I64x\n",
801            FileOffset->QuadPart);
802 
803     AddressSpace = MmGetKernelAddressSpace();
804 
805     MmLockAddressSpace(AddressSpace);
806     MmLockSectionSegment(Segment);
807 
808     Status = MiMapViewOfSegment(AddressSpace,
809                                 Segment,
810                                 MappedBase,
811                                 *ViewSize,
812                                 PAGE_READWRITE,
813                                 FileOffset,
814                                 0);
815 
816     MmUnlockSectionSegment(Segment);
817     MmUnlockAddressSpace(AddressSpace);
818 
819     return Status;
820 }
821 
822 /*
823  * @implemented
824  */
825 NTSTATUS NTAPI
826 MmUnmapCacheViewInSystemSpace (IN PVOID MappedBase)
827 {
828     PMMSUPPORT AddressSpace;
829     NTSTATUS Status;
830 
831     DPRINT("MmUnmapViewInSystemSpace() called\n");
832 
833     AddressSpace = MmGetKernelAddressSpace();
834 
835     Status = MmUnmapViewOfCacheSegment(AddressSpace, MappedBase);
836 
837     return Status;
838 }
839 
840 /* EOF */
841