2 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
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.
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.
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.
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/section/fault.c
21 * PURPOSE: Consolidate fault handlers for sections
37 * Thomas Weidenmueller
38 * Gunnar Andre' Dalsnes
46 /* INCLUDES *****************************************************************/
53 #define DPRINTC DPRINT
55 extern KEVENT MmWaitPageEvent
;
56 extern FAST_MUTEX RmapListLock
;
58 FAST_MUTEX GlobalPageOperation
;
63 (PMM_CACHE_SECTION_SEGMENT Segment
, PLARGE_INTEGER FileOffset
, BOOLEAN
*Dirty
)
67 DPRINT("MmWithdrawSectionPage(%x,%08x%08x,%x)\n", Segment
, FileOffset
->HighPart
, FileOffset
->LowPart
, Dirty
);
69 MmLockCacheSectionSegment(Segment
);
70 Entry
= MiGetPageEntryCacheSectionSegment(Segment
, FileOffset
);
72 *Dirty
= !!IS_DIRTY_SSE(Entry
);
74 DPRINT("Withdraw %x (%x) of %wZ\n", FileOffset
->LowPart
, Entry
, Segment
->FileObject
? &Segment
->FileObject
->FileName
: NULL
);
79 MmUnlockCacheSectionSegment(Segment
);
82 else if (MM_IS_WAIT_PTE(Entry
))
85 MmUnlockCacheSectionSegment(Segment
);
88 else if (Entry
&& !IS_SWAP_FROM_SSE(Entry
))
90 DPRINT("Page %x\n", PFN_FROM_SSE(Entry
));
91 *Dirty
|= (Entry
& 2);
92 MiSetPageEntryCacheSectionSegment(Segment
, FileOffset
, MAKE_SWAP_SSE(MM_WAIT_ENTRY
));
93 MmUnlockCacheSectionSegment(Segment
);
94 return PFN_FROM_SSE(Entry
);
98 DPRINT1("SWAP ENTRY?! (%x:%08x%08x)\n", Segment
, FileOffset
->HighPart
, FileOffset
->LowPart
);
100 MmUnlockCacheSectionSegment(Segment
);
107 MmFinalizeSectionPageOut
108 (PMM_CACHE_SECTION_SEGMENT Segment
, PLARGE_INTEGER FileOffset
, PFN_NUMBER Page
,
111 NTSTATUS Status
= STATUS_SUCCESS
;
112 BOOLEAN WriteZero
= FALSE
, WritePage
= FALSE
;
113 SWAPENTRY Swap
= MmGetSavedSwapEntryPage(Page
);
115 MmLockCacheSectionSegment(Segment
);
116 (void)InterlockedIncrementUL(&Segment
->ReferenceCount
);
120 DPRINT("Finalize (dirty) Segment %x Page %x\n", Segment
, Page
);
121 DPRINT("Segment->FileObject %x\n", Segment
->FileObject
);
122 DPRINT("Segment->Flags %x\n", Segment
->Flags
);
132 DPRINT("Status %x\n", Status
);
134 MmUnlockCacheSectionSegment(Segment
);
138 DPRINT("MiWriteBackPage(Segment %x FileObject %x Offset %x)\n", Segment
, Segment
->FileObject
, FileOffset
->LowPart
);
139 Status
= MiWriteBackPage(Segment
->FileObject
, FileOffset
, PAGE_SIZE
, Page
);
142 MmLockCacheSectionSegment(Segment
);
144 if (WriteZero
&& NT_SUCCESS(Status
))
146 DPRINT("Setting page entry in segment %x:%x to swap %x\n", Segment
, FileOffset
->LowPart
, Swap
);
147 MiSetPageEntryCacheSectionSegment(Segment
, FileOffset
, Swap
? MAKE_SWAP_SSE(Swap
) : 0);
151 DPRINT("Setting page entry in segment %x:%x to page %x\n", Segment
, FileOffset
->LowPart
, Page
);
152 MiSetPageEntryCacheSectionSegment
153 (Segment
, FileOffset
, Page
? (Dirty
? DIRTY_SSE(MAKE_PFN_SSE(Page
)) : MAKE_PFN_SSE(Page
)) : 0);
156 if (NT_SUCCESS(Status
))
158 DPRINT("Removing page %x for real\n", Page
);
159 MmSetSavedSwapEntryPage(Page
, 0);
160 // Note: the other one is held by MmTrimUserMemory
161 if (MmGetReferenceCountPage(Page
) != 2) {
162 DPRINT1("ALERT: Page %x about to be evicted with ref count %d\n", Page
, MmGetReferenceCountPage(Page
));
164 MmDereferencePage(Page
);
167 MmUnlockCacheSectionSegment(Segment
);
169 if (InterlockedDecrementUL(&Segment
->ReferenceCount
) == 0)
171 MmFinalizeSegment(Segment
);
174 /* Note: Writing may evict the segment... Nothing is guaranteed from here down */
175 MiSetPageEvent(Segment
, FileOffset
->LowPart
);
177 DPRINT("Status %x\n", Status
);
183 MmPageOutCacheSection
184 (PMMSUPPORT AddressSpace
,
185 MEMORY_AREA
* MemoryArea
,
187 PMM_REQUIRED_RESOURCES Required
)
189 NTSTATUS Status
= STATUS_SUCCESS
;
191 BOOLEAN Dirty
= FALSE
;
192 PEPROCESS Process
= MmGetAddressSpaceOwner(AddressSpace
);
193 LARGE_INTEGER TotalOffset
;
194 PMM_CACHE_SECTION_SEGMENT Segment
;
195 PVOID PAddress
= MM_ROUND_DOWN(Address
, PAGE_SIZE
);
197 TotalOffset
.QuadPart
= (ULONG_PTR
)PAddress
- (ULONG_PTR
)MemoryArea
->StartingAddress
+
198 MemoryArea
->Data
.CacheData
.ViewOffset
.QuadPart
;
200 Segment
= MemoryArea
->Data
.CacheData
.Segment
;
202 MmLockCacheSectionSegment(Segment
);
203 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
205 Dirty
= MmIsDirtyPageRmap(Required
->Page
[0]);
206 Entry
= MiGetPageEntryCacheSectionSegment(Segment
, &TotalOffset
);
211 MiSetPageEntryCacheSectionSegment(Segment
, &TotalOffset
, DIRTY_SSE(Entry
));
212 MmDeleteRmap(Required
->Page
[0], Process
, Address
);
213 MmDeleteVirtualMapping(Process
, Address
, FALSE
, &Dirty
, &OurPage
);
214 ASSERT(OurPage
== Required
->Page
[0]);
216 /* Just unmap if the page wasn't dirty */
218 MmDeleteRmap(Required
->Page
[0], Process
, Address
);
219 MmDeleteVirtualMapping(Process
, Address
, FALSE
, &Dirty
, &OurPage
);
220 DPRINT("OurPage %x ThePage %x\n", OurPage
, Required
->Page
[0]);
221 ASSERT(OurPage
== Required
->Page
[0]);
224 if (NT_SUCCESS(Status
))
226 MmDereferencePage(Required
->Page
[0]);
229 MmUnlockCacheSectionSegment(Segment
);
230 MiSetPageEvent(Process
, Address
);
236 MmpPageOutPhysicalAddress(PFN_NUMBER Page
)
238 BOOLEAN ProcRef
= FALSE
;
239 PFN_NUMBER SectionPage
= 0;
240 PMM_RMAP_ENTRY entry
;
241 PMM_CACHE_SECTION_SEGMENT Segment
= NULL
;
242 LARGE_INTEGER FileOffset
;
243 PMEMORY_AREA MemoryArea
;
244 PMMSUPPORT AddressSpace
= MmGetKernelAddressSpace();
245 BOOLEAN Dirty
= FALSE
;
246 PVOID Address
= NULL
;
247 PEPROCESS Process
= NULL
;
248 NTSTATUS Status
= STATUS_SUCCESS
;
249 MM_REQUIRED_RESOURCES Resources
= { 0 };
251 DPRINTC("Page out %x (ref ct %x)\n", Page
, MmGetReferenceCountPage(Page
));
253 ExAcquireFastMutex(&GlobalPageOperation
);
254 if ((Segment
= MmGetSectionAssociation(Page
, &FileOffset
)))
256 DPRINT1("Withdrawing page (%x) %x:%x\n", Page
, Segment
, FileOffset
.LowPart
);
257 SectionPage
= MmWithdrawSectionPage(Segment
, &FileOffset
, &Dirty
);
258 DPRINTC("SectionPage %x\n", SectionPage
);
260 if (SectionPage
== MM_WAIT_ENTRY
|| SectionPage
== 0)
262 DPRINT1("In progress page out %x\n", SectionPage
);
263 ExReleaseFastMutex(&GlobalPageOperation
);
264 return STATUS_UNSUCCESSFUL
;
268 ASSERT(SectionPage
== Page
);
270 Resources
.State
= Dirty
? 1 : 0;
274 DPRINT("No segment association for %x\n", Page
);
278 Dirty
= MmIsDirtyPageRmap(Page
);
280 DPRINTC("Trying to unmap all instances of %x\n", Page
);
281 ExAcquireFastMutex(&RmapListLock
);
282 entry
= MmGetRmapListHeadPage(Page
);
284 // Entry and Segment might be null here in the case that the page
285 // is new and is in the process of being swapped in
286 if (!entry
&& !Segment
)
288 Status
= STATUS_UNSUCCESSFUL
;
289 DPRINT1("Page %x is in transit\n", Page
);
290 ExReleaseFastMutex(&RmapListLock
);
294 while (entry
!= NULL
&& NT_SUCCESS(Status
))
296 Process
= entry
->Process
;
297 Address
= entry
->Address
;
299 DPRINTC("Process %x Address %x Page %x\n", Process
, Address
, Page
);
301 if (RMAP_IS_SEGMENT(Address
)) {
306 if (Process
&& Address
< MmSystemRangeStart
)
308 // Make sure we don't try to page out part of an exiting process
309 if (PspIsProcessExiting(Process
))
312 ExReleaseFastMutex(&RmapListLock
);
315 Status
= ObReferenceObject(Process
);
316 if (!NT_SUCCESS(Status
))
319 ExReleaseFastMutex(&RmapListLock
);
323 AddressSpace
= &Process
->Vm
;
327 AddressSpace
= MmGetKernelAddressSpace();
329 ExReleaseFastMutex(&RmapListLock
);
331 RtlZeroMemory(&Resources
, sizeof(Resources
));
333 if ((((ULONG_PTR
)Address
) & 0xFFF) != 0)
335 KeBugCheck(MEMORY_MANAGEMENT
);
338 if (!MmTryToLockAddressSpace(AddressSpace
))
340 DPRINT1("Could not lock address space for process %x\n", MmGetAddressSpaceOwner(AddressSpace
));
341 Status
= STATUS_UNSUCCESSFUL
;
347 MemoryArea
= MmLocateMemoryAreaByAddress(AddressSpace
, Address
);
348 if (MemoryArea
== NULL
||
349 MemoryArea
->DeleteInProgress
)
351 Status
= STATUS_UNSUCCESSFUL
;
352 MmUnlockAddressSpace(AddressSpace
);
358 ("Type %x (%x -> %x)\n",
360 MemoryArea
->StartingAddress
,
361 MemoryArea
->EndingAddress
);
363 Resources
.DoAcquisition
= NULL
;
365 Resources
.Page
[0] = Page
;
367 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
369 DPRINT("%x:%x, page %x %x\n", Process
, Address
, Page
, Resources
.Page
[0]);
370 Status
= MmPageOutCacheSection
371 (AddressSpace
, MemoryArea
, Address
, &Resources
);
372 DPRINT("%x\n", Status
);
374 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
376 MmUnlockAddressSpace(AddressSpace
);
378 if (Status
== STATUS_SUCCESS
+ 1)
380 // Wait page ... the other guy has it, so we'll just fail for now
381 DPRINTC("Wait entry ... can't continue\n");
382 Status
= STATUS_UNSUCCESSFUL
;
385 else if (Status
== STATUS_MORE_PROCESSING_REQUIRED
)
387 DPRINTC("DoAcquisition %x\n", Resources
.DoAcquisition
);
388 Status
= Resources
.DoAcquisition(AddressSpace
, MemoryArea
, &Resources
);
389 DPRINTC("Status %x\n", Status
);
390 if (!NT_SUCCESS(Status
))
395 else Status
= STATUS_MM_RESTART_OPERATION
;
398 MmLockAddressSpace(AddressSpace
);
400 while (Status
== STATUS_MM_RESTART_OPERATION
);
401 Dirty
|= Resources
.State
& 1; // Accumulate dirty
403 MmUnlockAddressSpace(AddressSpace
);
407 ObDereferenceObject(Process
);
411 ExAcquireFastMutex(&RmapListLock
);
412 ASSERT(!MM_IS_WAIT_PTE(MmGetPfnForProcess(Process
, Address
)));
413 entry
= MmGetRmapListHeadPage(Page
);
415 DPRINTC("Entry %x\n", entry
);
418 ExReleaseFastMutex(&RmapListLock
);
421 DPRINTC("BAIL %x\n", Status
);
425 DPRINTC("About to finalize section page %x (%x:%x) Status %x %s\n", Page
, Segment
, FileOffset
.LowPart
, Status
, Dirty
? "dirty" : "clean");
427 if (!NT_SUCCESS(Status
) ||
429 (Status
= MmFinalizeSectionPageOut
430 (Segment
, &FileOffset
, Page
, Dirty
)))
433 ("Failed to page out %x, replacing %x at %x in segment %x\n",
434 SectionPage
, FileOffset
.LowPart
, Segment
);
435 MmLockCacheSectionSegment(Segment
);
436 MiSetPageEntryCacheSectionSegment(Segment
, &FileOffset
, Dirty
? MAKE_PFN_SSE(Page
) : DIRTY_SSE(MAKE_PFN_SSE(Page
)));
437 MmUnlockCacheSectionSegment(Segment
);
440 // Alas, we had the last reference
442 if ((RefCount
= InterlockedDecrementUL(&Segment
->ReferenceCount
)) == 0)
443 MmFinalizeSegment(Segment
);
448 DPRINTC("Dereferencing process...\n");
449 ObDereferenceObject(Process
);
452 ExReleaseFastMutex(&GlobalPageOperation
);
453 DPRINTC("%s %x %x\n", NT_SUCCESS(Status
) ? "Evicted" : "Spared", Page
, Status
);
454 return NT_SUCCESS(Status
) ? STATUS_SUCCESS
: STATUS_UNSUCCESSFUL
;
459 MiCacheEvictPages(PVOID BaseAddress
, ULONG Target
)
461 ULONG i
, Entry
, Result
= 0;
464 PMEMORY_AREA MemoryArea
;
465 LARGE_INTEGER Offset
;
466 PMM_CACHE_SECTION_SEGMENT Segment
;
468 MmLockAddressSpace(MmGetKernelAddressSpace());
469 MemoryArea
= MmLocateMemoryAreaByAddress
470 (MmGetKernelAddressSpace(),
474 ASSERT(MemoryArea
->Type
== MEMORY_AREA_CACHE
);
476 Segment
= MemoryArea
->Data
.CacheData
.Segment
;
480 MmLockCacheSectionSegment(Segment
);
483 i
< Segment
->Length
.QuadPart
-
484 MemoryArea
->Data
.CacheData
.ViewOffset
.QuadPart
&&
487 Offset
.QuadPart
= MemoryArea
->Data
.CacheData
.ViewOffset
.QuadPart
+ i
;
488 Entry
= MiGetPageEntryCacheSectionSegment(Segment
, &Offset
);
489 if (Entry
&& !IS_SWAP_FROM_SSE(Entry
)) {
490 Page
= PFN_FROM_SSE(Entry
);
491 MmReferencePage(Page
);
492 MmUnlockCacheSectionSegment(Segment
);
493 MmUnlockAddressSpace(MmGetKernelAddressSpace());
494 Status
= MmpPageOutPhysicalAddress(Page
);
495 if (NT_SUCCESS(Status
))
497 MmLockCacheSectionSegment(Segment
);
498 MmLockAddressSpace(MmGetKernelAddressSpace());
499 MmReleasePageMemoryConsumer(MC_CACHE
, Page
);
503 MmUnlockCacheSectionSegment(Segment
);
504 MmUnlockAddressSpace(MmGetKernelAddressSpace());