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

1023 lines
24 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. compress.c
  5. Abstract:
  6. This module contains the routines to support allow hardware to
  7. transparently compress physical memory.
  8. Author:
  9. Landy Wang (landyw) 21-Oct-2000
  10. Revision History:
  11. --*/
  12. #include "mi.h"
  13. #if defined (_MI_COMPRESSION)
  14. Enable the #if 0 code in cmdat3.c to allow Ratio specification.
  15. //
  16. // Compression public interface.
  17. //
  18. #define MM_PHYSICAL_MEMORY_PRODUCED_VIA_COMPRESSION 0x1
  19. typedef
  20. NTSTATUS
  21. (*PMM_SET_COMPRESSION_THRESHOLD) (
  22. IN ULONGLONG CompressionByteThreshold
  23. );
  24. typedef struct _MM_COMPRESSION_CONTEXT {
  25. ULONG Version;
  26. ULONG SizeInBytes;
  27. ULONGLONG ReservedBytes;
  28. PMM_SET_COMPRESSION_THRESHOLD SetCompressionThreshold;
  29. } MM_COMPRESSION_CONTEXT, *PMM_COMPRESSION_CONTEXT;
  30. #define MM_COMPRESSION_VERSION_INITIAL 1
  31. #define MM_COMPRESSION_VERSION_CURRENT 1
  32. NTSTATUS
  33. MmRegisterCompressionDevice (
  34. IN PMM_COMPRESSION_CONTEXT Context
  35. );
  36. NTSTATUS
  37. MmDeregisterCompressionDevice (
  38. IN PMM_COMPRESSION_CONTEXT Context
  39. );
  40. //
  41. // This defaults to 75% but can be overridden in the registry. At this
  42. // percentage of *real* physical memory in use, an interrupt is generated so
  43. // that memory management can zero pages to make more memory available.
  44. //
  45. #define MI_DEFAULT_COMPRESSION_THRESHOLD 75
  46. ULONG MmCompressionThresholdRatio;
  47. PFN_NUMBER MiNumberOfCompressionPages;
  48. PMM_SET_COMPRESSION_THRESHOLD MiSetCompressionThreshold;
  49. #if DBG
  50. KIRQL MiCompressionIrql;
  51. #endif
  52. //
  53. // Note there is also code in dynmem.c that is dependent on this #define.
  54. //
  55. #if defined (_MI_COMPRESSION_SUPPORTED_)
  56. typedef struct _MI_COMPRESSION_INFO {
  57. ULONG IsrPageProcessed;
  58. ULONG DpcPageProcessed;
  59. ULONG IsrForcedDpc;
  60. ULONG IsrFailedDpc;
  61. ULONG IsrRan;
  62. ULONG DpcRan;
  63. ULONG DpcsFired;
  64. ULONG IsrSkippedZeroedPage;
  65. ULONG DpcSkippedZeroedPage;
  66. ULONG CtxswapForcedDpcInsert;
  67. ULONG CtxswapFailedDpcInsert;
  68. ULONG PfnForcedDpcInsert;
  69. ULONG PfnFailedDpcInsert;
  70. } MI_COMPRESSION_INFO, *PMI_COMPRESSION_INFO;
  71. MI_COMPRESSION_INFO MiCompressionInfo; // LWFIX - temp remove.
  72. PFN_NUMBER MiCompressionOverHeadInPages;
  73. PKDPC MiCompressionDpcArray;
  74. CCHAR MiCompressionProcessors;
  75. VOID
  76. MiCompressionDispatch (
  77. IN PKDPC Dpc,
  78. IN PVOID DeferredContext,
  79. IN PVOID SystemArgument1,
  80. IN PVOID SystemArgument2
  81. );
  82. PVOID
  83. MiMapCompressionInHyperSpace (
  84. IN PFN_NUMBER PageFrameIndex
  85. );
  86. VOID
  87. MiUnmapCompressionInHyperSpace (
  88. VOID
  89. );
  90. SIZE_T
  91. MiMakeCompressibleMemoryAtDispatch (
  92. IN SIZE_T NumberOfBytes OPTIONAL
  93. );
  94. NTSTATUS
  95. MmRegisterCompressionDevice (
  96. IN PMM_COMPRESSION_CONTEXT Context
  97. )
  98. /*++
  99. Routine Description:
  100. This routine notifies memory management that compression hardware exists
  101. in the system. Memory management responds by initializing compression
  102. support here.
  103. Arguments:
  104. Context - Supplies the compression context pointer.
  105. Return Value:
  106. NTSTATUS.
  107. Environment:
  108. Kernel mode, PASSIVE_LEVEL.
  109. --*/
  110. {
  111. KIRQL OldIrql;
  112. PFN_NUMBER OverHeadInPages;
  113. CCHAR Processor;
  114. CCHAR NumberProcessors;
  115. PKDPC CompressionDpcArray;
  116. ASSERT (KeGetCurrentIrql () == PASSIVE_LEVEL);
  117. if (Context->Version != MM_COMPRESSION_VERSION_CURRENT) {
  118. return STATUS_INVALID_PARAMETER_1;
  119. }
  120. if (Context->SizeInBytes < sizeof (MM_COMPRESSION_CONTEXT)) {
  121. return STATUS_INVALID_PARAMETER_1;
  122. }
  123. //
  124. // If the subsequent hot-add cannot succeed then fail this API now.
  125. //
  126. if (MmDynamicPfn == 0) {
  127. return STATUS_NOT_SUPPORTED;
  128. }
  129. //
  130. // Hardware that can't generate a configurable interrupt is not supported.
  131. //
  132. if (Context->SetCompressionThreshold == NULL) {
  133. return STATUS_INVALID_PARAMETER_1;
  134. }
  135. //
  136. // ReservedBytes indicates the number of reserved bytes required by the
  137. // underlying hardware. For example, some hardware might have:
  138. //
  139. // 1. translation tables which are 1/64 of the fictional RAM total.
  140. //
  141. // 2. the first MB of memory is never compressed.
  142. //
  143. // 3. an L3 which is never compressed.
  144. //
  145. // etc.
  146. //
  147. // ReservedBytes would be the sum of all of these types of ranges.
  148. //
  149. OverHeadInPages = (PFN_COUNT)(Context->ReservedBytes / PAGE_SIZE);
  150. if (MmResidentAvailablePages < (SPFN_NUMBER) OverHeadInPages) {
  151. return STATUS_INSUFFICIENT_RESOURCES;
  152. }
  153. if (MmAvailablePages < OverHeadInPages) {
  154. MmEmptyAllWorkingSets ();
  155. if (MmAvailablePages < OverHeadInPages) {
  156. return STATUS_INSUFFICIENT_RESOURCES;
  157. }
  158. }
  159. //
  160. // Create a DPC for every processor in the system as servicing the
  161. // compression interrupt is critical.
  162. //
  163. NumberProcessors = KeNumberProcessors;
  164. CompressionDpcArray = ExAllocatePoolWithTag (NonPagedPool,
  165. NumberProcessors * sizeof (KDPC),
  166. 'pDmM');
  167. if (CompressionDpcArray == NULL) {
  168. return STATUS_INSUFFICIENT_RESOURCES;
  169. }
  170. for (Processor = 0; Processor < NumberProcessors; Processor += 1) {
  171. KeInitializeDpc (CompressionDpcArray + Processor, MiCompressionDispatch, NULL);
  172. //
  173. // Set importance so this DPC always gets queued at the head.
  174. //
  175. KeSetImportanceDpc (CompressionDpcArray + Processor, HighImportance);
  176. KeSetTargetProcessorDpc (CompressionDpcArray + Processor, Processor);
  177. }
  178. LOCK_PFN (OldIrql);
  179. if (MmCompressionThresholdRatio == 0) {
  180. MmCompressionThresholdRatio = MI_DEFAULT_COMPRESSION_THRESHOLD;
  181. }
  182. else if (MmCompressionThresholdRatio > 100) {
  183. MmCompressionThresholdRatio = 100;
  184. }
  185. if ((MmResidentAvailablePages < (SPFN_NUMBER) OverHeadInPages) ||
  186. (MmAvailablePages < OverHeadInPages)) {
  187. UNLOCK_PFN (OldIrql);
  188. ExFreePool (CompressionDpcArray);
  189. return STATUS_INSUFFICIENT_RESOURCES;
  190. }
  191. MmResidentAvailablePages -= OverHeadInPages;
  192. MmAvailablePages -= (PFN_COUNT) OverHeadInPages;
  193. //
  194. // Snap our own copy to prevent busted drivers from causing overcommits
  195. // if they deregister improperly.
  196. //
  197. MiCompressionOverHeadInPages += OverHeadInPages;
  198. ASSERT (MiNumberOfCompressionPages == 0);
  199. ASSERT (MiSetCompressionThreshold == NULL);
  200. MiSetCompressionThreshold = Context->SetCompressionThreshold;
  201. if (MiCompressionDpcArray == NULL) {
  202. MiCompressionDpcArray = CompressionDpcArray;
  203. CompressionDpcArray = NULL;
  204. MiCompressionProcessors = NumberProcessors;
  205. }
  206. UNLOCK_PFN (OldIrql);
  207. if (CompressionDpcArray != NULL) {
  208. ExFreePool (CompressionDpcArray);
  209. }
  210. return STATUS_SUCCESS;
  211. }
  212. NTSTATUS
  213. MiArmCompressionInterrupt (
  214. VOID
  215. )
  216. /*++
  217. Routine Description:
  218. This routine arms the hardware-generated compression interrupt.
  219. Arguments:
  220. None.
  221. Return Value:
  222. NTSTATUS.
  223. Environment:
  224. Kernel mode, PFN lock held.
  225. --*/
  226. {
  227. NTSTATUS Status;
  228. PFN_NUMBER RealPages;
  229. ULONGLONG ByteThreshold;
  230. MM_PFN_LOCK_ASSERT();
  231. if (MiSetCompressionThreshold == NULL) {
  232. return STATUS_SUCCESS;
  233. }
  234. RealPages = MmNumberOfPhysicalPages - MiNumberOfCompressionPages - MiCompressionOverHeadInPages;
  235. ByteThreshold = (RealPages * MmCompressionThresholdRatio) / 100;
  236. ByteThreshold *= PAGE_SIZE;
  237. //
  238. // Note this callout is made with the PFN lock held !
  239. //
  240. Status = (*MiSetCompressionThreshold) (ByteThreshold);
  241. if (!NT_SUCCESS (Status)) {
  242. //
  243. // If the hardware fails, all is lost.
  244. //
  245. KeBugCheckEx (MEMORY_MANAGEMENT,
  246. 0x61941,
  247. MmNumberOfPhysicalPages,
  248. RealPages,
  249. MmCompressionThresholdRatio);
  250. }
  251. return Status;
  252. }
  253. NTSTATUS
  254. MmDeregisterCompressionDevice (
  255. IN PMM_COMPRESSION_CONTEXT Context
  256. )
  257. /*++
  258. Routine Description:
  259. This routine notifies memory management that compression hardware is
  260. being removed. Note the compression driver must have already SUCCESSFULLY
  261. called MmRemovePhysicalMemoryEx.
  262. Arguments:
  263. Context - Supplies the compression context pointer.
  264. Return Value:
  265. STATUS_SUCCESS if compression support is initialized properly.
  266. Environment:
  267. Kernel mode, PASSIVE_LEVEL.
  268. --*/
  269. {
  270. KIRQL OldIrql;
  271. PFN_COUNT OverHeadInPages;
  272. ASSERT (KeGetCurrentIrql () == PASSIVE_LEVEL);
  273. OverHeadInPages = (PFN_COUNT)(Context->ReservedBytes / PAGE_SIZE);
  274. LOCK_PFN (OldIrql);
  275. if (OverHeadInPages > MiCompressionOverHeadInPages) {
  276. UNLOCK_PFN (OldIrql);
  277. return STATUS_INVALID_PARAMETER;
  278. }
  279. MmResidentAvailablePages += OverHeadInPages;
  280. MmAvailablePages += OverHeadInPages;
  281. ASSERT (MiCompressionOverHeadInPages == OverHeadInPages);
  282. MiCompressionOverHeadInPages -= OverHeadInPages;
  283. MiSetCompressionThreshold = NULL;
  284. UNLOCK_PFN (OldIrql);
  285. return STATUS_SUCCESS;
  286. }
  287. VOID
  288. MiCompressionDispatch (
  289. IN PKDPC Dpc,
  290. IN PVOID DeferredContext,
  291. IN PVOID SystemArgument1,
  292. IN PVOID SystemArgument2
  293. )
  294. /*++
  295. Routine Description:
  296. Called to make memory compressible if the PFN lock could not be
  297. acquired during the original device interrupt.
  298. Arguments:
  299. Dpc - Supplies a pointer to a control object of type DPC.
  300. SystemArgument1 - Supplies the number of bytes to make compressible.
  301. Return Value:
  302. None.
  303. Environment:
  304. Kernel mode. DISPATCH_LEVEL.
  305. --*/
  306. {
  307. SIZE_T NumberOfBytes;
  308. UNREFERENCED_PARAMETER (Dpc);
  309. UNREFERENCED_PARAMETER (DeferredContext);
  310. UNREFERENCED_PARAMETER (SystemArgument2);
  311. NumberOfBytes = (SIZE_T) SystemArgument1;
  312. MiCompressionInfo.DpcsFired += 1;
  313. MiMakeCompressibleMemoryAtDispatch (NumberOfBytes);
  314. }
  315. SIZE_T
  316. MmMakeCompressibleMemory (
  317. IN SIZE_T NumberOfBytes OPTIONAL
  318. )
  319. /*++
  320. Routine Description:
  321. This routine attempts to move pages from transition to zero so that
  322. hardware compression can reclaim the physical memory.
  323. Arguments:
  324. NumberOfBytes - Supplies the number of bytes to make compressible.
  325. Zero indicates as much as possible.
  326. Return Value:
  327. Returns the number of bytes made compressible.
  328. Environment:
  329. Kernel mode. Any IRQL as this is called from device interrupt service
  330. routines.
  331. --*/
  332. {
  333. KIRQL OldIrql;
  334. BOOLEAN Queued;
  335. #if !defined(NT_UP)
  336. PFN_NUMBER PageFrameIndex;
  337. MMLISTS MemoryList;
  338. PMMPFNLIST ListHead;
  339. PMMPFN Pfn1;
  340. CCHAR Processor;
  341. PFN_NUMBER Total;
  342. PVOID ZeroBase;
  343. PKPRCB Prcb;
  344. PFN_NUMBER RequestedPages;
  345. PFN_NUMBER ActualPages;
  346. PKSPIN_LOCK_QUEUE LockQueuePfn;
  347. PKSPIN_LOCK_QUEUE LockQueueContextSwap;
  348. #endif
  349. //
  350. // LWFIX: interlocked add in the request size above so overlapping
  351. // requests can be processed.
  352. //
  353. OldIrql = KeGetCurrentIrql();
  354. if (OldIrql <= DISPATCH_LEVEL) {
  355. return MiMakeCompressibleMemoryAtDispatch (NumberOfBytes);
  356. }
  357. #if defined(NT_UP)
  358. //
  359. // In uniprocessor configurations, there is no indication as to whether
  360. // various locks of interest (context swap & PFN) are owned because the
  361. // uniprocessor kernel macros these into merely IRQL raises. Therefore
  362. // this routine must be conservative when called above DISPATCH_LEVEL and
  363. // assume the lock is owned and just always queue a DPC in these cases.
  364. //
  365. Queued = KeInsertQueueDpc (MiCompressionDpcArray,
  366. (PVOID) NumberOfBytes,
  367. NULL);
  368. if (Queued == TRUE) {
  369. MiCompressionInfo.CtxswapForcedDpcInsert += 1;
  370. }
  371. else {
  372. MiCompressionInfo.CtxswapFailedDpcInsert += 1;
  373. }
  374. return 0;
  375. #else
  376. #if DBG
  377. //
  378. // Make sure this interrupt always comes in at the same device IRQL.
  379. //
  380. ASSERT ((MiCompressionIrql == 0) || (OldIrql == MiCompressionIrql));
  381. MiCompressionIrql = OldIrql;
  382. #endif
  383. Prcb = KeGetCurrentPrcb();
  384. LockQueueContextSwap = &Prcb->LockQueue[LockQueueContextSwapLock];
  385. //
  386. // The context swap lock is needed for TB flushes. The interrupted thread
  387. // may already own this in the midst of doing a TB flush without the PFN
  388. // lock being held. Since there is no safe way to tell if the current
  389. // processor owns the context swap lock, just do a try-acquire followed
  390. // by an immediate release to decide if it is safe to proceed.
  391. //
  392. if (KeTryToAcquireQueuedSpinLockAtRaisedIrql (LockQueueContextSwap) == FALSE) {
  393. //
  394. // Unable to acquire the spinlock, queue a DPC to pick it up instead.
  395. //
  396. for (Processor = 0; Processor < MiCompressionProcessors; Processor += 1) {
  397. Queued = KeInsertQueueDpc (MiCompressionDpcArray + Processor,
  398. (PVOID) NumberOfBytes,
  399. NULL);
  400. if (Queued == TRUE) {
  401. MiCompressionInfo.CtxswapForcedDpcInsert += 1;
  402. }
  403. else {
  404. MiCompressionInfo.CtxswapFailedDpcInsert += 1;
  405. }
  406. }
  407. return 0;
  408. }
  409. KeReleaseQueuedSpinLockFromDpcLevel (LockQueueContextSwap);
  410. RequestedPages = NumberOfBytes >> PAGE_SHIFT;
  411. ActualPages = 0;
  412. MemoryList = FreePageList;
  413. ListHead = MmPageLocationList[MemoryList];
  414. LockQueuePfn = &Prcb->LockQueue[LockQueuePfnLock];
  415. if (KeTryToAcquireQueuedSpinLockAtRaisedIrql (LockQueuePfn) == FALSE) {
  416. //
  417. // Unable to acquire the spinlock, queue a DPC to pick it up instead.
  418. //
  419. for (Processor = 0; Processor < MiCompressionProcessors; Processor += 1) {
  420. Queued = KeInsertQueueDpc (MiCompressionDpcArray + Processor,
  421. (PVOID) NumberOfBytes,
  422. NULL);
  423. if (Queued == TRUE) {
  424. MiCompressionInfo.PfnForcedDpcInsert += 1;
  425. }
  426. else {
  427. MiCompressionInfo.PfnFailedDpcInsert += 1;
  428. }
  429. }
  430. return 0;
  431. }
  432. MiCompressionInfo.IsrRan += 1;
  433. //
  434. // Run the free and transition list and zero the pages.
  435. //
  436. while (MemoryList <= StandbyPageList) {
  437. Total = ListHead->Total;
  438. PageFrameIndex = ListHead->Flink;
  439. while (Total != 0) {
  440. //
  441. // Transition pages may need restoration which requires a
  442. // hyperspace mapping plus control area deletion actions all of
  443. // which occur at DISPATCH_LEVEL. So if we're at device IRQL,
  444. // only do the minimum and queue the rest.
  445. //
  446. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  447. if ((Pfn1->u3.e1.InPageError == 1) &&
  448. (Pfn1->u3.e1.ReadInProgress == 1)) {
  449. //
  450. // This page is already zeroed so skip it.
  451. //
  452. MiCompressionInfo.IsrSkippedZeroedPage += 1;
  453. }
  454. else {
  455. //
  456. // Zero the page directly now instead of waiting for the low
  457. // priority zeropage thread to get a slice. Note that the
  458. // slower mapping and zeroing routines are used here because
  459. // the faster ones are for the zeropage thread only.
  460. // Maybe we should change this someday.
  461. //
  462. ZeroBase = MiMapCompressionInHyperSpace (PageFrameIndex);
  463. RtlZeroMemory (ZeroBase, PAGE_SIZE);
  464. MiUnmapCompressionInHyperSpace ();
  465. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  466. //
  467. // Overload ReadInProgress to signify that collided faults that
  468. // occur before the PTE is completely restored will know to
  469. // delay and retry until the page (and PTE) are updated.
  470. //
  471. Pfn1->u3.e1.InPageError = 1;
  472. ASSERT (Pfn1->u3.e1.ReadInProgress == 0);
  473. Pfn1->u3.e1.ReadInProgress = 1;
  474. ActualPages += 1;
  475. if (ActualPages == RequestedPages) {
  476. MemoryList = StandbyPageList;
  477. ListHead = MmPageLocationList[MemoryList];
  478. break;
  479. }
  480. }
  481. Total -= 1;
  482. PageFrameIndex = Pfn1->u1.Flink;
  483. }
  484. MemoryList += 1;
  485. ListHead += 1;
  486. }
  487. if (ActualPages != 0) {
  488. //
  489. // Rearm the interrupt as pages have now been zeroed.
  490. //
  491. MiArmCompressionInterrupt ();
  492. }
  493. KeReleaseQueuedSpinLockFromDpcLevel (LockQueuePfn);
  494. if (ActualPages != 0) {
  495. //
  496. // Pages were zeroed - queue a DPC to the current processor to
  497. // move them to the zero list. Note this is not critical path so
  498. // don't bother sending a DPC to every processor for this case.
  499. //
  500. MiCompressionInfo.IsrPageProcessed += (ULONG)ActualPages;
  501. Processor = (CCHAR) KeGetCurrentProcessorNumber ();
  502. //
  503. // Ensure a hot-added processor scenario just works.
  504. //
  505. if (Processor >= MiCompressionProcessors) {
  506. Processor = MiCompressionProcessors;
  507. }
  508. Queued = KeInsertQueueDpc (MiCompressionDpcArray + Processor,
  509. (PVOID) NumberOfBytes,
  510. NULL);
  511. if (Queued == TRUE) {
  512. MiCompressionInfo.IsrForcedDpc += 1;
  513. }
  514. else {
  515. MiCompressionInfo.IsrFailedDpc += 1;
  516. }
  517. }
  518. return (ActualPages << PAGE_SHIFT);
  519. #endif
  520. }
  521. SIZE_T
  522. MiMakeCompressibleMemoryAtDispatch (
  523. IN SIZE_T NumberOfBytes OPTIONAL
  524. )
  525. /*++
  526. Routine Description:
  527. This routine attempts to move pages from transition to zero so that
  528. hardware compression can reclaim the physical memory.
  529. Arguments:
  530. NumberOfBytes - Supplies the number of bytes to make compressible.
  531. Zero indicates as much as possible.
  532. Return Value:
  533. Returns the number of bytes made compressible.
  534. Environment:
  535. Kernel mode. DISPATCH_LEVEL.
  536. --*/
  537. {
  538. KIRQL OldIrql;
  539. PFN_NUMBER PageFrameIndex;
  540. PFN_NUMBER PageFrameIndex2;
  541. PVOID ZeroBase;
  542. PMMPFN Pfn1;
  543. MMLISTS MemoryList;
  544. PMMPFNLIST ListHead;
  545. PFN_NUMBER RequestedPages;
  546. PFN_NUMBER ActualPages;
  547. LOGICAL NeedToZero;
  548. ASSERT (KeGetCurrentIrql () == DISPATCH_LEVEL);
  549. RequestedPages = NumberOfBytes >> PAGE_SHIFT;
  550. ActualPages = 0;
  551. MemoryList = FreePageList;
  552. ListHead = MmPageLocationList[MemoryList];
  553. MiCompressionInfo.DpcRan += 1;
  554. LOCK_PFN2 (OldIrql);
  555. //
  556. // Run the free and transition list and zero the pages.
  557. //
  558. while (MemoryList <= StandbyPageList) {
  559. while (ListHead->Total != 0) {
  560. //
  561. // Before removing the page from the head of the list (which will
  562. // zero the flag bits), snap whether it's been zeroed by our ISR
  563. // or whether we need to zero it here.
  564. //
  565. PageFrameIndex = ListHead->Flink;
  566. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  567. NeedToZero = TRUE;
  568. if ((Pfn1->u3.e1.InPageError == 1) && (Pfn1->u3.e1.ReadInProgress == 1)) {
  569. MiCompressionInfo.DpcSkippedZeroedPage += 1;
  570. NeedToZero = FALSE;
  571. }
  572. //
  573. // Transition pages may need restoration which requires a
  574. // hyperspace mapping plus control area deletion actions all of
  575. // which occur at DISPATCH_LEVEL. Since we're at DISPATCH_LEVEL
  576. // now, go ahead and do it.
  577. //
  578. PageFrameIndex2 = MiRemovePageFromList (ListHead);
  579. ASSERT (PageFrameIndex == PageFrameIndex2);
  580. //
  581. // Zero the page directly now instead of waiting for the low
  582. // priority zeropage thread to get a slice. Note that the
  583. // slower mapping and zeroing routines are used here because
  584. // the faster ones are for the zeropage thread only.
  585. // Maybe we should change this someday.
  586. //
  587. if (NeedToZero == TRUE) {
  588. ZeroBase = MiMapCompressionInHyperSpace (PageFrameIndex);
  589. RtlZeroMemory (ZeroBase, PAGE_SIZE);
  590. MiUnmapCompressionInHyperSpace ();
  591. }
  592. ASSERT (Pfn1->u2.ShareCount == 0);
  593. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  594. MiInsertPageInList (&MmZeroedPageListHead, PageFrameIndex);
  595. //
  596. // We have changed (zeroed) the contents of this page.
  597. // If memory mirroring is in progress, the bitmap must be updated.
  598. //
  599. if (MiMirroringActive == TRUE) {
  600. RtlSetBit (MiMirrorBitMap2, (ULONG)PageFrameIndex);
  601. }
  602. MiCompressionInfo.DpcPageProcessed += 1;
  603. ActualPages += 1;
  604. if (ActualPages == RequestedPages) {
  605. MemoryList = StandbyPageList;
  606. ListHead = MmPageLocationList[MemoryList];
  607. break;
  608. }
  609. }
  610. MemoryList += 1;
  611. ListHead += 1;
  612. }
  613. //
  614. // Rearm the interrupt as pages have now been zeroed.
  615. //
  616. MiArmCompressionInterrupt ();
  617. UNLOCK_PFN2 (OldIrql);
  618. return (ActualPages << PAGE_SHIFT);
  619. }
  620. PVOID
  621. MiMapCompressionInHyperSpace (
  622. IN PFN_NUMBER PageFrameIndex
  623. )
  624. /*++
  625. Routine Description:
  626. This procedure maps the specified physical page into the
  627. PTE within hyper space reserved explicitly for compression page
  628. mapping.
  629. The PTE is guaranteed to always be available since the PFN lock is held.
  630. Arguments:
  631. PageFrameIndex - Supplies the physical page number to map.
  632. Return Value:
  633. Returns the virtual address where the specified physical page was
  634. mapped.
  635. Environment:
  636. Kernel mode, PFN lock held, any IRQL.
  637. --*/
  638. {
  639. MMPTE TempPte;
  640. PMMPTE PointerPte;
  641. PVOID FlushVaPointer;
  642. ASSERT (PageFrameIndex != 0);
  643. TempPte = ValidPtePte;
  644. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  645. FlushVaPointer = (PVOID) (ULONG_PTR) COMPRESSION_MAPPING_PTE;
  646. //
  647. // Ensure both modified and accessed bits are set so the hardware doesn't
  648. // ever write this PTE.
  649. //
  650. ASSERT (TempPte.u.Hard.Dirty == 1);
  651. ASSERT (TempPte.u.Hard.Accessed == 1);
  652. PointerPte = MiGetPteAddress (COMPRESSION_MAPPING_PTE);
  653. ASSERT (PointerPte->u.Long == 0);
  654. //
  655. // Only flush the TB on the current processor as no context switch can
  656. // occur while using this mapping.
  657. //
  658. KeFlushSingleTb (FlushVaPointer,
  659. TRUE,
  660. FALSE,
  661. (PHARDWARE_PTE) PointerPte,
  662. TempPte.u.Flush);
  663. return (PVOID) MiGetVirtualAddressMappedByPte (PointerPte);
  664. }
  665. __forceinline
  666. VOID
  667. MiUnmapCompressionInHyperSpace (
  668. VOID
  669. )
  670. /*++
  671. Routine Description:
  672. This procedure unmaps the PTE reserved for mapping the compression page.
  673. Arguments:
  674. None.
  675. Return Value:
  676. None.
  677. Environment:
  678. Kernel mode, PFN lock held, any IRQL.
  679. --*/
  680. {
  681. PMMPTE PointerPte;
  682. PointerPte = MiGetPteAddress (COMPRESSION_MAPPING_PTE);
  683. //
  684. // Capture the number of waiters.
  685. //
  686. ASSERT (PointerPte->u.Long != 0);
  687. PointerPte->u.Long = 0;
  688. return;
  689. }
  690. #else
  691. NTSTATUS
  692. MmRegisterCompressionDevice (
  693. IN PMM_COMPRESSION_CONTEXT Context
  694. )
  695. {
  696. UNREFERENCED_PARAMETER (Context);
  697. return STATUS_NOT_SUPPORTED;
  698. }
  699. NTSTATUS
  700. MmDeregisterCompressionDevice (
  701. IN PMM_COMPRESSION_CONTEXT Context
  702. )
  703. {
  704. UNREFERENCED_PARAMETER (Context);
  705. return STATUS_NOT_SUPPORTED;
  706. }
  707. SIZE_T
  708. MmMakeCompressibleMemory (
  709. IN SIZE_T NumberOfBytes OPTIONAL
  710. )
  711. {
  712. UNREFERENCED_PARAMETER (NumberOfBytes);
  713. return 0;
  714. }
  715. NTSTATUS
  716. MiArmCompressionInterrupt (
  717. VOID
  718. )
  719. {
  720. return STATUS_NOT_SUPPORTED;
  721. }
  722. #endif
  723. #endif