66ac8f277b2afc40ea77f91bc752f5f955a951f2
[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 static
68 PVOID
69 NTAPI
70 MiSectionPageTableAllocate(PRTL_GENERIC_TABLE Table, CLONG Bytes)
71 {
72 PVOID Result;
73 Result = ExAllocatePoolWithTag(NonPagedPool, Bytes, 'MmPt');
74 //DPRINT("MiSectionPageTableAllocate(%d) => %p\n", Bytes, Result);
75 return Result;
76 }
77
78 static
79 VOID
80 NTAPI
81 MiSectionPageTableFree(PRTL_GENERIC_TABLE Table, PVOID Data)
82 {
83 //DPRINT("MiSectionPageTableFree(%p)\n", Data);
84 ExFreePoolWithTag(Data, 'MmPt');
85 }
86
87 static
88 RTL_GENERIC_COMPARE_RESULTS
89 NTAPI
90 MiSectionPageTableCompare(PRTL_GENERIC_TABLE Table,
91 PVOID PtrA,
92 PVOID PtrB)
93 {
94 PLARGE_INTEGER A = PtrA, B = PtrB;
95 BOOLEAN Result = (A->QuadPart < B->QuadPart) ? GenericLessThan :
96 (A->QuadPart == B->QuadPart) ? GenericEqual : GenericGreaterThan;
97
98 #if 0
99 DPRINT
100 ("Compare: %08x%08x vs %08x%08x => %s\n",
101 A->u.HighPart, A->u.LowPart,
102 B->u.HighPart, B->u.LowPart,
103 Result == GenericLessThan ? "GenericLessThan" :
104 Result == GenericGreaterThan ? "GenericGreaterThan" :
105 "GenericEqual");
106 #endif
107
108 return Result;
109 }
110
111 static
112 PCACHE_SECTION_PAGE_TABLE
113 NTAPI
114 MiSectionPageTableGet(PRTL_GENERIC_TABLE Table,
115 PLARGE_INTEGER FileOffset)
116 {
117 LARGE_INTEGER SearchFileOffset;
118 PCACHE_SECTION_PAGE_TABLE PageTable;
119 SearchFileOffset.QuadPart = ROUND_DOWN(FileOffset->QuadPart,
120 ENTRIES_PER_ELEMENT * PAGE_SIZE);
121 PageTable = RtlLookupElementGenericTable(Table, &SearchFileOffset);
122
123 DPRINT("MiSectionPageTableGet(%08x,%08x%08x)\n",
124 Table,
125 FileOffset->HighPart,
126 FileOffset->LowPart);
127
128 return PageTable;
129 }
130
131 static
132 PCACHE_SECTION_PAGE_TABLE
133 NTAPI
134 MiSectionPageTableGetOrAllocate(PRTL_GENERIC_TABLE Table,
135 PLARGE_INTEGER FileOffset)
136 {
137 LARGE_INTEGER SearchFileOffset;
138 CACHE_SECTION_PAGE_TABLE SectionZeroPageTable;
139 PCACHE_SECTION_PAGE_TABLE PageTableSlice = MiSectionPageTableGet(Table,
140 FileOffset);
141 /* Please zero memory when taking away zero initialization. */
142 RtlZeroMemory(&SectionZeroPageTable, sizeof(CACHE_SECTION_PAGE_TABLE));
143 if (!PageTableSlice)
144 {
145 SearchFileOffset.QuadPart = ROUND_DOWN(FileOffset->QuadPart,
146 ENTRIES_PER_ELEMENT * PAGE_SIZE);
147 SectionZeroPageTable.FileOffset = SearchFileOffset;
148 SectionZeroPageTable.Refcount = 1;
149 PageTableSlice = RtlInsertElementGenericTable(Table,
150 &SectionZeroPageTable,
151 sizeof(SectionZeroPageTable),
152 NULL);
153 if (!PageTableSlice) return NULL;
154 DPRINT("Allocate page table %x (%08x%08x)\n",
155 PageTableSlice,
156 PageTableSlice->FileOffset.u.HighPart,
157 PageTableSlice->FileOffset.u.LowPart);
158 }
159 return PageTableSlice;
160 }
161
162 VOID
163 NTAPI
164 MiInitializeSectionPageTable(PMM_SECTION_SEGMENT Segment)
165 {
166 RtlInitializeGenericTable(&Segment->PageTable,
167 MiSectionPageTableCompare,
168 MiSectionPageTableAllocate,
169 MiSectionPageTableFree,
170 NULL);
171
172 DPRINT("MiInitializeSectionPageTable(%p)\n", &Segment->PageTable);
173 }
174
175 NTSTATUS
176 NTAPI
177 _MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
178 PLARGE_INTEGER Offset,
179 ULONG_PTR Entry,
180 const char *file,
181 int line)
182 {
183 ULONG_PTR PageIndex, OldEntry;
184 PCACHE_SECTION_PAGE_TABLE PageTable;
185
186 ASSERT(Segment->Locked);
187 ASSERT(!IS_SWAP_FROM_SSE(Entry) || !IS_DIRTY_SSE(Entry));
188
189 if (Entry && !IS_SWAP_FROM_SSE(Entry))
190 MmGetRmapListHeadPage(PFN_FROM_SSE(Entry));
191
192 PageTable = MiSectionPageTableGetOrAllocate(&Segment->PageTable, Offset);
193
194 if (!PageTable) return STATUS_NO_MEMORY;
195
196 ASSERT(MiSectionPageTableGet(&Segment->PageTable, Offset));
197
198 PageTable->Segment = Segment;
199 PageIndex = (ULONG_PTR)((Offset->QuadPart - PageTable->FileOffset.QuadPart) / PAGE_SIZE);
200 OldEntry = PageTable->PageEntries[PageIndex];
201
202 DPRINT("MiSetPageEntrySectionSegment(%p,%08x%08x,%x=>%x)\n",
203 Segment,
204 Offset->u.HighPart,
205 Offset->u.LowPart,
206 OldEntry,
207 Entry);
208
209 if (PFN_FROM_SSE(Entry) == PFN_FROM_SSE(OldEntry)) {
210 /* Nothing */
211 } else if (Entry && !IS_SWAP_FROM_SSE(Entry)) {
212 ASSERT(!OldEntry || IS_SWAP_FROM_SSE(OldEntry));
213 MmSetSectionAssociation(PFN_FROM_SSE(Entry), Segment, Offset);
214 } else if (OldEntry && !IS_SWAP_FROM_SSE(OldEntry)) {
215 ASSERT(!Entry || IS_SWAP_FROM_SSE(Entry));
216 MmDeleteSectionAssociation(PFN_FROM_SSE(OldEntry));
217 } else if (IS_SWAP_FROM_SSE(Entry)) {
218 ASSERT(!IS_SWAP_FROM_SSE(OldEntry) ||
219 SWAPENTRY_FROM_SSE(OldEntry) == MM_WAIT_ENTRY);
220 if (OldEntry && SWAPENTRY_FROM_SSE(OldEntry) != MM_WAIT_ENTRY)
221 MmDeleteSectionAssociation(PFN_FROM_SSE(OldEntry));
222 } else if (IS_SWAP_FROM_SSE(OldEntry)) {
223 ASSERT(!IS_SWAP_FROM_SSE(Entry));
224 if (Entry)
225 MmSetSectionAssociation(PFN_FROM_SSE(OldEntry), Segment, Offset);
226 } else {
227 /* We should not be replacing a page like this */
228 ASSERT(FALSE);
229 }
230 PageTable->PageEntries[PageIndex] = Entry;
231 return STATUS_SUCCESS;
232 }
233
234 ULONG_PTR
235 NTAPI
236 _MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
237 PLARGE_INTEGER Offset,
238 const char *file,
239 int line)
240 {
241 LARGE_INTEGER FileOffset;
242 ULONG_PTR PageIndex, Result;
243 PCACHE_SECTION_PAGE_TABLE PageTable;
244
245 ASSERT(Segment->Locked);
246 FileOffset.QuadPart = ROUND_DOWN(Offset->QuadPart,
247 ENTRIES_PER_ELEMENT * PAGE_SIZE);
248 PageTable = MiSectionPageTableGet(&Segment->PageTable, &FileOffset);
249 if (!PageTable) return 0;
250 PageIndex = (ULONG_PTR)((Offset->QuadPart - PageTable->FileOffset.QuadPart) / PAGE_SIZE);
251 Result = PageTable->PageEntries[PageIndex];
252 #if 0
253 DPRINTC
254 ("MiGetPageEntrySectionSegment(%p,%08x%08x) => %x %s:%d\n",
255 Segment,
256 FileOffset.u.HighPart,
257 FileOffset.u.LowPart + PageIndex * PAGE_SIZE,
258 Result,
259 file, line);
260 #endif
261 return Result;
262 }
263
264 /*
265
266 Destroy the rtl generic table that serves as the section's page table. Call
267 the FreePage function for each non-zero entry in the section page table as
268 we go. Note that the page table is still techinally valid until after all
269 pages are destroyed, as we don't finally destroy the table until we've free
270 each slice. There is no order guarantee for deletion of individual elements
271 although it's in-order as written now.
272
273 */
274
275 VOID
276 NTAPI
277 MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment,
278 FREE_SECTION_PAGE_FUN FreePage)
279 {
280 PCACHE_SECTION_PAGE_TABLE Element;
281 DPRINT("MiFreePageTablesSectionSegment(%p)\n", &Segment->PageTable);
282 while ((Element = RtlGetElementGenericTable(&Segment->PageTable, 0))) {
283 DPRINT("Delete table for <%wZ> %x -> %08x%08x\n",
284 Segment->FileObject ? &Segment->FileObject->FileName : NULL,
285 Segment,
286 Element->FileOffset.u.HighPart,
287 Element->FileOffset.u.LowPart);
288 if (FreePage)
289 {
290 ULONG i;
291 for (i = 0; i < ENTRIES_PER_ELEMENT; i++)
292 {
293 ULONG_PTR Entry;
294 LARGE_INTEGER Offset;
295 Offset.QuadPart = Element->FileOffset.QuadPart + i * PAGE_SIZE;
296 Entry = Element->PageEntries[i];
297 if (Entry && !IS_SWAP_FROM_SSE(Entry))
298 {
299 DPRINT("Freeing page %x:%x @ %x\n",
300 Segment,
301 Entry,
302 Offset.LowPart);
303
304 FreePage(Segment, &Offset);
305 }
306 }
307 }
308 DPRINT("Remove memory\n");
309 RtlDeleteElementGenericTable(&Segment->PageTable, Element);
310 }
311 DPRINT("Done\n");
312 }
313
314 /*
315
316 Retrieves the MM_SECTION_SEGMENT and fills in the LARGE_INTEGER Offset given
317 by the caller that corresponds to the page specified. This uses
318 MmGetSegmentRmap to find the rmap belonging to the segment itself, and uses
319 the result as a pointer to a 256-entry page table structure. The rmap also
320 includes 8 bits of offset information indication one of 256 page entries that
321 the rmap corresponds to. This information together gives us an exact offset
322 into the file, as well as the MM_SECTION_SEGMENT pointer stored in the page
323 table slice.
324
325 NULL is returned is there is no segment rmap for the page.
326
327 */
328
329 PMM_SECTION_SEGMENT
330 NTAPI
331 MmGetSectionAssociation(PFN_NUMBER Page,
332 PLARGE_INTEGER Offset)
333 {
334 ULONG RawOffset;
335 PMM_SECTION_SEGMENT Segment = NULL;
336 PCACHE_SECTION_PAGE_TABLE PageTable;
337
338 PageTable = (PCACHE_SECTION_PAGE_TABLE)MmGetSegmentRmap(Page,
339 &RawOffset);
340 if (PageTable)
341 {
342 Segment = PageTable->Segment;
343 Offset->QuadPart = PageTable->FileOffset.QuadPart +
344 (RawOffset << PAGE_SHIFT);
345 }
346
347 return Segment;
348 }
349
350 NTSTATUS
351 NTAPI
352 MmSetSectionAssociation(PFN_NUMBER Page,
353 PMM_SECTION_SEGMENT Segment,
354 PLARGE_INTEGER Offset)
355 {
356 PCACHE_SECTION_PAGE_TABLE PageTable;
357 ULONG ActualOffset;
358
359 PageTable = MiSectionPageTableGet(&Segment->PageTable, Offset);
360 ASSERT(PageTable);
361
362 ActualOffset = (ULONG)(Offset->QuadPart - PageTable->FileOffset.QuadPart);
363 MmInsertRmap(Page,
364 (PEPROCESS)PageTable,
365 (PVOID)(RMAP_SEGMENT_MASK | (ActualOffset >> PAGE_SHIFT)));
366
367 return STATUS_SUCCESS;
368 }