[NTOS:MM] Use inline functions to acquire/release the PFN lock.
[reactos.git] / ntoskrnl / cache / section / data.c
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 */