[CMAKE]
[reactos.git] / ntoskrnl / cache / section / swapout.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/fault.c
21 * PURPOSE: Consolidate fault handlers for sections
22 *
23 * PROGRAMMERS: Arty
24 * Rex Jolliff
25 * David Welch
26 * Eric Kohl
27 * Emanuele Aliberti
28 * Eugene Ingerman
29 * Casper Hornstrup
30 * KJK::Hyperion
31 * Guido de Jong
32 * Ge van Geldorp
33 * Royce Mitchell III
34 * Filip Navara
35 * Aleksey Bragin
36 * Jason Filby
37 * Thomas Weidenmueller
38 * Gunnar Andre' Dalsnes
39 * Mike Nordell
40 * Alex Ionescu
41 * Gregor Anich
42 * Steven Edwards
43 * Herve Poussineau
44 */
45
46 /* INCLUDES *****************************************************************/
47
48 #include <ntoskrnl.h>
49 #include "newmm.h"
50 #define NDEBUG
51 #include <debug.h>
52
53 #define DPRINTC DPRINT
54
55 extern KEVENT MmWaitPageEvent;
56 extern FAST_MUTEX RmapListLock;
57
58 FAST_MUTEX GlobalPageOperation;
59
60 PFN_NUMBER
61 NTAPI
62 MmWithdrawSectionPage
63 (PMM_CACHE_SECTION_SEGMENT Segment, PLARGE_INTEGER FileOffset, BOOLEAN *Dirty)
64 {
65 ULONG Entry;
66
67 DPRINT("MmWithdrawSectionPage(%x,%08x%08x,%x)\n", Segment, FileOffset->HighPart, FileOffset->LowPart, Dirty);
68
69 MmLockCacheSectionSegment(Segment);
70 Entry = MiGetPageEntryCacheSectionSegment(Segment, FileOffset);
71
72 *Dirty = !!IS_DIRTY_SSE(Entry);
73
74 DPRINT("Withdraw %x (%x) of %wZ\n", FileOffset->LowPart, Entry, Segment->FileObject ? &Segment->FileObject->FileName : NULL);
75
76 if (!Entry)
77 {
78 DPRINT("Stoeled!\n");
79 MmUnlockCacheSectionSegment(Segment);
80 return 0;
81 }
82 else if (MM_IS_WAIT_PTE(Entry))
83 {
84 DPRINT("WAIT\n");
85 MmUnlockCacheSectionSegment(Segment);
86 return MM_WAIT_ENTRY;
87 }
88 else if (Entry && !IS_SWAP_FROM_SSE(Entry))
89 {
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);
95 }
96 else
97 {
98 DPRINT1("SWAP ENTRY?! (%x:%08x%08x)\n", Segment, FileOffset->HighPart, FileOffset->LowPart);
99 ASSERT(FALSE);
100 MmUnlockCacheSectionSegment(Segment);
101 return 0;
102 }
103 }
104
105 NTSTATUS
106 NTAPI
107 MmFinalizeSectionPageOut
108 (PMM_CACHE_SECTION_SEGMENT Segment, PLARGE_INTEGER FileOffset, PFN_NUMBER Page,
109 BOOLEAN Dirty)
110 {
111 NTSTATUS Status = STATUS_SUCCESS;
112 BOOLEAN WriteZero = FALSE, WritePage = FALSE;
113 SWAPENTRY Swap = MmGetSavedSwapEntryPage(Page);
114
115 MmLockCacheSectionSegment(Segment);
116 (void)InterlockedIncrementUL(&Segment->ReferenceCount);
117
118 if (Dirty)
119 {
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);
123
124 WriteZero = TRUE;
125 WritePage = TRUE;
126 }
127 else
128 {
129 WriteZero = TRUE;
130 }
131
132 DPRINT("Status %x\n", Status);
133
134 MmUnlockCacheSectionSegment(Segment);
135
136 if (WritePage)
137 {
138 DPRINT("MiWriteBackPage(Segment %x FileObject %x Offset %x)\n", Segment, Segment->FileObject, FileOffset->LowPart);
139 Status = MiWriteBackPage(Segment->FileObject, FileOffset, PAGE_SIZE, Page);
140 }
141
142 MmLockCacheSectionSegment(Segment);
143
144 if (WriteZero && NT_SUCCESS(Status))
145 {
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);
148 }
149 else
150 {
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);
154 }
155
156 if (NT_SUCCESS(Status))
157 {
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));
163 }
164 MmDereferencePage(Page);
165 }
166
167 MmUnlockCacheSectionSegment(Segment);
168
169 if (InterlockedDecrementUL(&Segment->ReferenceCount) == 0)
170 {
171 MmFinalizeSegment(Segment);
172 }
173
174 /* Note: Writing may evict the segment... Nothing is guaranteed from here down */
175 MiSetPageEvent(Segment, FileOffset->LowPart);
176
177 DPRINT("Status %x\n", Status);
178 return Status;
179 }
180
181 NTSTATUS
182 NTAPI
183 MmPageOutCacheSection
184 (PMMSUPPORT AddressSpace,
185 MEMORY_AREA* MemoryArea,
186 PVOID Address,
187 PMM_REQUIRED_RESOURCES Required)
188 {
189 NTSTATUS Status = STATUS_SUCCESS;
190 ULONG Entry;
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);
196
197 TotalOffset.QuadPart = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress +
198 MemoryArea->Data.CacheData.ViewOffset.QuadPart;
199
200 Segment = MemoryArea->Data.CacheData.Segment;
201
202 MmLockCacheSectionSegment(Segment);
203 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
204
205 Dirty = MmIsDirtyPageRmap(Required->Page[0]);
206 Entry = MiGetPageEntryCacheSectionSegment(Segment, &TotalOffset);
207
208 if (Dirty)
209 {
210 PFN_NUMBER OurPage;
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]);
215 } else {
216 /* Just unmap if the page wasn't dirty */
217 PFN_NUMBER OurPage;
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]);
222 }
223
224 if (NT_SUCCESS(Status))
225 {
226 MmDereferencePage(Required->Page[0]);
227 }
228
229 MmUnlockCacheSectionSegment(Segment);
230 MiSetPageEvent(Process, Address);
231 return Status;
232 }
233
234 NTSTATUS
235 NTAPI
236 MmpPageOutPhysicalAddress(PFN_NUMBER Page)
237 {
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 };
250
251 DPRINTC("Page out %x (ref ct %x)\n", Page, MmGetReferenceCountPage(Page));
252
253 ExAcquireFastMutex(&GlobalPageOperation);
254 if ((Segment = MmGetSectionAssociation(Page, &FileOffset)))
255 {
256 DPRINT1("Withdrawing page (%x) %x:%x\n", Page, Segment, FileOffset.LowPart);
257 SectionPage = MmWithdrawSectionPage(Segment, &FileOffset, &Dirty);
258 DPRINTC("SectionPage %x\n", SectionPage);
259
260 if (SectionPage == MM_WAIT_ENTRY || SectionPage == 0)
261 {
262 DPRINT1("In progress page out %x\n", SectionPage);
263 ExReleaseFastMutex(&GlobalPageOperation);
264 return STATUS_UNSUCCESSFUL;
265 }
266 else
267 {
268 ASSERT(SectionPage == Page);
269 }
270 Resources.State = Dirty ? 1 : 0;
271 }
272 else
273 {
274 DPRINT("No segment association for %x\n", Page);
275 }
276
277
278 Dirty = MmIsDirtyPageRmap(Page);
279
280 DPRINTC("Trying to unmap all instances of %x\n", Page);
281 ExAcquireFastMutex(&RmapListLock);
282 entry = MmGetRmapListHeadPage(Page);
283
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)
287 {
288 Status = STATUS_UNSUCCESSFUL;
289 DPRINT1("Page %x is in transit\n", Page);
290 ExReleaseFastMutex(&RmapListLock);
291 goto bail;
292 }
293
294 while (entry != NULL && NT_SUCCESS(Status))
295 {
296 Process = entry->Process;
297 Address = entry->Address;
298
299 DPRINTC("Process %x Address %x Page %x\n", Process, Address, Page);
300
301 if (RMAP_IS_SEGMENT(Address)) {
302 entry = entry->Next;
303 continue;
304 }
305
306 if (Process && Address < MmSystemRangeStart)
307 {
308 // Make sure we don't try to page out part of an exiting process
309 if (PspIsProcessExiting(Process))
310 {
311 DPRINT("bail\n");
312 ExReleaseFastMutex(&RmapListLock);
313 goto bail;
314 }
315 Status = ObReferenceObject(Process);
316 if (!NT_SUCCESS(Status))
317 {
318 DPRINT("bail\n");
319 ExReleaseFastMutex(&RmapListLock);
320 goto bail;
321 }
322 ProcRef = TRUE;
323 AddressSpace = &Process->Vm;
324 }
325 else
326 {
327 AddressSpace = MmGetKernelAddressSpace();
328 }
329 ExReleaseFastMutex(&RmapListLock);
330
331 RtlZeroMemory(&Resources, sizeof(Resources));
332
333 if ((((ULONG_PTR)Address) & 0xFFF) != 0)
334 {
335 KeBugCheck(MEMORY_MANAGEMENT);
336 }
337
338 if (!MmTryToLockAddressSpace(AddressSpace))
339 {
340 DPRINT1("Could not lock address space for process %x\n", MmGetAddressSpaceOwner(AddressSpace));
341 Status = STATUS_UNSUCCESSFUL;
342 goto bail;
343 }
344
345 do
346 {
347 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
348 if (MemoryArea == NULL ||
349 MemoryArea->DeleteInProgress)
350 {
351 Status = STATUS_UNSUCCESSFUL;
352 MmUnlockAddressSpace(AddressSpace);
353 DPRINTC("bail\n");
354 goto bail;
355 }
356
357 DPRINTC
358 ("Type %x (%x -> %x)\n",
359 MemoryArea->Type,
360 MemoryArea->StartingAddress,
361 MemoryArea->EndingAddress);
362
363 Resources.DoAcquisition = NULL;
364
365 Resources.Page[0] = Page;
366
367 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
368
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);
373
374 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
375
376 MmUnlockAddressSpace(AddressSpace);
377
378 if (Status == STATUS_SUCCESS + 1)
379 {
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;
383 goto bail;
384 }
385 else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
386 {
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))
391 {
392 DPRINT1("bail\n");
393 goto bail;
394 }
395 else Status = STATUS_MM_RESTART_OPERATION;
396 }
397
398 MmLockAddressSpace(AddressSpace);
399 }
400 while (Status == STATUS_MM_RESTART_OPERATION);
401 Dirty |= Resources.State & 1; // Accumulate dirty
402
403 MmUnlockAddressSpace(AddressSpace);
404
405 if (ProcRef)
406 {
407 ObDereferenceObject(Process);
408 ProcRef = FALSE;
409 }
410
411 ExAcquireFastMutex(&RmapListLock);
412 ASSERT(!MM_IS_WAIT_PTE(MmGetPfnForProcess(Process, Address)));
413 entry = MmGetRmapListHeadPage(Page);
414
415 DPRINTC("Entry %x\n", entry);
416 }
417
418 ExReleaseFastMutex(&RmapListLock);
419
420 bail:
421 DPRINTC("BAIL %x\n", Status);
422
423 if (Segment)
424 {
425 DPRINTC("About to finalize section page %x (%x:%x) Status %x %s\n", Page, Segment, FileOffset.LowPart, Status, Dirty ? "dirty" : "clean");
426
427 if (!NT_SUCCESS(Status) ||
428 !NT_SUCCESS
429 (Status = MmFinalizeSectionPageOut
430 (Segment, &FileOffset, Page, Dirty)))
431 {
432 DPRINTC
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);
438 }
439
440 // Alas, we had the last reference
441 ULONG RefCount;
442 if ((RefCount = InterlockedDecrementUL(&Segment->ReferenceCount)) == 0)
443 MmFinalizeSegment(Segment);
444 }
445
446 if (ProcRef)
447 {
448 DPRINTC("Dereferencing process...\n");
449 ObDereferenceObject(Process);
450 }
451
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;
455 }
456
457 ULONG
458 NTAPI
459 MiCacheEvictPages(PVOID BaseAddress, ULONG Target)
460 {
461 ULONG i, Entry, Result = 0;
462 NTSTATUS Status;
463 PFN_NUMBER Page;
464 PMEMORY_AREA MemoryArea;
465 LARGE_INTEGER Offset;
466 PMM_CACHE_SECTION_SEGMENT Segment;
467
468 MmLockAddressSpace(MmGetKernelAddressSpace());
469 MemoryArea = MmLocateMemoryAreaByAddress
470 (MmGetKernelAddressSpace(),
471 BaseAddress);
472
473 ASSERT(MemoryArea);
474 ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE);
475
476 Segment = MemoryArea->Data.CacheData.Segment;
477
478 ASSERT(Segment);
479
480 MmLockCacheSectionSegment(Segment);
481
482 for (i = 0;
483 i < Segment->Length.QuadPart -
484 MemoryArea->Data.CacheData.ViewOffset.QuadPart &&
485 Result < Target;
486 i += PAGE_SIZE) {
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))
496 Result++;
497 MmLockCacheSectionSegment(Segment);
498 MmLockAddressSpace(MmGetKernelAddressSpace());
499 MmReleasePageMemoryConsumer(MC_CACHE, Page);
500 }
501 }
502
503 MmUnlockCacheSectionSegment(Segment);
504 MmUnlockAddressSpace(MmGetKernelAddressSpace());
505
506 return Result;
507 }
508