Leaked source code of windows server 2003
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.

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