[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / cache / section / sptab.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: Section object page tables
22 *
23 * PROGRAMMERS: arty
24 */
25
26 /*
27
28 This file implements the section page table. It relies on rtl generic table
29 functionality to provide access to 256-page chunks. Calls to
30 MiSetPageEntrySectionSegment and MiGetPageEntrySectionSegment must be
31 synchronized by holding the segment lock.
32
33 Each page table entry is a ULONG as in x86.
34
35 Bit 1 is used as a swap entry indication as in the main page table.
36 Bit 2 is used as a dirty indication. A dirty page will eventually be written
37 back to the file.
38 Bits 3-11 are used as a map count in the legacy mm code, Note that zero is
39 illegal, as the legacy code does not take advantage of segment rmaps.
40 Therefore, every segment page is mapped in at least one address space, and
41 MmUnsharePageEntry is quite complicated. In addition, the page may also be
42 owned by the legacy cache manager, giving an implied additional reference.
43 Upper bits are a PFN_NUMBER.
44
45 These functions, in addition to maintaining the segment page table also
46 automatically maintain the segment rmap by calling MmSetSectionAssociation
47 and MmDeleteSectionAssociation. Segment rmaps are discussed in rmap.c. The
48 upshot is that it is impossible to have a page properly registered in a segment
49 page table and not also found in a segment rmap that can be found from the
50 paging machinery.
51
52 */
53
54 /* INCLUDES *****************************************************************/
55
56 #include <ntoskrnl.h>
57 #include "newmm.h"
58 #define NDEBUG
59 #include <debug.h>
60
61 #define DPRINTC DPRINT
62
63 /* TYPES *********************************************************************/
64
65 extern KSPIN_LOCK MiSectionPageTableLock;
66
67 _Function_class_(RTL_GENERIC_ALLOCATE_ROUTINE)
68 static
69 PVOID
70 NTAPI
71 MiSectionPageTableAllocate(PRTL_GENERIC_TABLE Table, CLONG Bytes)
72 {
73 PVOID Result;
74 Result = ExAllocatePoolWithTag(NonPagedPool, Bytes, 'MmPt');
75 //DPRINT("MiSectionPageTableAllocate(%d) => %p\n", Bytes, Result);
76 return Result;
77 }
78
79 _Function_class_(RTL_GENERIC_FREE_ROUTINE)
80 static
81 VOID
82 NTAPI
83 MiSectionPageTableFree(PRTL_GENERIC_TABLE Table, PVOID Data)
84 {
85 //DPRINT("MiSectionPageTableFree(%p)\n", Data);
86 ExFreePoolWithTag(Data, 'MmPt');
87 }
88
89 _Function_class_(RTL_GENERIC_COMPARE_ROUTINE)
90 static
91 RTL_GENERIC_COMPARE_RESULTS
92 NTAPI
93 MiSectionPageTableCompare(PRTL_GENERIC_TABLE Table,
94 PVOID PtrA,
95 PVOID PtrB)
96 {
97 PLARGE_INTEGER A = PtrA, B = PtrB;
98 BOOLEAN Result = (A->QuadPart < B->QuadPart) ? GenericLessThan :
99 (A->QuadPart == B->QuadPart) ? GenericEqual : GenericGreaterThan;
100
101 #if 0
102 DPRINT
103 ("Compare: %08x%08x vs %08x%08x => %s\n",
104 A->u.HighPart, A->u.LowPart,
105 B->u.HighPart, B->u.LowPart,
106 Result == GenericLessThan ? "GenericLessThan" :
107 Result == GenericGreaterThan ? "GenericGreaterThan" :
108 "GenericEqual");
109 #endif
110
111 return Result;
112 }
113
114 static
115 PCACHE_SECTION_PAGE_TABLE
116 NTAPI
117 MiSectionPageTableGet(PRTL_GENERIC_TABLE Table,
118 PLARGE_INTEGER FileOffset)
119 {
120 LARGE_INTEGER SearchFileOffset;
121 PCACHE_SECTION_PAGE_TABLE PageTable;
122 SearchFileOffset.QuadPart = ROUND_DOWN(FileOffset->QuadPart,
123 ENTRIES_PER_ELEMENT * PAGE_SIZE);
124 PageTable = RtlLookupElementGenericTable(Table, &SearchFileOffset);
125
126 DPRINT("MiSectionPageTableGet(%08x,%08x%08x)\n",
127 Table,
128 FileOffset->HighPart,
129 FileOffset->LowPart);
130
131 return PageTable;
132 }
133
134 static
135 PCACHE_SECTION_PAGE_TABLE
136 NTAPI
137 MiSectionPageTableGetOrAllocate(PRTL_GENERIC_TABLE Table,
138 PLARGE_INTEGER FileOffset)
139 {
140 LARGE_INTEGER SearchFileOffset;
141 CACHE_SECTION_PAGE_TABLE SectionZeroPageTable;
142 PCACHE_SECTION_PAGE_TABLE PageTableSlice = MiSectionPageTableGet(Table,
143 FileOffset);
144 /* Please zero memory when taking away zero initialization. */
145 RtlZeroMemory(&SectionZeroPageTable, sizeof(CACHE_SECTION_PAGE_TABLE));
146 if (!PageTableSlice)
147 {
148 SearchFileOffset.QuadPart = ROUND_DOWN(FileOffset->QuadPart,
149 ENTRIES_PER_ELEMENT * PAGE_SIZE);
150 SectionZeroPageTable.FileOffset = SearchFileOffset;
151 SectionZeroPageTable.Refcount = 1;
152 PageTableSlice = RtlInsertElementGenericTable(Table,
153 &SectionZeroPageTable,
154 sizeof(SectionZeroPageTable),
155 NULL);
156 if (!PageTableSlice) return NULL;
157 DPRINT("Allocate page table %x (%08x%08x)\n",
158 PageTableSlice,
159 PageTableSlice->FileOffset.u.HighPart,
160 PageTableSlice->FileOffset.u.LowPart);
161 }
162 return PageTableSlice;
163 }
164
165 VOID
166 NTAPI
167 MiInitializeSectionPageTable(PMM_SECTION_SEGMENT Segment)
168 {
169 RtlInitializeGenericTable(&Segment->PageTable,
170 MiSectionPageTableCompare,
171 MiSectionPageTableAllocate,
172 MiSectionPageTableFree,
173 NULL);
174
175 DPRINT("MiInitializeSectionPageTable(%p)\n", &Segment->PageTable);
176 }
177
178 NTSTATUS
179 NTAPI
180 _MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
181 PLARGE_INTEGER Offset,
182 ULONG_PTR Entry,
183 const char *file,
184 int line)
185 {
186 ULONG_PTR PageIndex, OldEntry;
187 PCACHE_SECTION_PAGE_TABLE PageTable;
188
189 ASSERT(Segment->Locked);
190 ASSERT(!IS_SWAP_FROM_SSE(Entry) || !IS_DIRTY_SSE(Entry));
191
192 if (Entry && !IS_SWAP_FROM_SSE(Entry))
193 MmGetRmapListHeadPage(PFN_FROM_SSE(Entry));
194
195 PageTable = MiSectionPageTableGetOrAllocate(&Segment->PageTable, Offset);
196
197 if (!PageTable) return STATUS_NO_MEMORY;
198
199 ASSERT(MiSectionPageTableGet(&Segment->PageTable, Offset));
200
201 PageTable->Segment = Segment;
202 PageIndex = (ULONG_PTR)((Offset->QuadPart - PageTable->FileOffset.QuadPart) / PAGE_SIZE);
203 OldEntry = PageTable->PageEntries[PageIndex];
204
205 DPRINT("MiSetPageEntrySectionSegment(%p,%08x%08x,%x=>%x)\n",
206 Segment,
207 Offset->u.HighPart,
208 Offset->u.LowPart,
209 OldEntry,
210 Entry);
211
212 if (PFN_FROM_SSE(Entry) == PFN_FROM_SSE(OldEntry)) {
213 /* Nothing */
214 } else if (Entry && !IS_SWAP_FROM_SSE(Entry)) {
215 ASSERT(!OldEntry || IS_SWAP_FROM_SSE(OldEntry));
216 MmSetSectionAssociation(PFN_FROM_SSE(Entry), Segment, Offset);
217 } else if (OldEntry && !IS_SWAP_FROM_SSE(OldEntry)) {
218 ASSERT(!Entry || IS_SWAP_FROM_SSE(Entry));
219 MmDeleteSectionAssociation(PFN_FROM_SSE(OldEntry));
220 } else if (IS_SWAP_FROM_SSE(Entry)) {
221 ASSERT(!IS_SWAP_FROM_SSE(OldEntry) ||
222 SWAPENTRY_FROM_SSE(OldEntry) == MM_WAIT_ENTRY);
223 if (OldEntry && SWAPENTRY_FROM_SSE(OldEntry) != MM_WAIT_ENTRY)
224 MmDeleteSectionAssociation(PFN_FROM_SSE(OldEntry));
225 } else if (IS_SWAP_FROM_SSE(OldEntry)) {
226 ASSERT(!IS_SWAP_FROM_SSE(Entry));
227 if (Entry)
228 MmSetSectionAssociation(PFN_FROM_SSE(OldEntry), Segment, Offset);
229 } else {
230 /* We should not be replacing a page like this */
231 ASSERT(FALSE);
232 }
233 PageTable->PageEntries[PageIndex] = Entry;
234 return STATUS_SUCCESS;
235 }
236
237 ULONG_PTR
238 NTAPI
239 _MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
240 PLARGE_INTEGER Offset,
241 const char *file,
242 int line)
243 {
244 LARGE_INTEGER FileOffset;
245 ULONG_PTR PageIndex, Result;
246 PCACHE_SECTION_PAGE_TABLE PageTable;
247
248 ASSERT(Segment->Locked);
249 FileOffset.QuadPart = ROUND_DOWN(Offset->QuadPart,
250 ENTRIES_PER_ELEMENT * PAGE_SIZE);
251 PageTable = MiSectionPageTableGet(&Segment->PageTable, &FileOffset);
252 if (!PageTable) return 0;
253 PageIndex = (ULONG_PTR)((Offset->QuadPart - PageTable->FileOffset.QuadPart) / PAGE_SIZE);
254 Result = PageTable->PageEntries[PageIndex];
255 #if 0
256 DPRINTC
257 ("MiGetPageEntrySectionSegment(%p,%08x%08x) => %x %s:%d\n",
258 Segment,
259 FileOffset.u.HighPart,
260 FileOffset.u.LowPart + PageIndex * PAGE_SIZE,
261 Result,
262 file, line);
263 #endif
264 return Result;
265 }
266
267 /*
268
269 Destroy the rtl generic table that serves as the section's page table. Call
270 the FreePage function for each non-zero entry in the section page table as
271 we go. Note that the page table is still techinally valid until after all
272 pages are destroyed, as we don't finally destroy the table until we've free
273 each slice. There is no order guarantee for deletion of individual elements
274 although it's in-order as written now.
275
276 */
277
278 VOID
279 NTAPI
280 MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment,
281 FREE_SECTION_PAGE_FUN FreePage)
282 {
283 PCACHE_SECTION_PAGE_TABLE Element;
284 DPRINT("MiFreePageTablesSectionSegment(%p)\n", &Segment->PageTable);
285 while ((Element = RtlGetElementGenericTable(&Segment->PageTable, 0))) {
286 DPRINT("Delete table for <%wZ> %x -> %08x%08x\n",
287 Segment->FileObject ? &Segment->FileObject->FileName : NULL,
288 Segment,
289 Element->FileOffset.u.HighPart,
290 Element->FileOffset.u.LowPart);
291 if (FreePage)
292 {
293 ULONG i;
294 for (i = 0; i < ENTRIES_PER_ELEMENT; i++)
295 {
296 ULONG_PTR Entry;
297 LARGE_INTEGER Offset;
298 Offset.QuadPart = Element->FileOffset.QuadPart + i * PAGE_SIZE;
299 Entry = Element->PageEntries[i];
300 if (Entry && !IS_SWAP_FROM_SSE(Entry))
301 {
302 DPRINT("Freeing page %x:%x @ %x\n",
303 Segment,
304 Entry,
305 Offset.LowPart);
306
307 FreePage(Segment, &Offset);
308 }
309 }
310 }
311 DPRINT("Remove memory\n");
312 RtlDeleteElementGenericTable(&Segment->PageTable, Element);
313 }
314 DPRINT("Done\n");
315 }
316
317 /*
318
319 Retrieves the MM_SECTION_SEGMENT and fills in the LARGE_INTEGER Offset given
320 by the caller that corresponds to the page specified. This uses
321 MmGetSegmentRmap to find the rmap belonging to the segment itself, and uses
322 the result as a pointer to a 256-entry page table structure. The rmap also
323 includes 8 bits of offset information indication one of 256 page entries that
324 the rmap corresponds to. This information together gives us an exact offset
325 into the file, as well as the MM_SECTION_SEGMENT pointer stored in the page
326 table slice.
327
328 NULL is returned is there is no segment rmap for the page.
329
330 */
331
332 PMM_SECTION_SEGMENT
333 NTAPI
334 MmGetSectionAssociation(PFN_NUMBER Page,
335 PLARGE_INTEGER Offset)
336 {
337 ULONG RawOffset;
338 PMM_SECTION_SEGMENT Segment = NULL;
339 PCACHE_SECTION_PAGE_TABLE PageTable;
340
341 PageTable = (PCACHE_SECTION_PAGE_TABLE)MmGetSegmentRmap(Page,
342 &RawOffset);
343 if (PageTable)
344 {
345 Segment = PageTable->Segment;
346 Offset->QuadPart = PageTable->FileOffset.QuadPart +
347 ((ULONG64)RawOffset << PAGE_SHIFT);
348 }
349
350 return Segment;
351 }
352
353 NTSTATUS
354 NTAPI
355 MmSetSectionAssociation(PFN_NUMBER Page,
356 PMM_SECTION_SEGMENT Segment,
357 PLARGE_INTEGER Offset)
358 {
359 PCACHE_SECTION_PAGE_TABLE PageTable;
360 ULONG ActualOffset;
361
362 PageTable = MiSectionPageTableGet(&Segment->PageTable, Offset);
363 ASSERT(PageTable);
364
365 ActualOffset = (ULONG)(Offset->QuadPart - PageTable->FileOffset.QuadPart);
366 MmInsertRmap(Page,
367 (PEPROCESS)PageTable,
368 (PVOID)(RMAP_SEGMENT_MASK | (ActualOffset >> PAGE_SHIFT)));
369
370 return STATUS_SUCCESS;
371 }