Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5149 lines
136 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. modwrite.c
  5. Abstract:
  6. This module contains the modified page writer for memory management.
  7. Author:
  8. Lou Perazzoli (loup) 10-Jun-1989
  9. Landy Wang (landyw) 02-Jun-1997
  10. Revision History:
  11. --*/
  12. #include "mi.h"
  13. #include "ntiodump.h"
  14. typedef enum _MODIFIED_WRITER_OBJECT {
  15. NormalCase,
  16. MappedPagesNeedWriting,
  17. ModifiedWriterMaximumObject
  18. } MODIFIED_WRITER_OBJECT;
  19. typedef struct _MM_WRITE_CLUSTER {
  20. ULONG Count;
  21. ULONG StartIndex;
  22. ULONG Cluster[2 * (MM_MAXIMUM_DISK_IO_SIZE / PAGE_SIZE) + 1];
  23. } MM_WRITE_CLUSTER, *PMM_WRITE_CLUSTER;
  24. typedef struct _MM_LDW_WORK_CONTEXT {
  25. WORK_QUEUE_ITEM WorkItem;
  26. PFILE_OBJECT FileObject;
  27. } MM_LDW_WORK_CONTEXT, *PMM_LDW_WORK_CONTEXT;
  28. ULONG MmWriteAllModifiedPages;
  29. LOGICAL MiFirstPageFileCreatedAndReady = FALSE;
  30. LOGICAL MiDrainingMappedWrites = FALSE;
  31. ULONG MmNumberOfMappedMdls;
  32. #if DBG
  33. ULONG MmNumberOfMappedMdlsInUse;
  34. ULONG MmNumberOfMappedMdlsInUsePeak;
  35. #endif
  36. ULONG MiClusterWritesDisabled;
  37. #define MI_SLOW_CLUSTER_WRITES 10
  38. #define ONEMB_IN_PAGES ((1024 * 1024) / PAGE_SIZE)
  39. VOID
  40. MiClusterWritePages (
  41. IN PMMPFN Pfn1,
  42. IN PFN_NUMBER PageFrameIndex,
  43. IN PMM_WRITE_CLUSTER WriteCluster,
  44. IN ULONG Size
  45. );
  46. VOID
  47. MiExtendPagingFileMaximum (
  48. IN ULONG PageFileNumber,
  49. IN PRTL_BITMAP NewBitmap
  50. );
  51. VOID
  52. MiLdwPopupWorker (
  53. IN PVOID Context
  54. );
  55. SIZE_T
  56. MiAttemptPageFileExtension (
  57. IN ULONG PageFileNumber,
  58. IN SIZE_T SizeNeeded,
  59. IN LOGICAL Maximum
  60. );
  61. NTSTATUS
  62. MiZeroPageFileFirstPage (
  63. IN PFILE_OBJECT File
  64. );
  65. #ifdef ALLOC_PRAGMA
  66. #pragma alloc_text(PAGE,NtCreatePagingFile)
  67. #pragma alloc_text(PAGE,MmGetPageFileInformation)
  68. #pragma alloc_text(PAGE,MmGetSystemPageFile)
  69. #pragma alloc_text(PAGE,MiLdwPopupWorker)
  70. #pragma alloc_text(PAGE,MiAttemptPageFileExtension)
  71. #pragma alloc_text(PAGE,MiExtendPagingFiles)
  72. #pragma alloc_text(PAGE,MiZeroPageFileFirstPage)
  73. #pragma alloc_text(PAGELK,MiModifiedPageWriter)
  74. #pragma alloc_text(PAGELK,MiFlushAllPages)
  75. #endif
  76. extern POBJECT_TYPE IoFileObjectType;
  77. extern SIZE_T MmSystemCommitReserve;
  78. LIST_ENTRY MmMappedPageWriterList;
  79. KEVENT MmMappedPageWriterEvent;
  80. KEVENT MmMappedFileIoComplete;
  81. ULONG MmSystemShutdown;
  82. BOOLEAN MmSystemPageFileLocated;
  83. NTSTATUS
  84. MiCheckPageFileMapping (
  85. IN PFILE_OBJECT File
  86. );
  87. VOID
  88. MiInsertPageFileInList (
  89. VOID
  90. );
  91. PFN_NUMBER
  92. MiGatherMappedPages (
  93. IN PMMPFN Pfn1,
  94. IN PFN_NUMBER PageFrameIndex
  95. );
  96. PFN_NUMBER
  97. MiGatherPagefilePages (
  98. IN PMMPFN Pfn1,
  99. IN PFN_NUMBER PageFrameIndex
  100. );
  101. VOID
  102. MiPageFileFull (
  103. VOID
  104. );
  105. #if DBG
  106. ULONG_PTR MmPagingFileDebug[8192];
  107. #endif
  108. extern PFN_NUMBER MmMoreThanEnoughFreePages;
  109. #define MINIMUM_PAGE_FILE_SIZE ((ULONG)(256*PAGE_SIZE))
  110. VOID
  111. MiModifiedPageWriterWorker (
  112. VOID
  113. );
  114. VOID
  115. MiReleaseModifiedWriter (
  116. VOID
  117. )
  118. /*++
  119. Routine Description:
  120. Nonpagable wrapper to signal the modified writer when the first pagefile
  121. creation has completely finished.
  122. --*/
  123. {
  124. KIRQL OldIrql;
  125. LOCK_PFN (OldIrql);
  126. MiFirstPageFileCreatedAndReady = TRUE;
  127. UNLOCK_PFN (OldIrql);
  128. }
  129. NTSTATUS
  130. MiZeroPageFileFirstPage (
  131. IN PFILE_OBJECT File
  132. )
  133. /*++
  134. Routine Description:
  135. This routine zeroes the first page of the newly created paging file
  136. to ensure no stale crashdump signatures get to live on.
  137. Arguments:
  138. File - Supplies a pointer to the file object for the paging file.
  139. Return Value:
  140. NTSTATUS.
  141. --*/
  142. {
  143. PMDL Mdl;
  144. LARGE_INTEGER Offset = {0};
  145. PULONG Block;
  146. IO_STATUS_BLOCK IoStatus;
  147. NTSTATUS Status;
  148. PPFN_NUMBER Page;
  149. PFN_NUMBER MdlHack[(sizeof(MDL)/sizeof(PFN_NUMBER)) + 1];
  150. KEVENT Event;
  151. Mdl = (PMDL)&MdlHack[0];
  152. MmCreateMdl (Mdl, NULL, PAGE_SIZE);
  153. Mdl->MdlFlags |= MDL_PAGES_LOCKED;
  154. Page = (PPFN_NUMBER)(Mdl + 1);
  155. *Page = MiGetPageForHeader ();
  156. Block = MmGetSystemAddressForMdl (Mdl);
  157. RtlZeroMemory (Block, PAGE_SIZE);
  158. KeInitializeEvent (&Event, NotificationEvent, FALSE);
  159. Status = IoSynchronousPageWrite (File,
  160. Mdl,
  161. &Offset,
  162. &Event,
  163. &IoStatus);
  164. if (NT_SUCCESS (Status)) {
  165. KeWaitForSingleObject (&Event,
  166. WrVirtualMemory,
  167. KernelMode,
  168. FALSE,
  169. NULL);
  170. Status = IoStatus.Status;
  171. }
  172. if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
  173. MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
  174. }
  175. MiRemoveImageHeaderPage (*Page);
  176. return Status;
  177. }
  178. NTSTATUS
  179. NtCreatePagingFile (
  180. IN PUNICODE_STRING PageFileName,
  181. IN PLARGE_INTEGER MinimumSize,
  182. IN PLARGE_INTEGER MaximumSize,
  183. IN ULONG Priority OPTIONAL
  184. )
  185. /*++
  186. Routine Description:
  187. This routine opens the specified file, attempts to write a page
  188. to the specified file, and creates the necessary structures to
  189. use the file as a paging file.
  190. If this file is the first paging file, the modified page writer
  191. is started.
  192. This system service requires the caller to have SeCreatePagefilePrivilege.
  193. Arguments:
  194. PageFileName - Supplies the fully qualified file name.
  195. MinimumSize - Supplies the starting size of the paging file.
  196. This value is rounded up to the host page size.
  197. MaximumSize - Supplies the maximum number of bytes to write to the file.
  198. This value is rounded up to the host page size.
  199. Priority - Supplies the relative priority of this paging file.
  200. Return Value:
  201. tbs
  202. --*/
  203. {
  204. PFILE_OBJECT File;
  205. NTSTATUS Status;
  206. OBJECT_ATTRIBUTES PagingFileAttributes;
  207. HANDLE FileHandle;
  208. IO_STATUS_BLOCK IoStatus;
  209. UNICODE_STRING CapturedName;
  210. PWSTR CapturedBuffer;
  211. LARGE_INTEGER CapturedMaximumSize;
  212. LARGE_INTEGER CapturedMinimumSize;
  213. FILE_END_OF_FILE_INFORMATION EndOfFileInformation;
  214. KPROCESSOR_MODE PreviousMode;
  215. FILE_FS_DEVICE_INFORMATION FileDeviceInfo;
  216. ULONG ReturnedLength;
  217. ULONG PageFileNumber;
  218. ULONG NewMaxSizeInPages;
  219. ULONG NewMinSizeInPages;
  220. PMMPAGING_FILE FoundExisting;
  221. PMMPAGING_FILE NewPagingFile;
  222. PRTL_BITMAP NewBitmap;
  223. PRTL_BITMAP OldBitmap;
  224. PDEVICE_OBJECT deviceObject;
  225. MMPAGE_FILE_EXPANSION PageExtend;
  226. SECURITY_DESCRIPTOR SecurityDescriptor;
  227. ULONG DaclLength;
  228. PACL Dacl;
  229. DBG_UNREFERENCED_PARAMETER (Priority);
  230. PAGED_CODE();
  231. CapturedBuffer = NULL;
  232. Dacl = NULL;
  233. if (MmNumberOfPagingFiles == MAX_PAGE_FILES) {
  234. //
  235. // The maximum number of paging files is already in use.
  236. //
  237. return STATUS_TOO_MANY_PAGING_FILES;
  238. }
  239. PreviousMode = KeGetPreviousMode();
  240. if (PreviousMode != KernelMode) {
  241. //
  242. // Make sure the caller has the proper privilege for this.
  243. //
  244. if (!SeSinglePrivilegeCheck (SeCreatePagefilePrivilege, PreviousMode)) {
  245. return STATUS_PRIVILEGE_NOT_HELD;
  246. }
  247. //
  248. // Probe arguments.
  249. //
  250. try {
  251. #if !defined (_WIN64)
  252. //
  253. // Note we only probe for byte alignment because early releases
  254. // of NT did and we don't want to break user apps
  255. // that had bad alignment if they worked before.
  256. //
  257. ProbeForReadSmallStructure (PageFileName,
  258. sizeof(*PageFileName),
  259. sizeof(UCHAR));
  260. #else
  261. ProbeForReadSmallStructure (PageFileName,
  262. sizeof(*PageFileName),
  263. PROBE_ALIGNMENT (UNICODE_STRING));
  264. #endif
  265. ProbeForReadSmallStructure (MaximumSize,
  266. sizeof(LARGE_INTEGER),
  267. PROBE_ALIGNMENT (LARGE_INTEGER));
  268. ProbeForReadSmallStructure (MinimumSize,
  269. sizeof(LARGE_INTEGER),
  270. PROBE_ALIGNMENT (LARGE_INTEGER));
  271. //
  272. // Capture arguments.
  273. //
  274. CapturedMinimumSize = *MinimumSize;
  275. } except (EXCEPTION_EXECUTE_HANDLER) {
  276. //
  277. // If an exception occurs during the probe or capture
  278. // of the initial values, then handle the exception and
  279. // return the exception code as the status value.
  280. //
  281. return GetExceptionCode();
  282. }
  283. }
  284. else {
  285. //
  286. // Capture arguments.
  287. //
  288. CapturedMinimumSize = *MinimumSize;
  289. }
  290. if ((CapturedMinimumSize.QuadPart > MI_MAXIMUM_PAGEFILE_SIZE) ||
  291. (CapturedMinimumSize.LowPart < MINIMUM_PAGE_FILE_SIZE)) {
  292. return STATUS_INVALID_PARAMETER_2;
  293. }
  294. if (PreviousMode != KernelMode) {
  295. try {
  296. CapturedMaximumSize = *MaximumSize;
  297. } except (EXCEPTION_EXECUTE_HANDLER) {
  298. //
  299. // If an exception occurs during the probe or capture
  300. // of the initial values, then handle the exception and
  301. // return the exception code as the status value.
  302. //
  303. return GetExceptionCode();
  304. }
  305. }
  306. else {
  307. CapturedMaximumSize = *MaximumSize;
  308. }
  309. if (CapturedMaximumSize.QuadPart > MI_MAXIMUM_PAGEFILE_SIZE) {
  310. return STATUS_INVALID_PARAMETER_3;
  311. }
  312. if (CapturedMinimumSize.QuadPart > CapturedMaximumSize.QuadPart) {
  313. return STATUS_INVALID_PARAMETER_3;
  314. }
  315. if (PreviousMode != KernelMode) {
  316. try {
  317. CapturedName = *PageFileName;
  318. } except (EXCEPTION_EXECUTE_HANDLER) {
  319. //
  320. // If an exception occurs during the probe or capture
  321. // of the initial values, then handle the exception and
  322. // return the exception code as the status value.
  323. //
  324. return GetExceptionCode();
  325. }
  326. }
  327. else {
  328. CapturedName = *PageFileName;
  329. }
  330. CapturedName.MaximumLength = CapturedName.Length;
  331. if ((CapturedName.Length == 0) ||
  332. (CapturedName.Length > MAXIMUM_FILENAME_LENGTH )) {
  333. return STATUS_OBJECT_NAME_INVALID;
  334. }
  335. CapturedBuffer = ExAllocatePoolWithTag (PagedPool,
  336. (ULONG)CapturedName.Length,
  337. ' mM');
  338. if (CapturedBuffer == NULL) {
  339. return STATUS_INSUFFICIENT_RESOURCES;
  340. }
  341. if (PreviousMode != KernelMode) {
  342. try {
  343. ProbeForRead (CapturedName.Buffer,
  344. CapturedName.Length,
  345. sizeof (UCHAR));
  346. //
  347. // Copy the string to the allocated buffer.
  348. //
  349. RtlCopyMemory (CapturedBuffer,
  350. CapturedName.Buffer,
  351. CapturedName.Length);
  352. } except (EXCEPTION_EXECUTE_HANDLER) {
  353. //
  354. // If an exception occurs during the probe or capture
  355. // of the initial values, then handle the exception and
  356. // return the exception code as the status value.
  357. //
  358. ExFreePool (CapturedBuffer);
  359. return GetExceptionCode();
  360. }
  361. }
  362. else {
  363. //
  364. // Copy the string to the allocated buffer.
  365. //
  366. RtlCopyMemory (CapturedBuffer,
  367. CapturedName.Buffer,
  368. CapturedName.Length);
  369. }
  370. //
  371. // Point the buffer to the string that was just copied.
  372. //
  373. CapturedName.Buffer = CapturedBuffer;
  374. //
  375. // Create a security descriptor to protect the pagefile.
  376. //
  377. Status = RtlCreateSecurityDescriptor (&SecurityDescriptor,
  378. SECURITY_DESCRIPTOR_REVISION);
  379. if (!NT_SUCCESS (Status)) {
  380. goto ErrorReturn1;
  381. }
  382. DaclLength = sizeof (ACL) + sizeof (ACCESS_ALLOWED_ACE) * 2 +
  383. RtlLengthSid (SeLocalSystemSid) +
  384. RtlLengthSid (SeAliasAdminsSid);
  385. Dacl = ExAllocatePoolWithTag (PagedPool, DaclLength, 'lcaD');
  386. if (Dacl == NULL) {
  387. Status = STATUS_INSUFFICIENT_RESOURCES;
  388. goto ErrorReturn1;
  389. }
  390. Status = RtlCreateAcl (Dacl, DaclLength, ACL_REVISION);
  391. if (!NT_SUCCESS (Status)) {
  392. goto ErrorReturn1;
  393. }
  394. Status = RtlAddAccessAllowedAce (Dacl,
  395. ACL_REVISION,
  396. FILE_ALL_ACCESS,
  397. SeAliasAdminsSid);
  398. if (!NT_SUCCESS (Status)) {
  399. goto ErrorReturn1;
  400. }
  401. Status = RtlAddAccessAllowedAce (Dacl,
  402. ACL_REVISION,
  403. FILE_ALL_ACCESS,
  404. SeLocalSystemSid);
  405. if (!NT_SUCCESS (Status)) {
  406. goto ErrorReturn1;
  407. }
  408. Status = RtlSetDaclSecurityDescriptor (&SecurityDescriptor,
  409. TRUE,
  410. Dacl,
  411. FALSE);
  412. if (!NT_SUCCESS (Status)) {
  413. goto ErrorReturn1;
  414. }
  415. //
  416. // Open a paging file and get the size.
  417. //
  418. InitializeObjectAttributes (&PagingFileAttributes,
  419. &CapturedName,
  420. (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
  421. NULL,
  422. NULL);
  423. //
  424. // Note this macro cannot use ULONG_PTR as it must also work on PAE.
  425. //
  426. #define ROUND64_TO_PAGES(Size) (((ULONG64)(Size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
  427. EndOfFileInformation.EndOfFile.QuadPart =
  428. ROUND64_TO_PAGES (CapturedMinimumSize.QuadPart);
  429. Status = IoCreateFile (&FileHandle,
  430. FILE_READ_DATA | FILE_WRITE_DATA | WRITE_DAC | SYNCHRONIZE,
  431. &PagingFileAttributes,
  432. &IoStatus,
  433. &CapturedMinimumSize,
  434. FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  435. FILE_SHARE_WRITE,
  436. FILE_SUPERSEDE,
  437. FILE_NO_INTERMEDIATE_BUFFERING | FILE_NO_COMPRESSION | FILE_DELETE_ON_CLOSE,
  438. NULL,
  439. 0L,
  440. CreateFileTypeNone,
  441. NULL,
  442. IO_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
  443. if (NT_SUCCESS(Status)) {
  444. //
  445. // Update the DACL in case there was a pre-existing regular file named
  446. // pagefile.sys (even supersede above does not do this).
  447. //
  448. if (NT_SUCCESS(IoStatus.Status)) {
  449. Status = ZwSetSecurityObject (FileHandle,
  450. DACL_SECURITY_INFORMATION,
  451. &SecurityDescriptor);
  452. if (!NT_SUCCESS(Status)) {
  453. goto ErrorReturn2;
  454. }
  455. }
  456. }
  457. else {
  458. //
  459. // Treat this as an extension of an existing pagefile maximum -
  460. // and try to open rather than create the paging file specified.
  461. //
  462. Status = IoCreateFile (&FileHandle,
  463. FILE_WRITE_DATA | SYNCHRONIZE,
  464. &PagingFileAttributes,
  465. &IoStatus,
  466. &CapturedMinimumSize,
  467. FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
  468. FILE_SHARE_READ | FILE_SHARE_WRITE,
  469. FILE_OPEN,
  470. FILE_NO_INTERMEDIATE_BUFFERING | FILE_NO_COMPRESSION,
  471. (PVOID) NULL,
  472. 0L,
  473. CreateFileTypeNone,
  474. (PVOID) NULL,
  475. IO_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
  476. if (!NT_SUCCESS(Status)) {
  477. #if DBG
  478. if (Status != STATUS_DISK_FULL) {
  479. DbgPrint("MM MODWRITE: unable to open paging file %wZ - status = %X \n", &CapturedName, Status);
  480. }
  481. #endif
  482. goto ErrorReturn1;
  483. }
  484. Status = ObReferenceObjectByHandle (FileHandle,
  485. FILE_READ_DATA | FILE_WRITE_DATA,
  486. IoFileObjectType,
  487. KernelMode,
  488. (PVOID *)&File,
  489. NULL);
  490. if (!NT_SUCCESS(Status)) {
  491. goto ErrorReturn2;
  492. }
  493. FoundExisting = NULL;
  494. ExAcquireFastMutex (&MmPageFileCreationLock);
  495. for (PageFileNumber = 0; PageFileNumber < MmNumberOfPagingFiles; PageFileNumber += 1) {
  496. if (MmPagingFile[PageFileNumber]->File->SectionObjectPointer == File->SectionObjectPointer) {
  497. FoundExisting = MmPagingFile[PageFileNumber];
  498. break;
  499. }
  500. }
  501. if (FoundExisting == NULL) {
  502. Status = STATUS_NOT_FOUND;
  503. goto ErrorReturn4;
  504. }
  505. //
  506. // Check for increases in the minimum or the maximum paging file sizes.
  507. // Decreasing either paging file size on the fly is not allowed.
  508. //
  509. NewMaxSizeInPages = (ULONG)(CapturedMaximumSize.QuadPart >> PAGE_SHIFT);
  510. NewMinSizeInPages = (ULONG)(CapturedMinimumSize.QuadPart >> PAGE_SHIFT);
  511. if (FoundExisting->MinimumSize > NewMinSizeInPages) {
  512. Status = STATUS_INVALID_PARAMETER_2;
  513. goto ErrorReturn4;
  514. }
  515. if (FoundExisting->MaximumSize > NewMaxSizeInPages) {
  516. Status = STATUS_INVALID_PARAMETER_3;
  517. goto ErrorReturn4;
  518. }
  519. if (NewMaxSizeInPages > FoundExisting->MaximumSize) {
  520. //
  521. // Make sure that the pagefile increase doesn't cause the commit
  522. // limit (in pages) to wrap. Currently this can only happen on
  523. // PAE systems where 16 pagefiles of 16TB (==256TB) is greater
  524. // than the 32-bit commit variable (max is 16TB).
  525. //
  526. if (MmTotalCommitLimitMaximum + (NewMaxSizeInPages - FoundExisting->MaximumSize) <= MmTotalCommitLimitMaximum) {
  527. Status = STATUS_INVALID_PARAMETER_3;
  528. goto ErrorReturn4;
  529. }
  530. //
  531. // Handle the increase to the maximum paging file size.
  532. //
  533. MiCreateBitMap (&NewBitmap, NewMaxSizeInPages, NonPagedPool);
  534. if (NewBitmap == NULL) {
  535. Status = STATUS_INSUFFICIENT_RESOURCES;
  536. goto ErrorReturn4;
  537. }
  538. OldBitmap = FoundExisting->Bitmap;
  539. MiExtendPagingFileMaximum (PageFileNumber, NewBitmap);
  540. MiRemoveBitMap (&OldBitmap);
  541. //
  542. // We may be low on commitment and/or may have put a temporary
  543. // stopgate on things. Clear up the logjam now by forcing an
  544. // extension and immediately returning it.
  545. //
  546. if (MmTotalCommittedPages + 100 > MmTotalCommitLimit) {
  547. if (MiChargeCommitment (200, NULL) == TRUE) {
  548. MiReturnCommitment (200);
  549. }
  550. }
  551. }
  552. if (NewMinSizeInPages > FoundExisting->MinimumSize) {
  553. //
  554. // Handle the increase to the minimum paging file size.
  555. //
  556. if (NewMinSizeInPages > FoundExisting->Size) {
  557. //
  558. // Queue a message to the segment dereferencing / pagefile
  559. // extending thread to see if the page file can be extended.
  560. //
  561. PageExtend.InProgress = 1;
  562. PageExtend.ActualExpansion = 0;
  563. PageExtend.RequestedExpansionSize = NewMinSizeInPages - FoundExisting->Size;
  564. PageExtend.Segment = NULL;
  565. PageExtend.PageFileNumber = PageFileNumber;
  566. KeInitializeEvent (&PageExtend.Event, NotificationEvent, FALSE);
  567. MiIssuePageExtendRequest (&PageExtend);
  568. }
  569. //
  570. // The current size is now greater than the new desired minimum.
  571. // Ensure subsequent contractions obey this new minimum.
  572. //
  573. if (FoundExisting->Size >= NewMinSizeInPages) {
  574. ASSERT (FoundExisting->Size >= FoundExisting->MinimumSize);
  575. ASSERT (NewMinSizeInPages >= FoundExisting->MinimumSize);
  576. FoundExisting->MinimumSize = NewMinSizeInPages;
  577. }
  578. else {
  579. //
  580. // The pagefile could not be expanded to handle the new minimum.
  581. // No easy way to undo any maximum raising that may have been
  582. // done as the space may have already been used, so just set
  583. // Status so our caller knows it didn't all go perfectly.
  584. //
  585. Status = STATUS_INSUFFICIENT_RESOURCES;
  586. }
  587. }
  588. goto ErrorReturn4;
  589. }
  590. if (!NT_SUCCESS(IoStatus.Status)) {
  591. KdPrint(("MM MODWRITE: unable to open paging file %wZ - iosb %lx\n", &CapturedName, IoStatus.Status));
  592. Status = IoStatus.Status;
  593. goto ErrorReturn1;
  594. }
  595. //
  596. // Make sure that the pagefile increase doesn't cause the commit
  597. // limit (in pages) to wrap. Currently this can only happen on
  598. // PAE systems where 16 pagefiles of 16TB (==256TB) is greater
  599. // than the 32-bit commit variable (max is 16TB).
  600. //
  601. if (MmTotalCommitLimitMaximum + (CapturedMaximumSize.QuadPart >> PAGE_SHIFT)
  602. <= MmTotalCommitLimitMaximum) {
  603. Status = STATUS_INVALID_PARAMETER_3;
  604. goto ErrorReturn2;
  605. }
  606. Status = ZwSetInformationFile (FileHandle,
  607. &IoStatus,
  608. &EndOfFileInformation,
  609. sizeof(EndOfFileInformation),
  610. FileEndOfFileInformation);
  611. if (!NT_SUCCESS(Status)) {
  612. KdPrint(("MM MODWRITE: unable to set length of paging file %wZ status = %X \n",
  613. &CapturedName, Status));
  614. goto ErrorReturn2;
  615. }
  616. if (!NT_SUCCESS(IoStatus.Status)) {
  617. KdPrint(("MM MODWRITE: unable to set length of paging file %wZ - iosb %lx\n",
  618. &CapturedName, IoStatus.Status));
  619. Status = IoStatus.Status;
  620. goto ErrorReturn2;
  621. }
  622. Status = ObReferenceObjectByHandle ( FileHandle,
  623. FILE_READ_DATA | FILE_WRITE_DATA,
  624. IoFileObjectType,
  625. KernelMode,
  626. (PVOID *)&File,
  627. NULL );
  628. if (!NT_SUCCESS(Status)) {
  629. KdPrint(("MM MODWRITE: Unable to reference paging file - %wZ\n",
  630. &CapturedName));
  631. goto ErrorReturn2;
  632. }
  633. //
  634. // Get the address of the target device object and ensure
  635. // the specified file is of a suitable type.
  636. //
  637. deviceObject = IoGetRelatedDeviceObject (File);
  638. if ((deviceObject->DeviceType != FILE_DEVICE_DISK_FILE_SYSTEM) &&
  639. (deviceObject->DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM) &&
  640. (deviceObject->DeviceType != FILE_DEVICE_DFS_VOLUME) &&
  641. (deviceObject->DeviceType != FILE_DEVICE_DFS_FILE_SYSTEM)) {
  642. KdPrint(("MM MODWRITE: Invalid paging file type - %x\n",
  643. deviceObject->DeviceType));
  644. Status = STATUS_UNRECOGNIZED_VOLUME;
  645. goto ErrorReturn3;
  646. }
  647. //
  648. // Make sure the specified file is not currently being used
  649. // as a mapped data file.
  650. //
  651. Status = MiCheckPageFileMapping (File);
  652. if (!NT_SUCCESS(Status)) {
  653. goto ErrorReturn3;
  654. }
  655. //
  656. // Make sure the volume is not a floppy disk.
  657. //
  658. Status = IoQueryVolumeInformation ( File,
  659. FileFsDeviceInformation,
  660. sizeof(FILE_FS_DEVICE_INFORMATION),
  661. &FileDeviceInfo,
  662. &ReturnedLength
  663. );
  664. if (FILE_FLOPPY_DISKETTE & FileDeviceInfo.Characteristics) {
  665. Status = STATUS_FLOPPY_VOLUME;
  666. goto ErrorReturn3;
  667. }
  668. //
  669. // Check with all of the drivers along the path to the file to ensure
  670. // that they are willing to follow the rules required of them and to
  671. // give them a chance to lock down code and data that needs to be locked.
  672. // If any of the drivers along the path refuses to participate, fail the
  673. // pagefile creation.
  674. //
  675. Status = PpPagePathAssign(File);
  676. if (!NT_SUCCESS(Status)) {
  677. KdPrint(( "PpPagePathAssign(%wZ) FAILED: %x\n", &CapturedName, Status ));
  678. //
  679. // Fail the pagefile creation if the storage stack tells us to.
  680. //
  681. goto ErrorReturn3;
  682. }
  683. NewPagingFile = ExAllocatePoolWithTag (NonPagedPool,
  684. sizeof(MMPAGING_FILE),
  685. ' mM');
  686. if (NewPagingFile == NULL) {
  687. //
  688. // Allocate pool failed.
  689. //
  690. Status = STATUS_INSUFFICIENT_RESOURCES;
  691. goto ErrorReturn3;
  692. }
  693. RtlZeroMemory (NewPagingFile, sizeof(MMPAGING_FILE));
  694. NewPagingFile->File = File;
  695. NewPagingFile->FileHandle = FileHandle;
  696. NewPagingFile->Size = (PFN_NUMBER)(CapturedMinimumSize.QuadPart >> PAGE_SHIFT);
  697. NewPagingFile->MinimumSize = NewPagingFile->Size;
  698. NewPagingFile->FreeSpace = NewPagingFile->Size - 1;
  699. NewPagingFile->MaximumSize = (PFN_NUMBER)(CapturedMaximumSize.QuadPart >>
  700. PAGE_SHIFT);
  701. //
  702. // Adjust the commit page limit to reflect the new page file space.
  703. //
  704. NewPagingFile->Entry[0] = ExAllocatePoolWithTag (NonPagedPool,
  705. sizeof(MMMOD_WRITER_MDL_ENTRY) +
  706. MmModifiedWriteClusterSize *
  707. sizeof(PFN_NUMBER),
  708. ' mM');
  709. if (NewPagingFile->Entry[0] == NULL) {
  710. //
  711. // Allocate pool failed.
  712. //
  713. ExFreePool (NewPagingFile);
  714. Status = STATUS_INSUFFICIENT_RESOURCES;
  715. goto ErrorReturn3;
  716. }
  717. RtlZeroMemory (NewPagingFile->Entry[0], sizeof(MMMOD_WRITER_MDL_ENTRY));
  718. NewPagingFile->Entry[0]->PagingListHead = &MmPagingFileHeader;
  719. NewPagingFile->Entry[0]->PagingFile = NewPagingFile;
  720. NewPagingFile->Entry[1] = ExAllocatePoolWithTag (NonPagedPool,
  721. sizeof(MMMOD_WRITER_MDL_ENTRY) +
  722. MmModifiedWriteClusterSize *
  723. sizeof(PFN_NUMBER),
  724. ' mM');
  725. if (NewPagingFile->Entry[1] == NULL) {
  726. //
  727. // Allocate pool failed.
  728. //
  729. ExFreePool (NewPagingFile->Entry[0]);
  730. ExFreePool (NewPagingFile);
  731. Status = STATUS_INSUFFICIENT_RESOURCES;
  732. goto ErrorReturn3;
  733. }
  734. RtlZeroMemory (NewPagingFile->Entry[1], sizeof(MMMOD_WRITER_MDL_ENTRY));
  735. NewPagingFile->Entry[1]->PagingListHead = &MmPagingFileHeader;
  736. NewPagingFile->Entry[1]->PagingFile = NewPagingFile;
  737. NewPagingFile->PageFileName = CapturedName;
  738. MiCreateBitMap (&NewPagingFile->Bitmap,
  739. NewPagingFile->MaximumSize,
  740. NonPagedPool);
  741. if (NewPagingFile->Bitmap == NULL) {
  742. //
  743. // Allocate pool failed.
  744. //
  745. ExFreePool (NewPagingFile->Entry[0]);
  746. ExFreePool (NewPagingFile->Entry[1]);
  747. ExFreePool (NewPagingFile);
  748. Status = STATUS_INSUFFICIENT_RESOURCES;
  749. goto ErrorReturn3;
  750. }
  751. Status = MiZeroPageFileFirstPage (File);
  752. if (!NT_SUCCESS (Status)) {
  753. //
  754. // The storage stack could not zero the first page of the file.
  755. // This means an old crashdump signature could still be around so
  756. // fail the create.
  757. //
  758. ExFreePool (NewPagingFile->Entry[0]);
  759. ExFreePool (NewPagingFile->Entry[1]);
  760. ExFreePool (NewPagingFile);
  761. MiRemoveBitMap (&NewPagingFile->Bitmap);
  762. goto ErrorReturn3;
  763. }
  764. RtlSetAllBits (NewPagingFile->Bitmap);
  765. //
  766. // Set the first bit as 0 is an invalid page location, clear the
  767. // following bits.
  768. //
  769. RtlClearBits (NewPagingFile->Bitmap,
  770. 1,
  771. (ULONG)(NewPagingFile->Size - 1));
  772. //
  773. // See if this pagefile is on the boot partition, and if so, mark it
  774. // so we can find it later if someone enables crashdump.
  775. //
  776. if (File->DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION) {
  777. NewPagingFile->BootPartition = TRUE;
  778. }
  779. else {
  780. NewPagingFile->BootPartition = FALSE;
  781. }
  782. //
  783. // Acquire the global page file creation mutex.
  784. //
  785. ExAcquireFastMutex (&MmPageFileCreationLock);
  786. PageFileNumber = MmNumberOfPagingFiles;
  787. MmPagingFile[PageFileNumber] = NewPagingFile;
  788. NewPagingFile->PageFileNumber = PageFileNumber;
  789. MiInsertPageFileInList ();
  790. if (PageFileNumber == 0) {
  791. //
  792. // The first paging file has been created and reservation of any
  793. // crashdump pages has completed, signal the modified
  794. // page writer.
  795. //
  796. MiReleaseModifiedWriter ();
  797. }
  798. ExReleaseFastMutex (&MmPageFileCreationLock);
  799. //
  800. // Note that the file handle (a kernel handle) is not closed during the
  801. // create path (it IS duped and closed in the pagefile size extending path)
  802. // to prevent the paging file from being deleted or opened again. It is
  803. // also kept open so that extensions of existing pagefiles can be detected
  804. // because successive IoCreateFile calls will fail.
  805. //
  806. if ((!MmSystemPageFileLocated) &&
  807. (File->DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION)) {
  808. MmSystemPageFileLocated = IoInitializeCrashDump (FileHandle);
  809. }
  810. return STATUS_SUCCESS;
  811. //
  812. // Error returns:
  813. //
  814. ErrorReturn4:
  815. ExReleaseFastMutex (&MmPageFileCreationLock);
  816. ErrorReturn3:
  817. ObDereferenceObject (File);
  818. ErrorReturn2:
  819. ZwClose (FileHandle);
  820. ErrorReturn1:
  821. if (Dacl != NULL) {
  822. ExFreePool (Dacl);
  823. }
  824. ExFreePool (CapturedBuffer);
  825. return Status;
  826. }
  827. HANDLE
  828. MmGetSystemPageFile (
  829. VOID
  830. )
  831. /*++
  832. Routine Description:
  833. Returns a filehandle to the paging file on the system boot partition. This is
  834. used by crashdump to enable crashdump after the system has already booted.
  835. Arguments:
  836. None
  837. Return Value:
  838. A filehandle to the paging file on the system boot partition
  839. NULL if no such pagefile exists
  840. --*/
  841. {
  842. HANDLE FileHandle;
  843. ULONG PageFileNumber;
  844. PAGED_CODE();
  845. FileHandle = NULL;
  846. ExAcquireFastMutex (&MmPageFileCreationLock);
  847. for (PageFileNumber = 0; PageFileNumber < MmNumberOfPagingFiles; PageFileNumber += 1) {
  848. if (MmPagingFile[PageFileNumber]->BootPartition) {
  849. FileHandle = MmPagingFile[PageFileNumber]->FileHandle;
  850. }
  851. }
  852. ExReleaseFastMutex (&MmPageFileCreationLock);
  853. return FileHandle;
  854. }
  855. LOGICAL
  856. MmIsFileObjectAPagingFile (
  857. IN PFILE_OBJECT FileObject
  858. )
  859. /*++
  860. Routine Description:
  861. Returns TRUE if the file object refers to a paging file, FALSE if not.
  862. Arguments:
  863. FileObject - Supplies the file object in question.
  864. Return Value:
  865. Returns TRUE if the file object refers to a paging file, FALSE if not.
  866. --*/
  867. {
  868. PMMPAGING_FILE PageFile;
  869. PMMPAGING_FILE *PagingFile;
  870. PMMPAGING_FILE *PagingFileEnd;
  871. //
  872. // It's ok to check without synchronization.
  873. //
  874. PagingFile = MmPagingFile;
  875. PagingFileEnd = PagingFile + MmNumberOfPagingFiles;
  876. while (PagingFile < PagingFileEnd) {
  877. PageFile = *PagingFile;
  878. if (PageFile->File == FileObject) {
  879. return TRUE;
  880. }
  881. PagingFile += 1;
  882. }
  883. return FALSE;
  884. }
  885. VOID
  886. MiExtendPagingFileMaximum (
  887. IN ULONG PageFileNumber,
  888. IN PRTL_BITMAP NewBitmap
  889. )
  890. /*++
  891. Routine Description:
  892. This routine switches from the old bitmap to the new (larger) bitmap.
  893. Arguments:
  894. PageFileNumber - Supplies the paging file number to be extended.
  895. NewBitmap - Supplies the new bitmap to use.
  896. Return Value:
  897. None.
  898. Environment:
  899. Kernel mode, APC_LEVEL, MmPageFileCreationLock held.
  900. --*/
  901. {
  902. KIRQL OldIrql;
  903. PRTL_BITMAP OldBitmap;
  904. SIZE_T Delta;
  905. OldBitmap = MmPagingFile[PageFileNumber]->Bitmap;
  906. RtlSetAllBits (NewBitmap);
  907. LOCK_PFN (OldIrql);
  908. //
  909. // Copy the bits from the existing map.
  910. //
  911. RtlCopyMemory (NewBitmap->Buffer,
  912. OldBitmap->Buffer,
  913. ((OldBitmap->SizeOfBitMap + 31) / 32) * sizeof (ULONG));
  914. Delta = NewBitmap->SizeOfBitMap - OldBitmap->SizeOfBitMap;
  915. InterlockedExchangeAddSizeT (&MmTotalCommitLimitMaximum, Delta);
  916. MmPagingFile[PageFileNumber]->MaximumSize = NewBitmap->SizeOfBitMap;
  917. MmPagingFile[PageFileNumber]->Bitmap = NewBitmap;
  918. //
  919. // If any MDLs are waiting for space, get them up now.
  920. //
  921. if (!IsListEmpty (&MmFreePagingSpaceLow)) {
  922. MiUpdateModifiedWriterMdls (PageFileNumber);
  923. }
  924. UNLOCK_PFN (OldIrql);
  925. }
  926. VOID
  927. MiFinishPageFileExtension (
  928. IN ULONG PageFileNumber,
  929. IN PFN_NUMBER AdditionalAllocation
  930. )
  931. /*++
  932. Routine Description:
  933. This routine finishes the specified page file extension.
  934. Arguments:
  935. PageFileNumber - Supplies the page file number to attempt to extend.
  936. SizeNeeded - Supplies the number of pages to extend the file by.
  937. Maximum - Supplies TRUE if the page file should be extended
  938. by the maximum size possible, but not to exceed
  939. SizeNeeded.
  940. Return Value:
  941. None.
  942. --*/
  943. {
  944. KIRQL OldIrql;
  945. PMMPAGING_FILE PagingFile;
  946. //
  947. // Clear bits within the paging file bitmap to allow the extension
  948. // to take effect.
  949. //
  950. PagingFile = MmPagingFile[PageFileNumber];
  951. LOCK_PFN (OldIrql);
  952. ASSERT (RtlCheckBit (PagingFile->Bitmap, PagingFile->Size) == 1);
  953. RtlClearBits (PagingFile->Bitmap,
  954. (ULONG)PagingFile->Size,
  955. (ULONG)AdditionalAllocation);
  956. PagingFile->Size += AdditionalAllocation;
  957. PagingFile->FreeSpace += AdditionalAllocation;
  958. MiUpdateModifiedWriterMdls (PageFileNumber);
  959. UNLOCK_PFN (OldIrql);
  960. return;
  961. }
  962. SIZE_T
  963. MiAttemptPageFileExtension (
  964. IN ULONG PageFileNumber,
  965. IN SIZE_T SizeNeeded,
  966. IN LOGICAL Maximum
  967. )
  968. /*++
  969. Routine Description:
  970. This routine attempts to extend the specified page file by SizeNeeded.
  971. Arguments:
  972. PageFileNumber - Supplies the page file number to attempt to extend.
  973. SizeNeeded - Supplies the number of pages to extend the file by.
  974. Maximum - Supplies TRUE if the page file should be extended
  975. by the maximum size possible, but not to exceed
  976. SizeNeeded.
  977. Return Value:
  978. Returns the size of the extension. Zero if the page file cannot
  979. be extended.
  980. --*/
  981. {
  982. NTSTATUS status;
  983. FILE_FS_SIZE_INFORMATION FileInfo;
  984. FILE_END_OF_FILE_INFORMATION EndOfFileInformation;
  985. ULONG AllocSize;
  986. ULONG ReturnedLength;
  987. PFN_NUMBER PagesAvailable;
  988. SIZE_T SizeToExtend;
  989. SIZE_T MinimumExtension;
  990. LARGE_INTEGER BytesAvailable;
  991. //
  992. // Check to see if this page file is at the maximum.
  993. //
  994. if (MmPagingFile[PageFileNumber]->Size ==
  995. MmPagingFile[PageFileNumber]->MaximumSize) {
  996. return 0;
  997. }
  998. //
  999. // Find out how much free space is on this volume.
  1000. //
  1001. status = IoQueryVolumeInformation (MmPagingFile[PageFileNumber]->File,
  1002. FileFsSizeInformation,
  1003. sizeof(FileInfo),
  1004. &FileInfo,
  1005. &ReturnedLength);
  1006. if (!NT_SUCCESS (status)) {
  1007. //
  1008. // The volume query did not succeed - return 0 indicating
  1009. // the paging file was not extended.
  1010. //
  1011. return 0;
  1012. }
  1013. //
  1014. // Attempt to extend by at least 16 megabytes, if that fails then attempt
  1015. // for at least a megabyte.
  1016. //
  1017. MinimumExtension = MmPageFileExtension << 4;
  1018. retry:
  1019. SizeToExtend = SizeNeeded;
  1020. if (SizeNeeded < MinimumExtension) {
  1021. SizeToExtend = MinimumExtension;
  1022. }
  1023. else {
  1024. MinimumExtension = MmPageFileExtension;
  1025. }
  1026. //
  1027. // Don't go over the maximum size for the paging file.
  1028. //
  1029. ASSERT (MmPagingFile[PageFileNumber]->MaximumSize >= MmPagingFile[PageFileNumber]->Size);
  1030. PagesAvailable = MmPagingFile[PageFileNumber]->MaximumSize -
  1031. MmPagingFile[PageFileNumber]->Size;
  1032. if (SizeToExtend > PagesAvailable) {
  1033. SizeToExtend = PagesAvailable;
  1034. if ((SizeToExtend < SizeNeeded) && (Maximum == FALSE)) {
  1035. //
  1036. // Can't meet the requested (mandatory) requirement.
  1037. //
  1038. return 0;
  1039. }
  1040. }
  1041. //
  1042. // See if there is enough space on the volume for the extension.
  1043. //
  1044. AllocSize = FileInfo.SectorsPerAllocationUnit * FileInfo.BytesPerSector;
  1045. BytesAvailable = RtlExtendedIntegerMultiply (
  1046. FileInfo.AvailableAllocationUnits,
  1047. AllocSize);
  1048. if ((UINT64)BytesAvailable.QuadPart > (UINT64)MmMinimumFreeDiskSpace) {
  1049. BytesAvailable.QuadPart = BytesAvailable.QuadPart -
  1050. (LONGLONG)MmMinimumFreeDiskSpace;
  1051. if ((UINT64)BytesAvailable.QuadPart > (UINT64)(SizeToExtend << PAGE_SHIFT)) {
  1052. BytesAvailable.QuadPart = (LONGLONG)(SizeToExtend << PAGE_SHIFT);
  1053. }
  1054. PagesAvailable = (PFN_NUMBER)(BytesAvailable.QuadPart >> PAGE_SHIFT);
  1055. if ((Maximum == FALSE) && (PagesAvailable < SizeNeeded)) {
  1056. //
  1057. // Can't meet the requested (mandatory) requirement.
  1058. //
  1059. return 0;
  1060. }
  1061. }
  1062. else {
  1063. //
  1064. // Not enough space is available period.
  1065. //
  1066. return 0;
  1067. }
  1068. #if defined (_WIN64) || defined (_X86PAE_)
  1069. EndOfFileInformation.EndOfFile.QuadPart =
  1070. ((ULONG64)MmPagingFile[PageFileNumber]->Size + PagesAvailable) * PAGE_SIZE;
  1071. #else
  1072. EndOfFileInformation.EndOfFile.LowPart =
  1073. (MmPagingFile[PageFileNumber]->Size + PagesAvailable) * PAGE_SIZE;
  1074. //
  1075. // Set high part to zero as paging files are limited to 4GB.
  1076. //
  1077. EndOfFileInformation.EndOfFile.HighPart = 0;
  1078. #endif
  1079. //
  1080. // Attempt to extend the file by setting the end-of-file position.
  1081. //
  1082. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  1083. status = IoSetInformation (MmPagingFile[PageFileNumber]->File,
  1084. FileEndOfFileInformation,
  1085. sizeof(FILE_END_OF_FILE_INFORMATION),
  1086. &EndOfFileInformation);
  1087. if (status != STATUS_SUCCESS) {
  1088. KdPrint(("MM MODWRITE: page file extension failed %p %lx\n",PagesAvailable,status));
  1089. if (MinimumExtension != MmPageFileExtension) {
  1090. MinimumExtension = MmPageFileExtension;
  1091. goto retry;
  1092. }
  1093. return 0;
  1094. }
  1095. MiFinishPageFileExtension (PageFileNumber, PagesAvailable);
  1096. return PagesAvailable;
  1097. }
  1098. SIZE_T
  1099. MiExtendPagingFiles (
  1100. IN PMMPAGE_FILE_EXPANSION PageExpand
  1101. )
  1102. /*++
  1103. Routine Description:
  1104. This routine attempts to extend the paging files to provide
  1105. SizeNeeded bytes.
  1106. Note - Page file expansion and page file reduction are synchronized
  1107. because a single thread is responsible for performing the
  1108. operation. Hence, while expansion is occurring, a reduction
  1109. request will be queued to the thread.
  1110. Arguments:
  1111. PageFileNumber - Supplies the page file number to extend.
  1112. MI_EXTEND_ANY_PAGFILE indicates to extend any page file.
  1113. Return Value:
  1114. Returns the size of the extension. Zero if the page file(s) cannot
  1115. be extended.
  1116. --*/
  1117. {
  1118. SIZE_T DesiredQuota;
  1119. ULONG PageFileNumber;
  1120. SIZE_T ExtendedSize;
  1121. SIZE_T SizeNeeded;
  1122. ULONG i;
  1123. SIZE_T CommitLimit;
  1124. SIZE_T CommittedPages;
  1125. DesiredQuota = PageExpand->RequestedExpansionSize;
  1126. PageFileNumber = PageExpand->PageFileNumber;
  1127. ASSERT (PageExpand->ActualExpansion == 0);
  1128. ASSERT (PageFileNumber < MmNumberOfPagingFiles || PageFileNumber == MI_EXTEND_ANY_PAGEFILE);
  1129. if (MmNumberOfPagingFiles == 0) {
  1130. InterlockedExchange ((PLONG)&PageExpand->InProgress, 0);
  1131. return 0;
  1132. }
  1133. if (PageFileNumber < MmNumberOfPagingFiles) {
  1134. i = PageFileNumber;
  1135. ExtendedSize = MmPagingFile[i]->MaximumSize - MmPagingFile[i]->Size;
  1136. if (ExtendedSize < DesiredQuota) {
  1137. InterlockedExchange ((PLONG)&PageExpand->InProgress, 0);
  1138. return 0;
  1139. }
  1140. ExtendedSize = MiAttemptPageFileExtension (i, DesiredQuota, FALSE);
  1141. goto alldone;
  1142. }
  1143. //
  1144. // Snap the globals into locals so calculations will be consistent from
  1145. // step to step. It is ok to snap the globals unsynchronized with respect
  1146. // to each other as even when pagefile expansion occurs, the expansion
  1147. // space is not reserved for the caller - any process could consume
  1148. // the expansion prior to this routine returning.
  1149. //
  1150. CommittedPages = MmTotalCommittedPages;
  1151. CommitLimit = MmTotalCommitLimit;
  1152. SizeNeeded = CommittedPages + DesiredQuota + MmSystemCommitReserve;
  1153. //
  1154. // Check to make sure the request does not wrap.
  1155. //
  1156. if (SizeNeeded < CommittedPages) {
  1157. InterlockedExchange ((PLONG)&PageExpand->InProgress, 0);
  1158. return 0;
  1159. }
  1160. //
  1161. // Check to see if ample space already exists.
  1162. //
  1163. if (SizeNeeded <= CommitLimit) {
  1164. PageExpand->ActualExpansion = 1;
  1165. InterlockedExchange ((PLONG)&PageExpand->InProgress, 0);
  1166. return 1;
  1167. }
  1168. //
  1169. // Calculate the additional pages needed.
  1170. //
  1171. SizeNeeded -= CommitLimit;
  1172. if (SizeNeeded > MmSystemCommitReserve) {
  1173. SizeNeeded -= MmSystemCommitReserve;
  1174. }
  1175. //
  1176. // Make sure ample space exists within the paging files.
  1177. //
  1178. i = 0;
  1179. ExtendedSize = 0;
  1180. do {
  1181. ExtendedSize += MmPagingFile[i]->MaximumSize - MmPagingFile[i]->Size;
  1182. i += 1;
  1183. } while (i < MmNumberOfPagingFiles);
  1184. if (ExtendedSize < SizeNeeded) {
  1185. InterlockedExchange ((PLONG)&PageExpand->InProgress, 0);
  1186. return 0;
  1187. }
  1188. //
  1189. // Attempt to extend only one of the paging files.
  1190. //
  1191. i = 0;
  1192. do {
  1193. ExtendedSize = MiAttemptPageFileExtension (i, SizeNeeded, FALSE);
  1194. if (ExtendedSize != 0) {
  1195. goto alldone;
  1196. }
  1197. i += 1;
  1198. } while (i < MmNumberOfPagingFiles);
  1199. ASSERT (ExtendedSize == 0);
  1200. if (MmNumberOfPagingFiles == 1) {
  1201. //
  1202. // If the attempt didn't succeed for one (not enough disk space free) -
  1203. // don't try to set it to the maximum size.
  1204. //
  1205. InterlockedExchange ((PLONG)&PageExpand->InProgress, 0);
  1206. return 0;
  1207. }
  1208. //
  1209. // Attempt to extend all paging files.
  1210. //
  1211. i = 0;
  1212. do {
  1213. ASSERT (SizeNeeded > ExtendedSize);
  1214. ExtendedSize += MiAttemptPageFileExtension (i,
  1215. SizeNeeded - ExtendedSize,
  1216. TRUE);
  1217. if (ExtendedSize >= SizeNeeded) {
  1218. goto alldone;
  1219. }
  1220. i += 1;
  1221. } while (i < MmNumberOfPagingFiles);
  1222. //
  1223. // Not enough space is available.
  1224. //
  1225. InterlockedExchange ((PLONG)&PageExpand->InProgress, 0);
  1226. return 0;
  1227. alldone:
  1228. ASSERT (ExtendedSize != 0);
  1229. PageExpand->ActualExpansion = ExtendedSize;
  1230. //
  1231. // Increase the systemwide commit limit.
  1232. //
  1233. InterlockedExchangeAddSizeT (&MmTotalCommitLimit, ExtendedSize);
  1234. //
  1235. // Clear the in progress flag - if this is the global cantexpand structure
  1236. // it is possible for it to be immediately reused.
  1237. //
  1238. InterlockedExchange ((PLONG)&PageExpand->InProgress, 0);
  1239. return ExtendedSize;
  1240. }
  1241. VOID
  1242. MiContractPagingFiles (
  1243. VOID
  1244. )
  1245. /*++
  1246. Routine Description:
  1247. This routine checks to see if ample space is no longer committed
  1248. and if so, does enough free space exist in any paging file.
  1249. IFF the answer to both these is affirmative, a reduction in the
  1250. paging file size(s) is attempted.
  1251. Arguments:
  1252. None.
  1253. Return Value:
  1254. None.
  1255. --*/
  1256. {
  1257. ULONG i;
  1258. KIRQL OldIrql;
  1259. PMMPAGE_FILE_EXPANSION PageReduce;
  1260. //
  1261. // This is an unsynchronized check but that's ok. The real check is
  1262. // made when the packet below is processed by the dereference thread.
  1263. //
  1264. if (MmTotalCommittedPages >= ((MmTotalCommitLimit/10)*8)) {
  1265. return;
  1266. }
  1267. if ((MmTotalCommitLimit - MmMinimumPageFileReduction) <=
  1268. MmTotalCommittedPages) {
  1269. return;
  1270. }
  1271. for (i = 0; i < MmNumberOfPagingFiles; i += 1) {
  1272. if (MmPagingFile[i]->Size != MmPagingFile[i]->MinimumSize) {
  1273. if (MmPagingFile[i]->FreeSpace > MmMinimumPageFileReduction) {
  1274. break;
  1275. }
  1276. }
  1277. }
  1278. if (i == MmNumberOfPagingFiles) {
  1279. return;
  1280. }
  1281. PageReduce = ExAllocatePoolWithTag (NonPagedPool,
  1282. sizeof(MMPAGE_FILE_EXPANSION),
  1283. ' mM');
  1284. if (PageReduce == NULL) {
  1285. return;
  1286. }
  1287. PageReduce->Segment = NULL;
  1288. PageReduce->RequestedExpansionSize = MI_CONTRACT_PAGEFILES;
  1289. ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql);
  1290. InsertTailList (&MmDereferenceSegmentHeader.ListHead,
  1291. &PageReduce->DereferenceList);
  1292. ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
  1293. KeReleaseSemaphore (&MmDereferenceSegmentHeader.Semaphore, 0L, 1L, FALSE);
  1294. return;
  1295. }
  1296. VOID
  1297. MiAttemptPageFileReduction (
  1298. VOID
  1299. )
  1300. /*++
  1301. Routine Description:
  1302. This routine attempts to reduce the size of the paging files to
  1303. their minimum levels.
  1304. Note - Page file expansion and page file reduction are synchronized
  1305. because a single thread is responsible for performing the
  1306. operation. Hence, while expansion is occurring, a reduction
  1307. request will be queued to the thread.
  1308. Arguments:
  1309. None.
  1310. Return Value:
  1311. None.
  1312. --*/
  1313. {
  1314. SIZE_T CommitLimit;
  1315. SIZE_T CommittedPages;
  1316. SIZE_T SafetyMargin;
  1317. KIRQL OldIrql;
  1318. ULONG i;
  1319. PFN_NUMBER StartReduction;
  1320. PFN_NUMBER ReductionSize;
  1321. PFN_NUMBER TryBit;
  1322. PFN_NUMBER TryReduction;
  1323. PMMPAGING_FILE PagingFile;
  1324. SIZE_T MaxReduce;
  1325. FILE_ALLOCATION_INFORMATION FileAllocationInfo;
  1326. NTSTATUS status;
  1327. //
  1328. // Snap the globals into locals so calculations will be consistent from
  1329. // step to step. It is ok to snap the globals unsynchronized with respect
  1330. // to each other.
  1331. //
  1332. CommittedPages = MmTotalCommittedPages;
  1333. CommitLimit = MmTotalCommitLimit;
  1334. //
  1335. // Make sure the commit limit is significantly greater than the number
  1336. // of committed pages to avoid thrashing.
  1337. //
  1338. SafetyMargin = 2 * MmMinimumPageFileReduction;
  1339. if (CommittedPages + SafetyMargin >= ((CommitLimit/10)*8)) {
  1340. return;
  1341. }
  1342. MaxReduce = ((CommitLimit/10)*8) - (CommittedPages + SafetyMargin);
  1343. ASSERT ((SSIZE_T)MaxReduce > 0);
  1344. ASSERT ((LONG_PTR)MaxReduce >= 0);
  1345. for (i = 0; i < MmNumberOfPagingFiles; i += 1) {
  1346. if (MaxReduce < MmMinimumPageFileReduction) {
  1347. //
  1348. // Don't reduce any more paging files.
  1349. //
  1350. break;
  1351. }
  1352. PagingFile = MmPagingFile[i];
  1353. if (PagingFile->Size == PagingFile->MinimumSize) {
  1354. continue;
  1355. }
  1356. //
  1357. // This unsynchronized check is ok because a synchronized check is
  1358. // made later.
  1359. //
  1360. if (PagingFile->FreeSpace < MmMinimumPageFileReduction) {
  1361. continue;
  1362. }
  1363. //
  1364. // Lock the PFN database and check to see if ample pages
  1365. // are free at the end of the paging file.
  1366. //
  1367. TryBit = PagingFile->Size - MmMinimumPageFileReduction;
  1368. TryReduction = MmMinimumPageFileReduction;
  1369. if (TryBit <= PagingFile->MinimumSize) {
  1370. TryBit = PagingFile->MinimumSize;
  1371. TryReduction = PagingFile->Size - PagingFile->MinimumSize;
  1372. }
  1373. StartReduction = 0;
  1374. ReductionSize = 0;
  1375. LOCK_PFN (OldIrql);
  1376. do {
  1377. //
  1378. // Try to reduce.
  1379. //
  1380. if ((ReductionSize + TryReduction) > MaxReduce) {
  1381. //
  1382. // The reduction attempt would remove more
  1383. // than MaxReduce pages.
  1384. //
  1385. break;
  1386. }
  1387. if (RtlAreBitsClear (PagingFile->Bitmap,
  1388. (ULONG)TryBit,
  1389. (ULONG)TryReduction)) {
  1390. //
  1391. // Can reduce it by TryReduction, see if it can
  1392. // be made smaller.
  1393. //
  1394. StartReduction = TryBit;
  1395. ReductionSize += TryReduction;
  1396. if (StartReduction == PagingFile->MinimumSize) {
  1397. break;
  1398. }
  1399. TryBit = StartReduction - MmMinimumPageFileReduction;
  1400. if (TryBit <= PagingFile->MinimumSize) {
  1401. TryReduction -= PagingFile->MinimumSize - TryBit;
  1402. TryBit = PagingFile->MinimumSize;
  1403. }
  1404. else {
  1405. TryReduction = MmMinimumPageFileReduction;
  1406. }
  1407. }
  1408. else {
  1409. //
  1410. // Reduction has failed.
  1411. //
  1412. break;
  1413. }
  1414. } while (TRUE);
  1415. //
  1416. // Make sure there are no outstanding writes to
  1417. // pages within the start reduction range.
  1418. //
  1419. if (StartReduction != 0) {
  1420. //
  1421. // There is an outstanding write past where the
  1422. // new end of the paging file should be. This
  1423. // is a very rare condition, so just punt shrinking
  1424. // the file.
  1425. //
  1426. ASSERT (MM_PAGING_FILE_MDLS == 2);
  1427. if ((PagingFile->Entry[0]->LastPageToWrite > StartReduction) ||
  1428. (PagingFile->Entry[1]->LastPageToWrite > StartReduction)) {
  1429. StartReduction = 0;
  1430. }
  1431. }
  1432. //
  1433. // If there are no pages to remove, march on to the next pagefile.
  1434. //
  1435. if (StartReduction == 0) {
  1436. UNLOCK_PFN (OldIrql);
  1437. continue;
  1438. }
  1439. //
  1440. // Reduce the paging file's size and free space.
  1441. //
  1442. ASSERT (ReductionSize == (PagingFile->Size - StartReduction));
  1443. PagingFile->Size = StartReduction;
  1444. PagingFile->FreeSpace -= ReductionSize;
  1445. RtlSetBits (PagingFile->Bitmap,
  1446. (ULONG)StartReduction,
  1447. (ULONG)ReductionSize );
  1448. //
  1449. // Release the PFN lock now that the size info
  1450. // has been updated.
  1451. //
  1452. UNLOCK_PFN (OldIrql);
  1453. MaxReduce -= ReductionSize;
  1454. ASSERT ((LONG)MaxReduce >= 0);
  1455. //
  1456. // Change the commit limit to reflect the returned page file space.
  1457. // First try to charge the reduction amount to confirm that the
  1458. // reduction is still a sensible thing to do.
  1459. //
  1460. if (MiChargeTemporaryCommitmentForReduction (ReductionSize + SafetyMargin) == FALSE) {
  1461. LOCK_PFN (OldIrql);
  1462. PagingFile->Size = StartReduction + ReductionSize;
  1463. PagingFile->FreeSpace += ReductionSize;
  1464. RtlClearBits (PagingFile->Bitmap,
  1465. (ULONG)StartReduction,
  1466. (ULONG)ReductionSize );
  1467. UNLOCK_PFN (OldIrql);
  1468. ASSERT ((LONG)(MaxReduce + ReductionSize) >= 0);
  1469. break;
  1470. }
  1471. //
  1472. // Reduce the systemwide commit limit - note this is carefully done
  1473. // *PRIOR* to returning this commitment so no one else (including a DPC
  1474. // in this very thread) can consume past the limit.
  1475. //
  1476. InterlockedExchangeAddSizeT (&MmTotalCommitLimit, 0 - ReductionSize);
  1477. //
  1478. // Now that the systemwide commit limit has been lowered, the amount
  1479. // we have removed can be safely returned.
  1480. //
  1481. MiReturnCommitment (ReductionSize + SafetyMargin);
  1482. #if defined (_WIN64) || defined (_X86PAE_)
  1483. FileAllocationInfo.AllocationSize.QuadPart =
  1484. ((ULONG64)StartReduction << PAGE_SHIFT);
  1485. #else
  1486. FileAllocationInfo.AllocationSize.LowPart = StartReduction * PAGE_SIZE;
  1487. //
  1488. // Set high part to zero, paging files are limited to 4gb.
  1489. //
  1490. FileAllocationInfo.AllocationSize.HighPart = 0;
  1491. #endif
  1492. //
  1493. // Reduce the allocated size of the paging file
  1494. // thereby actually freeing the space and
  1495. // setting a new end of file.
  1496. //
  1497. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  1498. status = IoSetInformation (PagingFile->File,
  1499. FileAllocationInformation,
  1500. sizeof(FILE_ALLOCATION_INFORMATION),
  1501. &FileAllocationInfo);
  1502. #if DBG
  1503. //
  1504. // Ignore errors on truncating the paging file
  1505. // as we can always have less space in the bitmap
  1506. // than the pagefile holds.
  1507. //
  1508. if (status != STATUS_SUCCESS) {
  1509. DbgPrint ("MM: pagefile truncate status %lx\n", status);
  1510. }
  1511. #endif
  1512. }
  1513. return;
  1514. }
  1515. VOID
  1516. MiLdwPopupWorker (
  1517. IN PVOID Context
  1518. )
  1519. /*++
  1520. Routine Description:
  1521. This routine is the worker routine to send a lost delayed write data popup
  1522. for a given control area/file.
  1523. Arguments:
  1524. Context - Supplies a pointer to the MM_LDW_WORK_CONTEXT for the failed I/O.
  1525. Return Value:
  1526. None.
  1527. Environment:
  1528. Kernel mode, PASSIVE_LEVEL.
  1529. --*/
  1530. {
  1531. NTSTATUS Status;
  1532. PFILE_OBJECT FileObject;
  1533. PMM_LDW_WORK_CONTEXT LdwContext;
  1534. POBJECT_NAME_INFORMATION FileNameInfo;
  1535. PAGED_CODE();
  1536. LdwContext = (PMM_LDW_WORK_CONTEXT) Context;
  1537. FileObject = LdwContext->FileObject;
  1538. FileNameInfo = NULL;
  1539. ExFreePool (LdwContext);
  1540. //
  1541. // Throw the popup with the user-friendly form, if possible.
  1542. // If everything fails, the user probably couldn't have figured
  1543. // out what failed either.
  1544. //
  1545. Status = IoQueryFileDosDeviceName (FileObject, &FileNameInfo);
  1546. if (Status == STATUS_SUCCESS) {
  1547. IoRaiseInformationalHardError (STATUS_LOST_WRITEBEHIND_DATA,
  1548. &FileNameInfo->Name,
  1549. NULL);
  1550. }
  1551. else {
  1552. if ((FileObject->FileName.Length) &&
  1553. (FileObject->FileName.MaximumLength) &&
  1554. (FileObject->FileName.Buffer)) {
  1555. IoRaiseInformationalHardError (STATUS_LOST_WRITEBEHIND_DATA,
  1556. &FileObject->FileName,
  1557. NULL);
  1558. }
  1559. }
  1560. //
  1561. // Now drop the reference to the file object and clean up.
  1562. //
  1563. ObDereferenceObject (FileObject);
  1564. if (FileNameInfo != NULL) {
  1565. ExFreePool(FileNameInfo);
  1566. }
  1567. }
  1568. VOID
  1569. MiWriteComplete (
  1570. IN PVOID Context,
  1571. IN PIO_STATUS_BLOCK IoStatus,
  1572. IN ULONG Reserved
  1573. )
  1574. /*++
  1575. Routine Description:
  1576. This routine is the APC write completion procedure. It is invoked
  1577. at APC_LEVEL when a page write operation is completed.
  1578. Arguments:
  1579. Context - Supplies a pointer to the MOD_WRITER_MDL_ENTRY which was
  1580. used for this I/O.
  1581. IoStatus - Supplies a pointer to the IO_STATUS_BLOCK which was used
  1582. for this I/O.
  1583. Return Value:
  1584. None.
  1585. Environment:
  1586. Kernel mode, APC_LEVEL.
  1587. --*/
  1588. {
  1589. PMMMOD_WRITER_MDL_ENTRY WriterEntry;
  1590. PMMMOD_WRITER_MDL_ENTRY NextWriterEntry;
  1591. PPFN_NUMBER Page;
  1592. PMMPFN Pfn1;
  1593. KIRQL OldIrql;
  1594. LONG ByteCount;
  1595. NTSTATUS status;
  1596. PCONTROL_AREA ControlArea;
  1597. ULONG FailAllIo;
  1598. PFILE_OBJECT FileObject;
  1599. PERESOURCE FileResource;
  1600. UNREFERENCED_PARAMETER (Reserved);
  1601. FailAllIo = FALSE;
  1602. #if DBG
  1603. if (MmDebug & MM_DBG_MOD_WRITE) {
  1604. DbgPrint("MM MODWRITE: modified page write completed\n");
  1605. }
  1606. #endif
  1607. //
  1608. // A page write has completed, at this time the pages are not
  1609. // on any lists, write-in-progress is set in the PFN database,
  1610. // and the reference count was incremented.
  1611. //
  1612. WriterEntry = (PMMMOD_WRITER_MDL_ENTRY)Context;
  1613. ByteCount = (LONG)WriterEntry->Mdl.ByteCount;
  1614. Page = &WriterEntry->Page[0];
  1615. if (WriterEntry->Mdl.MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
  1616. MmUnmapLockedPages (WriterEntry->Mdl.MappedSystemVa,
  1617. &WriterEntry->Mdl);
  1618. }
  1619. //
  1620. // Get the PFN lock so the PFN database can be manipulated.
  1621. //
  1622. status = IoStatus->Status;
  1623. ControlArea = WriterEntry->ControlArea;
  1624. LOCK_PFN (OldIrql);
  1625. //
  1626. // Indicate that the write is complete.
  1627. //
  1628. WriterEntry->LastPageToWrite = 0;
  1629. while (ByteCount > 0) {
  1630. Pfn1 = MI_PFN_ELEMENT (*Page);
  1631. ASSERT (Pfn1->u3.e1.WriteInProgress == 1);
  1632. #if DBG
  1633. #if !defined (_WIN64)
  1634. if (Pfn1->OriginalPte.u.Soft.Prototype == 0) {
  1635. ULONG Offset;
  1636. Offset = GET_PAGING_FILE_OFFSET(Pfn1->OriginalPte);
  1637. if ((Offset < 8192) &&
  1638. (GET_PAGING_FILE_NUMBER(Pfn1->OriginalPte) == 0)) {
  1639. ASSERT ((MmPagingFileDebug[Offset] & 1) != 0);
  1640. if (!MI_IS_PFN_DELETED(Pfn1)) {
  1641. if ((GET_PAGING_FILE_NUMBER (Pfn1->OriginalPte)) == 0) {
  1642. if ((MmPagingFileDebug[Offset] & ~0x1f) !=
  1643. ((ULONG_PTR)Pfn1->PteAddress << 3)) {
  1644. if (Pfn1->PteAddress != MiGetPteAddress(PDE_BASE)) {
  1645. //
  1646. // Make sure this isn't a PTE that was forked
  1647. // during the I/O.
  1648. //
  1649. if ((Pfn1->PteAddress < (PMMPTE)PTE_TOP) ||
  1650. ((Pfn1->OriginalPte.u.Soft.Protection &
  1651. MM_COPY_ON_WRITE_MASK) ==
  1652. MM_PROTECTION_WRITE_MASK)) {
  1653. DbgPrint("MMWRITE: Mismatch Pfn1 %p Offset %lx info %p\n",
  1654. Pfn1,
  1655. Offset,
  1656. MmPagingFileDebug[Offset]);
  1657. DbgBreakPoint();
  1658. }
  1659. else {
  1660. MmPagingFileDebug[Offset] &= 0x1f;
  1661. MmPagingFileDebug[Offset] |=
  1662. ((ULONG_PTR)Pfn1->PteAddress << 3);
  1663. }
  1664. }
  1665. }
  1666. }
  1667. }
  1668. }
  1669. }
  1670. #endif
  1671. #endif //DBG
  1672. Pfn1->u3.e1.WriteInProgress = 0;
  1673. if (NT_ERROR(status)) {
  1674. //
  1675. // If the file object is over the network, assume that this
  1676. // I/O operation can never complete and mark the pages as
  1677. // clean and indicate in the control area all I/O should fail.
  1678. // Note that the modified bit in the PFN database is not set.
  1679. //
  1680. // If the user changes the protection on the volume containing the
  1681. // file to readonly, this puts us in a problematic situation. We
  1682. // cannot just keep retrying the writes because if there are no
  1683. // other pages that can be written, not writing these can cause the
  1684. // system to run out of pages, ie: bugcheck 4D. So throw away
  1685. // these pages just as if they were on a network that has
  1686. // disappeared.
  1687. //
  1688. if (((status != STATUS_FILE_LOCK_CONFLICT) &&
  1689. (ControlArea != NULL) &&
  1690. (ControlArea->u.Flags.Networked == 1))
  1691. ||
  1692. (status == STATUS_FILE_INVALID)
  1693. ||
  1694. ((status == STATUS_MEDIA_WRITE_PROTECTED) &&
  1695. (ControlArea != NULL))) {
  1696. if (ControlArea->u.Flags.FailAllIo == 0) {
  1697. ControlArea->u.Flags.FailAllIo = 1;
  1698. FailAllIo = TRUE;
  1699. KdPrint(("MM MODWRITE: failing all io, controlarea %p status %lx\n",
  1700. ControlArea, status));
  1701. }
  1702. }
  1703. else {
  1704. //
  1705. // The modified write operation failed, SET the modified bit
  1706. // for each page which was written and free the page file
  1707. // space.
  1708. //
  1709. #if DBG
  1710. if ((status != STATUS_FILE_LOCK_CONFLICT) &&
  1711. ((MmDebug & MM_DBG_PRINTS_MODWRITES) == 0)) {
  1712. KdPrint(("MM MODWRITE: modified page write iosb failed - status 0x%lx\n",
  1713. status));
  1714. }
  1715. #endif
  1716. MI_SET_MODIFIED (Pfn1, 1, 0x9);
  1717. }
  1718. }
  1719. if ((Pfn1->u3.e1.Modified == 1) &&
  1720. (Pfn1->OriginalPte.u.Soft.Prototype == 0)) {
  1721. //
  1722. // This page was modified since the write was done,
  1723. // release the page file space.
  1724. //
  1725. MiReleasePageFileSpace (Pfn1->OriginalPte);
  1726. Pfn1->OriginalPte.u.Soft.PageFileHigh = 0;
  1727. }
  1728. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF (Pfn1, 15);
  1729. #if DBG
  1730. *Page = 0xF0FFFFFF;
  1731. #endif
  1732. Page += 1;
  1733. ByteCount -= (LONG)PAGE_SIZE;
  1734. }
  1735. //
  1736. // Check to which list to insert this entry into depending on
  1737. // the amount of free space left in the paging file.
  1738. //
  1739. FileObject = WriterEntry->File;
  1740. FileResource = WriterEntry->FileResource;
  1741. if ((WriterEntry->PagingFile != NULL) &&
  1742. (WriterEntry->PagingFile->FreeSpace < MM_USABLE_PAGES_FREE)) {
  1743. InsertTailList (&MmFreePagingSpaceLow, &WriterEntry->Links);
  1744. WriterEntry->CurrentList = &MmFreePagingSpaceLow;
  1745. MmNumberOfActiveMdlEntries -= 1;
  1746. if (MmNumberOfActiveMdlEntries == 0) {
  1747. //
  1748. // If we leave this entry on the list, there will be
  1749. // no more paging. Locate all entries which are non
  1750. // zero and pull them from the list.
  1751. //
  1752. WriterEntry = (PMMMOD_WRITER_MDL_ENTRY)MmFreePagingSpaceLow.Flink;
  1753. while ((PLIST_ENTRY)WriterEntry != &MmFreePagingSpaceLow) {
  1754. NextWriterEntry =
  1755. (PMMMOD_WRITER_MDL_ENTRY)WriterEntry->Links.Flink;
  1756. if (WriterEntry->PagingFile->FreeSpace != 0) {
  1757. RemoveEntryList (&WriterEntry->Links);
  1758. //
  1759. // Insert this into the active list.
  1760. //
  1761. if (IsListEmpty (&WriterEntry->PagingListHead->ListHead)) {
  1762. KeSetEvent (&WriterEntry->PagingListHead->Event,
  1763. 0,
  1764. FALSE);
  1765. }
  1766. InsertTailList (&WriterEntry->PagingListHead->ListHead,
  1767. &WriterEntry->Links);
  1768. WriterEntry->CurrentList = &MmPagingFileHeader.ListHead;
  1769. MmNumberOfActiveMdlEntries += 1;
  1770. }
  1771. WriterEntry = NextWriterEntry;
  1772. }
  1773. }
  1774. }
  1775. else {
  1776. #if DBG
  1777. if (WriterEntry->PagingFile == NULL) {
  1778. MmNumberOfMappedMdlsInUse -= 1;
  1779. }
  1780. #endif
  1781. //
  1782. // Ample space exists, put this on the active list.
  1783. //
  1784. if (IsListEmpty (&WriterEntry->PagingListHead->ListHead)) {
  1785. KeSetEvent (&WriterEntry->PagingListHead->Event, 0, FALSE);
  1786. }
  1787. InsertTailList (&WriterEntry->PagingListHead->ListHead,
  1788. &WriterEntry->Links);
  1789. }
  1790. ASSERT (((ULONG_PTR)WriterEntry->Links.Flink & 1) == 0);
  1791. UNLOCK_PFN (OldIrql);
  1792. if (FileResource != NULL) {
  1793. FsRtlReleaseFileForModWrite (FileObject, FileResource);
  1794. }
  1795. if (FailAllIo) {
  1796. PMM_LDW_WORK_CONTEXT LdwContext;
  1797. //
  1798. // Reference our fileobject and queue the popup.
  1799. // The DOS name translation must occur at PASSIVE_LEVEL - we're at APC.
  1800. //
  1801. LdwContext = ExAllocatePoolWithTag (NonPagedPool,
  1802. sizeof(MM_LDW_WORK_CONTEXT),
  1803. 'pdmM');
  1804. if (LdwContext != NULL) {
  1805. LdwContext->FileObject = ControlArea->FilePointer;
  1806. ObReferenceObject (LdwContext->FileObject);
  1807. ExInitializeWorkItem (&LdwContext->WorkItem,
  1808. MiLdwPopupWorker,
  1809. (PVOID)LdwContext);
  1810. ExQueueWorkItem (&LdwContext->WorkItem, DelayedWorkQueue);
  1811. }
  1812. }
  1813. if (ControlArea != NULL) {
  1814. LOCK_PFN (OldIrql);
  1815. //
  1816. // A write to a mapped file just completed, check to see if
  1817. // there are any waiters on the completion of this i/o.
  1818. //
  1819. ControlArea->ModifiedWriteCount -= 1;
  1820. ASSERT ((SHORT)ControlArea->ModifiedWriteCount >= 0);
  1821. if (ControlArea->u.Flags.SetMappedFileIoComplete != 0) {
  1822. KePulseEvent (&MmMappedFileIoComplete,
  1823. 0,
  1824. FALSE);
  1825. }
  1826. if (MiDrainingMappedWrites == TRUE) {
  1827. if (MmModifiedPageListHead.Flink != MM_EMPTY_LIST) {
  1828. MiTimerPending = TRUE;
  1829. KeSetEvent (&MiMappedPagesTooOldEvent, 0, FALSE);
  1830. }
  1831. else {
  1832. MiDrainingMappedWrites = FALSE;
  1833. }
  1834. }
  1835. ControlArea->NumberOfPfnReferences -= 1;
  1836. if (ControlArea->NumberOfPfnReferences == 0) {
  1837. //
  1838. // This routine return with the PFN lock released!.
  1839. //
  1840. MiCheckControlArea (ControlArea, NULL, OldIrql);
  1841. }
  1842. else {
  1843. UNLOCK_PFN (OldIrql);
  1844. }
  1845. }
  1846. if (NT_ERROR(status)) {
  1847. //
  1848. // Wait for a short time so other processing can continue.
  1849. //
  1850. KeDelayExecutionThread (KernelMode,
  1851. FALSE,
  1852. (PLARGE_INTEGER)&Mm30Milliseconds);
  1853. if (MmIsRetryIoStatus(status)) {
  1854. //
  1855. // Low resource scenarios are a chicken and egg problem. The
  1856. // mapped and modified writers must make forward progress to
  1857. // alleviate low memory situations. If these threads are
  1858. // unable to write data due to resource problems in the driver
  1859. // stack then temporarily fall back to single page I/Os as
  1860. // the stack guarantees forward progress with those. This
  1861. // causes the low memory situation to persist slightly longer
  1862. // but ensures that it won't become terminal either.
  1863. //
  1864. LOCK_PFN (OldIrql);
  1865. MiClusterWritesDisabled = MI_SLOW_CLUSTER_WRITES;
  1866. UNLOCK_PFN (OldIrql);
  1867. }
  1868. }
  1869. else {
  1870. //
  1871. // Check first without lock synchronization so the common case is
  1872. // not slowed.
  1873. //
  1874. if (MiClusterWritesDisabled != 0) {
  1875. LOCK_PFN (OldIrql);
  1876. //
  1877. // Recheck now that the lock is held.
  1878. //
  1879. if (MiClusterWritesDisabled != 0) {
  1880. ASSERT (MiClusterWritesDisabled <= MI_SLOW_CLUSTER_WRITES);
  1881. MiClusterWritesDisabled -= 1;
  1882. }
  1883. UNLOCK_PFN (OldIrql);
  1884. }
  1885. }
  1886. return;
  1887. }
  1888. LOGICAL
  1889. MiCancelWriteOfMappedPfn (
  1890. IN PFN_NUMBER PageToStop
  1891. )
  1892. /*++
  1893. Routine Description:
  1894. This routine attempts to stop a pending mapped page writer write for the
  1895. specified PFN. Note that if the write can be stopped, any other pages
  1896. that may be clustered with the write are also stopped.
  1897. Arguments:
  1898. PageToStop - Supplies the frame number that the caller wants to stop.
  1899. Return Value:
  1900. TRUE if the write was stopped, FALSE if not.
  1901. Environment:
  1902. Kernel mode, PFN lock held. The PFN lock is released and reacquired if
  1903. the write was stopped.
  1904. N.B. No other locks may be held as IRQL is lowered to APC_LEVEL here.
  1905. --*/
  1906. {
  1907. ULONG i;
  1908. ULONG PageCount;
  1909. KIRQL OldIrql;
  1910. PPFN_NUMBER Page;
  1911. PLIST_ENTRY NextEntry;
  1912. PMDL MemoryDescriptorList;
  1913. PMMMOD_WRITER_MDL_ENTRY ModWriterEntry;
  1914. //
  1915. // Walk the MmMappedPageWriterList looking for an MDL which contains
  1916. // the argument page. If found, remove it and cancel the write.
  1917. //
  1918. NextEntry = MmMappedPageWriterList.Flink;
  1919. while (NextEntry != &MmMappedPageWriterList) {
  1920. ModWriterEntry = CONTAINING_RECORD(NextEntry,
  1921. MMMOD_WRITER_MDL_ENTRY,
  1922. Links);
  1923. MemoryDescriptorList = &ModWriterEntry->Mdl;
  1924. PageCount = (MemoryDescriptorList->ByteCount >> PAGE_SHIFT);
  1925. Page = (PPFN_NUMBER)(MemoryDescriptorList + 1);
  1926. for (i = 0; i < PageCount; i += 1) {
  1927. if (*Page == PageToStop) {
  1928. RemoveEntryList (NextEntry);
  1929. goto CancelWrite;
  1930. }
  1931. Page += 1;
  1932. }
  1933. NextEntry = NextEntry->Flink;
  1934. }
  1935. return FALSE;
  1936. CancelWrite:
  1937. UNLOCK_PFN (APC_LEVEL);
  1938. //
  1939. // File lock conflict to indicate an error has occurred,
  1940. // but that future I/Os should be allowed. Keep APCs disabled and
  1941. // call the write completion routine.
  1942. //
  1943. ModWriterEntry->u.IoStatus.Status = STATUS_FILE_LOCK_CONFLICT;
  1944. ModWriterEntry->u.IoStatus.Information = 0;
  1945. MiWriteComplete ((PVOID)ModWriterEntry,
  1946. &ModWriterEntry->u.IoStatus,
  1947. 0 );
  1948. LOCK_PFN (OldIrql);
  1949. return TRUE;
  1950. }
  1951. VOID
  1952. MiModifiedPageWriter (
  1953. IN PVOID StartContext
  1954. )
  1955. /*++
  1956. Routine Description:
  1957. Implements the NT modified page writer thread. When the modified
  1958. page threshold is reached, or memory becomes overcommitted the
  1959. modified page writer event is set, and this thread becomes active.
  1960. Arguments:
  1961. StartContext - not used.
  1962. Return Value:
  1963. None.
  1964. Environment:
  1965. Kernel mode.
  1966. --*/
  1967. {
  1968. ULONG i;
  1969. HANDLE ThreadHandle;
  1970. OBJECT_ATTRIBUTES ObjectAttributes;
  1971. PMMMOD_WRITER_MDL_ENTRY ModWriteEntry;
  1972. PAGED_CODE();
  1973. UNREFERENCED_PARAMETER (StartContext);
  1974. //
  1975. // Initialize listheads as empty.
  1976. //
  1977. MmSystemShutdown = 0;
  1978. KeInitializeEvent (&MmPagingFileHeader.Event, NotificationEvent, FALSE);
  1979. KeInitializeEvent (&MmMappedFileHeader.Event, NotificationEvent, FALSE);
  1980. InitializeListHead(&MmPagingFileHeader.ListHead);
  1981. InitializeListHead(&MmMappedFileHeader.ListHead);
  1982. InitializeListHead(&MmFreePagingSpaceLow);
  1983. //
  1984. // Allocate enough MDLs such that 2% of system memory can be pending
  1985. // at any point in time in mapped writes. Even smaller memory systems
  1986. // get 20 MDLs as the minimum.
  1987. //
  1988. MmNumberOfMappedMdls = MmNumberOfPhysicalPages / (32 * 1024);
  1989. if (MmNumberOfMappedMdls < 20) {
  1990. MmNumberOfMappedMdls = 20;
  1991. }
  1992. for (i = 0; i < MmNumberOfMappedMdls; i += 1) {
  1993. ModWriteEntry = ExAllocatePoolWithTag (NonPagedPool,
  1994. sizeof(MMMOD_WRITER_MDL_ENTRY) +
  1995. MmModifiedWriteClusterSize *
  1996. sizeof(PFN_NUMBER),
  1997. 'eWmM');
  1998. if (ModWriteEntry == NULL) {
  1999. break;
  2000. }
  2001. ModWriteEntry->PagingFile = NULL;
  2002. ModWriteEntry->PagingListHead = &MmMappedFileHeader;
  2003. InsertTailList (&MmMappedFileHeader.ListHead, &ModWriteEntry->Links);
  2004. }
  2005. MmNumberOfMappedMdls = i;
  2006. //
  2007. // Make this a real time thread.
  2008. //
  2009. KeSetPriorityThread (&PsGetCurrentThread()->Tcb, LOW_REALTIME_PRIORITY + 1);
  2010. //
  2011. // Start a secondary thread for writing mapped file pages. This
  2012. // is required as the writing of mapped file pages could cause
  2013. // page faults resulting in requests for free pages. But there
  2014. // could be no free pages - hence a dead lock. Rather than deadlock
  2015. // the whole system waiting on the modified page writer, creating
  2016. // a secondary thread allows that thread to block without affecting
  2017. // on going page file writes.
  2018. //
  2019. KeInitializeEvent (&MmMappedPageWriterEvent, NotificationEvent, FALSE);
  2020. InitializeListHead(&MmMappedPageWriterList);
  2021. InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL, NULL );
  2022. PsCreateSystemThread (&ThreadHandle,
  2023. THREAD_ALL_ACCESS,
  2024. &ObjectAttributes,
  2025. 0L,
  2026. NULL,
  2027. MiMappedPageWriter,
  2028. NULL );
  2029. ZwClose (ThreadHandle);
  2030. MiModifiedPageWriterWorker();
  2031. //
  2032. // Shutdown in progress, wait forever.
  2033. //
  2034. {
  2035. LARGE_INTEGER Forever;
  2036. //
  2037. // System has shutdown, go into LONG wait.
  2038. //
  2039. Forever.LowPart = 0;
  2040. Forever.HighPart = 0xF000000;
  2041. KeDelayExecutionThread (KernelMode, FALSE, &Forever);
  2042. }
  2043. return;
  2044. }
  2045. VOID
  2046. MiModifiedPageWriterTimerDispatch (
  2047. IN PKDPC Dpc,
  2048. IN PVOID DeferredContext,
  2049. IN PVOID SystemArgument1,
  2050. IN PVOID SystemArgument2
  2051. )
  2052. /*++
  2053. Routine Description:
  2054. This routine is executed whenever modified mapped pages are waiting to
  2055. be written. Its job is to signal the Modified Page Writer to write
  2056. these out.
  2057. Arguments:
  2058. Dpc - Supplies a pointer to a control object of type DPC.
  2059. DeferredContext - Optional deferred context; not used.
  2060. SystemArgument1 - Optional argument 1; not used.
  2061. SystemArgument2 - Optional argument 2; not used.
  2062. Return Value:
  2063. None.
  2064. --*/
  2065. {
  2066. UNREFERENCED_PARAMETER (Dpc);
  2067. UNREFERENCED_PARAMETER (DeferredContext);
  2068. UNREFERENCED_PARAMETER (SystemArgument1);
  2069. UNREFERENCED_PARAMETER (SystemArgument2);
  2070. LOCK_PFN_AT_DPC ();
  2071. MiTimerPending = TRUE;
  2072. KeSetEvent (&MiMappedPagesTooOldEvent, 0, FALSE);
  2073. UNLOCK_PFN_FROM_DPC ();
  2074. }
  2075. VOID
  2076. MiModifiedPageWriterWorker (
  2077. VOID
  2078. )
  2079. /*++
  2080. Routine Description:
  2081. Implements the NT modified page writer thread. When the modified
  2082. page threshold is reached, or memory becomes overcommitted the
  2083. modified page writer event is set, and this thread becomes active.
  2084. Arguments:
  2085. None.
  2086. Return Value:
  2087. None.
  2088. Environment:
  2089. Kernel mode.
  2090. --*/
  2091. {
  2092. ULONG i;
  2093. PMMPFN Pfn1;
  2094. PFN_NUMBER PageFrameIndex;
  2095. PFN_NUMBER PagesWritten;
  2096. KIRQL OldIrql;
  2097. ULONG NextColor;
  2098. static KWAIT_BLOCK WaitBlockArray[ModifiedWriterMaximumObject];
  2099. PVOID WaitObjects[ModifiedWriterMaximumObject];
  2100. NTSTATUS WakeupStatus;
  2101. PsGetCurrentThread()->MemoryMaker = 1;
  2102. //
  2103. // Wait for the modified page writer event or the mapped pages event.
  2104. //
  2105. WaitObjects[NormalCase] = (PVOID)&MmModifiedPageWriterEvent;
  2106. WaitObjects[MappedPagesNeedWriting] = (PVOID)&MiMappedPagesTooOldEvent;
  2107. for (;;) {
  2108. WakeupStatus = KeWaitForMultipleObjects(ModifiedWriterMaximumObject,
  2109. &WaitObjects[0],
  2110. WaitAny,
  2111. WrFreePage,
  2112. KernelMode,
  2113. FALSE,
  2114. NULL,
  2115. &WaitBlockArray[0]);
  2116. //
  2117. // Switch on the wait status.
  2118. //
  2119. switch (WakeupStatus) {
  2120. case NormalCase:
  2121. break;
  2122. case MappedPagesNeedWriting:
  2123. //
  2124. // Our mapped pages DPC went off, only deal with those pages.
  2125. // Write all the mapped pages (ONLY), then clear the flag
  2126. // and come back to the top.
  2127. //
  2128. break;
  2129. default:
  2130. break;
  2131. }
  2132. //
  2133. // Indicate that the hint values have not been reset in
  2134. // the paging files.
  2135. //
  2136. if (MmNumberOfPagingFiles != 0) {
  2137. i = 0;
  2138. do {
  2139. MmPagingFile[i]->HintSetToZero = FALSE;
  2140. i += 1;
  2141. } while (i < MmNumberOfPagingFiles);
  2142. }
  2143. NextColor = 0;
  2144. PagesWritten = 0;
  2145. LOCK_PFN (OldIrql);
  2146. for (;;) {
  2147. //
  2148. // Modified page writer was signalled.
  2149. //
  2150. if (MmModifiedPageListHead.Total == 0) {
  2151. //
  2152. // No more pages, clear the event(s) and wait again...
  2153. // Note we can clear both events regardless of why we woke up
  2154. // since no modified pages of any type exist.
  2155. //
  2156. if (MiTimerPending == TRUE) {
  2157. MiTimerPending = FALSE;
  2158. KeClearEvent (&MiMappedPagesTooOldEvent);
  2159. }
  2160. UNLOCK_PFN (OldIrql);
  2161. KeClearEvent (&MmModifiedPageWriterEvent);
  2162. break;
  2163. }
  2164. //
  2165. // If we didn't wake up explicitly to deal with mapped pages,
  2166. // then determine which type of pages are the most popular:
  2167. // page file backed pages, or mapped file backed pages.
  2168. //
  2169. if (WakeupStatus == MappedPagesNeedWriting) {
  2170. PageFrameIndex = MmModifiedPageListHead.Flink;
  2171. if (PageFrameIndex == MM_EMPTY_LIST) {
  2172. //
  2173. // No more modified mapped pages (there may still be
  2174. // modified pagefile-destined pages), so clear only the
  2175. // mapped pages event and check for directions at the top
  2176. // again.
  2177. //
  2178. MiTimerPending = FALSE;
  2179. KeClearEvent (&MiMappedPagesTooOldEvent);
  2180. UNLOCK_PFN (OldIrql);
  2181. break;
  2182. }
  2183. MiDrainingMappedWrites = TRUE;
  2184. }
  2185. else if (MmTotalPagesForPagingFile >=
  2186. (MmModifiedPageListHead.Total - MmTotalPagesForPagingFile)) {
  2187. //
  2188. // More pages are destined for the paging file.
  2189. //
  2190. MI_GET_MODIFIED_PAGE_ANY_COLOR (PageFrameIndex, NextColor);
  2191. }
  2192. else {
  2193. //
  2194. // More pages are destined for mapped files.
  2195. //
  2196. PageFrameIndex = MmModifiedPageListHead.Flink;
  2197. }
  2198. //
  2199. // Check to see what type of page (section file backed or page
  2200. // file backed) and write out that page and more if possible.
  2201. //
  2202. //
  2203. // Check to see if this page is destined for a paging file or
  2204. // a mapped file.
  2205. //
  2206. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2207. if (Pfn1->OriginalPte.u.Soft.Prototype == 1) {
  2208. if (IsListEmpty (&MmMappedFileHeader.ListHead)) {
  2209. //
  2210. // Make sure page is destined for paging file as there
  2211. // are no MDLs for mapped writes free.
  2212. //
  2213. if (WakeupStatus != MappedPagesNeedWriting) {
  2214. MI_GET_MODIFIED_PAGE_ANY_COLOR (PageFrameIndex, NextColor);
  2215. //
  2216. // No pages are destined for the paging file, get the
  2217. // first page destined for a mapped file.
  2218. //
  2219. if (PageFrameIndex == MM_EMPTY_LIST) {
  2220. //
  2221. // Select the first page from the list anyway.
  2222. //
  2223. PageFrameIndex = MmModifiedPageListHead.Flink;
  2224. }
  2225. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2226. }
  2227. }
  2228. }
  2229. else if ((IsListEmpty(&MmPagingFileHeader.ListHead)) ||
  2230. (MiFirstPageFileCreatedAndReady == FALSE)) {
  2231. //
  2232. // Try for a dirty section-backed page as no paging file MDLs
  2233. // are available.
  2234. //
  2235. if (MmModifiedPageListHead.Flink != MM_EMPTY_LIST) {
  2236. ASSERT (MmTotalPagesForPagingFile != MmModifiedPageListHead.Total);
  2237. PageFrameIndex = MmModifiedPageListHead.Flink;
  2238. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2239. }
  2240. else {
  2241. ASSERT (MmTotalPagesForPagingFile == MmModifiedPageListHead.Total);
  2242. if ((MiFirstPageFileCreatedAndReady == FALSE) &&
  2243. (MmNumberOfPagingFiles != 0)) {
  2244. //
  2245. // The first paging has been created but the reservation
  2246. // checking for crashdumps has not finished yet. Delay
  2247. // a bit as this will finish shortly and then restart.
  2248. //
  2249. UNLOCK_PFN (OldIrql);
  2250. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  2251. LOCK_PFN (OldIrql);
  2252. continue;
  2253. }
  2254. }
  2255. }
  2256. if (Pfn1->OriginalPte.u.Soft.Prototype == 1) {
  2257. if (IsListEmpty(&MmMappedFileHeader.ListHead)) {
  2258. if (WakeupStatus == MappedPagesNeedWriting) {
  2259. //
  2260. // Since we woke up only to take care of mapped pages,
  2261. // don't wait for an MDL below because drivers may take
  2262. // an inordinate amount of time processing the
  2263. // outstanding ones. We might have to wait too long,
  2264. // resulting in the system running out of pages.
  2265. //
  2266. if (MiTimerPending == TRUE) {
  2267. //
  2268. // This should be normal case - the reason we must
  2269. // first check timer pending above is for the rare
  2270. // case - when this thread first ran for normal
  2271. // modified page processing and took
  2272. // care of all the pages including the mapped ones.
  2273. // Then this thread woke up again for the mapped
  2274. // reason and here we are.
  2275. //
  2276. MiTimerPending = FALSE;
  2277. KeClearEvent (&MiMappedPagesTooOldEvent);
  2278. }
  2279. MiTimerPending = TRUE;
  2280. (VOID) KeSetTimerEx( &MiModifiedPageWriterTimer, MiModifiedPageLife, 0, &MiModifiedPageWriterTimerDpc );
  2281. UNLOCK_PFN (OldIrql);
  2282. break;
  2283. }
  2284. //
  2285. // Reset the event indicating no mapped files in
  2286. // the list, drop the PFN lock and wait for an
  2287. // I/O operation to complete with a one second
  2288. // timeout.
  2289. //
  2290. KeClearEvent (&MmMappedFileHeader.Event);
  2291. UNLOCK_PFN (OldIrql);
  2292. KeWaitForSingleObject( &MmMappedFileHeader.Event,
  2293. WrPageOut,
  2294. KernelMode,
  2295. FALSE,
  2296. (PLARGE_INTEGER)&Mm30Milliseconds);
  2297. LOCK_PFN (OldIrql);
  2298. //
  2299. // Don't go on as the old PageFrameIndex at the
  2300. // top of the ModifiedList may have changed states.
  2301. //
  2302. continue;
  2303. }
  2304. PagesWritten += MiGatherMappedPages (Pfn1, PageFrameIndex);
  2305. }
  2306. else {
  2307. PagesWritten += MiGatherPagefilePages (Pfn1, PageFrameIndex);
  2308. }
  2309. if (MmSystemShutdown) {
  2310. //
  2311. // Shutdown has returned. Stop the modified page writer.
  2312. //
  2313. UNLOCK_PFN (OldIrql);
  2314. return;
  2315. }
  2316. //
  2317. // If this is a mapped page timer, then keep on writing till there's
  2318. // nothing left.
  2319. //
  2320. if (WakeupStatus == MappedPagesNeedWriting) {
  2321. continue;
  2322. }
  2323. //
  2324. // If this is a request to write all modified pages, then keep on
  2325. // writing.
  2326. //
  2327. if (MmWriteAllModifiedPages) {
  2328. continue;
  2329. }
  2330. if ((MmAvailablePages > MmFreeGoal) &&
  2331. (MmModifiedPageListHead.Total < MmFreeGoal)) {
  2332. //
  2333. // There are ample pages, clear the event and wait again...
  2334. //
  2335. UNLOCK_PFN (OldIrql);
  2336. KeClearEvent (&MmModifiedPageWriterEvent);
  2337. break;
  2338. }
  2339. if (MmAvailablePages > MmMoreThanEnoughFreePages) {
  2340. if (PagesWritten >= 96) {
  2341. //
  2342. // Always try to write at least 96 pages worth
  2343. // to make our chore worthwhile. Otherwise on laptops,
  2344. // waking up once per second to write a single page
  2345. // needlessly drains battery life.
  2346. //
  2347. UNLOCK_PFN (OldIrql);
  2348. KeClearEvent (&MmModifiedPageWriterEvent);
  2349. break;
  2350. }
  2351. }
  2352. } // end for
  2353. } // end for
  2354. }
  2355. PFN_NUMBER
  2356. MiGatherMappedPages (
  2357. IN PMMPFN Pfn1,
  2358. IN PFN_NUMBER PageFrameIndex
  2359. )
  2360. /*++
  2361. Routine Description:
  2362. This routine processes the specified modified page by examining
  2363. the prototype PTE for that page and the adjacent prototype PTEs
  2364. building a cluster of modified pages destined for a mapped file.
  2365. Once the cluster is built, it is sent to the mapped writer thread
  2366. to be processed.
  2367. Arguments:
  2368. Pfn1 - Supplies a pointer to the PFN element for the corresponding
  2369. page.
  2370. PageFrameIndex - Supplies the physical page frame to write.
  2371. Return Value:
  2372. The number of pages in the attempted write.
  2373. Environment:
  2374. PFN lock held.
  2375. --*/
  2376. {
  2377. PMMPFN Pfn2;
  2378. PFN_NUMBER PagesWritten;
  2379. PMMMOD_WRITER_MDL_ENTRY ModWriterEntry;
  2380. PSUBSECTION Subsection;
  2381. PCONTROL_AREA ControlArea;
  2382. PPFN_NUMBER Page;
  2383. PMMPTE LastPte;
  2384. PMMPTE BasePte;
  2385. PMMPTE NextPte;
  2386. PMMPTE PointerPte;
  2387. PMMPTE StartingPte;
  2388. MMPTE PteContents;
  2389. KIRQL OldIrql;
  2390. PVOID HyperMapped;
  2391. PEPROCESS Process;
  2392. //
  2393. // This page is destined for a mapped file, check to see if
  2394. // there are any physically adjacent pages are also in the
  2395. // modified page list and write them out at the same time.
  2396. //
  2397. Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte);
  2398. ControlArea = Subsection->ControlArea;
  2399. if (ControlArea->u.Flags.NoModifiedWriting) {
  2400. //
  2401. // This page should not be written out, add it to the
  2402. // tail of the modified NO WRITE list and get the next page.
  2403. //
  2404. MiUnlinkPageFromList (Pfn1);
  2405. MiInsertPageInList (&MmModifiedNoWritePageListHead,
  2406. PageFrameIndex);
  2407. return 0;
  2408. }
  2409. if (ControlArea->u.Flags.Image) {
  2410. #if 0
  2411. //
  2412. // Assert that there are no dangling shared global pages
  2413. // for an image section that is not being used.
  2414. //
  2415. // This assert can be re-enabled when the segment dereference
  2416. // thread list re-insertion is fixed. Note the recovery code is
  2417. // fine, so disabling the assert is benign.
  2418. //
  2419. ASSERT ((ControlArea->NumberOfMappedViews != 0) ||
  2420. (ControlArea->NumberOfSectionReferences != 0) ||
  2421. (ControlArea->u.Flags.FloppyMedia != 0));
  2422. #endif
  2423. //
  2424. // This is an image section, writes are not
  2425. // allowed to an image section.
  2426. //
  2427. //
  2428. // Change page contents to look like it's a demand zero
  2429. // page and put it back into the modified list.
  2430. //
  2431. //
  2432. // Decrement the count for PfnReferences to the
  2433. // segment as paging file pages are not counted as
  2434. // "image" references.
  2435. //
  2436. ControlArea->NumberOfPfnReferences -= 1;
  2437. ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0);
  2438. MiUnlinkPageFromList (Pfn1);
  2439. Pfn1->OriginalPte.u.Soft.PageFileHigh = 0;
  2440. Pfn1->OriginalPte.u.Soft.Prototype = 0;
  2441. Pfn1->OriginalPte.u.Soft.Transition = 0;
  2442. //
  2443. // Insert the page at the tail of the list and get
  2444. // color update performed.
  2445. //
  2446. MiInsertPageInList (&MmModifiedPageListHead, PageFrameIndex);
  2447. return 0;
  2448. }
  2449. //
  2450. // Look at backwards at previous prototype PTEs to see if
  2451. // this can be clustered into a larger write operation.
  2452. //
  2453. PointerPte = Pfn1->PteAddress;
  2454. NextPte = PointerPte - (MmModifiedWriteClusterSize - 1);
  2455. //
  2456. // Make sure NextPte is in the same page.
  2457. //
  2458. if (NextPte < (PMMPTE)PAGE_ALIGN (PointerPte)) {
  2459. NextPte = (PMMPTE)PAGE_ALIGN (PointerPte);
  2460. }
  2461. //
  2462. // Make sure NextPte is within the subsection.
  2463. //
  2464. if (NextPte < Subsection->SubsectionBase) {
  2465. NextPte = Subsection->SubsectionBase;
  2466. }
  2467. //
  2468. // If the prototype PTEs are not currently mapped,
  2469. // map them via hyperspace. BasePte refers to the
  2470. // prototype PTEs for nonfaulting references.
  2471. //
  2472. if (MmIsAddressValid (PointerPte)) {
  2473. Process = NULL;
  2474. HyperMapped = NULL;
  2475. BasePte = PointerPte;
  2476. }
  2477. else {
  2478. Process = PsGetCurrentProcess ();
  2479. HyperMapped = MiMapPageInHyperSpaceAtDpc (Process, Pfn1->u4.PteFrame);
  2480. BasePte = (PMMPTE)((PCHAR)HyperMapped + BYTE_OFFSET (PointerPte));
  2481. }
  2482. ASSERT (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (BasePte) == PageFrameIndex);
  2483. PointerPte -= 1;
  2484. BasePte -= 1;
  2485. if (MiClusterWritesDisabled != 0) {
  2486. NextPte = PointerPte + 1;
  2487. }
  2488. //
  2489. // Don't go before the start of the subsection nor cross
  2490. // a page boundary.
  2491. //
  2492. while (PointerPte >= NextPte) {
  2493. PteContents = *BasePte;
  2494. //
  2495. // If the page is not in transition, exit loop.
  2496. //
  2497. if ((PteContents.u.Hard.Valid == 1) ||
  2498. (PteContents.u.Soft.Transition == 0) ||
  2499. (PteContents.u.Soft.Prototype == 1)) {
  2500. break;
  2501. }
  2502. Pfn2 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  2503. //
  2504. // Make sure page is modified and on the modified list.
  2505. //
  2506. if ((Pfn2->u3.e1.Modified == 0 ) ||
  2507. (Pfn2->u3.e2.ReferenceCount != 0)) {
  2508. break;
  2509. }
  2510. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  2511. PointerPte -= 1;
  2512. BasePte -= 1;
  2513. }
  2514. StartingPte = PointerPte + 1;
  2515. BasePte = BasePte + 1;
  2516. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2517. ASSERT (StartingPte == Pfn1->PteAddress);
  2518. MiUnlinkPageFromList (Pfn1);
  2519. //
  2520. // Get an entry from the list and fill it in.
  2521. //
  2522. ModWriterEntry = (PMMMOD_WRITER_MDL_ENTRY)RemoveHeadList (
  2523. &MmMappedFileHeader.ListHead);
  2524. #if DBG
  2525. MmNumberOfMappedMdlsInUse += 1;
  2526. if (MmNumberOfMappedMdlsInUse > MmNumberOfMappedMdlsInUsePeak) {
  2527. MmNumberOfMappedMdlsInUsePeak = MmNumberOfMappedMdlsInUse;
  2528. }
  2529. #endif
  2530. ModWriterEntry->File = ControlArea->FilePointer;
  2531. ModWriterEntry->ControlArea = ControlArea;
  2532. //
  2533. // Calculate the offset to read into the file.
  2534. // offset = base + ((thispte - basepte) << PAGE_SHIFT)
  2535. //
  2536. ModWriterEntry->WriteOffset.QuadPart = MiStartingOffset (Subsection,
  2537. Pfn1->PteAddress);
  2538. MmInitializeMdl(&ModWriterEntry->Mdl, NULL, PAGE_SIZE);
  2539. ModWriterEntry->Mdl.MdlFlags |= MDL_PAGES_LOCKED;
  2540. ModWriterEntry->Mdl.Size = (CSHORT)(sizeof(MDL) +
  2541. (sizeof(PFN_NUMBER) * MmModifiedWriteClusterSize));
  2542. Page = &ModWriterEntry->Page[0];
  2543. //
  2544. // Up the reference count for the physical page as there
  2545. // is I/O in progress.
  2546. //
  2547. MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE (Pfn1, 14);
  2548. Pfn1->u3.e2.ReferenceCount += 1;
  2549. //
  2550. // Clear the modified bit for the page and set the write
  2551. // in progress bit.
  2552. //
  2553. MI_SET_MODIFIED (Pfn1, 0, 0x23);
  2554. Pfn1->u3.e1.WriteInProgress = 1;
  2555. //
  2556. // Put this physical page into the MDL.
  2557. //
  2558. *Page = PageFrameIndex;
  2559. //
  2560. // See if any adjacent pages are also modified and in
  2561. // the transition state and if so, write them out at
  2562. // the same time.
  2563. //
  2564. LastPte = StartingPte + MmModifiedWriteClusterSize;
  2565. //
  2566. // Look at the last PTE, ensuring a page boundary is not crossed.
  2567. //
  2568. // If LastPte is not in the same page as the StartingPte,
  2569. // set LastPte so the cluster will not cross.
  2570. //
  2571. if (StartingPte < (PMMPTE)PAGE_ALIGN(LastPte)) {
  2572. LastPte = (PMMPTE)PAGE_ALIGN(LastPte);
  2573. }
  2574. //
  2575. // Make sure LastPte is within the subsection.
  2576. //
  2577. if (LastPte > &Subsection->SubsectionBase[Subsection->PtesInSubsection]) {
  2578. LastPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  2579. }
  2580. //
  2581. // Look forwards.
  2582. //
  2583. NextPte = BasePte + 1;
  2584. PointerPte = StartingPte + 1;
  2585. if (MiClusterWritesDisabled != 0) {
  2586. LastPte = PointerPte;
  2587. }
  2588. //
  2589. // Loop until an MDL is filled, the end of a subsection
  2590. // is reached, or a page boundary is reached.
  2591. // Note, PointerPte points to the PTE. NextPte points
  2592. // to where it is mapped in hyperspace (if required).
  2593. //
  2594. while (PointerPte < LastPte) {
  2595. PteContents = *NextPte;
  2596. //
  2597. // If the page is not in transition, exit loop.
  2598. //
  2599. if ((PteContents.u.Hard.Valid == 1) ||
  2600. (PteContents.u.Soft.Transition == 0) ||
  2601. (PteContents.u.Soft.Prototype == 1)) {
  2602. break;
  2603. }
  2604. Pfn2 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  2605. if ((Pfn2->u3.e1.Modified == 0 ) ||
  2606. (Pfn2->u3.e2.ReferenceCount != 0)) {
  2607. //
  2608. // Page is not dirty or not on the modified list,
  2609. // end clustering operation.
  2610. //
  2611. break;
  2612. }
  2613. Page += 1;
  2614. //
  2615. // Add physical page to MDL.
  2616. //
  2617. *Page = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  2618. ASSERT (PointerPte == Pfn2->PteAddress);
  2619. MiUnlinkPageFromList (Pfn2);
  2620. //
  2621. // Up the reference count for the physical page as there
  2622. // is I/O in progress.
  2623. //
  2624. MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE (Pfn2, 14);
  2625. Pfn2->u3.e2.ReferenceCount += 1;
  2626. //
  2627. // Clear the modified bit for the page and set the
  2628. // write in progress bit.
  2629. //
  2630. MI_SET_MODIFIED (Pfn2, 0, 0x24);
  2631. Pfn2->u3.e1.WriteInProgress = 1;
  2632. ModWriterEntry->Mdl.ByteCount += PAGE_SIZE;
  2633. NextPte += 1;
  2634. PointerPte += 1;
  2635. }
  2636. if (HyperMapped != NULL) {
  2637. MiUnmapPageInHyperSpaceFromDpc (Process, HyperMapped);
  2638. }
  2639. ASSERT (BYTES_TO_PAGES (ModWriterEntry->Mdl.ByteCount) <= MmModifiedWriteClusterSize);
  2640. ModWriterEntry->u.LastByte.QuadPart = ModWriterEntry->WriteOffset.QuadPart +
  2641. ModWriterEntry->Mdl.ByteCount;
  2642. ASSERT (Subsection->ControlArea->u.Flags.Image == 0);
  2643. #if DBG
  2644. if ((ULONG)ModWriterEntry->Mdl.ByteCount >
  2645. ((1+MmModifiedWriteClusterSize)*PAGE_SIZE)) {
  2646. DbgPrint("Mdl %p, MDL End Offset %lx %lx Subsection %p\n",
  2647. ModWriterEntry->Mdl,
  2648. ModWriterEntry->u.LastByte.LowPart,
  2649. ModWriterEntry->u.LastByte.HighPart,
  2650. Subsection);
  2651. DbgBreakPoint();
  2652. }
  2653. #endif
  2654. PagesWritten = (ModWriterEntry->Mdl.ByteCount >> PAGE_SHIFT);
  2655. MmInfoCounters.MappedWriteIoCount += 1;
  2656. MmInfoCounters.MappedPagesWriteCount += (ULONG)PagesWritten;
  2657. //
  2658. // Increment the count of modified page writes outstanding
  2659. // in the control area.
  2660. //
  2661. ControlArea->ModifiedWriteCount += 1;
  2662. //
  2663. // Increment the number of PFN references. This allows the file
  2664. // system to purge (i.e. call MmPurgeSection) modified writes.
  2665. //
  2666. ControlArea->NumberOfPfnReferences += 1;
  2667. ModWriterEntry->FileResource = NULL;
  2668. if (ControlArea->u.Flags.BeingPurged == 1) {
  2669. UNLOCK_PFN (PASSIVE_LEVEL);
  2670. ModWriterEntry->u.IoStatus.Status = STATUS_FILE_LOCK_CONFLICT;
  2671. ModWriterEntry->u.IoStatus.Information = 0;
  2672. KeRaiseIrql (APC_LEVEL, &OldIrql);
  2673. MiWriteComplete ((PVOID)ModWriterEntry,
  2674. &ModWriterEntry->u.IoStatus,
  2675. 0 );
  2676. KeLowerIrql (OldIrql);
  2677. LOCK_PFN (OldIrql);
  2678. return PagesWritten;
  2679. }
  2680. //
  2681. // Send the entry for the MappedPageWriter.
  2682. //
  2683. InsertTailList (&MmMappedPageWriterList, &ModWriterEntry->Links);
  2684. KeSetEvent (&MmMappedPageWriterEvent, 0, FALSE);
  2685. #if 0
  2686. UNLOCK_PFN (OldIrql);
  2687. ModWriterEntry->FileResource = NULL;
  2688. if (ModWriterEntry->ControlArea->u.Flags.FailAllIo == 1) {
  2689. Status = STATUS_UNSUCCESSFUL;
  2690. }
  2691. else {
  2692. Status = FsRtlAcquireFileForModWriteEx (ModWriterEntry->File,
  2693. &ModWriterEntry->u.LastByte,
  2694. &ModWriterEntry->FileResource);
  2695. if (NT_SUCCESS(Status)) {
  2696. //
  2697. // Issue the write request.
  2698. //
  2699. Status = IoAsynchronousPageWrite (ModWriterEntry->File,
  2700. &ModWriterEntry->Mdl,
  2701. &ModWriterEntry->WriteOffset,
  2702. MiWriteComplete,
  2703. (PVOID)ModWriterEntry,
  2704. &ModWriterEntry->IoStatus,
  2705. &ModWriterEntry->Irp);
  2706. }
  2707. else {
  2708. //
  2709. // Unable to get the file system resources, set error status
  2710. // to lock conflict (ignored by MiWriteComplete) so the APC
  2711. // routine is explicitly called.
  2712. //
  2713. Status = STATUS_FILE_LOCK_CONFLICT;
  2714. }
  2715. }
  2716. if (NT_ERROR(Status)) {
  2717. //
  2718. // An error has occurred, disable APCs and
  2719. // call the write completion routine.
  2720. //
  2721. ModWriterEntry->IoStatus.Status = Status;
  2722. ModWriterEntry->IoStatus.Information = 0;
  2723. KeRaiseIrql (APC_LEVEL, &OldIrql);
  2724. MiWriteComplete ((PVOID)ModWriterEntry,
  2725. &ModWriterEntry->IoStatus,
  2726. 0 );
  2727. KeLowerIrql (OldIrql);
  2728. }
  2729. LOCK_PFN (OldIrql);
  2730. #endif //0
  2731. return PagesWritten;
  2732. }
  2733. PFN_NUMBER
  2734. MiGatherPagefilePages (
  2735. IN PMMPFN Pfn1,
  2736. IN PFN_NUMBER PageFrameIndex
  2737. )
  2738. /*++
  2739. Routine Description:
  2740. This routine processes the specified modified page by getting
  2741. that page and gather any other pages on the modified list destined
  2742. for the paging file in a large write cluster. This cluster is
  2743. then written to the paging file.
  2744. Arguments:
  2745. Pfn1 - Supplies a pointer to the PFN element for the corresponding page.
  2746. PageFrameIndex - Supplies the physical page frame to write.
  2747. Return Value:
  2748. The number of pages in the attempted write.
  2749. Environment:
  2750. PFN lock held.
  2751. --*/
  2752. {
  2753. PFILE_OBJECT File;
  2754. PMMMOD_WRITER_MDL_ENTRY ModWriterEntry;
  2755. PMMPAGING_FILE CurrentPagingFile;
  2756. NTSTATUS Status;
  2757. PPFN_NUMBER Page;
  2758. ULONG StartBit;
  2759. LARGE_INTEGER StartingOffset;
  2760. PFN_NUMBER ClusterSize;
  2761. PFN_NUMBER ThisCluster;
  2762. MMPTE LongPte;
  2763. KIRQL OldIrql;
  2764. ULONG NextColor;
  2765. LOGICAL PageFileFull;
  2766. //MM_WRITE_CLUSTER WriteCluster;
  2767. OldIrql = PASSIVE_LEVEL;
  2768. if (IsListEmpty(&MmPagingFileHeader.ListHead)) {
  2769. //
  2770. // Reset the event indicating no paging files MDLs in
  2771. // the list, drop the PFN lock and wait for an
  2772. // I/O operation to complete.
  2773. //
  2774. KeClearEvent (&MmPagingFileHeader.Event);
  2775. UNLOCK_PFN (OldIrql);
  2776. KeWaitForSingleObject( &MmPagingFileHeader.Event,
  2777. WrPageOut,
  2778. KernelMode,
  2779. FALSE,
  2780. (PLARGE_INTEGER)&Mm30Milliseconds);
  2781. LOCK_PFN (OldIrql);
  2782. //
  2783. // Don't go on as the old PageFrameIndex at the
  2784. // top of the ModifiedList may have changed states.
  2785. //
  2786. return 0;
  2787. }
  2788. //
  2789. // Page is destined for the paging file.
  2790. // Find the paging file with the most free space and get a cluster.
  2791. //
  2792. #if MM_MAXIMUM_NUMBER_OF_COLORS > 1
  2793. NextColor = Pfn1->u3.e1.PageColor;
  2794. #else
  2795. NextColor = 0;
  2796. #endif
  2797. ModWriterEntry = (PMMMOD_WRITER_MDL_ENTRY)RemoveHeadList (
  2798. &MmPagingFileHeader.ListHead);
  2799. #if DBG
  2800. ModWriterEntry->Links.Flink = MM_IO_IN_PROGRESS;
  2801. #endif
  2802. CurrentPagingFile = ModWriterEntry->PagingFile;
  2803. File = ModWriterEntry->PagingFile->File;
  2804. if (MiClusterWritesDisabled == 0) {
  2805. ThisCluster = MmModifiedWriteClusterSize;
  2806. }
  2807. else {
  2808. ThisCluster = 1;
  2809. }
  2810. PageFileFull = FALSE;
  2811. do {
  2812. //
  2813. // Attempt to cluster MmModifiedWriteClusterSize pages
  2814. // together. Reduce by one half until we succeed or
  2815. // can't find a single page free in the paging file.
  2816. //
  2817. if (((CurrentPagingFile->Hint + MmModifiedWriteClusterSize) >
  2818. CurrentPagingFile->MinimumSize)
  2819. &&
  2820. (CurrentPagingFile->HintSetToZero == FALSE)) {
  2821. CurrentPagingFile->HintSetToZero = TRUE;
  2822. CurrentPagingFile->Hint = 0;
  2823. }
  2824. StartBit = RtlFindClearBitsAndSet (CurrentPagingFile->Bitmap,
  2825. (ULONG)ThisCluster,
  2826. (ULONG)CurrentPagingFile->Hint);
  2827. if (StartBit != NO_BITS_FOUND) {
  2828. break;
  2829. }
  2830. if (CurrentPagingFile->Hint != 0) {
  2831. //
  2832. // Start looking from front of the file.
  2833. //
  2834. CurrentPagingFile->Hint = 0;
  2835. }
  2836. else {
  2837. ThisCluster = ThisCluster >> 1;
  2838. PageFileFull = TRUE;
  2839. }
  2840. } while (ThisCluster != 0);
  2841. if (StartBit == NO_BITS_FOUND) {
  2842. //
  2843. // Paging file must be full.
  2844. //
  2845. KdPrint(("MM MODWRITE: page file full\n"));
  2846. ASSERT(CurrentPagingFile->FreeSpace == 0);
  2847. //
  2848. // Move this entry to the not enough space list,
  2849. // and try again.
  2850. //
  2851. InsertTailList (&MmFreePagingSpaceLow,
  2852. &ModWriterEntry->Links);
  2853. ModWriterEntry->CurrentList = &MmFreePagingSpaceLow;
  2854. MmNumberOfActiveMdlEntries -= 1;
  2855. UNLOCK_PFN (OldIrql);
  2856. MiPageFileFull ();
  2857. LOCK_PFN (OldIrql);
  2858. return 0;
  2859. }
  2860. CurrentPagingFile->FreeSpace -= ThisCluster;
  2861. CurrentPagingFile->CurrentUsage += ThisCluster;
  2862. if (CurrentPagingFile->FreeSpace < 32) {
  2863. PageFileFull = TRUE;
  2864. }
  2865. StartingOffset.QuadPart = (UINT64)StartBit << PAGE_SHIFT;
  2866. MmInitializeMdl(&ModWriterEntry->Mdl, NULL, PAGE_SIZE);
  2867. ModWriterEntry->Mdl.MdlFlags |= MDL_PAGES_LOCKED;
  2868. ModWriterEntry->Mdl.Size = (CSHORT)(sizeof(MDL) +
  2869. sizeof(PFN_NUMBER) * MmModifiedWriteClusterSize);
  2870. Page = &ModWriterEntry->Page[0];
  2871. ClusterSize = 0;
  2872. //
  2873. // Search through the modified page list looking for other
  2874. // pages destined for the paging file and build a cluster.
  2875. //
  2876. while (ClusterSize != ThisCluster) {
  2877. //
  2878. // Is this page destined for a paging file?
  2879. //
  2880. if (Pfn1->OriginalPte.u.Soft.Prototype == 0) {
  2881. #if 0 //********* commented out
  2882. MiClusterWritePages (Pfn1,
  2883. PageFrameIndex,
  2884. &WriteCluster,
  2885. ThisCluster - ClusterSize);
  2886. do {
  2887. PageFrameIndex = WriteCluster.Cluster[WriteCluster.StartIndex];
  2888. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2889. #endif //0
  2890. *Page = PageFrameIndex;
  2891. //
  2892. // Remove the page from the modified list. Note that
  2893. // write-in-progress marks the state.
  2894. //
  2895. //
  2896. // Unlink the page so the same page won't be found
  2897. // on the modified page list by color.
  2898. //
  2899. MiUnlinkPageFromList (Pfn1);
  2900. NextColor = MI_GET_NEXT_COLOR(NextColor);
  2901. MI_GET_MODIFIED_PAGE_BY_COLOR (PageFrameIndex,
  2902. NextColor);
  2903. //
  2904. // Up the reference count for the physical page as there
  2905. // is I/O in progress.
  2906. //
  2907. MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE (Pfn1, 16);
  2908. Pfn1->u3.e2.ReferenceCount += 1;
  2909. //
  2910. // Clear the modified bit for the page and set the
  2911. // write in progress bit.
  2912. //
  2913. MI_SET_MODIFIED (Pfn1, 0, 0x25);
  2914. Pfn1->u3.e1.WriteInProgress = 1;
  2915. ASSERT (Pfn1->OriginalPte.u.Soft.PageFileHigh == 0);
  2916. MI_SET_PAGING_FILE_INFO (LongPte,
  2917. Pfn1->OriginalPte,
  2918. CurrentPagingFile->PageFileNumber,
  2919. StartBit);
  2920. #if DBG
  2921. if ((StartBit < 8192) &&
  2922. (CurrentPagingFile->PageFileNumber == 0)) {
  2923. ASSERT ((MmPagingFileDebug[StartBit] & 1) == 0);
  2924. MmPagingFileDebug[StartBit] =
  2925. (((ULONG_PTR)Pfn1->PteAddress << 3) |
  2926. ((ClusterSize & 0xf) << 1) | 1);
  2927. }
  2928. #endif
  2929. //
  2930. // Change the original PTE contents to refer to
  2931. // the paging file offset where this was written.
  2932. //
  2933. Pfn1->OriginalPte = LongPte;
  2934. ClusterSize += 1;
  2935. Page += 1;
  2936. StartBit += 1;
  2937. #if 0 // COMMENTED OUT
  2938. WriteCluster.Count -= 1;
  2939. WriteCluster.StartIndex += 1;
  2940. } while (WriteCluster.Count != 0);
  2941. #endif //0
  2942. }
  2943. else {
  2944. //
  2945. // This page was not destined for a paging file,
  2946. // get another page.
  2947. //
  2948. // Get a page of the same color as the one which
  2949. // was not usable.
  2950. //
  2951. MI_GET_MODIFIED_PAGE_BY_COLOR (PageFrameIndex,
  2952. NextColor);
  2953. }
  2954. if (PageFrameIndex == MM_EMPTY_LIST) {
  2955. break;
  2956. }
  2957. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2958. } //end while
  2959. if (ClusterSize != ThisCluster) {
  2960. //
  2961. // A complete cluster could not be located, free the
  2962. // excess page file space that was reserved and adjust
  2963. // the size of the packet.
  2964. //
  2965. RtlClearBits (CurrentPagingFile->Bitmap,
  2966. StartBit,
  2967. (ULONG)(ThisCluster - ClusterSize));
  2968. CurrentPagingFile->FreeSpace += ThisCluster - ClusterSize;
  2969. CurrentPagingFile->CurrentUsage -= ThisCluster - ClusterSize;
  2970. //
  2971. // If there are no pages to write, don't issue a write
  2972. // request and restart the scan loop.
  2973. //
  2974. if (ClusterSize == 0) {
  2975. //
  2976. // No pages to write. Insert the entry back in the list.
  2977. //
  2978. if (IsListEmpty (&ModWriterEntry->PagingListHead->ListHead)) {
  2979. KeSetEvent (&ModWriterEntry->PagingListHead->Event,
  2980. 0,
  2981. FALSE);
  2982. }
  2983. InsertTailList (&ModWriterEntry->PagingListHead->ListHead,
  2984. &ModWriterEntry->Links);
  2985. return 0;
  2986. }
  2987. }
  2988. if (CurrentPagingFile->PeakUsage <
  2989. CurrentPagingFile->CurrentUsage) {
  2990. CurrentPagingFile->PeakUsage =
  2991. CurrentPagingFile->CurrentUsage;
  2992. }
  2993. ModWriterEntry->Mdl.ByteCount = (ULONG)(ClusterSize * PAGE_SIZE);
  2994. ModWriterEntry->LastPageToWrite = StartBit - 1;
  2995. MmInfoCounters.DirtyWriteIoCount += 1;
  2996. MmInfoCounters.DirtyPagesWriteCount += (ULONG)ClusterSize;
  2997. //
  2998. // For now release the PFN lock and wait for the write to complete.
  2999. //
  3000. UNLOCK_PFN (OldIrql);
  3001. #if DBG
  3002. if (MmDebug & MM_DBG_MOD_WRITE) {
  3003. DbgPrint("MM MODWRITE: modified page write begun @ %08lx by %08lx\n",
  3004. StartingOffset.LowPart, ModWriterEntry->Mdl.ByteCount);
  3005. }
  3006. #endif
  3007. //
  3008. // Issue the write request.
  3009. //
  3010. Status = IoAsynchronousPageWrite (File,
  3011. &ModWriterEntry->Mdl,
  3012. &StartingOffset,
  3013. MiWriteComplete,
  3014. (PVOID)ModWriterEntry,
  3015. &ModWriterEntry->u.IoStatus,
  3016. &ModWriterEntry->Irp);
  3017. if (NT_ERROR(Status)) {
  3018. KdPrint(("MM MODWRITE: modified page write failed %lx\n", Status));
  3019. //
  3020. // An error has occurred, disable APCs and
  3021. // call the write completion routine.
  3022. //
  3023. ModWriterEntry->u.IoStatus.Status = Status;
  3024. ModWriterEntry->u.IoStatus.Information = 0;
  3025. KeRaiseIrql (APC_LEVEL, &OldIrql);
  3026. MiWriteComplete ((PVOID)ModWriterEntry,
  3027. &ModWriterEntry->u.IoStatus,
  3028. 0 );
  3029. KeLowerIrql (OldIrql);
  3030. }
  3031. if (PageFileFull == TRUE) {
  3032. MiPageFileFull ();
  3033. }
  3034. LOCK_PFN (OldIrql);
  3035. return ClusterSize;
  3036. }
  3037. #if 0 // COMMENTED OUT **************************************************
  3038. ULONG ClusterCounts[20];
  3039. ULONG ClusterSizes[20];
  3040. VOID
  3041. MiClusterWritePages (
  3042. IN PMMPFN Pfn1,
  3043. IN PFN_NUMBER PageFrameIndex,
  3044. IN PMM_WRITE_CLUSTER WriteCluster,
  3045. IN ULONG Size
  3046. )
  3047. {
  3048. PMMPTE PointerClusterPte;
  3049. PMMPTE OriginalPte;
  3050. PMMPTE StopPte;
  3051. PMMPTE ThisPage;
  3052. PMMPTE BasePage;
  3053. ULONG Start;
  3054. PMMPFN Pfn2;
  3055. KIRQL OldIrql = 99;
  3056. Start = MM_MAXIMUM_DISK_IO_SIZE / PAGE_SIZE;
  3057. WriteCluster->Cluster[Start] = PageFrameIndex;
  3058. WriteCluster->Count = 1;
  3059. ClusterSizes[Size] += 1;
  3060. if (Size == 1) {
  3061. WriteCluster->StartIndex = Start;
  3062. return;
  3063. }
  3064. //
  3065. // The page points to a page table page which may not be
  3066. // for the current process. Map the page into hyperspace
  3067. // reference it through hyperspace.
  3068. //
  3069. PointerClusterPte = Pfn1->PteAddress;
  3070. BasePage = (PMMPTE)((ULONG_PTR)PointerClusterPte & ~(PAGE_SIZE - 1));
  3071. ThisPage = BasePage;
  3072. if ((PointerClusterPte < (PMMPTE)PDE_TOP) ||
  3073. (!MmIsAddressValid (PointerClusterPte))) {
  3074. //
  3075. // Map page into hyperspace as it is either a page table
  3076. // page or nonresident paged pool.
  3077. //
  3078. PointerClusterPte = (PMMPTE)((PCHAR)MiMapPageInHyperSpace (
  3079. Pfn1->PteFrame, &OldIrql)
  3080. +
  3081. BYTE_OFFSET (PointerClusterPte));
  3082. ThisPage = (PMMPTE)((ULONG_PTR)PointerClusterPte & ~(PAGE_SIZE - 1));
  3083. }
  3084. OriginalPte = PointerClusterPte;
  3085. ASSERT (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerClusterPte) == PageFrameIndex);
  3086. //
  3087. // Check backwards and forwards for other pages from this process
  3088. // destined for the paging file.
  3089. //
  3090. StopPte = PointerClusterPte - (Size - 1);
  3091. if (StopPte < ThisPage) {
  3092. StopPte = ThisPage;
  3093. }
  3094. while (PointerClusterPte > StopPte) {
  3095. PointerClusterPte -= 1;
  3096. //
  3097. // Look for the pointer at start of segment, quit as this is NOT
  3098. // a prototype PTE. Normal PTEs will not match this.
  3099. //
  3100. if (BasePage != (PMMPTE)
  3101. (ULONG_PTR)(PointerClusterPte->u.Long & ~(PAGE_SIZE - 1))) {
  3102. if ((PointerClusterPte->u.Hard.Valid == 0) &&
  3103. (PointerClusterPte->u.Soft.Prototype == 0) &&
  3104. (PointerClusterPte->u.Soft.Transition == 1)) {
  3105. //
  3106. // PTE is in transition state, see if it is modified.
  3107. //
  3108. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerClusterPte);
  3109. Pfn2 = MI_PFN_ELEMENT(PageFrameIndex);
  3110. ASSERT (Pfn2->OriginalPte.u.Soft.Prototype == 0);
  3111. if ((Pfn2->u3.e1.Modified != 0 ) &&
  3112. (Pfn2->u3.e2.ReferenceCount == 0)) {
  3113. Start -= 1;
  3114. WriteCluster->Count += 1;
  3115. WriteCluster->Cluster[Start] = PageFrameIndex;
  3116. }
  3117. }
  3118. }
  3119. break;
  3120. }
  3121. WriteCluster->StartIndex = Start;
  3122. PointerClusterPte = OriginalPte + 1;
  3123. Start = MM_MAXIMUM_DISK_IO_SIZE / PAGE_SIZE;
  3124. //
  3125. // Remove pages looking forward from PointerClusterPte until
  3126. // a cluster is filled or a PTE is not on the modified list.
  3127. //
  3128. ThisPage = (PMMPTE)((PCHAR)ThisPage + PAGE_SIZE);
  3129. while ((WriteCluster->Count < Size) &&
  3130. (PointerClusterPte < ThisPage)) {
  3131. if ((PointerClusterPte->u.Hard.Valid == 0) &&
  3132. (PointerClusterPte->u.Soft.Prototype == 0) &&
  3133. (PointerClusterPte->u.Soft.Transition == 1)) {
  3134. //
  3135. // PTE is in transition state, see if it is modified.
  3136. //
  3137. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerClusterPte);
  3138. Pfn2 = MI_PFN_ELEMENT(PageFrameIndex);
  3139. ASSERT (Pfn2->OriginalPte.u.Soft.Prototype == 0);
  3140. if ((Pfn2->u3.e1.Modified != 0 ) &&
  3141. (Pfn2->u3.e2.ReferenceCount == 0)) {
  3142. Start += 1;
  3143. WriteCluster->Count += 1;
  3144. WriteCluster->Cluster[Start] = PageFrameIndex;
  3145. PointerClusterPte += 1;
  3146. continue;
  3147. }
  3148. }
  3149. break;
  3150. }
  3151. if (OldIrql != 99) {
  3152. MiUnmapPageInHyperSpace (OldIrql);
  3153. }
  3154. ClusterCounts[WriteCluster->Count] += 1;
  3155. return;
  3156. }
  3157. #endif // COMMENTED OUT **************************************************
  3158. VOID
  3159. MiMappedPageWriter (
  3160. IN PVOID StartContext
  3161. )
  3162. /*++
  3163. Routine Description:
  3164. Implements the NT secondary modified page writer thread.
  3165. Requests for writes to mapped files are sent to this thread.
  3166. This is required as the writing of mapped file pages could cause
  3167. page faults resulting in requests for free pages. But there
  3168. could be no free pages - hence a dead lock. Rather than deadlock
  3169. the whole system waiting on the modified page writer, creating
  3170. a secondary thread allows that thread to block without affecting
  3171. on going page file writes.
  3172. Arguments:
  3173. StartContext - not used.
  3174. Return Value:
  3175. None.
  3176. Environment:
  3177. Kernel mode.
  3178. --*/
  3179. {
  3180. KIRQL OldIrql;
  3181. NTSTATUS Status;
  3182. KEVENT TempEvent;
  3183. PETHREAD CurrentThread;
  3184. PMMMOD_WRITER_MDL_ENTRY ModWriterEntry;
  3185. UNREFERENCED_PARAMETER (StartContext);
  3186. //
  3187. // Make this a real time thread.
  3188. //
  3189. CurrentThread = PsGetCurrentThread ();
  3190. KeSetPriorityThread (&CurrentThread->Tcb, LOW_REALTIME_PRIORITY + 1);
  3191. CurrentThread->MemoryMaker = 1;
  3192. //
  3193. // Let the file system know that we are getting resources.
  3194. //
  3195. FsRtlSetTopLevelIrpForModWriter();
  3196. KeInitializeEvent (&TempEvent, NotificationEvent, FALSE);
  3197. OldIrql = PASSIVE_LEVEL;
  3198. while (TRUE) {
  3199. KeWaitForSingleObject (&MmMappedPageWriterEvent,
  3200. WrVirtualMemory,
  3201. KernelMode,
  3202. FALSE,
  3203. (PLARGE_INTEGER)NULL);
  3204. LOCK_PFN (OldIrql);
  3205. if (IsListEmpty (&MmMappedPageWriterList)) {
  3206. KeClearEvent (&MmMappedPageWriterEvent);
  3207. UNLOCK_PFN (OldIrql);
  3208. }
  3209. else {
  3210. ModWriterEntry = (PMMMOD_WRITER_MDL_ENTRY)RemoveHeadList (
  3211. &MmMappedPageWriterList);
  3212. UNLOCK_PFN (OldIrql);
  3213. if (ModWriterEntry->ControlArea->u.Flags.FailAllIo == 1) {
  3214. Status = STATUS_UNSUCCESSFUL;
  3215. }
  3216. else {
  3217. Status = FsRtlAcquireFileForModWriteEx (ModWriterEntry->File,
  3218. &ModWriterEntry->u.LastByte,
  3219. &ModWriterEntry->FileResource);
  3220. if (NT_SUCCESS(Status)) {
  3221. //
  3222. // Issue the write request.
  3223. //
  3224. Status = IoAsynchronousPageWrite (ModWriterEntry->File,
  3225. &ModWriterEntry->Mdl,
  3226. &ModWriterEntry->WriteOffset,
  3227. MiWriteComplete,
  3228. (PVOID)ModWriterEntry,
  3229. &ModWriterEntry->u.IoStatus,
  3230. &ModWriterEntry->Irp);
  3231. }
  3232. else {
  3233. //
  3234. // Unable to get the file system resources, set error status
  3235. // to lock conflict (ignored by MiWriteComplete) so the APC
  3236. // routine is explicitly called.
  3237. //
  3238. Status = STATUS_FILE_LOCK_CONFLICT;
  3239. }
  3240. }
  3241. if (NT_ERROR(Status)) {
  3242. //
  3243. // An error has occurred, disable APC's and
  3244. // call the write completion routine.
  3245. //
  3246. ModWriterEntry->u.IoStatus.Status = Status;
  3247. ModWriterEntry->u.IoStatus.Information = 0;
  3248. KeRaiseIrql (APC_LEVEL, &OldIrql);
  3249. MiWriteComplete ((PVOID)ModWriterEntry,
  3250. &ModWriterEntry->u.IoStatus,
  3251. 0 );
  3252. KeLowerIrql (OldIrql);
  3253. }
  3254. #if 0
  3255. //TEMPORARY code to use synchronous I/O here.
  3256. //
  3257. // Issue the write request.
  3258. //
  3259. Status = IoSynchronousPageWrite (
  3260. ModWriterEntry->File,
  3261. &ModWriterEntry->Mdl,
  3262. &ModWriterEntry->WriteOffset,
  3263. &TempEvent,
  3264. &ModWriterEntry->u.IoStatus );
  3265. if (NT_ERROR(Status)) {
  3266. ModWriterEntry->u.IoStatus.Status = Status;
  3267. ModWriterEntry->u.IoStatus.Information = 0;
  3268. }
  3269. if (NT_ERROR(ModWriterEntry->u.IoStatus.Status)) {
  3270. KdPrint(("MM MODWRITE: modified page write failed %lx\n", Status));
  3271. }
  3272. //
  3273. // Call the write completion routine.
  3274. //
  3275. KeRaiseIrql (APC_LEVEL, &OldIrql);
  3276. MiWriteComplete ((PVOID)ModWriterEntry,
  3277. &ModWriterEntry->IoStatus,
  3278. 0 );
  3279. KeLowerIrql (OldIrql);
  3280. #endif //0
  3281. }
  3282. }
  3283. }
  3284. BOOLEAN
  3285. MmDisableModifiedWriteOfSection (
  3286. IN PSECTION_OBJECT_POINTERS SectionObjectPointer
  3287. )
  3288. /*++
  3289. Routine Description:
  3290. This function disables page writing by the modified page writer for
  3291. the section which is mapped by the specified file object pointer.
  3292. This should only be used for files which CANNOT be mapped by user
  3293. programs, e.g., volume files, directory files, etc.
  3294. Arguments:
  3295. SectionObjectPointer - Supplies a pointer to the section objects
  3296. Return Value:
  3297. Returns TRUE if the operation was a success, FALSE if either
  3298. the there is no section or the section already has a view.
  3299. --*/
  3300. {
  3301. KIRQL OldIrql;
  3302. BOOLEAN state;
  3303. PCONTROL_AREA ControlArea;
  3304. state = TRUE;
  3305. LOCK_PFN (OldIrql);
  3306. ControlArea = ((PCONTROL_AREA)(SectionObjectPointer->DataSectionObject));
  3307. if (ControlArea != NULL) {
  3308. if (ControlArea->NumberOfMappedViews == 0) {
  3309. //
  3310. // There are no views to this section, indicate no modified
  3311. // page writing is allowed.
  3312. //
  3313. ControlArea->u.Flags.NoModifiedWriting = 1;
  3314. }
  3315. else {
  3316. //
  3317. // Return the current modified page writing state.
  3318. //
  3319. state = (BOOLEAN)ControlArea->u.Flags.NoModifiedWriting;
  3320. }
  3321. }
  3322. else {
  3323. //
  3324. // This file no longer has an associated segment.
  3325. //
  3326. state = 0;
  3327. }
  3328. UNLOCK_PFN (OldIrql);
  3329. return state;
  3330. }
  3331. BOOLEAN
  3332. MmEnableModifiedWriteOfSection (
  3333. IN PSECTION_OBJECT_POINTERS SectionObjectPointer
  3334. )
  3335. /*++
  3336. Routine Description:
  3337. This function enables page writing by the modified page writer for
  3338. the section which is mapped by the specified file object pointer.
  3339. This should only be used for files which have previously been disabled.
  3340. Normal sections are created allowing modified write.
  3341. Arguments:
  3342. SectionObjectPointer - Supplies a pointer to the section objects
  3343. Return Value:
  3344. Returns TRUE if the operation was a success, FALSE if either
  3345. the there is no section or the section already has a view.
  3346. --*/
  3347. {
  3348. KIRQL OldIrql;
  3349. BOOLEAN state;
  3350. PCONTROL_AREA ControlArea;
  3351. state = TRUE;
  3352. LOCK_PFN2 (OldIrql);
  3353. ControlArea = ((PCONTROL_AREA)(SectionObjectPointer->DataSectionObject));
  3354. if (ControlArea != NULL) {
  3355. if (ControlArea->NumberOfMappedViews == 0) {
  3356. //
  3357. // There are no views to this section, indicate modified
  3358. // page writing is allowed.
  3359. //
  3360. ControlArea->u.Flags.NoModifiedWriting = 0;
  3361. }
  3362. else {
  3363. //
  3364. // Return the current modified page writing state.
  3365. //
  3366. state = (BOOLEAN)!ControlArea->u.Flags.NoModifiedWriting;
  3367. }
  3368. }
  3369. UNLOCK_PFN2 (OldIrql);
  3370. return state;
  3371. }
  3372. #define ROUND_UP(VALUE,ROUND) ((ULONG)(((ULONG)VALUE + \
  3373. ((ULONG)ROUND - 1L)) & (~((ULONG)ROUND - 1L))))
  3374. NTSTATUS
  3375. MmGetPageFileInformation (
  3376. OUT PVOID SystemInformation,
  3377. IN ULONG SystemInformationLength,
  3378. OUT PULONG Length
  3379. )
  3380. /*++
  3381. Routine Description:
  3382. This routine returns information about the currently active paging
  3383. files.
  3384. Arguments:
  3385. SystemInformation - Returns the paging file information.
  3386. SystemInformationLength - Supplies the length of the SystemInformation
  3387. buffer.
  3388. Length - Returns the length of the paging file information placed in the
  3389. buffer.
  3390. Return Value:
  3391. Returns the status of the operation.
  3392. --*/
  3393. {
  3394. PSYSTEM_PAGEFILE_INFORMATION PageFileInfo;
  3395. ULONG NextEntryOffset = 0;
  3396. ULONG TotalSize = 0;
  3397. ULONG i;
  3398. UNICODE_STRING UserBufferPageFileName;
  3399. PAGED_CODE();
  3400. *Length = 0;
  3401. PageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)SystemInformation;
  3402. PageFileInfo->TotalSize = 0;
  3403. for (i = 0; i < MmNumberOfPagingFiles; i += 1) {
  3404. PageFileInfo = (PSYSTEM_PAGEFILE_INFORMATION)(
  3405. (PUCHAR)PageFileInfo + NextEntryOffset);
  3406. NextEntryOffset = sizeof(SYSTEM_PAGEFILE_INFORMATION);
  3407. TotalSize += sizeof(SYSTEM_PAGEFILE_INFORMATION);
  3408. if (TotalSize > SystemInformationLength) {
  3409. return STATUS_INFO_LENGTH_MISMATCH;
  3410. }
  3411. PageFileInfo->TotalSize = (ULONG)MmPagingFile[i]->Size;
  3412. PageFileInfo->TotalInUse = (ULONG)MmPagingFile[i]->CurrentUsage;
  3413. PageFileInfo->PeakUsage = (ULONG)MmPagingFile[i]->PeakUsage;
  3414. //
  3415. // The PageFileName portion of the UserBuffer must be saved locally
  3416. // to protect against a malicious thread changing the contents. This
  3417. // is because we will reference the contents ourselves when the actual
  3418. // string is copied out carefully below.
  3419. //
  3420. UserBufferPageFileName.Length = MmPagingFile[i]->PageFileName.Length;
  3421. UserBufferPageFileName.MaximumLength = (USHORT)(MmPagingFile[i]->PageFileName.Length + sizeof(WCHAR));
  3422. UserBufferPageFileName.Buffer = (PWCHAR)(PageFileInfo + 1);
  3423. PageFileInfo->PageFileName = UserBufferPageFileName;
  3424. TotalSize += ROUND_UP (UserBufferPageFileName.MaximumLength,
  3425. sizeof(ULONG));
  3426. NextEntryOffset += ROUND_UP (UserBufferPageFileName.MaximumLength,
  3427. sizeof(ULONG));
  3428. if (TotalSize > SystemInformationLength) {
  3429. return STATUS_INFO_LENGTH_MISMATCH;
  3430. }
  3431. //
  3432. // Carefully reference the user buffer here.
  3433. //
  3434. RtlCopyMemory(UserBufferPageFileName.Buffer,
  3435. MmPagingFile[i]->PageFileName.Buffer,
  3436. MmPagingFile[i]->PageFileName.Length);
  3437. UserBufferPageFileName.Buffer[
  3438. MmPagingFile[i]->PageFileName.Length/sizeof(WCHAR)] = UNICODE_NULL;
  3439. PageFileInfo->NextEntryOffset = NextEntryOffset;
  3440. }
  3441. PageFileInfo->NextEntryOffset = 0;
  3442. *Length = TotalSize;
  3443. return STATUS_SUCCESS;
  3444. }
  3445. NTSTATUS
  3446. MiCheckPageFileMapping (
  3447. IN PFILE_OBJECT File
  3448. )
  3449. /*++
  3450. Routine Description:
  3451. Non-pagable routine to check to see if a given file has
  3452. no sections and therefore is eligible to become a paging file.
  3453. Arguments:
  3454. File - Supplies a pointer to the file object.
  3455. Return Value:
  3456. Returns STATUS_SUCCESS if the file can be used as a paging file.
  3457. --*/
  3458. {
  3459. KIRQL OldIrql;
  3460. LOCK_PFN (OldIrql);
  3461. if (File->SectionObjectPointer == NULL) {
  3462. UNLOCK_PFN (OldIrql);
  3463. return STATUS_SUCCESS;
  3464. }
  3465. if ((File->SectionObjectPointer->DataSectionObject != NULL) ||
  3466. (File->SectionObjectPointer->ImageSectionObject != NULL)) {
  3467. UNLOCK_PFN (OldIrql);
  3468. return STATUS_INCOMPATIBLE_FILE_MAP;
  3469. }
  3470. UNLOCK_PFN (OldIrql);
  3471. return STATUS_SUCCESS;
  3472. }
  3473. VOID
  3474. MiInsertPageFileInList (
  3475. VOID
  3476. )
  3477. /*++
  3478. Routine Description:
  3479. Non-pagable routine to add a page file into the list
  3480. of system wide page files.
  3481. Arguments:
  3482. None, implicitly found through page file structures.
  3483. Return Value:
  3484. None. Operation cannot fail.
  3485. --*/
  3486. {
  3487. KIRQL OldIrql;
  3488. SIZE_T FreeSpace;
  3489. SIZE_T MaximumSize;
  3490. LOCK_PFN (OldIrql);
  3491. MmNumberOfPagingFiles += 1;
  3492. if (IsListEmpty (&MmPagingFileHeader.ListHead)) {
  3493. KeSetEvent (&MmPagingFileHeader.Event, 0, FALSE);
  3494. }
  3495. InsertTailList (&MmPagingFileHeader.ListHead,
  3496. &MmPagingFile[MmNumberOfPagingFiles - 1]->Entry[0]->Links);
  3497. MmPagingFile[MmNumberOfPagingFiles - 1]->Entry[0]->CurrentList =
  3498. &MmPagingFileHeader.ListHead;
  3499. InsertTailList (&MmPagingFileHeader.ListHead,
  3500. &MmPagingFile[MmNumberOfPagingFiles - 1]->Entry[1]->Links);
  3501. MmPagingFile[MmNumberOfPagingFiles - 1]->Entry[1]->CurrentList =
  3502. &MmPagingFileHeader.ListHead;
  3503. FreeSpace = MmPagingFile[MmNumberOfPagingFiles - 1]->FreeSpace;
  3504. MaximumSize = MmPagingFile[MmNumberOfPagingFiles - 1]->MaximumSize;
  3505. MmNumberOfActiveMdlEntries += 2;
  3506. UNLOCK_PFN (OldIrql);
  3507. //
  3508. // Increase the systemwide commit limit maximum first. Then increase
  3509. // the current limit.
  3510. //
  3511. InterlockedExchangeAddSizeT (&MmTotalCommitLimitMaximum, MaximumSize);
  3512. InterlockedExchangeAddSizeT (&MmTotalCommitLimit, FreeSpace);
  3513. return;
  3514. }
  3515. VOID
  3516. MiPageFileFull (
  3517. VOID
  3518. )
  3519. /*++
  3520. Routine Description:
  3521. This routine is called when no space can be found in a paging file.
  3522. It looks through all the paging files to see if ample space is
  3523. available and if not, tries to expand the paging files.
  3524. If more than 90% of all the paging files are in use, the commitment limit
  3525. is set to the total and then 100 pages are added.
  3526. Arguments:
  3527. None.
  3528. Return Value:
  3529. None.
  3530. --*/
  3531. {
  3532. ULONG i;
  3533. PFN_NUMBER Total;
  3534. PFN_NUMBER Free;
  3535. SIZE_T QuotaCharge;
  3536. if (MmNumberOfPagingFiles == 0) {
  3537. return;
  3538. }
  3539. Total = 0;
  3540. Free = 0;
  3541. for (i = 0; i < MmNumberOfPagingFiles; i += 1) {
  3542. Total += MmPagingFile[i]->Size;
  3543. Free += MmPagingFile[i]->FreeSpace;
  3544. }
  3545. //
  3546. // Check to see if more than 90% of the total space has been used.
  3547. //
  3548. if (((Total >> 5) + (Total >> 4)) >= Free) {
  3549. //
  3550. // Try to expand the paging files.
  3551. //
  3552. // Check (unsynchronized is ok) commit limits of each pagefile.
  3553. // If all the pagefiles are already at their maximums, then don't
  3554. // make things worse by setting commit to the maximum - this gives
  3555. // systems with lots of memory a longer lease on life when they have
  3556. // small pagefiles.
  3557. //
  3558. i = 0;
  3559. do {
  3560. if (MmPagingFile[i]->MaximumSize > MmPagingFile[i]->Size) {
  3561. break;
  3562. }
  3563. i += 1;
  3564. } while (i < MmNumberOfPagingFiles);
  3565. if (i == MmNumberOfPagingFiles) {
  3566. //
  3567. // No pagefiles can be extended,
  3568. // display a popup if we haven't before.
  3569. //
  3570. MiCauseOverCommitPopup ();
  3571. return;
  3572. }
  3573. QuotaCharge = MmTotalCommitLimit - MmTotalCommittedPages;
  3574. //
  3575. // IFF the total number of committed pages is less than the limit,
  3576. // or in any event, no more than 50 pages past the limit,
  3577. // then charge pages against the commitment to trigger pagefile
  3578. // expansion.
  3579. //
  3580. // If the total commit is more than 50 past the limit, then don't
  3581. // bother trying to extend the pagefile.
  3582. //
  3583. if ((SSIZE_T)QuotaCharge + 50 > 0) {
  3584. if ((SSIZE_T)QuotaCharge < 50) {
  3585. QuotaCharge = 50;
  3586. }
  3587. MiChargeCommitmentCantExpand (QuotaCharge, TRUE);
  3588. MM_TRACK_COMMIT (MM_DBG_COMMIT_PAGEFILE_FULL, QuotaCharge);
  3589. //
  3590. // Display a popup if we haven't before.
  3591. //
  3592. MiCauseOverCommitPopup ();
  3593. MiReturnCommitment (QuotaCharge);
  3594. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_PAGEFILE_FULL, QuotaCharge);
  3595. }
  3596. }
  3597. return;
  3598. }
  3599. VOID
  3600. MiFlushAllPages (
  3601. VOID
  3602. )
  3603. /*++
  3604. Routine Description:
  3605. Forces a write of all modified pages.
  3606. Arguments:
  3607. None.
  3608. Return Value:
  3609. None.
  3610. Environment:
  3611. Kernel mode. No locks held. APC_LEVEL or less.
  3612. --*/
  3613. {
  3614. ULONG j;
  3615. //
  3616. // If there are no paging files, then no sense in waiting for
  3617. // modified writes to complete.
  3618. //
  3619. if (MmNumberOfPagingFiles == 0) {
  3620. return;
  3621. }
  3622. MmWriteAllModifiedPages = TRUE;
  3623. KeSetEvent (&MmModifiedPageWriterEvent, 0, FALSE);
  3624. j = 0xff;
  3625. do {
  3626. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&Mm30Milliseconds);
  3627. j -= 1;
  3628. } while ((MmModifiedPageListHead.Total > 50) && (j > 0));
  3629. MmWriteAllModifiedPages = FALSE;
  3630. return;
  3631. }
  3632. LOGICAL
  3633. MiIssuePageExtendRequest (
  3634. IN PMMPAGE_FILE_EXPANSION PageExtend
  3635. )
  3636. /*++
  3637. Routine Description:
  3638. Queue a message to the segment dereferencing / pagefile extending
  3639. thread to see if the page file can be extended. Extension is done
  3640. in the context of a system thread due to mutexes which the current
  3641. thread may be holding.
  3642. Arguments:
  3643. PageExtend - Supplies a pointer to the page file extension request.
  3644. Return Value:
  3645. TRUE indicates the request completed. FALSE indicates the request timed
  3646. out and was removed.
  3647. Environment:
  3648. Kernel mode. No locks held. APC_LEVEL or below.
  3649. --*/
  3650. {
  3651. KIRQL OldIrql;
  3652. NTSTATUS status;
  3653. PLIST_ENTRY NextEntry;
  3654. PETHREAD Thread;
  3655. Thread = PsGetCurrentThread ();
  3656. //
  3657. // The segment dereference thread cannot wait for itself !
  3658. //
  3659. if (Thread->StartAddress == (PVOID)(ULONG_PTR)MiDereferenceSegmentThread) {
  3660. return FALSE;
  3661. }
  3662. ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql);
  3663. InsertHeadList (&MmDereferenceSegmentHeader.ListHead,
  3664. &PageExtend->DereferenceList);
  3665. ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
  3666. KeReleaseSemaphore (&MmDereferenceSegmentHeader.Semaphore,
  3667. 0L,
  3668. 1L,
  3669. TRUE);
  3670. //
  3671. // Wait for the thread to extend the paging file.
  3672. //
  3673. status = KeWaitForSingleObject (&PageExtend->Event,
  3674. Executive,
  3675. KernelMode,
  3676. FALSE,
  3677. (PageExtend->RequestedExpansionSize < 10) ?
  3678. (PLARGE_INTEGER)&MmOneSecond : (PLARGE_INTEGER)&MmTwentySeconds);
  3679. if (status == STATUS_TIMEOUT) {
  3680. //
  3681. // The wait has timed out, if this request has not
  3682. // been processed, remove it from the list and check
  3683. // to see if we should allow this request to succeed.
  3684. // This prevents a deadlock between the file system
  3685. // trying to allocate memory in the FSP and the
  3686. // segment dereferencing thread trying to close a
  3687. // file object, and waiting in the file system.
  3688. //
  3689. KdPrint(("MiIssuePageExtendRequest: wait timed out, page-extend= %p, quota = %lx\n",
  3690. PageExtend, PageExtend->RequestedExpansionSize));
  3691. ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql);
  3692. NextEntry = MmDereferenceSegmentHeader.ListHead.Flink;
  3693. while (NextEntry != &MmDereferenceSegmentHeader.ListHead) {
  3694. //
  3695. // Check to see if this is the entry we are waiting for.
  3696. //
  3697. if (NextEntry == &PageExtend->DereferenceList) {
  3698. //
  3699. // Remove this entry.
  3700. //
  3701. RemoveEntryList (&PageExtend->DereferenceList);
  3702. ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
  3703. return FALSE;
  3704. }
  3705. NextEntry = NextEntry->Flink;
  3706. }
  3707. ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
  3708. //
  3709. // Entry is being processed, wait for completion.
  3710. //
  3711. KdPrint (("MiIssuePageExtendRequest: rewaiting...\n"));
  3712. KeWaitForSingleObject (&PageExtend->Event,
  3713. Executive,
  3714. KernelMode,
  3715. FALSE,
  3716. NULL);
  3717. }
  3718. return TRUE;
  3719. }
  3720. VOID
  3721. MiIssuePageExtendRequestNoWait (
  3722. IN PFN_NUMBER SizeInPages
  3723. )
  3724. /*++
  3725. Routine Description:
  3726. Queue a message to the segment dereferencing / pagefile extending
  3727. thread to see if the page file can be extended. Extension is done
  3728. in the context of a system thread due to mutexes which the current
  3729. thread may be holding.
  3730. Arguments:
  3731. SizeInPages - Supplies the size in pages to increase the page file(s) by.
  3732. This is rounded up to a 1MB multiple by this routine.
  3733. Return Value:
  3734. TRUE indicates the request completed. FALSE indicates the request timed
  3735. out and was removed.
  3736. Environment:
  3737. Kernel mode. No locks held. DISPATCH_LEVEL or less.
  3738. Note this routine must be very careful to not use any paged
  3739. pool as the only reason it is being called is because pool is depleted.
  3740. --*/
  3741. {
  3742. KIRQL OldIrql;
  3743. LONG OriginalInProgress;
  3744. OriginalInProgress = InterlockedCompareExchange (
  3745. &MmAttemptForCantExtend.InProgress, 1, 0);
  3746. if (OriginalInProgress != 0) {
  3747. //
  3748. // An expansion request is already in progress, assume
  3749. // it will help enough (another can always be issued later) and
  3750. // that it will succeed.
  3751. //
  3752. return;
  3753. }
  3754. ASSERT (MmAttemptForCantExtend.InProgress == 1);
  3755. SizeInPages = (SizeInPages + ONEMB_IN_PAGES - 1) & ~(ONEMB_IN_PAGES - 1);
  3756. MmAttemptForCantExtend.ActualExpansion = 0;
  3757. MmAttemptForCantExtend.RequestedExpansionSize = SizeInPages;
  3758. ExAcquireSpinLock (&MmDereferenceSegmentHeader.Lock, &OldIrql);
  3759. InsertHeadList (&MmDereferenceSegmentHeader.ListHead,
  3760. &MmAttemptForCantExtend.DereferenceList);
  3761. ExReleaseSpinLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
  3762. KeReleaseSemaphore (&MmDereferenceSegmentHeader.Semaphore,
  3763. 0L,
  3764. 1L,
  3765. FALSE);
  3766. return;
  3767. }