xref: /reactos/ntoskrnl/cache/section/swapout.c (revision 91587a43)
1c2c66affSColin Finck /*
2c2c66affSColin Finck  * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3c2c66affSColin Finck  *
4c2c66affSColin Finck  * This program is free software; you can redistribute it and/or
5c2c66affSColin Finck  * modify it under the terms of the GNU General Public License
6c2c66affSColin Finck  * as published by the Free Software Foundation; either version 2
7c2c66affSColin Finck  * of the License, or (at your option) any later version.
8c2c66affSColin Finck  *
9c2c66affSColin Finck  * This program is distributed in the hope that it will be useful,
10c2c66affSColin Finck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11c2c66affSColin Finck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12c2c66affSColin Finck  * GNU General Public License for more details.
13c2c66affSColin Finck  *
14c2c66affSColin Finck  * You should have received a copy of the GNU General Public License
15c2c66affSColin Finck  * along with this program; if not, write to the Free Software
16c2c66affSColin Finck  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17c2c66affSColin Finck  *
18c2c66affSColin Finck  *
19c2c66affSColin Finck  * PROJECT:         ReactOS kernel
20c2c66affSColin Finck  * FILE:            ntoskrnl/cache/section/swapout.c
21c2c66affSColin Finck  * PURPOSE:         Consolidate fault handlers for sections
22c2c66affSColin Finck  *
23c2c66affSColin Finck  * PROGRAMMERS:     Arty
24c2c66affSColin Finck  *                  Rex Jolliff
25c2c66affSColin Finck  *                  David Welch
26c2c66affSColin Finck  *                  Eric Kohl
27c2c66affSColin Finck  *                  Emanuele Aliberti
28c2c66affSColin Finck  *                  Eugene Ingerman
29c2c66affSColin Finck  *                  Casper Hornstrup
30c2c66affSColin Finck  *                  KJK::Hyperion
31c2c66affSColin Finck  *                  Guido de Jong
32c2c66affSColin Finck  *                  Ge van Geldorp
33c2c66affSColin Finck  *                  Royce Mitchell III
34c2c66affSColin Finck  *                  Filip Navara
35c2c66affSColin Finck  *                  Aleksey Bragin
36c2c66affSColin Finck  *                  Jason Filby
37c2c66affSColin Finck  *                  Thomas Weidenmueller
38c2c66affSColin Finck  *                  Gunnar Andre' Dalsnes
39c2c66affSColin Finck  *                  Mike Nordell
40c2c66affSColin Finck  *                  Alex Ionescu
41c2c66affSColin Finck  *                  Gregor Anich
42c2c66affSColin Finck  *                  Steven Edwards
43c2c66affSColin Finck  *                  Herve Poussineau
44c2c66affSColin Finck  */
45c2c66affSColin Finck 
46c2c66affSColin Finck /*
47c2c66affSColin Finck 
48c2c66affSColin Finck This file implements page out infrastructure for cache type sections.  This
49c2c66affSColin Finck is implemented a little differently from the legacy mm because mapping in an
50c2c66affSColin Finck address space and membership in a segment are considered separate.
51c2c66affSColin Finck 
52c2c66affSColin Finck The general strategy here is to try to remove all mappings as gently as
53c2c66affSColin Finck possible, then to remove the page entry from the section itself as a final
54c2c66affSColin Finck step.  If at any time during the page out operation, the page is mapped in
55c2c66affSColin Finck a new address space by a competing thread, the operation will abort before
56c2c66affSColin Finck the segment page is finally removed, and the page will be naturally faulted
57c2c66affSColin Finck back into any address spaces required in the normal way.
58c2c66affSColin Finck 
59c2c66affSColin Finck */
60c2c66affSColin Finck 
61c2c66affSColin Finck /* INCLUDES *****************************************************************/
62c2c66affSColin Finck 
63c2c66affSColin Finck #include <ntoskrnl.h>
64c2c66affSColin Finck #include "newmm.h"
65c2c66affSColin Finck #define NDEBUG
66c2c66affSColin Finck #include <debug.h>
67c2c66affSColin Finck 
68c2c66affSColin Finck #define DPRINTC DPRINT
69c2c66affSColin Finck 
70c2c66affSColin Finck extern KEVENT MmWaitPageEvent;
71c2c66affSColin Finck extern FAST_MUTEX RmapListLock;
72c2c66affSColin Finck extern PMMWSL MmWorkingSetList;
73c2c66affSColin Finck 
74c2c66affSColin Finck FAST_MUTEX MiGlobalPageOperation;
75c2c66affSColin Finck 
76c2c66affSColin Finck /*
77c2c66affSColin Finck 
78c2c66affSColin Finck MmWithdrawSectionPage removes a page entry from the section segment, replacing
79c2c66affSColin Finck it with a wait entry.  The caller must replace the wait entry with a 0, when
80c2c66affSColin Finck any required writing is done.  The wait entry must remain until the page is
81c2c66affSColin Finck written to protect against cases where a fault brings a stale copy of the page
82c2c66affSColin Finck back before writing is complete.
83c2c66affSColin Finck 
84c2c66affSColin Finck */
85c2c66affSColin Finck PFN_NUMBER
86c2c66affSColin Finck NTAPI
MmWithdrawSectionPage(PMM_SECTION_SEGMENT Segment,PLARGE_INTEGER FileOffset,BOOLEAN * Dirty)87c2c66affSColin Finck MmWithdrawSectionPage(PMM_SECTION_SEGMENT Segment,
88c2c66affSColin Finck                       PLARGE_INTEGER FileOffset,
89c2c66affSColin Finck                       BOOLEAN *Dirty)
90c2c66affSColin Finck {
91c2c66affSColin Finck     ULONG_PTR Entry;
92c2c66affSColin Finck 
93c2c66affSColin Finck     DPRINT("MmWithdrawSectionPage(%p,%08x%08x,%p)\n",
94c2c66affSColin Finck            Segment,
95c2c66affSColin Finck            FileOffset->HighPart,
96c2c66affSColin Finck            FileOffset->LowPart,
97c2c66affSColin Finck            Dirty);
98c2c66affSColin Finck 
99c2c66affSColin Finck     MmLockSectionSegment(Segment);
100c2c66affSColin Finck     Entry = MmGetPageEntrySectionSegment(Segment, FileOffset);
101c2c66affSColin Finck 
102c2c66affSColin Finck     *Dirty = !!IS_DIRTY_SSE(Entry);
103c2c66affSColin Finck 
104c2c66affSColin Finck     DPRINT("Withdraw %x (%x) of %wZ\n",
105c2c66affSColin Finck            FileOffset->LowPart,
106c2c66affSColin Finck            Entry,
107c2c66affSColin Finck            Segment->FileObject ? &Segment->FileObject->FileName : NULL);
108c2c66affSColin Finck 
109c2c66affSColin Finck     if (!Entry)
110c2c66affSColin Finck     {
111c2c66affSColin Finck         DPRINT("Stoeled!\n");
112c2c66affSColin Finck         MmUnlockSectionSegment(Segment);
113c2c66affSColin Finck         return 0;
114c2c66affSColin Finck     }
115c2c66affSColin Finck     else if (MM_IS_WAIT_PTE(Entry))
116c2c66affSColin Finck     {
117c2c66affSColin Finck         DPRINT("WAIT\n");
118c2c66affSColin Finck         MmUnlockSectionSegment(Segment);
119c2c66affSColin Finck         return MM_WAIT_ENTRY;
120c2c66affSColin Finck     }
121c2c66affSColin Finck     else if (Entry && !IS_SWAP_FROM_SSE(Entry))
122c2c66affSColin Finck     {
123c2c66affSColin Finck         DPRINT("Page %x\n", PFN_FROM_SSE(Entry));
124c2c66affSColin Finck 
125c2c66affSColin Finck         *Dirty |= (Entry & 2);
126c2c66affSColin Finck 
127c2c66affSColin Finck         MmSetPageEntrySectionSegment(Segment,
128c2c66affSColin Finck                                      FileOffset,
129c2c66affSColin Finck                                      MAKE_SWAP_SSE(MM_WAIT_ENTRY));
130c2c66affSColin Finck 
131c2c66affSColin Finck         MmUnlockSectionSegment(Segment);
132c2c66affSColin Finck         return PFN_FROM_SSE(Entry);
133c2c66affSColin Finck     }
134c2c66affSColin Finck     else
135c2c66affSColin Finck     {
136c2c66affSColin Finck         DPRINT1("SWAP ENTRY?! (%p:%08x%08x)\n",
137c2c66affSColin Finck                 Segment,
138c2c66affSColin Finck                 FileOffset->HighPart,
139c2c66affSColin Finck                 FileOffset->LowPart);
140c2c66affSColin Finck 
141c2c66affSColin Finck         ASSERT(FALSE);
142c2c66affSColin Finck         MmUnlockSectionSegment(Segment);
143c2c66affSColin Finck         return 0;
144c2c66affSColin Finck     }
145c2c66affSColin Finck }
146c2c66affSColin Finck 
147c2c66affSColin Finck /*
148c2c66affSColin Finck 
149c2c66affSColin Finck This function determines whether the segment holds the very last reference to
150c2c66affSColin Finck the page being considered and if so, writes it back or discards it as
151c2c66affSColin Finck approriate.  One small niggle here is that we might be holding the last
152c2c66affSColin Finck reference to the section segment associated with this page.  That happens
153c2c66affSColin Finck when the segment is destroyed at the same time that an active swap operation
154c2c66affSColin Finck is occurring, and all maps were already withdrawn.  In that case, it's our
155c2c66affSColin Finck responsiblity for finalizing the segment.
156c2c66affSColin Finck 
157c2c66affSColin Finck Note that in the current code, WriteZero is always TRUE because the section
158c2c66affSColin Finck always backs a file.  In the ultimate form of this code, it also writes back
159c2c66affSColin Finck pages without necessarily evicting them.  In reactos' trunk, this is vestigal.
160c2c66affSColin Finck 
161c2c66affSColin Finck */
162c2c66affSColin Finck 
163c2c66affSColin Finck NTSTATUS
164c2c66affSColin Finck NTAPI
MmFinalizeSectionPageOut(PMM_SECTION_SEGMENT Segment,PLARGE_INTEGER FileOffset,PFN_NUMBER Page,BOOLEAN Dirty)165c2c66affSColin Finck MmFinalizeSectionPageOut(PMM_SECTION_SEGMENT Segment,
166c2c66affSColin Finck                          PLARGE_INTEGER FileOffset,
167c2c66affSColin Finck                          PFN_NUMBER Page,
168c2c66affSColin Finck                          BOOLEAN Dirty)
169c2c66affSColin Finck {
170c2c66affSColin Finck     NTSTATUS Status = STATUS_SUCCESS;
171c2c66affSColin Finck     BOOLEAN WriteZero = FALSE, WritePage = FALSE;
172c2c66affSColin Finck     SWAPENTRY Swap = MmGetSavedSwapEntryPage(Page);
173c2c66affSColin Finck 
174c2c66affSColin Finck     /* Bail early if the reference count isn't where we need it */
175*91587a43SJérôme Gardou     if (MmGetReferenceCountPageWithoutLock(Page) != 1)
176c2c66affSColin Finck     {
177c2c66affSColin Finck         DPRINT1("Cannot page out locked page %x with ref count %lu\n",
178c2c66affSColin Finck                 Page,
179*91587a43SJérôme Gardou                 MmGetReferenceCountPageWithoutLock(Page));
180c2c66affSColin Finck         return STATUS_UNSUCCESSFUL;
181c2c66affSColin Finck     }
182c2c66affSColin Finck 
183c2c66affSColin Finck     MmLockSectionSegment(Segment);
184c2c66affSColin Finck     (void)InterlockedIncrementUL(&Segment->ReferenceCount);
185c2c66affSColin Finck 
186c2c66affSColin Finck     if (Dirty)
187c2c66affSColin Finck     {
188c2c66affSColin Finck         DPRINT("Finalize (dirty) Segment %p Page %x\n", Segment, Page);
189c2c66affSColin Finck         DPRINT("Segment->FileObject %p\n", Segment->FileObject);
190c2c66affSColin Finck         DPRINT("Segment->Flags %x\n", Segment->Flags);
191c2c66affSColin Finck 
192c2c66affSColin Finck         WriteZero = TRUE;
193c2c66affSColin Finck         WritePage = TRUE;
194c2c66affSColin Finck     }
195c2c66affSColin Finck     else
196c2c66affSColin Finck     {
197c2c66affSColin Finck         WriteZero = TRUE;
198c2c66affSColin Finck     }
199c2c66affSColin Finck 
200c2c66affSColin Finck     DPRINT("Status %x\n", Status);
201c2c66affSColin Finck 
202c2c66affSColin Finck     MmUnlockSectionSegment(Segment);
203c2c66affSColin Finck 
204c2c66affSColin Finck     if (WritePage)
205c2c66affSColin Finck     {
206c2c66affSColin Finck         DPRINT("MiWriteBackPage(Segment %p FileObject %p Offset %x)\n",
207c2c66affSColin Finck                Segment,
208c2c66affSColin Finck                Segment->FileObject,
209c2c66affSColin Finck                FileOffset->LowPart);
210c2c66affSColin Finck 
211c2c66affSColin Finck         Status = MiWriteBackPage(Segment->FileObject,
212c2c66affSColin Finck                                  FileOffset,
213c2c66affSColin Finck                                  PAGE_SIZE,
214c2c66affSColin Finck                                  Page);
215c2c66affSColin Finck     }
216c2c66affSColin Finck 
217c2c66affSColin Finck     MmLockSectionSegment(Segment);
218c2c66affSColin Finck 
219c2c66affSColin Finck     if (WriteZero && NT_SUCCESS(Status))
220c2c66affSColin Finck     {
221c2c66affSColin Finck         DPRINT("Setting page entry in segment %p:%x to swap %x\n",
222c2c66affSColin Finck                Segment,
223c2c66affSColin Finck                FileOffset->LowPart,
224c2c66affSColin Finck                Swap);
225c2c66affSColin Finck 
226c2c66affSColin Finck         MmSetPageEntrySectionSegment(Segment,
227c2c66affSColin Finck                                      FileOffset,
228c2c66affSColin Finck                                      Swap ? MAKE_SWAP_SSE(Swap) : 0);
229c2c66affSColin Finck     }
230c2c66affSColin Finck     else
231c2c66affSColin Finck     {
232c2c66affSColin Finck         DPRINT("Setting page entry in segment %p:%x to page %x\n",
233c2c66affSColin Finck                Segment,
234c2c66affSColin Finck                FileOffset->LowPart,
235c2c66affSColin Finck                Page);
236c2c66affSColin Finck 
237c2c66affSColin Finck         MmSetPageEntrySectionSegment(Segment,
238c2c66affSColin Finck                                      FileOffset,
239c2c66affSColin Finck                                      Page ? (Dirty ? DIRTY_SSE(MAKE_PFN_SSE(Page)) : MAKE_PFN_SSE(Page)) : 0);
240c2c66affSColin Finck     }
241c2c66affSColin Finck 
242c2c66affSColin Finck     if (NT_SUCCESS(Status))
243c2c66affSColin Finck     {
244c2c66affSColin Finck         DPRINT("Removing page %x for real\n", Page);
245c2c66affSColin Finck         MmSetSavedSwapEntryPage(Page, 0);
246c2c66affSColin Finck         MmReleasePageMemoryConsumer(MC_CACHE, Page);
247c2c66affSColin Finck     }
248c2c66affSColin Finck 
249c2c66affSColin Finck     MmUnlockSectionSegment(Segment);
250c2c66affSColin Finck 
251c2c66affSColin Finck     if (InterlockedDecrementUL(&Segment->ReferenceCount) == 0)
252c2c66affSColin Finck     {
253c2c66affSColin Finck         MmFinalizeSegment(Segment);
254c2c66affSColin Finck     }
255c2c66affSColin Finck 
256c2c66affSColin Finck     /* Note: Writing may evict the segment... Nothing is guaranteed from here down */
2572dade10dSTimo Kreuzer     MiSetPageEvent(Segment, (ULONG_PTR)FileOffset->QuadPart);
258c2c66affSColin Finck 
259c2c66affSColin Finck     DPRINT("Status %x\n", Status);
260c2c66affSColin Finck     return Status;
261c2c66affSColin Finck }
262c2c66affSColin Finck 
263c2c66affSColin Finck /*
264c2c66affSColin Finck 
265c2c66affSColin Finck The slightly misnamed MmPageOutCacheSection removes a page from an address
266c2c66affSColin Finck space in the manner of fault handlers found in fault.c.  In the ultimate form
267c2c66affSColin Finck of the code, this is one of the function pointers stored in a memory area
268c2c66affSColin Finck to control how pages in that memory area are managed.
269c2c66affSColin Finck 
270c2c66affSColin Finck Also misleading is the call to MmReleasePageMemoryConsumer, which releases
271c2c66affSColin Finck the reference held by this address space only.  After all address spaces
272c2c66affSColin Finck have had MmPageOutCacheSection succeed on them for the indicated page,
273c2c66affSColin Finck then paging out of a cache page can continue.
274c2c66affSColin Finck 
275c2c66affSColin Finck */
276c2c66affSColin Finck 
277c2c66affSColin Finck NTSTATUS
278c2c66affSColin Finck NTAPI
MmPageOutCacheSection(PMMSUPPORT AddressSpace,MEMORY_AREA * MemoryArea,PVOID Address,PBOOLEAN Dirty,PMM_REQUIRED_RESOURCES Required)279c2c66affSColin Finck MmPageOutCacheSection(PMMSUPPORT AddressSpace,
280c2c66affSColin Finck                       MEMORY_AREA* MemoryArea,
281c2c66affSColin Finck                       PVOID Address,
282c2c66affSColin Finck                       PBOOLEAN Dirty,
283c2c66affSColin Finck                       PMM_REQUIRED_RESOURCES Required)
284c2c66affSColin Finck {
285c2c66affSColin Finck     ULONG_PTR Entry;
286c2c66affSColin Finck     PFN_NUMBER OurPage;
287c2c66affSColin Finck     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
288c2c66affSColin Finck     LARGE_INTEGER TotalOffset;
289c2c66affSColin Finck     PMM_SECTION_SEGMENT Segment;
290c2c66affSColin Finck     PVOID PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
291c2c66affSColin Finck 
292c2c66affSColin Finck     TotalOffset.QuadPart = (ULONG_PTR)PAddress -
293c2c66affSColin Finck                            MA_GetStartingAddress(MemoryArea) +
294c2c66affSColin Finck                            MemoryArea->Data.SectionData.ViewOffset.QuadPart;
295c2c66affSColin Finck 
296c2c66affSColin Finck     Segment = MemoryArea->Data.SectionData.Segment;
297c2c66affSColin Finck 
298c2c66affSColin Finck     MmLockSectionSegment(Segment);
299c2c66affSColin Finck     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
300c2c66affSColin Finck 
301c2c66affSColin Finck     Entry = MmGetPageEntrySectionSegment(Segment, &TotalOffset);
302c2c66affSColin Finck     DBG_UNREFERENCED_LOCAL_VARIABLE(Entry);
303c2c66affSColin Finck 
304c2c66affSColin Finck     if (MmIsPageSwapEntry(Process, PAddress))
305c2c66affSColin Finck     {
306c2c66affSColin Finck         SWAPENTRY SwapEntry;
307c2c66affSColin Finck         MmGetPageFileMapping(Process, PAddress, &SwapEntry);
308c2c66affSColin Finck         MmUnlockSectionSegment(Segment);
309c2c66affSColin Finck         return SwapEntry == MM_WAIT_ENTRY ? STATUS_SUCCESS + 1 : STATUS_UNSUCCESSFUL;
310c2c66affSColin Finck     }
311c2c66affSColin Finck 
312c2c66affSColin Finck     MmDeleteRmap(Required->Page[0], Process, Address);
313c2c66affSColin Finck     MmDeleteVirtualMapping(Process, Address, Dirty, &OurPage);
314c2c66affSColin Finck     ASSERT(OurPage == Required->Page[0]);
315c2c66affSColin Finck 
316c2c66affSColin Finck     /* Note: this releases the reference held by this address space only. */
317c2c66affSColin Finck     MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
318c2c66affSColin Finck 
319c2c66affSColin Finck     MmUnlockSectionSegment(Segment);
320c2c66affSColin Finck     MiSetPageEvent(Process, Address);
321c2c66affSColin Finck     return STATUS_SUCCESS;
322c2c66affSColin Finck }
323c2c66affSColin Finck 
324c2c66affSColin Finck /*
325c2c66affSColin Finck 
326c2c66affSColin Finck This function is called by rmap when spare pages are needed by the blancer.
327c2c66affSColin Finck It attempts first to release the page from every address space in which it
328c2c66affSColin Finck appears, and, after a final check that no competing thread has mapped the
329c2c66affSColin Finck page again, uses MmFinalizeSectionPageOut to completely evict the page.  If
330c2c66affSColin Finck that's successful, then a suitable non-page map will be left in the segment
331c2c66affSColin Finck page table, otherwise, the original page is replaced in the section page
332c2c66affSColin Finck map.  Failure may result from a variety of conditions, but always leaves
333c2c66affSColin Finck the page mapped.
334c2c66affSColin Finck 
335c2c66affSColin Finck This code is like the other fault handlers, in that MmPageOutCacheSection has
336c2c66affSColin Finck the option of returning either STATUS_SUCCESS + 1 to wait for a wait entry
337c2c66affSColin Finck to disppear or to use the blocking callout facility by returning
338c2c66affSColin Finck STATUS_MORE_PROCESSING_REQUIRED and placing a pointer to a function from
339c2c66affSColin Finck reqtools.c in the MM_REQUIRED_RESOURCES struct.
340c2c66affSColin Finck 
341c2c66affSColin Finck */
342c2c66affSColin Finck 
343c2c66affSColin Finck NTSTATUS
344c2c66affSColin Finck NTAPI
MmpPageOutPhysicalAddress(PFN_NUMBER Page)345c2c66affSColin Finck MmpPageOutPhysicalAddress(PFN_NUMBER Page)
346c2c66affSColin Finck {
347c2c66affSColin Finck     BOOLEAN ProcRef = FALSE, PageDirty;
348c2c66affSColin Finck     PFN_NUMBER SectionPage = 0;
349c2c66affSColin Finck     PMM_RMAP_ENTRY entry;
350c2c66affSColin Finck     PMM_SECTION_SEGMENT Segment = NULL;
351c2c66affSColin Finck     LARGE_INTEGER FileOffset;
352c2c66affSColin Finck     PMEMORY_AREA MemoryArea;
353c2c66affSColin Finck     PMMSUPPORT AddressSpace = NULL;
354c2c66affSColin Finck     BOOLEAN Dirty = FALSE;
355c2c66affSColin Finck     PVOID Address = NULL;
356c2c66affSColin Finck     PEPROCESS Process = NULL;
357c2c66affSColin Finck     NTSTATUS Status = STATUS_SUCCESS;
358c2c66affSColin Finck     MM_REQUIRED_RESOURCES Resources = { 0 };
359c2c66affSColin Finck 
360*91587a43SJérôme Gardou     DPRINTC("Page out %x (ref ct %x)\n", Page, MmGetReferenceCountPageWithoutLock(Page));
361c2c66affSColin Finck 
362c2c66affSColin Finck     ExAcquireFastMutex(&MiGlobalPageOperation);
363c2c66affSColin Finck     if ((Segment = MmGetSectionAssociation(Page, &FileOffset)))
364c2c66affSColin Finck     {
365c2c66affSColin Finck         DPRINTC("Withdrawing page (%x) %p:%x\n",
366c2c66affSColin Finck                 Page,
367c2c66affSColin Finck                 Segment,
368c2c66affSColin Finck                 FileOffset.LowPart);
369c2c66affSColin Finck 
370c2c66affSColin Finck         SectionPage = MmWithdrawSectionPage(Segment, &FileOffset, &Dirty);
371c2c66affSColin Finck         DPRINTC("SectionPage %x\n", SectionPage);
372c2c66affSColin Finck 
373c2c66affSColin Finck         if (SectionPage == MM_WAIT_ENTRY || SectionPage == 0)
374c2c66affSColin Finck         {
375c2c66affSColin Finck             DPRINT1("In progress page out %x\n", SectionPage);
376c2c66affSColin Finck             ExReleaseFastMutex(&MiGlobalPageOperation);
377c2c66affSColin Finck             return STATUS_UNSUCCESSFUL;
378c2c66affSColin Finck         }
379c2c66affSColin Finck         else
380c2c66affSColin Finck         {
381c2c66affSColin Finck             ASSERT(SectionPage == Page);
382c2c66affSColin Finck         }
383c2c66affSColin Finck         Resources.State = Dirty ? 1 : 0;
384c2c66affSColin Finck     }
385c2c66affSColin Finck     else
386c2c66affSColin Finck     {
387c2c66affSColin Finck         DPRINT("No segment association for %x\n", Page);
388c2c66affSColin Finck     }
389c2c66affSColin Finck 
390c2c66affSColin Finck     Dirty = MmIsDirtyPageRmap(Page);
391c2c66affSColin Finck 
392c2c66affSColin Finck     DPRINTC("Trying to unmap all instances of %x\n", Page);
393c2c66affSColin Finck     ExAcquireFastMutex(&RmapListLock);
394c2c66affSColin Finck     entry = MmGetRmapListHeadPage(Page);
395c2c66affSColin Finck 
396c2c66affSColin Finck     // Entry and Segment might be null here in the case that the page
397c2c66affSColin Finck     // is new and is in the process of being swapped in
398c2c66affSColin Finck     if (!entry && !Segment)
399c2c66affSColin Finck     {
400c2c66affSColin Finck         Status = STATUS_UNSUCCESSFUL;
401c2c66affSColin Finck         DPRINT1("Page %x is in transit\n", Page);
402c2c66affSColin Finck         ExReleaseFastMutex(&RmapListLock);
403c2c66affSColin Finck         goto bail;
404c2c66affSColin Finck     }
405c2c66affSColin Finck 
406c2c66affSColin Finck     while (entry != NULL && NT_SUCCESS(Status))
407c2c66affSColin Finck     {
408c2c66affSColin Finck         Process = entry->Process;
409c2c66affSColin Finck         Address = entry->Address;
410c2c66affSColin Finck 
411c2c66affSColin Finck         DPRINTC("Process %p Address %p Page %x\n", Process, Address, Page);
412c2c66affSColin Finck 
413c2c66affSColin Finck         if (RMAP_IS_SEGMENT(Address))
414c2c66affSColin Finck         {
415c2c66affSColin Finck             entry = entry->Next;
416c2c66affSColin Finck             continue;
417c2c66affSColin Finck         }
418c2c66affSColin Finck 
419c2c66affSColin Finck         if (Process && Address < MmSystemRangeStart)
420c2c66affSColin Finck         {
421c2c66affSColin Finck             /* Make sure we don't try to page out part of an exiting process */
422c2c66affSColin Finck             if (PspIsProcessExiting(Process))
423c2c66affSColin Finck             {
424c2c66affSColin Finck                 DPRINT("bail\n");
425c2c66affSColin Finck                 ExReleaseFastMutex(&RmapListLock);
426c2c66affSColin Finck                 goto bail;
427c2c66affSColin Finck             }
428c2c66affSColin Finck             ObReferenceObject(Process);
429c2c66affSColin Finck             ProcRef = TRUE;
430c2c66affSColin Finck             AddressSpace = &Process->Vm;
431c2c66affSColin Finck         }
432c2c66affSColin Finck         else
433c2c66affSColin Finck         {
434c2c66affSColin Finck             AddressSpace = MmGetKernelAddressSpace();
435c2c66affSColin Finck         }
436c2c66affSColin Finck         ExReleaseFastMutex(&RmapListLock);
437c2c66affSColin Finck 
438c2c66affSColin Finck         RtlZeroMemory(&Resources, sizeof(Resources));
439c2c66affSColin Finck 
440c2c66affSColin Finck         if ((((ULONG_PTR)Address) & 0xFFF) != 0)
441c2c66affSColin Finck         {
442c2c66affSColin Finck             KeBugCheck(MEMORY_MANAGEMENT);
443c2c66affSColin Finck         }
444c2c66affSColin Finck 
445c2c66affSColin Finck         do
446c2c66affSColin Finck         {
447c2c66affSColin Finck             MmLockAddressSpace(AddressSpace);
448c2c66affSColin Finck 
449c2c66affSColin Finck             MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
450c2c66affSColin Finck             if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
451c2c66affSColin Finck             {
452c2c66affSColin Finck                 Status = STATUS_UNSUCCESSFUL;
453c2c66affSColin Finck                 MmUnlockAddressSpace(AddressSpace);
454c2c66affSColin Finck                 DPRINTC("bail\n");
455c2c66affSColin Finck                 goto bail;
456c2c66affSColin Finck             }
457c2c66affSColin Finck 
458c2c66affSColin Finck             DPRINTC("Type %x (%p -> %p)\n",
459c2c66affSColin Finck                     MemoryArea->Type,
460c2c66affSColin Finck                     MA_GetStartingAddress(MemoryArea),
461c2c66affSColin Finck                     MA_GetEndingAddress(MemoryArea));
462c2c66affSColin Finck 
463c2c66affSColin Finck             Resources.DoAcquisition = NULL;
464c2c66affSColin Finck             Resources.Page[0] = Page;
465c2c66affSColin Finck 
466c2c66affSColin Finck             ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
467c2c66affSColin Finck 
468c2c66affSColin Finck             DPRINT("%p:%p, page %x %x\n",
469c2c66affSColin Finck                    Process,
470c2c66affSColin Finck                    Address,
471c2c66affSColin Finck                    Page,
472c2c66affSColin Finck                    Resources.Page[0]);
473c2c66affSColin Finck 
474c2c66affSColin Finck             PageDirty = FALSE;
475c2c66affSColin Finck 
476c2c66affSColin Finck             Status = MmPageOutCacheSection(AddressSpace,
477c2c66affSColin Finck                                            MemoryArea,
478c2c66affSColin Finck                                            Address,
479c2c66affSColin Finck                                            &PageDirty,
480c2c66affSColin Finck                                            &Resources);
481c2c66affSColin Finck 
482c2c66affSColin Finck             Dirty |= PageDirty;
483c2c66affSColin Finck             DPRINT("%x\n", Status);
484c2c66affSColin Finck 
485c2c66affSColin Finck             ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
486c2c66affSColin Finck 
487c2c66affSColin Finck             MmUnlockAddressSpace(AddressSpace);
488c2c66affSColin Finck 
489c2c66affSColin Finck             if (Status == STATUS_SUCCESS + 1)
490c2c66affSColin Finck             {
491c2c66affSColin Finck                 // Wait page ... the other guy has it, so we'll just fail for now
492c2c66affSColin Finck                 DPRINT1("Wait entry ... can't continue\n");
493c2c66affSColin Finck                 Status = STATUS_UNSUCCESSFUL;
494c2c66affSColin Finck                 goto bail;
495c2c66affSColin Finck             }
496c2c66affSColin Finck             else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
497c2c66affSColin Finck             {
498c2c66affSColin Finck                 DPRINTC("DoAcquisition %p\n", Resources.DoAcquisition);
499c2c66affSColin Finck 
500c2c66affSColin Finck                 Status = Resources.DoAcquisition(AddressSpace,
501c2c66affSColin Finck                                                  MemoryArea,
502c2c66affSColin Finck                                                  &Resources);
503c2c66affSColin Finck 
504c2c66affSColin Finck                 DPRINTC("Status %x\n", Status);
505c2c66affSColin Finck                 if (!NT_SUCCESS(Status))
506c2c66affSColin Finck                 {
507c2c66affSColin Finck                     DPRINT1("bail\n");
508c2c66affSColin Finck                     goto bail;
509c2c66affSColin Finck                 }
510c2c66affSColin Finck                 else
511c2c66affSColin Finck                 {
512c2c66affSColin Finck                     Status = STATUS_MM_RESTART_OPERATION;
513c2c66affSColin Finck                 }
514c2c66affSColin Finck             }
515c2c66affSColin Finck         }
516c2c66affSColin Finck         while (Status == STATUS_MM_RESTART_OPERATION);
517c2c66affSColin Finck 
518c2c66affSColin Finck         if (ProcRef)
519c2c66affSColin Finck         {
520c2c66affSColin Finck             ObDereferenceObject(Process);
521c2c66affSColin Finck             ProcRef = FALSE;
522c2c66affSColin Finck         }
523c2c66affSColin Finck 
524c2c66affSColin Finck         ExAcquireFastMutex(&RmapListLock);
525c2c66affSColin Finck         ASSERT(!MM_IS_WAIT_PTE(MmGetPfnForProcess(Process, Address)));
526c2c66affSColin Finck         entry = MmGetRmapListHeadPage(Page);
527c2c66affSColin Finck 
528c2c66affSColin Finck         DPRINTC("Entry %p\n", entry);
529c2c66affSColin Finck     }
530c2c66affSColin Finck 
531c2c66affSColin Finck     ExReleaseFastMutex(&RmapListLock);
532c2c66affSColin Finck 
533c2c66affSColin Finck bail:
534c2c66affSColin Finck     DPRINTC("BAIL %x\n", Status);
535c2c66affSColin Finck 
536c2c66affSColin Finck     if (Segment)
537c2c66affSColin Finck     {
538c2c66affSColin Finck         ULONG RefCount;
539c2c66affSColin Finck 
540c2c66affSColin Finck         DPRINTC("About to finalize section page %x (%p:%x) Status %x %s\n",
541c2c66affSColin Finck                 Page,
542c2c66affSColin Finck                 Segment,
543c2c66affSColin Finck                 FileOffset.LowPart,
544c2c66affSColin Finck                 Status,
545c2c66affSColin Finck                 Dirty ? "dirty" : "clean");
546c2c66affSColin Finck 
547c2c66affSColin Finck         if (!NT_SUCCESS(Status) ||
548c2c66affSColin Finck             !NT_SUCCESS(Status = MmFinalizeSectionPageOut(Segment,
549c2c66affSColin Finck                                                           &FileOffset,
550c2c66affSColin Finck                                                           Page,
551c2c66affSColin Finck                                                           Dirty)))
552c2c66affSColin Finck         {
553c2c66affSColin Finck             DPRINTC("Failed to page out %x, replacing %x at %x in segment %x\n",
554c2c66affSColin Finck                     SectionPage,
555c2c66affSColin Finck                     FileOffset.LowPart,
556c2c66affSColin Finck                     Segment);
557c2c66affSColin Finck 
558c2c66affSColin Finck             MmLockSectionSegment(Segment);
559c2c66affSColin Finck 
560c2c66affSColin Finck             MmSetPageEntrySectionSegment(Segment,
561c2c66affSColin Finck                                          &FileOffset,
562c2c66affSColin Finck                                          Dirty ? MAKE_PFN_SSE(Page) : DIRTY_SSE(MAKE_PFN_SSE(Page)));
563c2c66affSColin Finck 
564c2c66affSColin Finck             MmUnlockSectionSegment(Segment);
565c2c66affSColin Finck         }
566c2c66affSColin Finck 
567c2c66affSColin Finck         /* Alas, we had the last reference */
568c2c66affSColin Finck         if ((RefCount = InterlockedDecrementUL(&Segment->ReferenceCount)) == 0)
569c2c66affSColin Finck             MmFinalizeSegment(Segment);
570c2c66affSColin Finck     }
571c2c66affSColin Finck 
572c2c66affSColin Finck     if (ProcRef)
573c2c66affSColin Finck     {
574c2c66affSColin Finck         DPRINTC("Dereferencing process...\n");
575c2c66affSColin Finck         ObDereferenceObject(Process);
576c2c66affSColin Finck     }
577c2c66affSColin Finck 
578c2c66affSColin Finck     ExReleaseFastMutex(&MiGlobalPageOperation);
579c2c66affSColin Finck 
580c2c66affSColin Finck     DPRINTC("%s %x %x\n",
581c2c66affSColin Finck             NT_SUCCESS(Status) ? "Evicted" : "Spared",
582c2c66affSColin Finck             Page,
583c2c66affSColin Finck             Status);
584c2c66affSColin Finck 
585c2c66affSColin Finck     return NT_SUCCESS(Status) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
586c2c66affSColin Finck }
587c2c66affSColin Finck 
588c2c66affSColin Finck ULONG
589c2c66affSColin Finck NTAPI
MiCacheEvictPages(PMM_SECTION_SEGMENT Segment,ULONG Target)590c2c66affSColin Finck MiCacheEvictPages(PMM_SECTION_SEGMENT Segment,
591c2c66affSColin Finck                   ULONG Target)
592c2c66affSColin Finck {
593c2c66affSColin Finck     ULONG_PTR Entry;
594c2c66affSColin Finck     ULONG Result = 0, i, j;
595c2c66affSColin Finck     NTSTATUS Status;
596c2c66affSColin Finck     PFN_NUMBER Page;
597c2c66affSColin Finck     LARGE_INTEGER Offset;
598c2c66affSColin Finck 
599c2c66affSColin Finck     MmLockSectionSegment(Segment);
600c2c66affSColin Finck 
601c2c66affSColin Finck     for (i = 0; i < RtlNumberGenericTableElements(&Segment->PageTable); i++) {
602c2c66affSColin Finck 
603c2c66affSColin Finck         PCACHE_SECTION_PAGE_TABLE Element = RtlGetElementGenericTable(&Segment->PageTable,
604c2c66affSColin Finck                                                                       i);
605c2c66affSColin Finck 
606c2c66affSColin Finck         ASSERT(Element);
607c2c66affSColin Finck 
608c2c66affSColin Finck         Offset = Element->FileOffset;
609c2c66affSColin Finck         for (j = 0; j < ENTRIES_PER_ELEMENT; j++, Offset.QuadPart += PAGE_SIZE) {
610c2c66affSColin Finck             Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
611c2c66affSColin Finck             if (Entry && !IS_SWAP_FROM_SSE(Entry)) {
612c2c66affSColin Finck                 Page = PFN_FROM_SSE(Entry);
613c2c66affSColin Finck                 MmUnlockSectionSegment(Segment);
614c2c66affSColin Finck                 Status = MmpPageOutPhysicalAddress(Page);
615c2c66affSColin Finck                 if (NT_SUCCESS(Status))
616c2c66affSColin Finck                     Result++;
617c2c66affSColin Finck                 MmLockSectionSegment(Segment);
618c2c66affSColin Finck             }
619c2c66affSColin Finck         }
620c2c66affSColin Finck     }
621c2c66affSColin Finck 
622c2c66affSColin Finck     MmUnlockSectionSegment(Segment);
623c2c66affSColin Finck 
624c2c66affSColin Finck     return Result;
625c2c66affSColin Finck }
626c2c66affSColin Finck 
627c2c66affSColin Finck extern LIST_ENTRY MiSegmentList;
628c2c66affSColin Finck 
629c2c66affSColin Finck // Interact with legacy balance manager for now
630c2c66affSColin Finck // This can fall away when our section implementation supports
631c2c66affSColin Finck // demand paging properly
632c2c66affSColin Finck NTSTATUS
MiRosTrimCache(ULONG Target,ULONG Priority,PULONG NrFreed)633c2c66affSColin Finck MiRosTrimCache(ULONG Target,
634c2c66affSColin Finck                ULONG Priority,
635c2c66affSColin Finck                PULONG NrFreed)
636c2c66affSColin Finck {
637c2c66affSColin Finck     ULONG Freed;
638c2c66affSColin Finck     PLIST_ENTRY Entry;
639c2c66affSColin Finck     PMM_SECTION_SEGMENT Segment;
640c2c66affSColin Finck     *NrFreed = 0;
641c2c66affSColin Finck 
642c2c66affSColin Finck     DPRINT1("Need to trim %lu cache pages\n", Target);
643c2c66affSColin Finck     for (Entry = MiSegmentList.Flink;
644c2c66affSColin Finck          *NrFreed < Target && Entry != &MiSegmentList;
645c2c66affSColin Finck          Entry = Entry->Flink) {
646c2c66affSColin Finck         Segment = CONTAINING_RECORD(Entry, MM_SECTION_SEGMENT, ListOfSegments);
647c2c66affSColin Finck         /* Defer to MM to try recovering pages from it */
648c2c66affSColin Finck         Freed = MiCacheEvictPages(Segment, Target);
649c2c66affSColin Finck         *NrFreed += Freed;
650c2c66affSColin Finck     }
651c2c66affSColin Finck     DPRINT1("Evicted %lu cache pages\n", Target);
652c2c66affSColin Finck 
653c2c66affSColin Finck     if (!IsListEmpty(&MiSegmentList)) {
654c2c66affSColin Finck         Entry = MiSegmentList.Flink;
655c2c66affSColin Finck         RemoveEntryList(Entry);
656c2c66affSColin Finck         InsertTailList(&MiSegmentList, Entry);
657c2c66affSColin Finck     }
658c2c66affSColin Finck 
659c2c66affSColin Finck     return STATUS_SUCCESS;
660c2c66affSColin Finck }
661