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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/pagefile.c
22 * PURPOSE: Paging file functions
23 * PROGRAMMER: David Welch (welch@mcmail.com)
29 /* INCLUDES *****************************************************************/
35 #if defined (ALLOC_PRAGMA)
36 #pragma alloc_text(INIT, MmInitPagingFile)
40 /* TYPES *********************************************************************/
42 typedef struct _MMPAGING_FILE
44 PFILE_OBJECT FileObject
;
46 LARGE_INTEGER MaximumSize
;
47 LARGE_INTEGER CurrentSize
;
51 KSPIN_LOCK AllocMapLock
;
53 MMPAGING_FILE
, *PMMPAGING_FILE
;
55 /* GLOBALS *******************************************************************/
57 #define PAIRS_PER_RUN (1024)
59 #define MAX_PAGING_FILES (16)
61 /* List of paging files, both used and free */
62 static PMMPAGING_FILE MmPagingFile
[MAX_PAGING_FILES
];
64 /* Lock for examining the list of paging files */
65 static KSPIN_LOCK PagingFileListLock
;
67 /* Number of paging files */
68 ULONG MmNumberOfPagingFiles
;
70 /* Number of pages that are available for swapping */
71 PFN_COUNT MiFreeSwapPages
;
73 /* Number of pages that have been allocated for swapping */
74 PFN_COUNT MiUsedSwapPages
;
76 BOOLEAN MmZeroPageFile
;
79 * Number of pages that have been reserved for swapping but not yet allocated
81 static PFN_COUNT MiReservedSwapPages
;
84 * Ratio between reserved and available swap pages, e.g. setting this to five
85 * forces one swap page to be available for every five swap pages that are
86 * reserved. Setting this to zero turns off commit checking altogether.
88 #define MM_PAGEFILE_COMMIT_RATIO (1)
91 * Number of pages that can be used for potentially swapable memory without
92 * pagefile space being reserved. The intention is that this allows smss
93 * to start up and create page files while ordinarily having a commit
96 #define MM_PAGEFILE_COMMIT_GRACE (256)
99 * Translate between a swap entry and a file and offset pair.
101 #define FILE_FROM_ENTRY(i) ((i) & 0x0f)
102 #define OFFSET_FROM_ENTRY(i) ((i) >> 11)
103 #define ENTRY_FROM_FILE_OFFSET(i, j) ((i) | ((j) << 11) | 0x400)
105 /* Make sure there can be only 16 paging files */
106 C_ASSERT(FILE_FROM_ENTRY(0xffffffff) < MAX_PAGING_FILES
);
108 static BOOLEAN MmSwapSpaceMessage
= FALSE
;
110 /* FUNCTIONS *****************************************************************/
114 MmBuildMdlFromPages(PMDL Mdl
, PPFN_NUMBER Pages
)
116 memcpy(Mdl
+ 1, Pages
, sizeof(PFN_NUMBER
) * (PAGE_ROUND_UP(Mdl
->ByteOffset
+Mdl
->ByteCount
)/PAGE_SIZE
));
118 /* FIXME: this flag should be set by the caller perhaps? */
119 Mdl
->MdlFlags
|= MDL_IO_PAGE_READ
;
125 MmIsFileObjectAPagingFile(PFILE_OBJECT FileObject
)
129 /* Loop through all the paging files */
130 for (i
= 0; i
< MmNumberOfPagingFiles
; i
++)
132 /* Check if this is one of them */
133 if (MmPagingFile
[i
]->FileObject
== FileObject
) return TRUE
;
142 MmShowOutOfSpaceMessagePagingFile(VOID
)
144 if (!MmSwapSpaceMessage
)
146 DPRINT1("MM: Out of swap space.\n");
147 MmSwapSpaceMessage
= TRUE
;
153 MmWriteToSwapPage(SWAPENTRY SwapEntry
, PFN_NUMBER Page
)
157 LARGE_INTEGER file_offset
;
158 IO_STATUS_BLOCK Iosb
;
161 UCHAR MdlBase
[sizeof(MDL
) + sizeof(ULONG
)];
162 PMDL Mdl
= (PMDL
)MdlBase
;
164 DPRINT("MmWriteToSwapPage\n");
168 KeBugCheck(MEMORY_MANAGEMENT
);
169 return(STATUS_UNSUCCESSFUL
);
172 i
= FILE_FROM_ENTRY(SwapEntry
);
173 offset
= OFFSET_FROM_ENTRY(SwapEntry
) - 1;
175 if (MmPagingFile
[i
]->FileObject
== NULL
||
176 MmPagingFile
[i
]->FileObject
->DeviceObject
== NULL
)
178 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry
);
179 KeBugCheck(MEMORY_MANAGEMENT
);
182 MmInitializeMdl(Mdl
, NULL
, PAGE_SIZE
);
183 MmBuildMdlFromPages(Mdl
, &Page
);
184 Mdl
->MdlFlags
|= MDL_PAGES_LOCKED
;
186 file_offset
.QuadPart
= offset
* PAGE_SIZE
;
188 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
189 Status
= IoSynchronousPageWrite(MmPagingFile
[i
]->FileObject
,
194 if (Status
== STATUS_PENDING
)
196 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
197 Status
= Iosb
.Status
;
200 if (Mdl
->MdlFlags
& MDL_MAPPED_TO_SYSTEM_VA
)
202 MmUnmapLockedPages (Mdl
->MappedSystemVa
, Mdl
);
210 MmReadFromSwapPage(SWAPENTRY SwapEntry
, PFN_NUMBER Page
)
212 return MiReadPageFile(Page
, FILE_FROM_ENTRY(SwapEntry
), OFFSET_FROM_ENTRY(SwapEntry
) - 1);
218 _In_ PFN_NUMBER Page
,
219 _In_ ULONG PageFileIndex
,
220 _In_ ULONG_PTR PageFileOffset
)
222 LARGE_INTEGER file_offset
;
223 IO_STATUS_BLOCK Iosb
;
226 UCHAR MdlBase
[sizeof(MDL
) + sizeof(ULONG
)];
227 PMDL Mdl
= (PMDL
)MdlBase
;
228 PMMPAGING_FILE PagingFile
;
230 DPRINT("MiReadSwapFile\n");
232 if (PageFileOffset
== 0)
234 KeBugCheck(MEMORY_MANAGEMENT
);
235 return(STATUS_UNSUCCESSFUL
);
238 ASSERT(PageFileIndex
< MAX_PAGING_FILES
);
240 PagingFile
= MmPagingFile
[PageFileIndex
];
242 if (PagingFile
->FileObject
== NULL
|| PagingFile
->FileObject
->DeviceObject
== NULL
)
244 DPRINT1("Bad paging file %u\n", PageFileIndex
);
245 KeBugCheck(MEMORY_MANAGEMENT
);
248 MmInitializeMdl(Mdl
, NULL
, PAGE_SIZE
);
249 MmBuildMdlFromPages(Mdl
, &Page
);
250 Mdl
->MdlFlags
|= MDL_PAGES_LOCKED
;
252 file_offset
.QuadPart
= PageFileOffset
* PAGE_SIZE
;
254 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
255 Status
= IoPageRead(PagingFile
->FileObject
,
260 if (Status
== STATUS_PENDING
)
262 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
263 Status
= Iosb
.Status
;
265 if (Mdl
->MdlFlags
& MDL_MAPPED_TO_SYSTEM_VA
)
267 MmUnmapLockedPages (Mdl
->MappedSystemVa
, Mdl
);
275 MmInitPagingFile(VOID
)
279 KeInitializeSpinLock(&PagingFileListLock
);
283 MiReservedSwapPages
= 0;
285 for (i
= 0; i
< MAX_PAGING_FILES
; i
++)
287 MmPagingFile
[i
] = NULL
;
289 MmNumberOfPagingFiles
= 0;
293 MiAllocPageFromPagingFile(PMMPAGING_FILE PagingFile
)
298 KeAcquireSpinLock(&PagingFile
->AllocMapLock
, &oldIrql
);
299 off
= RtlFindClearBitsAndSet(PagingFile
->AllocMap
, 1, 0);
300 KeReleaseSpinLock(&PagingFile
->AllocMapLock
, oldIrql
);
307 MmFreeSwapPage(SWAPENTRY Entry
)
312 PMMPAGING_FILE PagingFile
;
314 i
= FILE_FROM_ENTRY(Entry
);
315 off
= OFFSET_FROM_ENTRY(Entry
) - 1;
317 KeAcquireSpinLock(&PagingFileListLock
, &oldIrql
);
319 PagingFile
= MmPagingFile
[i
];
320 if (PagingFile
== NULL
)
322 KeBugCheck(MEMORY_MANAGEMENT
);
324 KeAcquireSpinLockAtDpcLevel(&PagingFile
->AllocMapLock
);
326 RtlClearBit(PagingFile
->AllocMap
, off
>> 5);
328 PagingFile
->FreePages
++;
329 PagingFile
->UsedPages
--;
334 KeReleaseSpinLockFromDpcLevel(&PagingFile
->AllocMapLock
);
335 KeReleaseSpinLock(&PagingFileListLock
, oldIrql
);
340 MmAllocSwapPage(VOID
)
347 KeAcquireSpinLock(&PagingFileListLock
, &oldIrql
);
349 if (MiFreeSwapPages
== 0)
351 KeReleaseSpinLock(&PagingFileListLock
, oldIrql
);
355 for (i
= 0; i
< MAX_PAGING_FILES
; i
++)
357 if (MmPagingFile
[i
] != NULL
&&
358 MmPagingFile
[i
]->FreePages
>= 1)
360 off
= MiAllocPageFromPagingFile(MmPagingFile
[i
]);
361 if (off
== 0xFFFFFFFF)
363 KeBugCheck(MEMORY_MANAGEMENT
);
364 KeReleaseSpinLock(&PagingFileListLock
, oldIrql
);
365 return(STATUS_UNSUCCESSFUL
);
369 KeReleaseSpinLock(&PagingFileListLock
, oldIrql
);
371 entry
= ENTRY_FROM_FILE_OFFSET(i
, off
+ 1);
376 KeReleaseSpinLock(&PagingFileListLock
, oldIrql
);
377 KeBugCheck(MEMORY_MANAGEMENT
);
382 NtCreatePagingFile(IN PUNICODE_STRING FileName
,
383 IN PLARGE_INTEGER InitialSize
,
384 IN PLARGE_INTEGER MaximumSize
,
388 OBJECT_ATTRIBUTES ObjectAttributes
;
390 IO_STATUS_BLOCK IoStatus
;
391 PFILE_OBJECT FileObject
;
392 PMMPAGING_FILE PagingFile
;
396 KPROCESSOR_MODE PreviousMode
;
397 UNICODE_STRING CapturedFileName
;
398 LARGE_INTEGER SafeInitialSize
, SafeMaximumSize
, AllocationSize
;
399 FILE_FS_DEVICE_INFORMATION FsDeviceInfo
;
400 SECURITY_DESCRIPTOR SecurityDescriptor
;
403 DPRINT("NtCreatePagingFile(FileName %wZ, InitialSize %I64d)\n",
404 FileName
, InitialSize
->QuadPart
);
406 if (MmNumberOfPagingFiles
>= MAX_PAGING_FILES
)
408 return STATUS_TOO_MANY_PAGING_FILES
;
411 PreviousMode
= ExGetPreviousMode();
413 if (PreviousMode
!= KernelMode
)
415 if (SeSinglePrivilegeCheck(SeCreatePagefilePrivilege
, PreviousMode
) != TRUE
)
417 return STATUS_PRIVILEGE_NOT_HELD
;
422 SafeInitialSize
= ProbeForReadLargeInteger(InitialSize
);
423 SafeMaximumSize
= ProbeForReadLargeInteger(MaximumSize
);
425 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
427 /* Return the exception code */
428 _SEH2_YIELD(return _SEH2_GetExceptionCode());
434 SafeInitialSize
= *InitialSize
;
435 SafeMaximumSize
= *MaximumSize
;
438 /* Pagefiles can't be larger than 4GB and ofcourse the minimum should be
439 smaller than the maximum */
440 if (0 != SafeInitialSize
.u
.HighPart
)
442 return STATUS_INVALID_PARAMETER_2
;
444 if (0 != SafeMaximumSize
.u
.HighPart
)
446 return STATUS_INVALID_PARAMETER_3
;
448 if (SafeMaximumSize
.u
.LowPart
< SafeInitialSize
.u
.LowPart
)
450 return STATUS_INVALID_PARAMETER_MIX
;
453 Status
= ProbeAndCaptureUnicodeString(&CapturedFileName
,
456 if (!NT_SUCCESS(Status
))
461 /* Create the security descriptor for the page file */
462 Status
= RtlCreateSecurityDescriptor(&SecurityDescriptor
, SECURITY_DESCRIPTOR_REVISION
);
463 if (!NT_SUCCESS(Status
))
465 ReleaseCapturedUnicodeString(&CapturedFileName
,
470 /* Create the DACL: we will only allow two SIDs */
471 Count
= sizeof(ACL
) + (sizeof(ACE
) + RtlLengthSid(SeLocalSystemSid
)) +
472 (sizeof(ACE
) + RtlLengthSid(SeAliasAdminsSid
));
473 Dacl
= ExAllocatePoolWithTag(PagedPool
, Count
, 'lcaD');
476 ReleaseCapturedUnicodeString(&CapturedFileName
,
478 return STATUS_INSUFFICIENT_RESOURCES
;
481 /* Initialize the DACL */
482 Status
= RtlCreateAcl(Dacl
, Count
, ACL_REVISION
);
483 if (!NT_SUCCESS(Status
))
485 ExFreePoolWithTag(Dacl
, 'lcaD');
486 ReleaseCapturedUnicodeString(&CapturedFileName
,
491 /* Grant full access to admins */
492 Status
= RtlAddAccessAllowedAce(Dacl
, ACL_REVISION
, FILE_ALL_ACCESS
, SeAliasAdminsSid
);
493 if (!NT_SUCCESS(Status
))
495 ExFreePoolWithTag(Dacl
, 'lcaD');
496 ReleaseCapturedUnicodeString(&CapturedFileName
,
501 /* Grant full access to SYSTEM */
502 Status
= RtlAddAccessAllowedAce(Dacl
, ACL_REVISION
, FILE_ALL_ACCESS
, SeLocalSystemSid
);
503 if (!NT_SUCCESS(Status
))
505 ExFreePoolWithTag(Dacl
, 'lcaD');
506 ReleaseCapturedUnicodeString(&CapturedFileName
,
511 /* Attach the DACL to the security descriptor */
512 Status
= RtlSetDaclSecurityDescriptor(&SecurityDescriptor
, TRUE
, Dacl
, FALSE
);
513 if (!NT_SUCCESS(Status
))
515 ExFreePoolWithTag(Dacl
, 'lcaD');
516 ReleaseCapturedUnicodeString(&CapturedFileName
,
521 InitializeObjectAttributes(&ObjectAttributes
,
525 &SecurityDescriptor
);
527 /* Make sure we can at least store a complete page:
528 * If we have 2048 BytesPerAllocationUnit (FAT16 < 128MB) there is
529 * a problem if the paging file is fragmented. Suppose the first cluster
530 * of the paging file is cluster 3042 but cluster 3043 is NOT part of the
531 * paging file but of another file. We can't write a complete page (4096
532 * bytes) to the physical location of cluster 3042 then. */
533 AllocationSize
.QuadPart
= SafeInitialSize
.QuadPart
+ PAGE_SIZE
;
535 /* First, attempt to replace the page file, if existing */
536 Status
= IoCreateFile(&FileHandle
,
537 SYNCHRONIZE
| WRITE_DAC
| FILE_READ_DATA
| FILE_WRITE_DATA
,
541 FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
544 FILE_DELETE_ON_CLOSE
| FILE_NO_COMPRESSION
| FILE_NO_INTERMEDIATE_BUFFERING
,
549 SL_OPEN_PAGING_FILE
| IO_NO_PARAMETER_CHECKING
);
550 /* If we failed, relax a bit constraints, someone may be already holding the
551 * the file, so share write, don't attempt to replace and don't delete on close
552 * (basically, don't do anything conflicting)
554 if (!NT_SUCCESS(Status
))
556 Status
= IoCreateFile(&FileHandle
,
557 SYNCHRONIZE
| FILE_WRITE_DATA
,
561 FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
,
562 FILE_SHARE_WRITE
| FILE_SHARE_READ
,
564 FILE_NO_COMPRESSION
| FILE_NO_INTERMEDIATE_BUFFERING
,
569 SL_OPEN_PAGING_FILE
| IO_NO_PARAMETER_CHECKING
);
572 ReleaseCapturedUnicodeString(&CapturedFileName
,
574 if (!NT_SUCCESS(Status
))
576 DPRINT1("Failed creating page file: %lx\n", Status
);
577 ExFreePoolWithTag(Dacl
, 'lcaD');
581 /* Set the security descriptor */
582 if (NT_SUCCESS(IoStatus
.Status
))
584 Status
= ZwSetSecurityObject(FileHandle
, DACL_SECURITY_INFORMATION
, &SecurityDescriptor
);
585 if (!NT_SUCCESS(Status
))
587 ExFreePoolWithTag(Dacl
, 'lcaD');
593 /* DACL is no longer needed, free it */
594 ExFreePoolWithTag(Dacl
, 'lcaD');
596 /* Set its end of file to initial size */
597 Status
= ZwSetInformationFile(FileHandle
,
600 sizeof(LARGE_INTEGER
),
601 FileEndOfFileInformation
);
602 if (!NT_SUCCESS(Status
) || !NT_SUCCESS(IoStatus
.Status
))
608 Status
= ObReferenceObjectByHandle(FileHandle
,
614 if (!NT_SUCCESS(Status
))
620 /* Deny page file creation on a floppy disk */
621 FsDeviceInfo
.Characteristics
= 0;
622 IoQueryVolumeInformation(FileObject
, FileFsDeviceInformation
, sizeof(FsDeviceInfo
), &FsDeviceInfo
, &Count
);
623 if (BooleanFlagOn(FsDeviceInfo
.Characteristics
, FILE_FLOPPY_DISKETTE
))
625 ObDereferenceObject(FileObject
);
627 return STATUS_FLOPPY_VOLUME
;
630 PagingFile
= ExAllocatePool(NonPagedPool
, sizeof(*PagingFile
));
631 if (PagingFile
== NULL
)
633 ObDereferenceObject(FileObject
);
635 return(STATUS_NO_MEMORY
);
638 RtlZeroMemory(PagingFile
, sizeof(*PagingFile
));
640 PagingFile
->FileHandle
= FileHandle
;
641 PagingFile
->FileObject
= FileObject
;
642 PagingFile
->MaximumSize
.QuadPart
= SafeMaximumSize
.QuadPart
;
643 PagingFile
->CurrentSize
.QuadPart
= SafeInitialSize
.QuadPart
;
644 PagingFile
->FreePages
= (ULONG
)(SafeInitialSize
.QuadPart
/ PAGE_SIZE
);
645 PagingFile
->UsedPages
= 0;
646 KeInitializeSpinLock(&PagingFile
->AllocMapLock
);
648 AllocMapSize
= sizeof(RTL_BITMAP
) + (((PagingFile
->FreePages
+ 31) / 32) * sizeof(ULONG
));
649 PagingFile
->AllocMap
= ExAllocatePoolWithTag(NonPagedPool
,
652 if (PagingFile
->AllocMap
== NULL
)
654 ExFreePool(PagingFile
);
655 ObDereferenceObject(FileObject
);
657 return(STATUS_NO_MEMORY
);
660 RtlInitializeBitMap(PagingFile
->AllocMap
,
661 (PULONG
)(PagingFile
->AllocMap
+ 1),
662 (ULONG
)(PagingFile
->FreePages
));
663 RtlClearAllBits(PagingFile
->AllocMap
);
665 KeAcquireSpinLock(&PagingFileListLock
, &oldIrql
);
666 ASSERT(MmPagingFile
[MmNumberOfPagingFiles
] == NULL
);
667 MmPagingFile
[MmNumberOfPagingFiles
] = PagingFile
;
668 MmNumberOfPagingFiles
++;
669 MiFreeSwapPages
= MiFreeSwapPages
+ PagingFile
->FreePages
;
670 KeReleaseSpinLock(&PagingFileListLock
, oldIrql
);
672 MmSwapSpaceMessage
= FALSE
;
674 return(STATUS_SUCCESS
);