8b4b5d3f379c222540e60ae48418a7aeb254e104
[reactos.git] / reactos / ntoskrnl / mm / pagefile.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: pagefile.c,v 1.17 2002/02/08 02:57:07 chorns Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/pagefile.c
23 * PURPOSE: Paging file functions
24 * PROGRAMMER: David Welch (welch@mcmail.com)
25 * UPDATE HISTORY:
26 * Created 22/05/98
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include <ddk/ntddk.h>
32 #include <internal/io.h>
33 #include <internal/mm.h>
34 #include <napi/core.h>
35
36 #define NDEBUG
37 #include <internal/debug.h>
38
39 /* TYPES *********************************************************************/
40
41 typedef struct _PAGINGFILE
42 {
43 LIST_ENTRY PagingFileListEntry;
44 PFILE_OBJECT FileObject;
45 ULONG MaximumSize;
46 ULONG CurrentSize;
47 ULONG FreePages;
48 ULONG UsedPages;
49 PULONG AllocMap;
50 KSPIN_LOCK AllocMapLock;
51 ULONG AllocMapSize;
52 } PAGINGFILE, *PPAGINGFILE;
53
54 /* GLOBALS *******************************************************************/
55
56 #define MAX_PAGING_FILES (32)
57
58 /* List of paging files, both used and free */
59 static PPAGINGFILE PagingFileList[MAX_PAGING_FILES];
60
61 /* Lock for examining the list of paging files */
62 static KSPIN_LOCK PagingFileListLock;
63
64 /* Number of pages that are available for swapping */
65 static ULONG MiFreeSwapPages;
66
67 /* Number of pages that have been allocated for swapping */
68 static ULONG MiUsedSwapPages;
69
70 /*
71 * Number of pages that have been reserved for swapping but not yet allocated
72 */
73 static ULONG MiReservedSwapPages;
74
75 /*
76 * Ratio between reserved and available swap pages, e.g. setting this to five
77 * forces one swap page to be available for every five swap pages that are
78 * reserved. Setting this to zero turns off commit checking altogether.
79 */
80 #define MM_PAGEFILE_COMMIT_RATIO (1)
81
82 /*
83 * Number of pages that can be used for potentially swapable memory without
84 * pagefile space being reserved. The intention is that this allows smss
85 * to start up and create page files while ordinarily having a commit
86 * ratio of one.
87 */
88 #define MM_PAGEFILE_COMMIT_GRACE (256)
89
90 #if 0
91 static PVOID MmCoreDumpPageFrame;
92 static BYTE MmCoreDumpHeader[PAGESIZE];
93 #endif
94
95 /*
96 * Translate between a swap entry and a file and offset pair.
97 */
98 #define FILE_FROM_ENTRY(i) ((i) >> 24)
99 #define OFFSET_FROM_ENTRY(i) (((i) & 0xffffff) - 1)
100 #define ENTRY_FROM_FILE_OFFSET(i, j) (((i) << 24) | ((j) + 1))
101
102 /* FUNCTIONS *****************************************************************/
103
104 NTSTATUS MmWriteToSwapPage(SWAPENTRY SwapEntry, PMDL Mdl)
105 {
106 ULONG i, offset;
107 LARGE_INTEGER file_offset;
108 IO_STATUS_BLOCK Iosb;
109 NTSTATUS Status;
110
111 if (SwapEntry == 0)
112 {
113 KeBugCheck(0);
114 return(STATUS_UNSUCCESSFUL);
115 }
116
117 i = FILE_FROM_ENTRY(SwapEntry);
118 offset = OFFSET_FROM_ENTRY(SwapEntry);
119
120 if (i > MAX_PAGING_FILES)
121 {
122 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
123 KeBugCheck(0);
124 }
125 if (PagingFileList[i]->FileObject == NULL ||
126 PagingFileList[i]->FileObject->DeviceObject == NULL)
127 {
128 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
129 KeBugCheck(0);
130 }
131
132 file_offset.QuadPart = offset * 4096;
133
134 Status = IoPageWrite(PagingFileList[i]->FileObject,
135 Mdl,
136 &file_offset,
137 &Iosb,
138 TRUE);
139 return(Status);
140 }
141
142 NTSTATUS MmReadFromSwapPage(SWAPENTRY SwapEntry, PMDL Mdl)
143 {
144 ULONG i, offset;
145 LARGE_INTEGER file_offset;
146 IO_STATUS_BLOCK Iosb;
147 NTSTATUS Status;
148
149 if (SwapEntry == 0)
150 {
151 KeBugCheck(0);
152 return(STATUS_UNSUCCESSFUL);
153 }
154
155 i = FILE_FROM_ENTRY(SwapEntry);
156 offset = OFFSET_FROM_ENTRY(SwapEntry);
157
158 if (i > MAX_PAGING_FILES)
159 {
160 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
161 KeBugCheck(0);
162 }
163 if (PagingFileList[i]->FileObject == NULL ||
164 PagingFileList[i]->FileObject->DeviceObject == NULL)
165 {
166 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
167 KeBugCheck(0);
168 }
169
170 file_offset.QuadPart = offset * 4096;
171
172 Status = IoPageRead(PagingFileList[i]->FileObject,
173 Mdl,
174 &file_offset,
175 &Iosb,
176 TRUE);
177 return(Status);
178 }
179
180 VOID
181 MmInitPagingFile(VOID)
182 {
183 ULONG i;
184
185 KeInitializeSpinLock(&PagingFileListLock);
186
187 MiFreeSwapPages = 0;
188 MiUsedSwapPages = 0;
189 MiReservedSwapPages = 0;
190
191 for (i = 0; i < MAX_PAGING_FILES; i++)
192 {
193 PagingFileList[i] = NULL;
194 }
195 }
196
197 BOOLEAN
198 MmReserveSwapPages(ULONG Nr)
199 {
200 KIRQL oldIrql;
201 ULONG MiAvailSwapPages;
202
203 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
204 MiAvailSwapPages =
205 (MiFreeSwapPages * MM_PAGEFILE_COMMIT_RATIO) + MM_PAGEFILE_COMMIT_GRACE;
206 if (MM_PAGEFILE_COMMIT_RATIO != 0 && MiAvailSwapPages < MiReservedSwapPages)
207 {
208 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
209 return(FALSE);
210 }
211 MiReservedSwapPages = MiReservedSwapPages + Nr;
212 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
213
214 return(TRUE);
215 }
216
217 VOID
218 MmDereserveSwapPages(ULONG Nr)
219 {
220 KIRQL oldIrql;
221
222 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
223 MiReservedSwapPages = MiReservedSwapPages - Nr;
224 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
225 }
226
227 static ULONG
228 MiAllocPageFromPagingFile(PPAGINGFILE PagingFile)
229 {
230 KIRQL oldIrql;
231 ULONG i, j;
232
233 KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
234
235 for (i = 0; i < PagingFile->AllocMapSize; i++)
236 {
237 for (j = 0; j < 32; j++)
238 {
239 if (!(PagingFile->AllocMap[i] & (1 << j)))
240 {
241 break;
242 }
243 }
244 if (j == 32)
245 {
246 continue;
247 }
248 PagingFile->AllocMap[i] |= (1 << j);
249 PagingFile->UsedPages++;
250 PagingFile->FreePages--;
251 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
252 return((i * 32) + j);
253 }
254
255 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
256 return(0xFFFFFFFF);
257 }
258
259 VOID
260 MmFreeSwapPage(SWAPENTRY Entry)
261 {
262 ULONG i;
263 ULONG off;
264 KIRQL oldIrql;
265
266 i = FILE_FROM_ENTRY(Entry);
267 off = OFFSET_FROM_ENTRY(Entry);
268
269 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
270 if (PagingFileList[i] == NULL)
271 {
272 KeBugCheck(0);
273 }
274 KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock);
275
276 PagingFileList[i]->AllocMap[off / 32] &= (~(1 << (off % 32)));
277
278 PagingFileList[i]->FreePages++;
279 PagingFileList[i]->UsedPages--;
280
281 MiFreeSwapPages++;
282 MiUsedSwapPages--;
283
284 KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock);
285 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
286 }
287
288 SWAPENTRY
289 MmAllocSwapPage(VOID)
290 {
291 KIRQL oldIrql;
292 ULONG i;
293 ULONG off;
294 SWAPENTRY entry;
295 static BOOLEAN SwapSpaceMessage = FALSE;
296
297 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
298
299 if (MiFreeSwapPages == 0)
300 {
301 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
302 if (!SwapSpaceMessage)
303 {
304 DPRINT1("MM: Out of swap space.\n");
305 SwapSpaceMessage = TRUE;
306 }
307 return(0);
308 }
309
310 for (i = 0; i < MAX_PAGING_FILES; i++)
311 {
312 if (PagingFileList[i] != NULL &&
313 PagingFileList[i]->FreePages >= 1)
314 {
315 off = MiAllocPageFromPagingFile(PagingFileList[i]);
316 if (off == 0xFFFFFFFF)
317 {
318 KeBugCheck(0);
319 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
320 return(STATUS_UNSUCCESSFUL);
321 }
322 MiUsedSwapPages++;
323 MiFreeSwapPages--;
324 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
325
326 entry = ENTRY_FROM_FILE_OFFSET(i, off);
327 return(entry);
328 }
329 }
330
331 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
332 if (!SwapSpaceMessage)
333 {
334 DPRINT1("MM: Out of swap space.\n");
335 SwapSpaceMessage = TRUE;
336 }
337 return(0);
338 }
339
340 #if 0
341 NTSTATUS STDCALL MmDumpToPagingFile(PCONTEXT Context,
342 ULONG BugCode,
343 ULONG ExceptionCode,
344 ULONG Cr2)
345 {
346 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Magic =
347 MM_CORE_DUMP_HEADER_MAGIC;
348 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Version =
349 MM_CORE_DUMP_HEADER_VERSION;
350 memcpy(&((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Context,
351 Context,
352 sizeof(CONTEXT));
353 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->DumpLength = 0;
354 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->BugCode = BugCode;
355 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->ExceptionCode =
356 ExceptionCode;
357 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Cr2 = Cr2;
358 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Cr3 = 0;
359 }
360 #endif
361
362 NTSTATUS STDCALL
363 NtCreatePagingFile(IN PUNICODE_STRING PageFileName,
364 IN ULONG MinimumSize,
365 IN ULONG MaximumSize,
366 OUT PULONG ActualSize)
367 {
368 NTSTATUS Status;
369 OBJECT_ATTRIBUTES ObjectAttributes;
370 HANDLE FileHandle;
371 IO_STATUS_BLOCK IoStatus;
372 PFILE_OBJECT FileObject;
373 PPAGINGFILE PagingFile;
374 KIRQL oldIrql;
375 ULONG AllocMapSize;
376 ULONG i;
377 PVOID Buffer;
378 LARGE_INTEGER ByteOffset;
379
380 DPRINT("NtCreatePagingFile(PageFileName %wZ, MinimumSize %d)\n",
381 PageFileName, MinimumSize);
382
383 InitializeObjectAttributes(&ObjectAttributes,
384 PageFileName,
385 0,
386 NULL,
387 NULL);
388
389 Status = IoCreateFile(&FileHandle,
390 FILE_ALL_ACCESS,
391 &ObjectAttributes,
392 &IoStatus,
393 NULL,
394 0,
395 0,
396 FILE_OPEN_IF,
397 FILE_SYNCHRONOUS_IO_NONALERT,
398 NULL,
399 0,
400 CreateFileTypeNone,
401 NULL,
402 SL_OPEN_PAGING_FILE);
403 if (!NT_SUCCESS(Status))
404 {
405 return(Status);
406 }
407
408 Buffer = ExAllocatePool(NonPagedPool, 4096);
409 memset(Buffer, 0, 4096);
410 ByteOffset.QuadPart = MinimumSize * 4096;
411 Status = NtWriteFile(FileHandle,
412 NULL,
413 NULL,
414 NULL,
415 &IoStatus,
416 Buffer,
417 4096,
418 &ByteOffset,
419 NULL);
420 if (!NT_SUCCESS(Status))
421 {
422 NtClose(FileHandle);
423 return(Status);
424 }
425 ExFreePool(Buffer);
426
427 Status = ObReferenceObjectByHandle(FileHandle,
428 FILE_ALL_ACCESS,
429 IoFileObjectType,
430 UserMode,
431 (PVOID*)&FileObject,
432 NULL);
433 if (!NT_SUCCESS(Status))
434 {
435 NtClose(FileHandle);
436 return(Status);
437 }
438
439 NtClose(FileHandle);
440
441 PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
442 if (PagingFile == NULL)
443 {
444 ObDereferenceObject(FileObject);
445 return(STATUS_NO_MEMORY);
446 }
447
448 PagingFile->FileObject = FileObject;
449 PagingFile->MaximumSize = PagingFile->CurrentSize = MinimumSize;
450 PagingFile->FreePages = MinimumSize;
451 PagingFile->UsedPages = 0;
452 KeInitializeSpinLock(&PagingFile->AllocMapLock);
453
454 AllocMapSize = (MinimumSize / 32) + 1;
455 PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
456 AllocMapSize * sizeof(ULONG));
457 PagingFile->AllocMapSize = AllocMapSize;
458
459 if (PagingFile->AllocMap == NULL)
460 {
461 ExFreePool(PagingFile);
462 ObDereferenceObject(FileObject);
463 return(STATUS_NO_MEMORY);
464 }
465
466 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
467 for (i = 0; i < MAX_PAGING_FILES; i++)
468 {
469 if (PagingFileList[i] == NULL)
470 {
471 PagingFileList[i] = PagingFile;
472 break;
473 }
474 }
475 MiFreeSwapPages = MiFreeSwapPages + MinimumSize;
476 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
477
478 return(STATUS_SUCCESS);
479 }
480
481
482 /* EOF */
483
484
485
486
487
488
489