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.
19 /* $Id: pagefile.c,v 1.24 2002/08/28 07:13:04 hbirr Exp $
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(PAGESIZE
);
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
* PAGESIZE
;
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
= PAGESIZE
;
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
/ PAGESIZE
); i
++)
467 Mdl
->MappedSystemVa
= (PVOID
)(Thread
->Tcb
.StackLimit
+ (i
* PAGESIZE
));
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
* PAGESIZE
;
489 MdlMap
[0] = i
* PAGESIZE
;
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
/ PAGESIZE
;
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
/ PAGESIZE
) * 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
/ PAGESIZE
) * 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
);