Fixed Nt/ZwCreatePagingFile() prototype.
[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.18 2002/03/18 16:15:08 ekohl 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 LARGE_INTEGER MaximumSize;
46 LARGE_INTEGER 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 paging files */
65 static ULONG MiPagingFileCount;
66
67 /* Number of pages that are available for swapping */
68 static ULONG MiFreeSwapPages;
69
70 /* Number of pages that have been allocated for swapping */
71 static ULONG MiUsedSwapPages;
72
73 /*
74 * Number of pages that have been reserved for swapping but not yet allocated
75 */
76 static ULONG MiReservedSwapPages;
77
78 /*
79 * Ratio between reserved and available swap pages, e.g. setting this to five
80 * forces one swap page to be available for every five swap pages that are
81 * reserved. Setting this to zero turns off commit checking altogether.
82 */
83 #define MM_PAGEFILE_COMMIT_RATIO (1)
84
85 /*
86 * Number of pages that can be used for potentially swapable memory without
87 * pagefile space being reserved. The intention is that this allows smss
88 * to start up and create page files while ordinarily having a commit
89 * ratio of one.
90 */
91 #define MM_PAGEFILE_COMMIT_GRACE (256)
92
93 #if 0
94 static PVOID MmCoreDumpPageFrame;
95 static BYTE MmCoreDumpHeader[PAGESIZE];
96 #endif
97
98 /*
99 * Translate between a swap entry and a file and offset pair.
100 */
101 #define FILE_FROM_ENTRY(i) ((i) >> 24)
102 #define OFFSET_FROM_ENTRY(i) (((i) & 0xffffff) - 1)
103 #define ENTRY_FROM_FILE_OFFSET(i, j) (((i) << 24) | ((j) + 1))
104
105 /* FUNCTIONS *****************************************************************/
106
107 NTSTATUS MmWriteToSwapPage(SWAPENTRY SwapEntry, PMDL Mdl)
108 {
109 ULONG i, offset;
110 LARGE_INTEGER file_offset;
111 IO_STATUS_BLOCK Iosb;
112 NTSTATUS Status;
113
114 if (SwapEntry == 0)
115 {
116 KeBugCheck(0);
117 return(STATUS_UNSUCCESSFUL);
118 }
119
120 i = FILE_FROM_ENTRY(SwapEntry);
121 offset = OFFSET_FROM_ENTRY(SwapEntry);
122
123 if (i > MAX_PAGING_FILES)
124 {
125 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
126 KeBugCheck(0);
127 }
128 if (PagingFileList[i]->FileObject == NULL ||
129 PagingFileList[i]->FileObject->DeviceObject == NULL)
130 {
131 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
132 KeBugCheck(0);
133 }
134
135 file_offset.QuadPart = offset * 4096;
136
137 Status = IoPageWrite(PagingFileList[i]->FileObject,
138 Mdl,
139 &file_offset,
140 &Iosb,
141 TRUE);
142 return(Status);
143 }
144
145 NTSTATUS MmReadFromSwapPage(SWAPENTRY SwapEntry, PMDL Mdl)
146 {
147 ULONG i, offset;
148 LARGE_INTEGER file_offset;
149 IO_STATUS_BLOCK Iosb;
150 NTSTATUS Status;
151
152 if (SwapEntry == 0)
153 {
154 KeBugCheck(0);
155 return(STATUS_UNSUCCESSFUL);
156 }
157
158 i = FILE_FROM_ENTRY(SwapEntry);
159 offset = OFFSET_FROM_ENTRY(SwapEntry);
160
161 if (i > MAX_PAGING_FILES)
162 {
163 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
164 KeBugCheck(0);
165 }
166 if (PagingFileList[i]->FileObject == NULL ||
167 PagingFileList[i]->FileObject->DeviceObject == NULL)
168 {
169 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
170 KeBugCheck(0);
171 }
172
173 file_offset.QuadPart = offset * 4096;
174
175 Status = IoPageRead(PagingFileList[i]->FileObject,
176 Mdl,
177 &file_offset,
178 &Iosb,
179 TRUE);
180 return(Status);
181 }
182
183 VOID
184 MmInitPagingFile(VOID)
185 {
186 ULONG i;
187
188 KeInitializeSpinLock(&PagingFileListLock);
189
190 MiFreeSwapPages = 0;
191 MiUsedSwapPages = 0;
192 MiReservedSwapPages = 0;
193
194 for (i = 0; i < MAX_PAGING_FILES; i++)
195 {
196 PagingFileList[i] = NULL;
197 }
198 MiPagingFileCount = 0;
199 }
200
201 BOOLEAN
202 MmReserveSwapPages(ULONG Nr)
203 {
204 KIRQL oldIrql;
205 ULONG MiAvailSwapPages;
206
207 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
208 MiAvailSwapPages =
209 (MiFreeSwapPages * MM_PAGEFILE_COMMIT_RATIO) + MM_PAGEFILE_COMMIT_GRACE;
210 if (MM_PAGEFILE_COMMIT_RATIO != 0 && MiAvailSwapPages < MiReservedSwapPages)
211 {
212 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
213 return(FALSE);
214 }
215 MiReservedSwapPages = MiReservedSwapPages + Nr;
216 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
217
218 return(TRUE);
219 }
220
221 VOID
222 MmDereserveSwapPages(ULONG Nr)
223 {
224 KIRQL oldIrql;
225
226 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
227 MiReservedSwapPages = MiReservedSwapPages - Nr;
228 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
229 }
230
231 static ULONG
232 MiAllocPageFromPagingFile(PPAGINGFILE PagingFile)
233 {
234 KIRQL oldIrql;
235 ULONG i, j;
236
237 KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
238
239 for (i = 0; i < PagingFile->AllocMapSize; i++)
240 {
241 for (j = 0; j < 32; j++)
242 {
243 if (!(PagingFile->AllocMap[i] & (1 << j)))
244 {
245 break;
246 }
247 }
248 if (j == 32)
249 {
250 continue;
251 }
252 PagingFile->AllocMap[i] |= (1 << j);
253 PagingFile->UsedPages++;
254 PagingFile->FreePages--;
255 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
256 return((i * 32) + j);
257 }
258
259 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
260 return(0xFFFFFFFF);
261 }
262
263 VOID
264 MmFreeSwapPage(SWAPENTRY Entry)
265 {
266 ULONG i;
267 ULONG off;
268 KIRQL oldIrql;
269
270 i = FILE_FROM_ENTRY(Entry);
271 off = OFFSET_FROM_ENTRY(Entry);
272
273 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
274 if (PagingFileList[i] == NULL)
275 {
276 KeBugCheck(0);
277 }
278 KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock);
279
280 PagingFileList[i]->AllocMap[off / 32] &= (~(1 << (off % 32)));
281
282 PagingFileList[i]->FreePages++;
283 PagingFileList[i]->UsedPages--;
284
285 MiFreeSwapPages++;
286 MiUsedSwapPages--;
287
288 KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock);
289 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
290 }
291
292 SWAPENTRY
293 MmAllocSwapPage(VOID)
294 {
295 KIRQL oldIrql;
296 ULONG i;
297 ULONG off;
298 SWAPENTRY entry;
299 static BOOLEAN SwapSpaceMessage = FALSE;
300
301 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
302
303 if (MiFreeSwapPages == 0)
304 {
305 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
306 if (!SwapSpaceMessage)
307 {
308 DPRINT1("MM: Out of swap space.\n");
309 SwapSpaceMessage = TRUE;
310 }
311 return(0);
312 }
313
314 for (i = 0; i < MAX_PAGING_FILES; i++)
315 {
316 if (PagingFileList[i] != NULL &&
317 PagingFileList[i]->FreePages >= 1)
318 {
319 off = MiAllocPageFromPagingFile(PagingFileList[i]);
320 if (off == 0xFFFFFFFF)
321 {
322 KeBugCheck(0);
323 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
324 return(STATUS_UNSUCCESSFUL);
325 }
326 MiUsedSwapPages++;
327 MiFreeSwapPages--;
328 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
329
330 entry = ENTRY_FROM_FILE_OFFSET(i, off);
331 return(entry);
332 }
333 }
334
335 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
336 if (!SwapSpaceMessage)
337 {
338 DPRINT1("MM: Out of swap space.\n");
339 SwapSpaceMessage = TRUE;
340 }
341 return(0);
342 }
343
344 #if 0
345 NTSTATUS STDCALL MmDumpToPagingFile(PCONTEXT Context,
346 ULONG BugCode,
347 ULONG ExceptionCode,
348 ULONG Cr2)
349 {
350 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Magic =
351 MM_CORE_DUMP_HEADER_MAGIC;
352 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Version =
353 MM_CORE_DUMP_HEADER_VERSION;
354 memcpy(&((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Context,
355 Context,
356 sizeof(CONTEXT));
357 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->DumpLength = 0;
358 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->BugCode = BugCode;
359 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->ExceptionCode =
360 ExceptionCode;
361 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Cr2 = Cr2;
362 ((PMM_CORE_DUMP_HEADER)MmCoreDumpHeader)->Cr3 = 0;
363 }
364 #endif
365
366 NTSTATUS STDCALL
367 NtCreatePagingFile(IN PUNICODE_STRING FileName,
368 IN PLARGE_INTEGER InitialSize,
369 IN PLARGE_INTEGER MaximumSize,
370 IN ULONG Reserved)
371 {
372 NTSTATUS Status;
373 OBJECT_ATTRIBUTES ObjectAttributes;
374 HANDLE FileHandle;
375 IO_STATUS_BLOCK IoStatus;
376 PFILE_OBJECT FileObject;
377 PPAGINGFILE PagingFile;
378 KIRQL oldIrql;
379 ULONG AllocMapSize;
380 ULONG i;
381 PVOID Buffer;
382
383 DPRINT("NtCreatePagingFile(FileName %wZ, InitialSize %I64d)\n",
384 FileName, InitialSize->QuadPart);
385
386 if (MiPagingFileCount >= MAX_PAGING_FILES)
387 {
388 return(STATUS_TOO_MANY_PAGING_FILES);
389 }
390
391 InitializeObjectAttributes(&ObjectAttributes,
392 FileName,
393 0,
394 NULL,
395 NULL);
396
397 Status = IoCreateFile(&FileHandle,
398 FILE_ALL_ACCESS,
399 &ObjectAttributes,
400 &IoStatus,
401 NULL,
402 0,
403 0,
404 FILE_OPEN_IF,
405 FILE_SYNCHRONOUS_IO_NONALERT,
406 NULL,
407 0,
408 CreateFileTypeNone,
409 NULL,
410 SL_OPEN_PAGING_FILE);
411 if (!NT_SUCCESS(Status))
412 {
413 return(Status);
414 }
415
416 Buffer = ExAllocatePool(NonPagedPool, 4096);
417 memset(Buffer, 0, 4096);
418 Status = NtWriteFile(FileHandle,
419 NULL,
420 NULL,
421 NULL,
422 &IoStatus,
423 Buffer,
424 4096,
425 InitialSize,
426 NULL);
427 if (!NT_SUCCESS(Status))
428 {
429 NtClose(FileHandle);
430 return(Status);
431 }
432 ExFreePool(Buffer);
433
434 Status = ObReferenceObjectByHandle(FileHandle,
435 FILE_ALL_ACCESS,
436 IoFileObjectType,
437 UserMode,
438 (PVOID*)&FileObject,
439 NULL);
440 if (!NT_SUCCESS(Status))
441 {
442 NtClose(FileHandle);
443 return(Status);
444 }
445
446 NtClose(FileHandle);
447
448 PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
449 if (PagingFile == NULL)
450 {
451 ObDereferenceObject(FileObject);
452 return(STATUS_NO_MEMORY);
453 }
454
455 PagingFile->FileObject = FileObject;
456 PagingFile->MaximumSize.QuadPart = MaximumSize->QuadPart;
457 PagingFile->CurrentSize.QuadPart = InitialSize->QuadPart;
458 PagingFile->FreePages = InitialSize->QuadPart / PAGESIZE;
459 PagingFile->UsedPages = 0;
460 KeInitializeSpinLock(&PagingFile->AllocMapLock);
461
462 AllocMapSize = (PagingFile->FreePages / 32) + 1;
463 PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
464 AllocMapSize * sizeof(ULONG));
465 PagingFile->AllocMapSize = AllocMapSize;
466
467 if (PagingFile->AllocMap == NULL)
468 {
469 ExFreePool(PagingFile);
470 ObDereferenceObject(FileObject);
471 return(STATUS_NO_MEMORY);
472 }
473
474 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
475 for (i = 0; i < MAX_PAGING_FILES; i++)
476 {
477 if (PagingFileList[i] == NULL)
478 {
479 PagingFileList[i] = PagingFile;
480 break;
481 }
482 }
483 MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
484 MiPagingFileCount++;
485 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
486
487 return(STATUS_SUCCESS);
488 }
489
490
491 /* EOF */
492
493
494
495
496
497
498