[NTOSKRNL] Close page files (and delete them!) on shutdown
[reactos.git] / 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 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.
18 */
19 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/pagefile.c
22 * PURPOSE: Paging file functions
23 * PROGRAMMER: David Welch (welch@mcmail.com)
24 * Pierre Schweitzer
25 * UPDATE HISTORY:
26 * Created 22/05/98
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include <ntoskrnl.h>
32 #define NDEBUG
33 #include <debug.h>
34
35 #if defined (ALLOC_PRAGMA)
36 #pragma alloc_text(INIT, MmInitPagingFile)
37 #endif
38
39 /* GLOBALS *******************************************************************/
40
41 #define PAIRS_PER_RUN (1024)
42
43 /* List of paging files, both used and free */
44 PMMPAGING_FILE MmPagingFile[MAX_PAGING_FILES];
45
46 /* Lock for examining the list of paging files */
47 static KSPIN_LOCK PagingFileListLock;
48
49 /* Number of paging files */
50 ULONG MmNumberOfPagingFiles;
51
52 /* Number of pages that are available for swapping */
53 PFN_COUNT MiFreeSwapPages;
54
55 /* Number of pages that have been allocated for swapping */
56 PFN_COUNT MiUsedSwapPages;
57
58 BOOLEAN MmZeroPageFile;
59
60 /*
61 * Number of pages that have been reserved for swapping but not yet allocated
62 */
63 static PFN_COUNT MiReservedSwapPages;
64
65 /*
66 * Ratio between reserved and available swap pages, e.g. setting this to five
67 * forces one swap page to be available for every five swap pages that are
68 * reserved. Setting this to zero turns off commit checking altogether.
69 */
70 #define MM_PAGEFILE_COMMIT_RATIO (1)
71
72 /*
73 * Number of pages that can be used for potentially swapable memory without
74 * pagefile space being reserved. The intention is that this allows smss
75 * to start up and create page files while ordinarily having a commit
76 * ratio of one.
77 */
78 #define MM_PAGEFILE_COMMIT_GRACE (256)
79
80 /*
81 * Translate between a swap entry and a file and offset pair.
82 */
83 #define FILE_FROM_ENTRY(i) ((i) & 0x0f)
84 #define OFFSET_FROM_ENTRY(i) ((i) >> 11)
85 #define ENTRY_FROM_FILE_OFFSET(i, j) ((i) | ((j) << 11) | 0x400)
86
87 /* Make sure there can be only 16 paging files */
88 C_ASSERT(FILE_FROM_ENTRY(0xffffffff) < MAX_PAGING_FILES);
89
90 static BOOLEAN MmSwapSpaceMessage = FALSE;
91
92 /* FUNCTIONS *****************************************************************/
93
94 VOID
95 NTAPI
96 MmBuildMdlFromPages(PMDL Mdl, PPFN_NUMBER Pages)
97 {
98 memcpy(Mdl + 1, Pages, sizeof(PFN_NUMBER) * (PAGE_ROUND_UP(Mdl->ByteOffset+Mdl->ByteCount)/PAGE_SIZE));
99
100 /* FIXME: this flag should be set by the caller perhaps? */
101 Mdl->MdlFlags |= MDL_IO_PAGE_READ;
102 }
103
104
105 BOOLEAN
106 NTAPI
107 MmIsFileObjectAPagingFile(PFILE_OBJECT FileObject)
108 {
109 ULONG i;
110
111 /* Loop through all the paging files */
112 for (i = 0; i < MmNumberOfPagingFiles; i++)
113 {
114 /* Check if this is one of them */
115 if (MmPagingFile[i]->FileObject == FileObject) return TRUE;
116 }
117
118 /* Nothing found */
119 return FALSE;
120 }
121
122 VOID
123 NTAPI
124 MmShowOutOfSpaceMessagePagingFile(VOID)
125 {
126 if (!MmSwapSpaceMessage)
127 {
128 DPRINT1("MM: Out of swap space.\n");
129 MmSwapSpaceMessage = TRUE;
130 }
131 }
132
133 NTSTATUS
134 NTAPI
135 MmWriteToSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
136 {
137 ULONG i;
138 ULONG_PTR offset;
139 LARGE_INTEGER file_offset;
140 IO_STATUS_BLOCK Iosb;
141 NTSTATUS Status;
142 KEVENT Event;
143 UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
144 PMDL Mdl = (PMDL)MdlBase;
145
146 DPRINT("MmWriteToSwapPage\n");
147
148 if (SwapEntry == 0)
149 {
150 KeBugCheck(MEMORY_MANAGEMENT);
151 return(STATUS_UNSUCCESSFUL);
152 }
153
154 i = FILE_FROM_ENTRY(SwapEntry);
155 offset = OFFSET_FROM_ENTRY(SwapEntry) - 1;
156
157 if (MmPagingFile[i]->FileObject == NULL ||
158 MmPagingFile[i]->FileObject->DeviceObject == NULL)
159 {
160 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
161 KeBugCheck(MEMORY_MANAGEMENT);
162 }
163
164 MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
165 MmBuildMdlFromPages(Mdl, &Page);
166 Mdl->MdlFlags |= MDL_PAGES_LOCKED;
167
168 file_offset.QuadPart = offset * PAGE_SIZE;
169
170 KeInitializeEvent(&Event, NotificationEvent, FALSE);
171 Status = IoSynchronousPageWrite(MmPagingFile[i]->FileObject,
172 Mdl,
173 &file_offset,
174 &Event,
175 &Iosb);
176 if (Status == STATUS_PENDING)
177 {
178 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
179 Status = Iosb.Status;
180 }
181
182 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
183 {
184 MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
185 }
186 return(Status);
187 }
188
189
190 NTSTATUS
191 NTAPI
192 MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
193 {
194 return MiReadPageFile(Page, FILE_FROM_ENTRY(SwapEntry), OFFSET_FROM_ENTRY(SwapEntry) - 1);
195 }
196
197 NTSTATUS
198 NTAPI
199 MiReadPageFile(
200 _In_ PFN_NUMBER Page,
201 _In_ ULONG PageFileIndex,
202 _In_ ULONG_PTR PageFileOffset)
203 {
204 LARGE_INTEGER file_offset;
205 IO_STATUS_BLOCK Iosb;
206 NTSTATUS Status;
207 KEVENT Event;
208 UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
209 PMDL Mdl = (PMDL)MdlBase;
210 PMMPAGING_FILE PagingFile;
211
212 DPRINT("MiReadSwapFile\n");
213
214 if (PageFileOffset == 0)
215 {
216 KeBugCheck(MEMORY_MANAGEMENT);
217 return(STATUS_UNSUCCESSFUL);
218 }
219
220 ASSERT(PageFileIndex < MAX_PAGING_FILES);
221
222 PagingFile = MmPagingFile[PageFileIndex];
223
224 if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject == NULL)
225 {
226 DPRINT1("Bad paging file %u\n", PageFileIndex);
227 KeBugCheck(MEMORY_MANAGEMENT);
228 }
229
230 MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
231 MmBuildMdlFromPages(Mdl, &Page);
232 Mdl->MdlFlags |= MDL_PAGES_LOCKED;
233
234 file_offset.QuadPart = PageFileOffset * PAGE_SIZE;
235
236 KeInitializeEvent(&Event, NotificationEvent, FALSE);
237 Status = IoPageRead(PagingFile->FileObject,
238 Mdl,
239 &file_offset,
240 &Event,
241 &Iosb);
242 if (Status == STATUS_PENDING)
243 {
244 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
245 Status = Iosb.Status;
246 }
247 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
248 {
249 MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
250 }
251 return(Status);
252 }
253
254 VOID
255 INIT_FUNCTION
256 NTAPI
257 MmInitPagingFile(VOID)
258 {
259 ULONG i;
260
261 KeInitializeSpinLock(&PagingFileListLock);
262
263 MiFreeSwapPages = 0;
264 MiUsedSwapPages = 0;
265 MiReservedSwapPages = 0;
266
267 for (i = 0; i < MAX_PAGING_FILES; i++)
268 {
269 MmPagingFile[i] = NULL;
270 }
271 MmNumberOfPagingFiles = 0;
272 }
273
274 static ULONG
275 MiAllocPageFromPagingFile(PMMPAGING_FILE PagingFile)
276 {
277 KIRQL oldIrql;
278 ULONG off;
279
280 KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
281 off = RtlFindClearBitsAndSet(PagingFile->AllocMap, 1, 0);
282 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
283
284 return off;
285 }
286
287 VOID
288 NTAPI
289 MmFreeSwapPage(SWAPENTRY Entry)
290 {
291 ULONG i;
292 ULONG_PTR off;
293 KIRQL oldIrql;
294 PMMPAGING_FILE PagingFile;
295
296 i = FILE_FROM_ENTRY(Entry);
297 off = OFFSET_FROM_ENTRY(Entry) - 1;
298
299 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
300
301 PagingFile = MmPagingFile[i];
302 if (PagingFile == NULL)
303 {
304 KeBugCheck(MEMORY_MANAGEMENT);
305 }
306 KeAcquireSpinLockAtDpcLevel(&PagingFile->AllocMapLock);
307
308 RtlClearBit(PagingFile->AllocMap, off >> 5);
309
310 PagingFile->FreePages++;
311 PagingFile->UsedPages--;
312
313 MiFreeSwapPages++;
314 MiUsedSwapPages--;
315
316 KeReleaseSpinLockFromDpcLevel(&PagingFile->AllocMapLock);
317 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
318 }
319
320 SWAPENTRY
321 NTAPI
322 MmAllocSwapPage(VOID)
323 {
324 KIRQL oldIrql;
325 ULONG i;
326 ULONG off;
327 SWAPENTRY entry;
328
329 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
330
331 if (MiFreeSwapPages == 0)
332 {
333 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
334 return(0);
335 }
336
337 for (i = 0; i < MAX_PAGING_FILES; i++)
338 {
339 if (MmPagingFile[i] != NULL &&
340 MmPagingFile[i]->FreePages >= 1)
341 {
342 off = MiAllocPageFromPagingFile(MmPagingFile[i]);
343 if (off == 0xFFFFFFFF)
344 {
345 KeBugCheck(MEMORY_MANAGEMENT);
346 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
347 return(STATUS_UNSUCCESSFUL);
348 }
349 MiUsedSwapPages++;
350 MiFreeSwapPages--;
351 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
352
353 entry = ENTRY_FROM_FILE_OFFSET(i, off + 1);
354 return(entry);
355 }
356 }
357
358 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
359 KeBugCheck(MEMORY_MANAGEMENT);
360 return(0);
361 }
362
363 NTSTATUS NTAPI
364 NtCreatePagingFile(IN PUNICODE_STRING FileName,
365 IN PLARGE_INTEGER InitialSize,
366 IN PLARGE_INTEGER MaximumSize,
367 IN ULONG Reserved)
368 {
369 NTSTATUS Status;
370 OBJECT_ATTRIBUTES ObjectAttributes;
371 HANDLE FileHandle;
372 IO_STATUS_BLOCK IoStatus;
373 PFILE_OBJECT FileObject;
374 PMMPAGING_FILE PagingFile;
375 KIRQL oldIrql;
376 ULONG AllocMapSize;
377 ULONG Count;
378 KPROCESSOR_MODE PreviousMode;
379 UNICODE_STRING CapturedFileName;
380 LARGE_INTEGER SafeInitialSize, SafeMaximumSize, AllocationSize;
381 FILE_FS_DEVICE_INFORMATION FsDeviceInfo;
382 SECURITY_DESCRIPTOR SecurityDescriptor;
383 PACL Dacl;
384
385 DPRINT("NtCreatePagingFile(FileName %wZ, InitialSize %I64d)\n",
386 FileName, InitialSize->QuadPart);
387
388 if (MmNumberOfPagingFiles >= MAX_PAGING_FILES)
389 {
390 return STATUS_TOO_MANY_PAGING_FILES;
391 }
392
393 PreviousMode = ExGetPreviousMode();
394
395 if (PreviousMode != KernelMode)
396 {
397 if (SeSinglePrivilegeCheck(SeCreatePagefilePrivilege, PreviousMode) != TRUE)
398 {
399 return STATUS_PRIVILEGE_NOT_HELD;
400 }
401
402 _SEH2_TRY
403 {
404 SafeInitialSize = ProbeForReadLargeInteger(InitialSize);
405 SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
406 }
407 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
408 {
409 /* Return the exception code */
410 _SEH2_YIELD(return _SEH2_GetExceptionCode());
411 }
412 _SEH2_END;
413 }
414 else
415 {
416 SafeInitialSize = *InitialSize;
417 SafeMaximumSize = *MaximumSize;
418 }
419
420 /* Pagefiles can't be larger than 4GB and ofcourse the minimum should be
421 smaller than the maximum */
422 if (0 != SafeInitialSize.u.HighPart)
423 {
424 return STATUS_INVALID_PARAMETER_2;
425 }
426 if (0 != SafeMaximumSize.u.HighPart)
427 {
428 return STATUS_INVALID_PARAMETER_3;
429 }
430 if (SafeMaximumSize.u.LowPart < SafeInitialSize.u.LowPart)
431 {
432 return STATUS_INVALID_PARAMETER_MIX;
433 }
434
435 Status = ProbeAndCaptureUnicodeString(&CapturedFileName,
436 PreviousMode,
437 FileName);
438 if (!NT_SUCCESS(Status))
439 {
440 return(Status);
441 }
442
443 /* Create the security descriptor for the page file */
444 Status = RtlCreateSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
445 if (!NT_SUCCESS(Status))
446 {
447 ReleaseCapturedUnicodeString(&CapturedFileName,
448 PreviousMode);
449 return Status;
450 }
451
452 /* Create the DACL: we will only allow two SIDs */
453 Count = sizeof(ACL) + (sizeof(ACE) + RtlLengthSid(SeLocalSystemSid)) +
454 (sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid));
455 Dacl = ExAllocatePoolWithTag(PagedPool, Count, 'lcaD');
456 if (Dacl == NULL)
457 {
458 ReleaseCapturedUnicodeString(&CapturedFileName,
459 PreviousMode);
460 return STATUS_INSUFFICIENT_RESOURCES;
461 }
462
463 /* Initialize the DACL */
464 Status = RtlCreateAcl(Dacl, Count, ACL_REVISION);
465 if (!NT_SUCCESS(Status))
466 {
467 ExFreePoolWithTag(Dacl, 'lcaD');
468 ReleaseCapturedUnicodeString(&CapturedFileName,
469 PreviousMode);
470 return Status;
471 }
472
473 /* Grant full access to admins */
474 Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, FILE_ALL_ACCESS, SeAliasAdminsSid);
475 if (!NT_SUCCESS(Status))
476 {
477 ExFreePoolWithTag(Dacl, 'lcaD');
478 ReleaseCapturedUnicodeString(&CapturedFileName,
479 PreviousMode);
480 return Status;
481 }
482
483 /* Grant full access to SYSTEM */
484 Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, FILE_ALL_ACCESS, SeLocalSystemSid);
485 if (!NT_SUCCESS(Status))
486 {
487 ExFreePoolWithTag(Dacl, 'lcaD');
488 ReleaseCapturedUnicodeString(&CapturedFileName,
489 PreviousMode);
490 return Status;
491 }
492
493 /* Attach the DACL to the security descriptor */
494 Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl, FALSE);
495 if (!NT_SUCCESS(Status))
496 {
497 ExFreePoolWithTag(Dacl, 'lcaD');
498 ReleaseCapturedUnicodeString(&CapturedFileName,
499 PreviousMode);
500 return Status;
501 }
502
503 InitializeObjectAttributes(&ObjectAttributes,
504 &CapturedFileName,
505 OBJ_KERNEL_HANDLE,
506 NULL,
507 &SecurityDescriptor);
508
509 /* Make sure we can at least store a complete page:
510 * If we have 2048 BytesPerAllocationUnit (FAT16 < 128MB) there is
511 * a problem if the paging file is fragmented. Suppose the first cluster
512 * of the paging file is cluster 3042 but cluster 3043 is NOT part of the
513 * paging file but of another file. We can't write a complete page (4096
514 * bytes) to the physical location of cluster 3042 then. */
515 AllocationSize.QuadPart = SafeInitialSize.QuadPart + PAGE_SIZE;
516
517 /* First, attempt to replace the page file, if existing */
518 Status = IoCreateFile(&FileHandle,
519 SYNCHRONIZE | WRITE_DAC | FILE_READ_DATA | FILE_WRITE_DATA,
520 &ObjectAttributes,
521 &IoStatus,
522 &AllocationSize,
523 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
524 FILE_SHARE_WRITE,
525 FILE_SUPERSEDE,
526 FILE_DELETE_ON_CLOSE | FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING,
527 NULL,
528 0,
529 CreateFileTypeNone,
530 NULL,
531 SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
532 /* If we failed, relax a bit constraints, someone may be already holding the
533 * the file, so share write, don't attempt to replace and don't delete on close
534 * (basically, don't do anything conflicting)
535 */
536 if (!NT_SUCCESS(Status))
537 {
538 Status = IoCreateFile(&FileHandle,
539 SYNCHRONIZE | FILE_WRITE_DATA,
540 &ObjectAttributes,
541 &IoStatus,
542 &AllocationSize,
543 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
544 FILE_SHARE_WRITE | FILE_SHARE_READ,
545 FILE_OPEN,
546 FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING,
547 NULL,
548 0,
549 CreateFileTypeNone,
550 NULL,
551 SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
552 }
553
554 ReleaseCapturedUnicodeString(&CapturedFileName,
555 PreviousMode);
556 if (!NT_SUCCESS(Status))
557 {
558 DPRINT1("Failed creating page file: %lx\n", Status);
559 ExFreePoolWithTag(Dacl, 'lcaD');
560 return(Status);
561 }
562
563 /* Set the security descriptor */
564 if (NT_SUCCESS(IoStatus.Status))
565 {
566 Status = ZwSetSecurityObject(FileHandle, DACL_SECURITY_INFORMATION, &SecurityDescriptor);
567 if (!NT_SUCCESS(Status))
568 {
569 ExFreePoolWithTag(Dacl, 'lcaD');
570 ZwClose(FileHandle);
571 return Status;
572 }
573 }
574
575 /* DACL is no longer needed, free it */
576 ExFreePoolWithTag(Dacl, 'lcaD');
577
578 /* Set its end of file to initial size */
579 Status = ZwSetInformationFile(FileHandle,
580 &IoStatus,
581 &SafeInitialSize,
582 sizeof(LARGE_INTEGER),
583 FileEndOfFileInformation);
584 if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatus.Status))
585 {
586 ZwClose(FileHandle);
587 return(Status);
588 }
589
590 Status = ObReferenceObjectByHandle(FileHandle,
591 FILE_ALL_ACCESS,
592 IoFileObjectType,
593 KernelMode,
594 (PVOID*)&FileObject,
595 NULL);
596 if (!NT_SUCCESS(Status))
597 {
598 ZwClose(FileHandle);
599 return(Status);
600 }
601
602 /* Deny page file creation on a floppy disk */
603 FsDeviceInfo.Characteristics = 0;
604 IoQueryVolumeInformation(FileObject, FileFsDeviceInformation, sizeof(FsDeviceInfo), &FsDeviceInfo, &Count);
605 if (BooleanFlagOn(FsDeviceInfo.Characteristics, FILE_FLOPPY_DISKETTE))
606 {
607 ObDereferenceObject(FileObject);
608 ZwClose(FileHandle);
609 return STATUS_FLOPPY_VOLUME;
610 }
611
612 PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
613 if (PagingFile == NULL)
614 {
615 ObDereferenceObject(FileObject);
616 ZwClose(FileHandle);
617 return(STATUS_NO_MEMORY);
618 }
619
620 RtlZeroMemory(PagingFile, sizeof(*PagingFile));
621
622 PagingFile->FileHandle = FileHandle;
623 PagingFile->FileObject = FileObject;
624 PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart;
625 PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart;
626 PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE);
627 PagingFile->UsedPages = 0;
628 KeInitializeSpinLock(&PagingFile->AllocMapLock);
629
630 AllocMapSize = sizeof(RTL_BITMAP) + (((PagingFile->FreePages + 31) / 32) * sizeof(ULONG));
631 PagingFile->AllocMap = ExAllocatePoolWithTag(NonPagedPool,
632 AllocMapSize,
633 TAG_MM);
634 if (PagingFile->AllocMap == NULL)
635 {
636 ExFreePool(PagingFile);
637 ObDereferenceObject(FileObject);
638 ZwClose(FileHandle);
639 return(STATUS_NO_MEMORY);
640 }
641
642 RtlInitializeBitMap(PagingFile->AllocMap,
643 (PULONG)(PagingFile->AllocMap + 1),
644 (ULONG)(PagingFile->FreePages));
645 RtlClearAllBits(PagingFile->AllocMap);
646
647 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
648 ASSERT(MmPagingFile[MmNumberOfPagingFiles] == NULL);
649 MmPagingFile[MmNumberOfPagingFiles] = PagingFile;
650 MmNumberOfPagingFiles++;
651 MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
652 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
653
654 MmSwapSpaceMessage = FALSE;
655
656 return(STATUS_SUCCESS);
657 }
658
659 /* EOF */