[NTOSKRNL]
[reactos.git] / reactos / 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/mm/section.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 "../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 /* GLOBALS *******************************************************************/
93
94 static const INFORMATION_CLASS_INFO ExSectionInfoClass[] =
95 {
96 ICI_SQ_SAME( sizeof(SECTION_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionBasicInformation */
97 ICI_SQ_SAME( sizeof(SECTION_IMAGE_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionImageInformation */
98 };
99
100 /* FUNCTIONS *****************************************************************/
101
102 /* Note: Mmsp prefix denotes "Memory Manager Section Private". */
103
104 VOID
105 NTAPI
106 _MmLockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
107 {
108 //DPRINT("MmLockSectionSegment(%p,%s:%d)\n", Segment, file, line);
109 ExAcquireFastMutex(&Segment->Lock);
110 Segment->Locked = TRUE;
111 }
112
113 VOID
114 NTAPI
115 _MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
116 {
117 ASSERT(Segment->Locked);
118 Segment->Locked = FALSE;
119 ExReleaseFastMutex(&Segment->Lock);
120 //DPRINT("MmUnlockSectionSegment(%p,%s:%d)\n", Segment, file, line);
121 }
122
123 NTSTATUS
124 NTAPI
125 MiZeroFillSection(PVOID Address, PLARGE_INTEGER FileOffsetPtr, ULONG Length)
126 {
127 PFN_NUMBER Page;
128 PMMSUPPORT AddressSpace;
129 PMEMORY_AREA MemoryArea;
130 PMM_SECTION_SEGMENT Segment;
131 LARGE_INTEGER FileOffset = *FileOffsetPtr, End, FirstMapped;
132 KIRQL OldIrql;
133
134 DPRINT("MiZeroFillSection(Address %x,Offset %x,Length %x)\n",
135 Address,
136 FileOffset.LowPart,
137 Length);
138
139 AddressSpace = MmGetKernelAddressSpace();
140
141 MmLockAddressSpace(AddressSpace);
142 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
143 MmUnlockAddressSpace(AddressSpace);
144 if (!MemoryArea || MemoryArea->Type != MEMORY_AREA_SECTION_VIEW || MemoryArea->DeleteInProgress)
145 {
146 return STATUS_NOT_MAPPED_DATA;
147 }
148
149 Segment = MemoryArea->Data.SectionData.Segment;
150 End.QuadPart = FileOffset.QuadPart + Length;
151 End.LowPart = PAGE_ROUND_DOWN(End.LowPart);
152 FileOffset.LowPart = PAGE_ROUND_UP(FileOffset.LowPart);
153 FirstMapped.QuadPart = MemoryArea->Data.SectionData.ViewOffset.QuadPart;
154 DPRINT("Pulling zero pages for %08x%08x-%08x%08x\n",
155 FileOffset.u.HighPart, FileOffset.u.LowPart,
156 End.u.HighPart, End.u.LowPart);
157 while (FileOffset.QuadPart < End.QuadPart)
158 {
159 PVOID Address;
160 ULONG_PTR Entry;
161
162 if (!NT_SUCCESS(MmRequestPageMemoryConsumer(MC_CACHE, TRUE, &Page)))
163 break;
164
165 MmLockAddressSpace(AddressSpace);
166 MmLockSectionSegment(Segment);
167
168 Entry = MmGetPageEntrySectionSegment(Segment, &FileOffset);
169 if (Entry == 0)
170 {
171 MmSetPageEntrySectionSegment(Segment, &FileOffset, MAKE_PFN_SSE(Page));
172 Address = ((PCHAR)MemoryArea->StartingAddress) + FileOffset.QuadPart - FirstMapped.QuadPart;
173
174 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
175 MmReferencePage(Page);
176 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
177
178 MmCreateVirtualMapping(NULL, Address, PAGE_READWRITE, &Page, 1);
179 MmInsertRmap(Page, NULL, Address);
180 }
181 else
182 {
183 MmReleasePageMemoryConsumer(MC_CACHE, Page);
184 }
185
186 MmUnlockSectionSegment(Segment);
187 MmUnlockAddressSpace(AddressSpace);
188
189 FileOffset.QuadPart += PAGE_SIZE;
190 }
191 return STATUS_SUCCESS;
192 }
193
194 /*
195
196 MiFlushMappedSection
197
198 Called from cache code to cause dirty pages of a section
199 to be written back. This doesn't affect the mapping.
200
201 BaseOffset is the base at which to start writing in file space.
202 FileSize is the length of the file as understood by the cache.
203
204 */
205 NTSTATUS
206 NTAPI
207 _MiFlushMappedSection(PVOID BaseAddress,
208 PLARGE_INTEGER BaseOffset,
209 PLARGE_INTEGER FileSize,
210 BOOLEAN WriteData,
211 const char *File,
212 int Line)
213 {
214 NTSTATUS Status = STATUS_SUCCESS;
215 ULONG_PTR PageAddress;
216 PMMSUPPORT AddressSpace = MmGetKernelAddressSpace();
217 PMEMORY_AREA MemoryArea;
218 PMM_SECTION_SEGMENT Segment;
219 ULONG_PTR BeginningAddress, EndingAddress;
220 LARGE_INTEGER ViewOffset;
221 LARGE_INTEGER FileOffset;
222 PFN_NUMBER Page;
223 PPFN_NUMBER Pages;
224 KIRQL OldIrql;
225
226 DPRINT("MiFlushMappedSection(%x,%08x,%x,%d,%s:%d)\n",
227 BaseAddress,
228 BaseOffset->LowPart,
229 FileSize,
230 WriteData,
231 File,
232 Line);
233
234 MmLockAddressSpace(AddressSpace);
235 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
236 if (!MemoryArea || MemoryArea->Type != MEMORY_AREA_CACHE || MemoryArea->DeleteInProgress)
237 {
238 MmUnlockAddressSpace(AddressSpace);
239 DPRINT("STATUS_NOT_MAPPED_DATA\n");
240 return STATUS_NOT_MAPPED_DATA;
241 }
242 BeginningAddress = PAGE_ROUND_DOWN((ULONG_PTR)MemoryArea->StartingAddress);
243 EndingAddress = PAGE_ROUND_UP((ULONG_PTR)MemoryArea->EndingAddress);
244 Segment = MemoryArea->Data.SectionData.Segment;
245 ViewOffset.QuadPart = MemoryArea->Data.SectionData.ViewOffset.QuadPart;
246
247 ASSERT(ViewOffset.QuadPart == BaseOffset->QuadPart);
248
249 MmLockSectionSegment(Segment);
250
251 Pages = ExAllocatePool(NonPagedPool,
252 sizeof(PFN_NUMBER) * ((EndingAddress - BeginningAddress) >> PAGE_SHIFT));
253
254 if (!Pages)
255 {
256 ASSERT(FALSE);
257 }
258
259 //DPRINT("Getting pages in range %08x-%08x\n", BeginningAddress, EndingAddress);
260
261 for (PageAddress = BeginningAddress;
262 PageAddress < EndingAddress;
263 PageAddress += PAGE_SIZE)
264 {
265 ULONG_PTR Entry;
266 FileOffset.QuadPart = ViewOffset.QuadPart + PageAddress - BeginningAddress;
267 Entry = MmGetPageEntrySectionSegment(MemoryArea->Data.SectionData.Segment,
268 &FileOffset);
269 Page = PFN_FROM_SSE(Entry);
270 if (Entry != 0 && !IS_SWAP_FROM_SSE(Entry) &&
271 (MmIsDirtyPageRmap(Page) || IS_DIRTY_SSE(Entry)) &&
272 FileOffset.QuadPart < FileSize->QuadPart)
273 {
274 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
275 MmReferencePage(Page);
276 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
277 Pages[(PageAddress - BeginningAddress) >> PAGE_SHIFT] = Entry;
278 }
279 else
280 {
281 Pages[(PageAddress - BeginningAddress) >> PAGE_SHIFT] = 0;
282 }
283 }
284
285 MmUnlockSectionSegment(Segment);
286 MmUnlockAddressSpace(AddressSpace);
287
288 for (PageAddress = BeginningAddress;
289 PageAddress < EndingAddress;
290 PageAddress += PAGE_SIZE)
291 {
292 ULONG_PTR Entry;
293 FileOffset.QuadPart = ViewOffset.QuadPart + PageAddress - BeginningAddress;
294 Entry = Pages[(PageAddress - BeginningAddress) >> PAGE_SHIFT];
295 Page = PFN_FROM_SSE(Entry);
296 if (Page)
297 {
298 if (WriteData) {
299 //DPRINT("MiWriteBackPage(%wZ,addr %x,%08x%08x)\n", &Segment->FileObject->FileName, PageAddress, FileOffset.u.HighPart, FileOffset.u.LowPart);
300 Status = MiWriteBackPage(Segment->FileObject, &FileOffset, PAGE_SIZE, Page);
301 } else
302 Status = STATUS_SUCCESS;
303
304 if (NT_SUCCESS(Status)) {
305 MmLockAddressSpace(AddressSpace);
306 MmSetCleanAllRmaps(Page);
307
308 MmSetPageProtect(MmGetAddressSpaceOwner(AddressSpace),
309 (PVOID)PageAddress,
310 PAGE_READONLY);
311
312 MmLockSectionSegment(Segment);
313 Entry = MmGetPageEntrySectionSegment(Segment, &FileOffset);
314
315 if (Entry && !IS_SWAP_FROM_SSE(Entry) && PFN_FROM_SSE(Entry) == Page)
316 MmSetPageEntrySectionSegment(Segment, &FileOffset, CLEAN_SSE(Entry));
317
318 MmUnlockSectionSegment(Segment);
319 MmUnlockAddressSpace(AddressSpace);
320 } else {
321 DPRINT("Writeback from section flush %08x%08x (%x) %x@%x (%08x%08x:%wZ) failed %x\n",
322 FileOffset.u.HighPart,
323 FileOffset.u.LowPart,
324 (ULONG)(FileSize->QuadPart - FileOffset.QuadPart),
325 PageAddress,
326 Page,
327 FileSize->u.HighPart,
328 FileSize->u.LowPart,
329 &Segment->FileObject->FileName,
330 Status);
331 }
332 MmReleasePageMemoryConsumer(MC_CACHE, Page);
333 }
334 }
335
336 ExFreePool(Pages);
337
338 return Status;
339 }
340
341 /*
342
343 This deletes a segment entirely including its page map.
344 It must have been unmapped in every address space.
345
346 */
347
348 VOID
349 NTAPI
350 MmFinalizeSegment(PMM_SECTION_SEGMENT Segment)
351 {
352 KIRQL OldIrql = 0;
353
354 DPRINT("Finalize segment %p\n", Segment);
355
356 MmLockSectionSegment(Segment);
357 RemoveEntryList(&Segment->ListOfSegments);
358 if (Segment->Flags & MM_DATAFILE_SEGMENT) {
359 KeAcquireSpinLock(&Segment->FileObject->IrpListLock, &OldIrql);
360 if (Segment->Flags & MM_SEGMENT_FINALIZE) {
361 KeReleaseSpinLock(&Segment->FileObject->IrpListLock, OldIrql);
362 MmUnlockSectionSegment(Segment);
363 return;
364 }
365 Segment->Flags |= MM_SEGMENT_FINALIZE;
366 DPRINTC("Finalizing data file segment %p\n", Segment);
367
368 Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
369 KeReleaseSpinLock(&Segment->FileObject->IrpListLock, OldIrql);
370 MmFreePageTablesSectionSegment(Segment, MiFreeSegmentPage);
371 MmUnlockSectionSegment(Segment);
372 DPRINT("Dereference file object %wZ\n", &Segment->FileObject->FileName);
373 ObDereferenceObject(Segment->FileObject);
374 DPRINT("Done with %wZ\n", &Segment->FileObject->FileName);
375 Segment->FileObject = NULL;
376 } else {
377 DPRINTC("Finalizing segment %p\n", Segment);
378 MmFreePageTablesSectionSegment(Segment, MiFreeSegmentPage);
379 MmUnlockSectionSegment(Segment);
380 }
381 DPRINTC("Segment %p destroy\n", Segment);
382 ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
383 }
384
385 NTSTATUS
386 NTAPI
387 MmCreateCacheSection(PROS_SECTION_OBJECT *SectionObject,
388 ACCESS_MASK DesiredAccess,
389 POBJECT_ATTRIBUTES ObjectAttributes,
390 PLARGE_INTEGER UMaximumSize,
391 ULONG SectionPageProtection,
392 ULONG AllocationAttributes,
393 PFILE_OBJECT FileObject)
394 /*
395 * Create a section backed by a data file.
396 */
397 {
398 PROS_SECTION_OBJECT Section;
399 NTSTATUS Status;
400 LARGE_INTEGER MaximumSize;
401 PMM_SECTION_SEGMENT Segment;
402 IO_STATUS_BLOCK Iosb;
403 CC_FILE_SIZES FileSizes;
404 FILE_STANDARD_INFORMATION FileInfo;
405 KIRQL OldIrql;
406
407 DPRINT("MmCreateDataFileSection\n");
408
409 /* Create the section */
410 Status = ObCreateObject(ExGetPreviousMode(),
411 MmSectionObjectType,
412 ObjectAttributes,
413 ExGetPreviousMode(),
414 NULL,
415 sizeof(ROS_SECTION_OBJECT),
416 0,
417 0,
418 (PVOID*)(PVOID)&Section);
419 if (!NT_SUCCESS(Status))
420 {
421 DPRINT("Failed: %x\n", Status);
422 return Status;
423 }
424
425 /* Initialize it */
426 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
427 Section->Type = 'SC';
428 Section->Size = 'TN';
429 Section->SectionPageProtection = SectionPageProtection;
430 Section->AllocationAttributes = AllocationAttributes;
431 Section->Segment = NULL;
432
433 Section->FileObject = FileObject;
434
435 DPRINT("Getting original file size\n");
436 /* A hack: If we're cached, we can overcome deadlocking with the upper
437 * layer filesystem call by retriving the object sizes from the cache
438 * which is made to keep track. If I had to guess, they were figuring
439 * out a similar problem.
440 */
441 if (!CcGetFileSizes(FileObject, &FileSizes))
442 {
443 ULONG Information;
444 /*
445 * FIXME: This is propably not entirely correct. We can't look into
446 * the standard FCB header because it might not be initialized yet
447 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
448 * standard file information is filled on first request).
449 */
450 DPRINT("Querying info\n");
451 Status = IoQueryFileInformation(FileObject,
452 FileStandardInformation,
453 sizeof(FILE_STANDARD_INFORMATION),
454 &FileInfo,
455 &Information);
456 Iosb.Information = Information;
457 DPRINT("Query => %x\n", Status);
458
459 if (!NT_SUCCESS(Status))
460 {
461 DPRINT("Status %x\n", Status);
462 ObDereferenceObject(Section);
463 return Status;
464 }
465 ASSERT(Status != STATUS_PENDING);
466
467 FileSizes.ValidDataLength = FileInfo.EndOfFile;
468 FileSizes.FileSize = FileInfo.EndOfFile;
469 }
470 DPRINT("Got %08x\n", FileSizes.ValidDataLength.u.LowPart);
471
472 /*
473 * FIXME: Revise this once a locking order for file size changes is
474 * decided
475 *
476 * We're handed down a maximum size in every case. Should we still check at all?
477 */
478 if (UMaximumSize != NULL && UMaximumSize->QuadPart)
479 {
480 DPRINT("Taking maximum %x\n", UMaximumSize->LowPart);
481 MaximumSize.QuadPart = UMaximumSize->QuadPart;
482 }
483 else
484 {
485 DPRINT("Got file size %08x%08x\n",
486 FileSizes.FileSize.u.HighPart,
487 FileSizes.FileSize.u.LowPart);
488
489 MaximumSize.QuadPart = FileSizes.FileSize.QuadPart;
490 }
491
492 /* Mapping zero-sized files isn't allowed. */
493 if (MaximumSize.QuadPart == 0)
494 {
495 DPRINT("Zero size file\n");
496 ObDereferenceObject(Section);
497 return STATUS_FILE_INVALID;
498 }
499
500 Segment = ExAllocatePoolWithTag(NonPagedPool,
501 sizeof(MM_SECTION_SEGMENT),
502 TAG_MM_SECTION_SEGMENT);
503 if (Segment == NULL)
504 {
505 DPRINT("Failed: STATUS_NO_MEMORY\n");
506 ObDereferenceObject(Section);
507 return STATUS_NO_MEMORY;
508 }
509
510 DPRINT("Zeroing %x\n", Segment);
511 RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
512 ExInitializeFastMutex(&Segment->Lock);
513
514 Segment->ReferenceCount = 1;
515 Segment->Locked = TRUE;
516 RtlZeroMemory(&Segment->Image, sizeof(Segment->Image));
517 Section->Segment = Segment;
518
519 KeAcquireSpinLock(&FileObject->IrpListLock, &OldIrql);
520 /*
521 * If this file hasn't been mapped as a data file before then allocate a
522 * section segment to describe the data file mapping
523 */
524 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
525 {
526 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
527 KeReleaseSpinLock(&FileObject->IrpListLock, OldIrql);
528
529 /*
530 * Set the lock before assigning the segment to the file object
531 */
532 ExAcquireFastMutex(&Segment->Lock);
533
534 DPRINT("Filling out Segment info (No previous data section)\n");
535 ObReferenceObject(FileObject);
536 Segment->FileObject = FileObject;
537 Segment->Protection = SectionPageProtection;
538 Segment->Flags = MM_DATAFILE_SEGMENT;
539 memset(&Segment->Image, 0, sizeof(Segment->Image));
540 Segment->WriteCopy = FALSE;
541
542 if (AllocationAttributes & SEC_RESERVE)
543 {
544 Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
545 }
546 else
547 {
548 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
549 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
550 }
551 MiInitializeSectionPageTable(Segment);
552 InsertHeadList(&MiSegmentList, &Segment->ListOfSegments);
553 }
554 else
555 {
556 KeReleaseSpinLock(&FileObject->IrpListLock, OldIrql);
557 DPRINTC("Free Segment %x\n", Segment);
558 ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
559
560 DPRINT("Filling out Segment info (previous data section)\n");
561
562 /*
563 * If the file is already mapped as a data file then we may need
564 * to extend it
565 */
566 Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->DataSectionObject;
567 Section->Segment = Segment;
568 (void)InterlockedIncrementUL(&Segment->ReferenceCount);
569
570 MmLockSectionSegment(Segment);
571
572 if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
573 !(AllocationAttributes & SEC_RESERVE))
574 {
575 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
576 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
577 }
578 }
579
580 MmUnlockSectionSegment(Segment);
581
582 Section->MaximumSize.QuadPart = MaximumSize.QuadPart;
583
584 /* Extend file if section is longer */
585 DPRINT("MaximumSize %08x%08x ValidDataLength %08x%08x\n",
586 MaximumSize.u.HighPart,
587 MaximumSize.u.LowPart,
588 FileSizes.ValidDataLength.u.HighPart,
589 FileSizes.ValidDataLength.u.LowPart);
590 if (MaximumSize.QuadPart > FileSizes.ValidDataLength.QuadPart)
591 {
592 DPRINT("Changing file size to %08x%08x, segment %x\n",
593 MaximumSize.u.HighPart,
594 MaximumSize.u.LowPart,
595 Segment);
596
597 Status = IoSetInformation(FileObject,
598 FileEndOfFileInformation,
599 sizeof(LARGE_INTEGER),
600 &MaximumSize);
601
602 DPRINT("Change: Status %x\n", Status);
603 if (!NT_SUCCESS(Status))
604 {
605 DPRINT("Could not expand section\n");
606 ObDereferenceObject(Section);
607 return Status;
608 }
609 }
610
611 DPRINTC("Segment %x created (%x)\n", Segment, Segment->Flags);
612
613 *SectionObject = Section;
614 return STATUS_SUCCESS;
615 }
616
617 NTSTATUS
618 NTAPI
619 _MiMapViewOfSegment(PMMSUPPORT AddressSpace,
620 PMM_SECTION_SEGMENT Segment,
621 PVOID* BaseAddress,
622 SIZE_T ViewSize,
623 ULONG Protect,
624 PLARGE_INTEGER ViewOffset,
625 ULONG AllocationType,
626 const char *file,
627 int line)
628 {
629 PMEMORY_AREA MArea;
630 NTSTATUS Status;
631 PHYSICAL_ADDRESS BoundaryAddressMultiple;
632
633 BoundaryAddressMultiple.QuadPart = 0;
634
635 Status = MmCreateMemoryArea(AddressSpace,
636 MEMORY_AREA_CACHE,
637 BaseAddress,
638 ViewSize,
639 Protect,
640 &MArea,
641 FALSE,
642 AllocationType,
643 BoundaryAddressMultiple);
644
645 if (!NT_SUCCESS(Status))
646 {
647 DPRINT("Mapping between 0x%.8X and 0x%.8X failed (%X).\n",
648 (*BaseAddress),
649 (char*)(*BaseAddress) + ViewSize,
650 Status);
651
652 return Status;
653 }
654
655 DPRINTC("MiMapViewOfSegment %x %x %x %x %x %wZ %s:%d\n",
656 MmGetAddressSpaceOwner(AddressSpace),
657 *BaseAddress,
658 Segment,
659 ViewOffset ? ViewOffset->LowPart : 0,
660 ViewSize,
661 Segment->FileObject ? &Segment->FileObject->FileName : NULL,
662 file,
663 line);
664
665 MArea->Data.SectionData.Segment = Segment;
666 if (ViewOffset)
667 MArea->Data.SectionData.ViewOffset = *ViewOffset;
668 else
669 MArea->Data.SectionData.ViewOffset.QuadPart = 0;
670
671 #if 0
672 MArea->NotPresent = MmNotPresentFaultPageFile;
673 MArea->AccessFault = MiCowSectionPage;
674 MArea->PageOut = MmPageOutPageFileView;
675 #endif
676
677 MmInitializeRegion(&MArea->Data.SectionData.RegionListHead,
678 ViewSize,
679 0,
680 Protect);
681
682 DPRINTC("MiMapViewOfSegment(P %x, A %x, T %x)\n",
683 MmGetAddressSpaceOwner(AddressSpace),
684 *BaseAddress,
685 MArea->Type);
686
687 return STATUS_SUCCESS;
688 }
689
690 /*
691
692 Completely remove the page at FileOffset in Segment. The page must not
693 be mapped.
694
695 */
696
697 VOID
698 NTAPI
699 MiFreeSegmentPage(PMM_SECTION_SEGMENT Segment,
700 PLARGE_INTEGER FileOffset)
701 {
702 ULONG_PTR Entry;
703 PFILE_OBJECT FileObject = Segment->FileObject;
704
705 Entry = MmGetPageEntrySectionSegment(Segment, FileOffset);
706 DPRINTC("MiFreeSegmentPage(%x:%08x%08x -> Entry %x\n",
707 Segment,
708 FileOffset->HighPart,
709 FileOffset->LowPart,
710 Entry);
711
712 if (Entry && !IS_SWAP_FROM_SSE(Entry))
713 {
714 // The segment is carrying a dirty page.
715 PFN_NUMBER OldPage = PFN_FROM_SSE(Entry);
716 if (IS_DIRTY_SSE(Entry) && FileObject)
717 {
718 DPRINT("MiWriteBackPage(%x,%wZ,%08x%08x)\n",
719 Segment,
720 &FileObject->FileName,
721 FileOffset->u.HighPart,
722 FileOffset->u.LowPart);
723
724 MiWriteBackPage(FileObject, FileOffset, PAGE_SIZE, OldPage);
725 }
726 DPRINTC("Free page %x (off %x from %x) (ref ct %d, ent %x, dirty? %s)\n",
727 OldPage,
728 FileOffset->LowPart,
729 Segment,
730 MmGetReferenceCountPage(OldPage),
731 Entry,
732 IS_DIRTY_SSE(Entry) ? "true" : "false");
733
734 MmSetPageEntrySectionSegment(Segment, FileOffset, 0);
735 MmReleasePageMemoryConsumer(MC_CACHE, OldPage);
736 }
737 else if (IS_SWAP_FROM_SSE(Entry))
738 {
739 DPRINT("Free swap\n");
740 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
741 }
742
743 DPRINT("Done\n");
744 }
745
746 VOID
747 MmFreeCacheSectionPage(PVOID Context,
748 MEMORY_AREA* MemoryArea,
749 PVOID Address,
750 PFN_NUMBER Page,
751 SWAPENTRY SwapEntry,
752 BOOLEAN Dirty)
753 {
754 ULONG_PTR Entry;
755 PVOID *ContextData = Context;
756 PMMSUPPORT AddressSpace;
757 PEPROCESS Process;
758 PMM_SECTION_SEGMENT Segment;
759 LARGE_INTEGER Offset;
760
761 DPRINT("MmFreeSectionPage(%x,%x,%x,%x,%d)\n",
762 MmGetAddressSpaceOwner(ContextData[0]),
763 Address,
764 Page,
765 SwapEntry,
766 Dirty);
767
768 AddressSpace = ContextData[0];
769 Process = MmGetAddressSpaceOwner(AddressSpace);
770 Address = (PVOID)PAGE_ROUND_DOWN(Address);
771 Segment = ContextData[1];
772 Offset.QuadPart = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress +
773 MemoryArea->Data.SectionData.ViewOffset.QuadPart;
774
775 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
776
777 if (Page != 0 && PFN_FROM_SSE(Entry) == Page && Dirty)
778 {
779 DPRINT("Freeing section page %x:%x -> %x\n", Segment, Offset.LowPart, Entry);
780 MmSetPageEntrySectionSegment(Segment, &Offset, DIRTY_SSE(Entry));
781 }
782 if (Page)
783 {
784 DPRINT("Removing page %x:%x -> %x\n", Segment, Offset.LowPart, Entry);
785 MmSetSavedSwapEntryPage(Page, 0);
786 MmDeleteRmap(Page, Process, Address);
787 MmDeleteVirtualMapping(Process, Address, FALSE, NULL, NULL);
788 MmReleasePageMemoryConsumer(MC_CACHE, Page);
789 }
790 if (SwapEntry != 0)
791 {
792 MmFreeSwapPage(SwapEntry);
793 }
794 }
795
796 NTSTATUS
797 NTAPI
798 MmUnmapViewOfCacheSegment(PMMSUPPORT AddressSpace,
799 PVOID BaseAddress)
800 {
801 PVOID Context[2];
802 PMEMORY_AREA MemoryArea;
803 PMM_SECTION_SEGMENT Segment;
804
805 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
806 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
807 {
808 ASSERT(MemoryArea);
809 return STATUS_UNSUCCESSFUL;
810 }
811
812 MemoryArea->DeleteInProgress = TRUE;
813 Segment = MemoryArea->Data.SectionData.Segment;
814 MemoryArea->Data.SectionData.Segment = NULL;
815
816 MmLockSectionSegment(Segment);
817
818 Context[0] = AddressSpace;
819 Context[1] = Segment;
820
821 DPRINT("MmFreeMemoryArea(%x,%x)\n",
822 MmGetAddressSpaceOwner(AddressSpace),
823 MemoryArea->StartingAddress);
824
825 MmFreeMemoryArea(AddressSpace, MemoryArea, MmFreeCacheSectionPage, Context);
826
827 MmUnlockSectionSegment(Segment);
828
829 DPRINTC("MiUnmapViewOfSegment %x %x %x\n",
830 MmGetAddressSpaceOwner(AddressSpace),
831 BaseAddress,
832 Segment);
833
834 return STATUS_SUCCESS;
835 }
836
837 NTSTATUS
838 NTAPI
839 MmExtendCacheSection(PROS_SECTION_OBJECT Section,
840 PLARGE_INTEGER NewSize,
841 BOOLEAN ExtendFile)
842 {
843 LARGE_INTEGER OldSize;
844 PMM_SECTION_SEGMENT Segment = Section->Segment;
845 DPRINT("Extend Segment %x\n", Segment);
846
847 MmLockSectionSegment(Segment);
848 OldSize.QuadPart = Segment->RawLength.QuadPart;
849 MmUnlockSectionSegment(Segment);
850
851 DPRINT("OldSize %08x%08x NewSize %08x%08x\n",
852 OldSize.u.HighPart, OldSize.u.LowPart,
853 NewSize->u.HighPart, NewSize->u.LowPart);
854
855 if (ExtendFile && OldSize.QuadPart < NewSize->QuadPart)
856 {
857 NTSTATUS Status;
858
859 Status = IoSetInformation(Segment->FileObject,
860 FileEndOfFileInformation,
861 sizeof(LARGE_INTEGER),
862 NewSize);
863
864 if (!NT_SUCCESS(Status)) return Status;
865 }
866
867 MmLockSectionSegment(Segment);
868 Segment->RawLength.QuadPart = NewSize->QuadPart;
869 Segment->Length.QuadPart = MAX(Segment->Length.QuadPart,
870 (LONG64)PAGE_ROUND_UP(Segment->RawLength.QuadPart));
871 MmUnlockSectionSegment(Segment);
872 return STATUS_SUCCESS;
873 }
874
875 NTSTATUS
876 NTAPI
877 MmMapCacheViewInSystemSpaceAtOffset(IN PMM_SECTION_SEGMENT Segment,
878 OUT PVOID *MappedBase,
879 PLARGE_INTEGER FileOffset,
880 IN OUT PULONG ViewSize)
881 {
882 PMMSUPPORT AddressSpace;
883 NTSTATUS Status;
884
885 DPRINT("MmMapViewInSystemSpaceAtOffset() called offset %08x%08x\n",
886 FileOffset->HighPart,
887 FileOffset->LowPart);
888
889 AddressSpace = MmGetKernelAddressSpace();
890
891 MmLockAddressSpace(AddressSpace);
892 MmLockSectionSegment(Segment);
893
894 Status = MiMapViewOfSegment(AddressSpace,
895 Segment,
896 MappedBase,
897 *ViewSize,
898 PAGE_READWRITE,
899 FileOffset,
900 0);
901
902 MmUnlockSectionSegment(Segment);
903 MmUnlockAddressSpace(AddressSpace);
904
905 return Status;
906 }
907
908 /*
909 * @implemented
910 */
911 NTSTATUS NTAPI
912 MmUnmapCacheViewInSystemSpace (IN PVOID MappedBase)
913 {
914 PMMSUPPORT AddressSpace;
915 NTSTATUS Status;
916
917 DPRINT("MmUnmapViewInSystemSpace() called\n");
918
919 AddressSpace = MmGetKernelAddressSpace();
920
921 Status = MmUnmapViewOfCacheSegment(AddressSpace, MappedBase);
922
923 return Status;
924 }
925
926 /* EOF */