3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
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.
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.
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.
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/pagefile.c
23 * PURPOSE: Paging file functions
24 * PROGRAMMER: David Welch (welch@mcmail.com)
29 /* INCLUDES *****************************************************************/
31 #include <ddk/ntddk.h>
32 #include <internal/io.h>
33 #include <internal/mm.h>
34 #include <napi/core.h>
35 #include <internal/ps.h>
38 #include <internal/debug.h>
40 /* TYPES *********************************************************************/
42 typedef struct _PAGINGFILE
44 LIST_ENTRY PagingFileListEntry;
45 PFILE_OBJECT FileObject;
46 LARGE_INTEGER MaximumSize;
47 LARGE_INTEGER CurrentSize;
51 KSPIN_LOCK AllocMapLock;
53 } PAGINGFILE, *PPAGINGFILE;
55 /* GLOBALS *******************************************************************/
57 #define MAX_PAGING_FILES (32)
59 /* List of paging files, both used and free */
60 static PPAGINGFILE PagingFileList[MAX_PAGING_FILES];
62 /* Lock for examining the list of paging files */
63 static KSPIN_LOCK PagingFileListLock;
65 /* Number of paging files */
66 static ULONG MiPagingFileCount;
68 /* Number of pages that are available for swapping */
69 static ULONG MiFreeSwapPages;
71 /* Number of pages that have been allocated for swapping */
72 static ULONG MiUsedSwapPages;
75 * Number of pages that have been reserved for swapping but not yet allocated
77 static ULONG MiReservedSwapPages;
80 * Ratio between reserved and available swap pages, e.g. setting this to five
81 * forces one swap page to be available for every five swap pages that are
82 * reserved. Setting this to zero turns off commit checking altogether.
84 #define MM_PAGEFILE_COMMIT_RATIO (1)
87 * Number of pages that can be used for potentially swapable memory without
88 * pagefile space being reserved. The intention is that this allows smss
89 * to start up and create page files while ordinarily having a commit
92 #define MM_PAGEFILE_COMMIT_GRACE (256)
94 static PVOID MmCoreDumpPageFrame;
95 static PULONG MmCoreDumpBlockMap;
96 static ULONG MmCoreDumpSize;
97 static PULONG MmCoreDumpBlockMap = NULL;
98 static MM_DUMP_POINTERS MmCoreDumpDeviceFuncs;
102 * Translate between a swap entry and a file and offset pair.
104 #define FILE_FROM_ENTRY(i) ((i) >> 24)
105 #define OFFSET_FROM_ENTRY(i) (((i) & 0xffffff) - 1)
106 #define ENTRY_FROM_FILE_OFFSET(i, j) (((i) << 24) | ((j) + 1))
108 static BOOLEAN MmSwapSpaceMessage = FALSE;
110 /* FUNCTIONS *****************************************************************/
113 MmShowOutOfSpaceMessagePagingFile(VOID)
115 if (!MmSwapSpaceMessage)
117 DPRINT1("MM: Out of swap space.\n");
118 MmSwapSpaceMessage = TRUE;
122 NTSTATUS MmWriteToSwapPage(SWAPENTRY SwapEntry, PMDL Mdl)
125 LARGE_INTEGER file_offset;
126 IO_STATUS_BLOCK Iosb;
133 return(STATUS_UNSUCCESSFUL);
136 i = FILE_FROM_ENTRY(SwapEntry);
137 offset = OFFSET_FROM_ENTRY(SwapEntry);
139 if (i > MAX_PAGING_FILES)
141 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
144 if (PagingFileList[i]->FileObject == NULL ||
145 PagingFileList[i]->FileObject->DeviceObject == NULL)
147 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
151 file_offset.QuadPart = offset * 4096;
152 KeInitializeEvent(&Event, NotificationEvent, FALSE);
153 Status = IoPageWrite(PagingFileList[i]->FileObject,
158 if (Status == STATUS_PENDING)
160 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
166 NTSTATUS MmReadFromSwapPage(SWAPENTRY SwapEntry, PMDL Mdl)
169 LARGE_INTEGER file_offset;
170 IO_STATUS_BLOCK Iosb;
177 return(STATUS_UNSUCCESSFUL);
180 i = FILE_FROM_ENTRY(SwapEntry);
181 offset = OFFSET_FROM_ENTRY(SwapEntry);
183 if (i > MAX_PAGING_FILES)
185 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
188 if (PagingFileList[i]->FileObject == NULL ||
189 PagingFileList[i]->FileObject->DeviceObject == NULL)
191 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
195 file_offset.QuadPart = offset * 4096;
196 KeInitializeEvent(&Event, NotificationEvent, FALSE);
197 Status = IoPageRead(PagingFileList[i]->FileObject,
202 if (Status == STATUS_PENDING)
204 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
211 MmInitPagingFile(VOID)
215 KeInitializeSpinLock(&PagingFileListLock);
219 MiReservedSwapPages = 0;
221 for (i = 0; i < MAX_PAGING_FILES; i++)
223 PagingFileList[i] = NULL;
225 MiPagingFileCount = 0;
228 * Initialize the crash dump support.
230 MmCoreDumpPageFrame = MmAllocateSection(PAGE_SIZE);
231 if (MmCoreDumpType == MM_CORE_DUMP_TYPE_FULL)
233 MmCoreDumpSize = MmStats.NrTotalPages * 4096 + 1024 * 1024;
237 MmCoreDumpSize = 1024 * 1024;
242 MmReserveSwapPages(ULONG Nr)
245 ULONG MiAvailSwapPages;
247 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
249 (MiFreeSwapPages * MM_PAGEFILE_COMMIT_RATIO) + MM_PAGEFILE_COMMIT_GRACE;
250 MiReservedSwapPages = MiReservedSwapPages + Nr;
251 if (MM_PAGEFILE_COMMIT_RATIO != 0 && MiAvailSwapPages < MiReservedSwapPages)
253 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
256 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
261 MmDereserveSwapPages(ULONG Nr)
265 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
266 MiReservedSwapPages = MiReservedSwapPages - Nr;
267 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
271 MiAllocPageFromPagingFile(PPAGINGFILE PagingFile)
276 KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
278 for (i = 0; i < PagingFile->AllocMapSize; i++)
280 for (j = 0; j < 32; j++)
282 if (!(PagingFile->AllocMap[i] & (1 << j)))
291 PagingFile->AllocMap[i] |= (1 << j);
292 PagingFile->UsedPages++;
293 PagingFile->FreePages--;
294 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
295 return((i * 32) + j);
298 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
303 MmFreeSwapPage(SWAPENTRY Entry)
309 i = FILE_FROM_ENTRY(Entry);
310 off = OFFSET_FROM_ENTRY(Entry);
312 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
313 if (PagingFileList[i] == NULL)
317 KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock);
319 PagingFileList[i]->AllocMap[off / 32] &= (~(1 << (off % 32)));
321 PagingFileList[i]->FreePages++;
322 PagingFileList[i]->UsedPages--;
327 KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock);
328 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
332 MmIsAvailableSwapPage(VOID)
334 return(MiFreeSwapPages > 0);
338 MmAllocSwapPage(VOID)
345 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
347 if (MiFreeSwapPages == 0)
349 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
353 for (i = 0; i < MAX_PAGING_FILES; i++)
355 if (PagingFileList[i] != NULL &&
356 PagingFileList[i]->FreePages >= 1)
358 off = MiAllocPageFromPagingFile(PagingFileList[i]);
359 if (off == 0xFFFFFFFF)
362 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
363 return(STATUS_UNSUCCESSFUL);
367 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
369 entry = ENTRY_FROM_FILE_OFFSET(i, off);
374 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
380 MmDumpToPagingFile(ULONG BugCode,
381 ULONG BugCodeParameter1,
382 ULONG BugCodeParameter2,
383 ULONG BugCodeParameter3,
384 ULONG BugCodeParameter4,
385 PKTRAP_FRAME TrapFrame)
387 PMM_CORE_DUMP_HEADER Headers;
390 UCHAR MdlBase[sizeof(MDL) + sizeof(PVOID)];
391 PMDL Mdl = (PMDL)MdlBase;
392 PETHREAD Thread = PsGetCurrentThread();
395 ULONG NextOffset = 0;
398 if (MmCoreDumpBlockMap == NULL)
400 return(STATUS_UNSUCCESSFUL);
403 DbgPrint("MM: Dumping core");
405 /* Prepare the dump headers. */
406 Headers = (PMM_CORE_DUMP_HEADER)MmCoreDumpPageFrame;
407 Headers->Magic = MM_CORE_DUMP_HEADER_MAGIC;
408 Headers->Version = MM_CORE_DUMP_HEADER_VERSION;
409 Headers->Type = MmCoreDumpType;
410 if (TrapFrame != NULL)
412 if (!(TrapFrame->Eflags & (1 << 17)))
414 memcpy(&Headers->TrapFrame, TrapFrame,
415 sizeof(KTRAP_FRAME) - (4 * sizeof(DWORD)));
419 memcpy(&Headers->TrapFrame, TrapFrame, sizeof(KTRAP_FRAME));
422 Headers->BugCheckCode = BugCode;
423 Headers->BugCheckParameters[0] = BugCodeParameter1;
424 Headers->BugCheckParameters[1] = BugCodeParameter2;
425 Headers->BugCheckParameters[2] = BugCodeParameter3;
426 Headers->BugCheckParameters[3] = BugCodeParameter4;
427 Headers->FaultingStackBase = (PVOID)Thread->Tcb.StackLimit;
428 Headers->FaultingStackSize = StackSize =
429 (ULONG)(Thread->Tcb.StackBase - Thread->Tcb.StackLimit);
430 Headers->PhysicalMemorySize = MmStats.NrTotalPages * PAGE_SIZE;
432 /* Initialize the dump device. */
433 Context = MmCoreDumpDeviceFuncs.Context;
434 Status = MmCoreDumpDeviceFuncs.DeviceInit(Context);
435 if (!NT_SUCCESS(Status))
437 DPRINT1("MM: Failed to initialize core dump device.\n");
441 /* Initialize the MDL. */
443 Mdl->Size = sizeof(MDL) + sizeof(PVOID);
444 Mdl->MdlFlags = MDL_SOURCE_IS_NONPAGED_POOL;
446 Mdl->MappedSystemVa = MmCoreDumpPageFrame;
448 Mdl->ByteCount = PAGE_SIZE;
450 MdlMap = (PULONG)(Mdl + 1);
452 /* Dump the header. */
453 MdlMap[0] = MmGetPhysicalAddress(MmCoreDumpPageFrame).u.LowPart;
454 Status = MmCoreDumpDeviceFuncs.DeviceWrite(Context,
455 MmCoreDumpBlockMap[NextOffset],
457 if (!NT_SUCCESS(Status))
459 DPRINT1("MM: Failed to write core dump header\n.");
464 /* Write out the kernel mode stack of the faulting thread. */
465 for (i = 0; i < (StackSize / PAGE_SIZE); i++)
467 Mdl->MappedSystemVa = (PVOID)(Thread->Tcb.StackLimit + (i * PAGE_SIZE));
468 MdlMap[0] = MmGetPhysicalAddress(Mdl->MappedSystemVa).u.LowPart;
470 MmCoreDumpDeviceFuncs.DeviceWrite(Context,
471 MmCoreDumpBlockMap[NextOffset],
473 if (!NT_SUCCESS(Status))
475 DPRINT1("MM: Failed to write page to core dump.\n");
482 /* Write out the contents of physical memory. */
483 if (MmCoreDumpType == MM_CORE_DUMP_TYPE_FULL)
485 for (i = 0; i < MmStats.NrTotalPages; i++)
487 LARGE_INTEGER PhysicalAddress;
488 PhysicalAddress.QuadPart = i * PAGE_SIZE;
489 MdlMap[0] = i * PAGE_SIZE;
490 MmCreateVirtualMappingForKernel(MmCoreDumpPageFrame,
494 MmCoreDumpDeviceFuncs.DeviceWrite(Context,
495 MmCoreDumpBlockMap[NextOffset],
497 if (!NT_SUCCESS(Status))
499 DPRINT1("MM: Failed to write page to core dump.\n");
508 return(STATUS_SUCCESS);
512 NtCreatePagingFile(IN PUNICODE_STRING FileName,
513 IN PLARGE_INTEGER InitialSize,
514 IN PLARGE_INTEGER MaximumSize,
518 OBJECT_ATTRIBUTES ObjectAttributes;
520 IO_STATUS_BLOCK IoStatus;
521 PFILE_OBJECT FileObject;
522 PPAGINGFILE PagingFile;
527 DPRINT("NtCreatePagingFile(FileName %wZ, InitialSize %I64d)\n",
528 FileName, InitialSize->QuadPart);
530 if (MiPagingFileCount >= MAX_PAGING_FILES)
532 return(STATUS_TOO_MANY_PAGING_FILES);
535 InitializeObjectAttributes(&ObjectAttributes,
541 Status = IoCreateFile(&FileHandle,
549 FILE_SYNCHRONOUS_IO_NONALERT,
554 SL_OPEN_PAGING_FILE);
555 if (!NT_SUCCESS(Status))
560 Status = NtSetInformationFile(FileHandle,
563 sizeof(LARGE_INTEGER),
564 FileAllocationInformation);
565 if (!NT_SUCCESS(Status))
571 Status = ObReferenceObjectByHandle(FileHandle,
577 if (!NT_SUCCESS(Status))
583 PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
584 if (PagingFile == NULL)
586 ObDereferenceObject(FileObject);
588 return(STATUS_NO_MEMORY);
591 PagingFile->FileObject = FileObject;
592 PagingFile->MaximumSize.QuadPart = MaximumSize->QuadPart;
593 PagingFile->CurrentSize.QuadPart = InitialSize->QuadPart;
594 PagingFile->FreePages = InitialSize->QuadPart / PAGE_SIZE;
595 PagingFile->UsedPages = 0;
596 KeInitializeSpinLock(&PagingFile->AllocMapLock);
598 AllocMapSize = (PagingFile->FreePages / 32) + 1;
599 PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
600 AllocMapSize * sizeof(ULONG));
601 PagingFile->AllocMapSize = AllocMapSize;
603 if (PagingFile->AllocMap == NULL)
605 ExFreePool(PagingFile);
606 ObDereferenceObject(FileObject);
608 return(STATUS_NO_MEMORY);
611 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
612 for (i = 0; i < MAX_PAGING_FILES; i++)
614 if (PagingFileList[i] == NULL)
616 PagingFileList[i] = PagingFile;
620 MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
622 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
624 /* Check whether this pagefile can be a crash dump target. */
625 if (PagingFile->CurrentSize.QuadPart >= MmCoreDumpSize &&
626 MmCoreDumpBlockMap != NULL)
629 ExAllocatePool(NonPagedPool,
630 (MmCoreDumpSize / PAGE_SIZE) * sizeof(ULONG));
631 if (MmCoreDumpBlockMap == NULL)
633 DPRINT1("Failed to allocate block map.\n");
635 return(STATUS_SUCCESS);
637 Status = ZwFsControlFile(FileHandle,
642 FSCTL_GET_DUMP_BLOCK_MAP,
646 (MmCoreDumpSize / PAGE_SIZE) * sizeof(ULONG));
647 if (!NT_SUCCESS(Status))
649 DPRINT1("Failed to get dump block map (Status %X)\n", Status);
651 ExFreePool(MmCoreDumpBlockMap);
652 MmCoreDumpBlockMap = NULL;
653 return(STATUS_SUCCESS);
655 Status = ZwDeviceIoControlFile(FileHandle,
660 IOCTL_GET_DUMP_POINTERS,
663 &MmCoreDumpDeviceFuncs,
664 sizeof(MmCoreDumpDeviceFuncs));
665 if (!NT_SUCCESS(Status))
667 DPRINT1("Failed to get dump block map (Status %X)\n", Status);
669 ExFreePool(MmCoreDumpBlockMap);
670 MmCoreDumpBlockMap = NULL;
671 return(STATUS_SUCCESS);
676 MmSwapSpaceMessage = FALSE;
678 return(STATUS_SUCCESS);