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.

749 lines
22 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. mirror.c
  5. Abstract:
  6. This module contains the routines to support memory mirroring.
  7. Author:
  8. Landy Wang (landyw) 17-Jan-2000
  9. Revision History:
  10. --*/
  11. #include "mi.h"
  12. #define MIRROR_MAX_PHASE_ZERO_PASSES 8
  13. //
  14. // This is set via the registry.
  15. //
  16. ULONG MmMirroring = 0;
  17. //
  18. // These bitmaps are allocated at system startup if the
  19. // registry key above is set.
  20. //
  21. PRTL_BITMAP MiMirrorBitMap;
  22. PRTL_BITMAP MiMirrorBitMap2;
  23. //
  24. // This is set if a mirroring operation is in progress.
  25. //
  26. LOGICAL MiMirroringActive = FALSE;
  27. extern LOGICAL MiZeroingDisabled;
  28. #if DBG
  29. ULONG MiMirrorDebug = 1;
  30. ULONG MiMirrorPassMax[2];
  31. #endif
  32. #pragma alloc_text(PAGELK, MmCreateMirror)
  33. NTSTATUS
  34. MmCreateMirror (
  35. VOID
  36. )
  37. {
  38. KIRQL OldIrql;
  39. KIRQL ExitIrql;
  40. ULONG Limit;
  41. ULONG Color;
  42. ULONG IterationCount;
  43. PMMPFN Pfn1;
  44. PMMPFNLIST ListHead;
  45. PFN_NUMBER PreviousPage;
  46. PFN_NUMBER ThisPage;
  47. PFN_NUMBER PageFrameIndex;
  48. MMLISTS MemoryList;
  49. ULONG LengthOfClearRun;
  50. ULONG LengthOfSetRun;
  51. ULONG StartingRunIndex;
  52. ULONG BitMapIndex;
  53. ULONG BitMapHint;
  54. ULONG BitMapBytes;
  55. PULONG BitMap1;
  56. PULONG BitMap2;
  57. PHYSICAL_ADDRESS PhysicalAddress;
  58. LARGE_INTEGER PhysicalBytes;
  59. NTSTATUS Status;
  60. ULONG BitMapSize;
  61. PFN_NUMBER PagesWritten;
  62. PFN_NUMBER PagesWrittenLast;
  63. #if DBG
  64. ULONG PassMaxRun;
  65. PFN_NUMBER PagesVerified;
  66. #endif
  67. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  68. if ((MmMirroring & MM_MIRRORING_ENABLED) == 0) {
  69. return STATUS_NOT_SUPPORTED;
  70. }
  71. if (MiMirrorBitMap == NULL) {
  72. return STATUS_INSUFFICIENT_RESOURCES;
  73. }
  74. if ((ExVerifySuite(DataCenter) == TRUE) ||
  75. ((MmProductType != 0x00690057) && (ExVerifySuite(Enterprise) == TRUE))) {
  76. //
  77. // DataCenter and Advanced Server are the only appropriate mirroring
  78. // platforms, allow them to proceed.
  79. //
  80. NOTHING;
  81. }
  82. else {
  83. return STATUS_LICENSE_VIOLATION;
  84. }
  85. //
  86. // Serialize here with dynamic memory additions and removals.
  87. //
  88. ExAcquireFastMutex (&MmDynamicMemoryMutex);
  89. ASSERT (MiMirroringActive == FALSE);
  90. MmLockPagableSectionByHandle (ExPageLockHandle);
  91. //
  92. // Setting all the bits here states all the pages need to be mirrored.
  93. // In the Phase0 loop below, the bits will be cleared as pages are
  94. // found on the lists and marked to be sent to the mirror. Bits are
  95. // set again if the pages are reclaimed for active use.
  96. //
  97. RtlSetAllBits (MiMirrorBitMap2);
  98. //
  99. // Put all readonly nonpaged kernel and HAL subsection pages into the
  100. // Phase0 list. The only way these could get written between Phase0
  101. // starting and Phase1 ending is via debugger breakpoints and those
  102. // don't matter. This is worth a couple of megabytes and could be done
  103. // at some point in the future if a reasonable perf gain can be shown.
  104. //
  105. MiZeroingDisabled = TRUE;
  106. IterationCount = 0;
  107. //
  108. // Compute initial "pages copied" so convergence can be ascertained
  109. // in the main loop below.
  110. //
  111. PagesWrittenLast = 0;
  112. #if DBG
  113. if (MiMirrorDebug != 0) {
  114. for (MemoryList = ZeroedPageList; MemoryList <= ModifiedNoWritePageList; MemoryList += 1) {
  115. PagesWrittenLast += (PFN_COUNT)MmPageLocationList[MemoryList]->Total;
  116. }
  117. DbgPrint ("Mirror P0 starting with %x pages\n", PagesWrittenLast);
  118. PagesWrittenLast = 0;
  119. }
  120. #endif
  121. //
  122. // Initiate Phase0 copying.
  123. // Inform the HAL so it can initialize if need be.
  124. //
  125. Status = HalStartMirroring ();
  126. if (!NT_SUCCESS(Status)) {
  127. MmUnlockPagableImageSection(ExPageLockHandle);
  128. MiZeroingDisabled = FALSE;
  129. ASSERT (MiMirroringActive == FALSE);
  130. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  131. return Status;
  132. }
  133. //
  134. // Scan system memory and mirror pages until a pass
  135. // doesn't find many pages to transfer.
  136. //
  137. do {
  138. //
  139. // The list of pages to be transferred on this iteration will be
  140. // formed in the MiMirrorBitMap array. Clear out prior usage.
  141. //
  142. RtlClearAllBits (MiMirrorBitMap);
  143. //
  144. // Trim all pages from all process working sets so that as many pages
  145. // as possible will be on the standby, modified and modnowrite lists.
  146. // These lists are written during Phase0 mirroring where locks are
  147. // not held and thus the system is still somewhat operational from
  148. // an application's perspective.
  149. //
  150. MmEmptyAllWorkingSets ();
  151. MiFreeAllExpansionNonPagedPool (FALSE);
  152. LOCK_PFN (OldIrql);
  153. //
  154. // Scan all the page lists so they can be copied during Phase0
  155. // mirroring.
  156. //
  157. for (MemoryList = ZeroedPageList; MemoryList <= ModifiedNoWritePageList; MemoryList += 1) {
  158. ListHead = MmPageLocationList[MemoryList];
  159. if (ListHead->Total == 0) {
  160. continue;
  161. }
  162. if ((MemoryList == ModifiedPageList) &&
  163. (ListHead->Total == MmTotalPagesForPagingFile)) {
  164. continue;
  165. }
  166. PageFrameIndex = ListHead->Flink;
  167. do {
  168. //
  169. // The scan is operating via the lists rather than the PFN
  170. // entries as read-in-progress pages are not on lists and
  171. // therefore do not have to be special cased here and elsewhere.
  172. //
  173. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  174. ASSERT (Pfn1->u3.e1.ReadInProgress == 0);
  175. //
  176. // Setting the bit in BitMap means this page is to be copied
  177. // in this Phase0 iteration. If it is reused after this
  178. // point (as indicated by its bit being set again in BitMap2),
  179. // it will be recopied on a later iteration or in Phase1.
  180. //
  181. if (RtlCheckBit(MiMirrorBitMap2, (ULONG)PageFrameIndex)) {
  182. RtlSetBit (MiMirrorBitMap, (ULONG)PageFrameIndex);
  183. RtlClearBit (MiMirrorBitMap2, (ULONG)PageFrameIndex);
  184. }
  185. PageFrameIndex = Pfn1->u1.Flink;
  186. } while (PageFrameIndex != MM_EMPTY_LIST);
  187. }
  188. //
  189. // Scan for modified pages destined for the paging file.
  190. //
  191. for (Color = 0; Color < MM_MAXIMUM_NUMBER_OF_COLORS; Color += 1) {
  192. ListHead = &MmModifiedPageListByColor[Color];
  193. if (ListHead->Total == 0) {
  194. continue;
  195. }
  196. PageFrameIndex = ListHead->Flink;
  197. do {
  198. //
  199. // The scan is operating via the lists rather than the PFN
  200. // entries as read-in-progress are not on lists. Thus this
  201. // case does not have to be handled here and just works out.
  202. //
  203. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  204. ASSERT (Pfn1->u3.e1.ReadInProgress == 0);
  205. //
  206. // Setting the bit in BitMap means this page is to be copied
  207. // on this iteration of Phase0. If it is reused after this
  208. // point (as indicated by its bit being set again in BitMap2),
  209. // it will be recopied on a later iteration or in Phase1.
  210. //
  211. if (RtlCheckBit(MiMirrorBitMap2, (ULONG)PageFrameIndex)) {
  212. RtlSetBit (MiMirrorBitMap, (ULONG)PageFrameIndex);
  213. RtlClearBit (MiMirrorBitMap2, (ULONG)PageFrameIndex);
  214. }
  215. PageFrameIndex = Pfn1->u1.Flink;
  216. } while (PageFrameIndex != MM_EMPTY_LIST);
  217. }
  218. #if DBG
  219. if (MiMirrorDebug != 0) {
  220. DbgPrint ("Mirror P0 pass %d: Transfer %x pages\n",
  221. IterationCount,
  222. RtlNumberOfSetBits(MiMirrorBitMap));
  223. }
  224. #endif
  225. MiMirroringActive = TRUE;
  226. //
  227. // The dirty PFN bitmap has been initialized and the flag set.
  228. // There are very intricate rules governing how different places in
  229. // memory management MUST update the bitmap when we are in this mode.
  230. //
  231. // The rules are:
  232. //
  233. // Anyone REMOVING a page from the zeroed, free, transition, modified
  234. // or modnowrite lists must update the bitmap IFF that page could
  235. // potentially be subsequently modified. Pages that are in transition
  236. // BUT NOT on one of these lists (ie inpages, freed pages that are
  237. // dangling due to nonzero reference counts, etc) do NOT need to
  238. // update the bitmap as they are not one of these lists. If the page
  239. // is removed from one of the five lists just to be immediately
  240. // placed--without modification--on another list, then the bitmap
  241. // does NOT need updating.
  242. //
  243. // Therefore :
  244. //
  245. // MiUnlinkPageFromList updates the bitmap. While some callers
  246. // immediately put a page acquired this way back on one of the 3 lists
  247. // above, this is generally rare. Having this routine update the
  248. // bitmap means cases like restoring a transition PTE "just work".
  249. //
  250. // Callers of MiRemovePageFromList where list >= Transition, must do the
  251. // bitmap updates as only they know if the page is immediately going
  252. // back to one of the five lists above or being
  253. // reused (reused == update REQUIRED).
  254. //
  255. // MiRemoveZeroPage updates the bitmap as the page is immediately
  256. // going to be modified. MiRemoveAnyPage does this also.
  257. //
  258. // Inserts into ANY list do not need to update bitmaps, as a remove had
  259. // to occur first (which would do the update) or it wasn't on a list to
  260. // begin with and thus wasn't subtracted above and therefore doesn't
  261. // need to update the bitmap either.
  262. //
  263. UNLOCK_PFN (OldIrql);
  264. BitMapHint = 0;
  265. PagesWritten = 0;
  266. #if DBG
  267. PassMaxRun = 0;
  268. #endif
  269. do {
  270. BitMapIndex = RtlFindSetBits (MiMirrorBitMap, 1, BitMapHint);
  271. if (BitMapIndex < BitMapHint) {
  272. break;
  273. }
  274. if (BitMapIndex == NO_BITS_FOUND) {
  275. break;
  276. }
  277. //
  278. // Found at least one page to copy - try for a cluster.
  279. //
  280. LengthOfClearRun = RtlFindNextForwardRunClear (MiMirrorBitMap,
  281. BitMapIndex,
  282. &StartingRunIndex);
  283. if (LengthOfClearRun != 0) {
  284. LengthOfSetRun = StartingRunIndex - BitMapIndex;
  285. }
  286. else {
  287. LengthOfSetRun = MiMirrorBitMap->SizeOfBitMap - BitMapIndex;
  288. }
  289. PagesWritten += LengthOfSetRun;
  290. #if DBG
  291. if (LengthOfSetRun > PassMaxRun) {
  292. PassMaxRun = LengthOfSetRun;
  293. }
  294. #endif
  295. //
  296. // Write out the page(s).
  297. //
  298. PhysicalAddress.QuadPart = BitMapIndex;
  299. PhysicalAddress.QuadPart = PhysicalAddress.QuadPart << PAGE_SHIFT;
  300. PhysicalBytes.QuadPart = LengthOfSetRun;
  301. PhysicalBytes.QuadPart = PhysicalBytes.QuadPart << PAGE_SHIFT;
  302. Status = HalMirrorPhysicalMemory (PhysicalAddress, PhysicalBytes);
  303. if (!NT_SUCCESS(Status)) {
  304. MiZeroingDisabled = FALSE;
  305. MmUnlockPagableImageSection(ExPageLockHandle);
  306. MiMirroringActive = FALSE;
  307. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  308. return Status;
  309. }
  310. BitMapHint = BitMapIndex + LengthOfSetRun + LengthOfClearRun;
  311. } while (BitMapHint < MiMirrorBitMap->SizeOfBitMap);
  312. ASSERT (RtlNumberOfSetBits(MiMirrorBitMap) == PagesWritten);
  313. #if DBG
  314. if (PassMaxRun > MiMirrorPassMax[0]) {
  315. MiMirrorPassMax[0] = PassMaxRun;
  316. }
  317. if (MiMirrorDebug != 0) {
  318. DbgPrint ("Mirror P0 pass %d: ended with %x (last= %x) pages\n",
  319. IterationCount, PagesWritten, PagesWrittenLast);
  320. }
  321. #endif
  322. ASSERT (MiMirroringActive == TRUE);
  323. //
  324. // Stop when PagesWritten by the current pass is not somewhat
  325. // better than the preceeding pass. If improvement is vanishing,
  326. // the method is at the steady state where working set removals and
  327. // transition faults are in balance. Also stop if PagesWritten is
  328. // small in absolute terms. Finally, there is a limit on iterations
  329. // for the misbehaving cases.
  330. //
  331. if (((PagesWritten > PagesWrittenLast - 256) && (IterationCount > 0)) ||
  332. (PagesWritten < 1024)) {
  333. break;
  334. }
  335. ASSERT (MiMirroringActive == TRUE);
  336. PagesWrittenLast = PagesWritten;
  337. IterationCount += 1;
  338. } while (IterationCount < MIRROR_MAX_PHASE_ZERO_PASSES);
  339. ASSERT (MiMirroringActive == TRUE);
  340. //
  341. // Notify the HAL that Phase0 is complete. The HAL is responsible for
  342. // doing things like disabling interrupts, processors and preparing the
  343. // hardware for Phase1. Note that some HALs may return from this
  344. // call at DISPATCH_LEVEL, so snap current IRQL now.
  345. //
  346. ExitIrql = KeGetCurrentIrql ();
  347. ASSERT (ExitIrql == APC_LEVEL);
  348. Status = HalEndMirroring (0);
  349. if (!NT_SUCCESS(Status)) {
  350. ASSERT (KeGetCurrentIrql () == APC_LEVEL);
  351. MmUnlockPagableImageSection(ExPageLockHandle);
  352. MiZeroingDisabled = FALSE;
  353. MiMirroringActive = FALSE;
  354. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  355. return Status;
  356. }
  357. ASSERT ((KeGetCurrentIrql () == APC_LEVEL) ||
  358. (KeGetCurrentIrql () == DISPATCH_LEVEL));
  359. //
  360. // Phase0 copying is now complete.
  361. //
  362. // BitMap2 contains the list of safely transmitted (bit == 0) and
  363. // pages needing transmission (bit == 1).
  364. //
  365. // BitMap content is obsolete and if mirror verification is enabled,
  366. // BitMap will be reused below to accumulate the pages needing
  367. // verification in the following steps.
  368. //
  369. // Prepare for Phase1:
  370. //
  371. // 1. Assume all pages are to be verified (set all bits in BitMap).
  372. // 2. Synchronize list updates by acquiring the PFN lock.
  373. // 3. Exclude all holes in the PFN database.
  374. //
  375. // Phase 1:
  376. //
  377. // 4. Copy all the remaining pages whose bits are set.
  378. // 5. Transmit the list of pages to be verified if so configured.
  379. //
  380. BitMapBytes = (ULONG)((((MiMirrorBitMap->SizeOfBitMap) + 31) / 32) * 4);
  381. BitMap1 = MiMirrorBitMap->Buffer;
  382. BitMap2 = MiMirrorBitMap2->Buffer;
  383. BitMapSize = MiMirrorBitMap->SizeOfBitMap;
  384. ASSERT (BitMapSize == MiMirrorBitMap2->SizeOfBitMap);
  385. //
  386. // Step 1: Assume all pages are to be verified (set all bits in BitMap).
  387. //
  388. if (MmMirroring & MM_MIRRORING_VERIFYING) {
  389. RtlSetAllBits(MiMirrorBitMap);
  390. }
  391. //
  392. // Step 2: Synchronize list updates by acquiring the PFN lock.
  393. //
  394. LOCK_PFN2 (OldIrql);
  395. //
  396. // No more updates of the bitmaps are needed - we've already snapped the
  397. // information we need and are going to hold the PFN lock from here until
  398. // we're done.
  399. //
  400. MiMirroringActive = FALSE;
  401. //
  402. // Step 3: Exclude any memory gaps.
  403. //
  404. Limit = 0;
  405. PreviousPage = 0;
  406. do {
  407. ThisPage = MmPhysicalMemoryBlock->Run[Limit].BasePage;
  408. if (ThisPage != PreviousPage) {
  409. RtlClearBits (MiMirrorBitMap2,
  410. (ULONG)PreviousPage,
  411. (ULONG)(ThisPage - PreviousPage));
  412. if (MmMirroring & MM_MIRRORING_VERIFYING) {
  413. RtlClearBits (MiMirrorBitMap,
  414. (ULONG)PreviousPage,
  415. (ULONG)(ThisPage - PreviousPage));
  416. }
  417. }
  418. PreviousPage = ThisPage + MmPhysicalMemoryBlock->Run[Limit].PageCount;
  419. Limit += 1;
  420. } while (Limit != MmPhysicalMemoryBlock->NumberOfRuns);
  421. if (PreviousPage != MmHighestPossiblePhysicalPage + 1) {
  422. RtlClearBits (MiMirrorBitMap2,
  423. (ULONG)PreviousPage,
  424. (ULONG)(MmHighestPossiblePhysicalPage + 1 - PreviousPage));
  425. if (MmMirroring & MM_MIRRORING_VERIFYING) {
  426. RtlClearBits (MiMirrorBitMap,
  427. (ULONG)PreviousPage,
  428. (ULONG)(MmHighestPossiblePhysicalPage + 1 - PreviousPage));
  429. }
  430. }
  431. //
  432. // Step 4: Initiate Phase1 copying.
  433. //
  434. // N.B. If this code or code that it calls, writes to non-stack
  435. // memory between this point and the completion of the call to
  436. // HalEndMirroring(1), the mirror *BREAKS*, because MmCreateMirror
  437. // does not know when that non-stack data will be transferred to
  438. // the new memory. [This rule can be broken if special arrangements
  439. // are made to re-copy the memory after the final write takes place.]
  440. //
  441. // N.B. The HAL *MUST* handle the writes into this routine's stack
  442. // frame at the same time it deals with the stack frame of HalEndMirroring
  443. // and any other frames pushed by the HAL.
  444. //
  445. BitMapHint = 0;
  446. #if DBG
  447. PagesWritten = 0;
  448. PassMaxRun = 0;
  449. #endif
  450. do {
  451. BitMapIndex = RtlFindSetBits (MiMirrorBitMap2, 1, BitMapHint);
  452. if (BitMapIndex < BitMapHint) {
  453. break;
  454. }
  455. if (BitMapIndex == NO_BITS_FOUND) {
  456. break;
  457. }
  458. //
  459. // Found at least one page to copy - try for a cluster.
  460. //
  461. LengthOfClearRun = RtlFindNextForwardRunClear (MiMirrorBitMap2,
  462. BitMapIndex,
  463. &StartingRunIndex);
  464. if (LengthOfClearRun != 0) {
  465. LengthOfSetRun = StartingRunIndex - BitMapIndex;
  466. }
  467. else {
  468. LengthOfSetRun = MiMirrorBitMap2->SizeOfBitMap - BitMapIndex;
  469. }
  470. #if DBG
  471. PagesWritten += LengthOfSetRun;
  472. if (LengthOfSetRun > PassMaxRun) {
  473. PassMaxRun = LengthOfSetRun;
  474. }
  475. #endif
  476. //
  477. // Write out the page(s).
  478. //
  479. PhysicalAddress.QuadPart = BitMapIndex;
  480. PhysicalAddress.QuadPart = PhysicalAddress.QuadPart << PAGE_SHIFT;
  481. PhysicalBytes.QuadPart = LengthOfSetRun;
  482. PhysicalBytes.QuadPart = PhysicalBytes.QuadPart << PAGE_SHIFT;
  483. Status = HalMirrorPhysicalMemory (PhysicalAddress, PhysicalBytes);
  484. if (!NT_SUCCESS(Status)) {
  485. UNLOCK_PFN2 (ExitIrql);
  486. MiZeroingDisabled = FALSE;
  487. MmUnlockPagableImageSection(ExPageLockHandle);
  488. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  489. return Status;
  490. }
  491. BitMapHint = BitMapIndex + LengthOfSetRun + LengthOfClearRun;
  492. } while (BitMapHint < MiMirrorBitMap2->SizeOfBitMap);
  493. //
  494. // Phase1 copying is now complete.
  495. //
  496. //
  497. // Step 5:
  498. //
  499. // If HAL verification is enabled, inform the HAL of the ranges the
  500. // systems expects were mirrored. Any range not in this list means
  501. // that the system doesn't care if it was mirrored and the contents may
  502. // very well be different between the mirrors. Note the PFN lock is still
  503. // held so that the HAL can see things consistently.
  504. //
  505. #if DBG
  506. PagesVerified = 0;
  507. #endif
  508. if (MmMirroring & MM_MIRRORING_VERIFYING) {
  509. BitMapHint = 0;
  510. do {
  511. BitMapIndex = RtlFindSetBits (MiMirrorBitMap, 1, BitMapHint);
  512. if (BitMapIndex < BitMapHint) {
  513. break;
  514. }
  515. if (BitMapIndex == NO_BITS_FOUND) {
  516. break;
  517. }
  518. //
  519. // Found at least one page in this mirror range - try for a cluster.
  520. //
  521. LengthOfClearRun = RtlFindNextForwardRunClear (MiMirrorBitMap,
  522. BitMapIndex,
  523. &StartingRunIndex);
  524. if (LengthOfClearRun != 0) {
  525. LengthOfSetRun = StartingRunIndex - BitMapIndex;
  526. }
  527. else {
  528. LengthOfSetRun = MiMirrorBitMap->SizeOfBitMap - BitMapIndex;
  529. }
  530. #if DBG
  531. PagesVerified += LengthOfSetRun;
  532. #endif
  533. //
  534. // Tell the HAL that this range must be in a mirrored state.
  535. //
  536. PhysicalAddress.QuadPart = BitMapIndex;
  537. PhysicalAddress.QuadPart = PhysicalAddress.QuadPart << PAGE_SHIFT;
  538. PhysicalBytes.QuadPart = LengthOfSetRun;
  539. PhysicalBytes.QuadPart = PhysicalBytes.QuadPart << PAGE_SHIFT;
  540. Status = HalMirrorVerify (PhysicalAddress, PhysicalBytes);
  541. if (!NT_SUCCESS(Status)) {
  542. UNLOCK_PFN2 (ExitIrql);
  543. MiZeroingDisabled = FALSE;
  544. MmUnlockPagableImageSection(ExPageLockHandle);
  545. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  546. return Status;
  547. }
  548. BitMapHint = BitMapIndex + LengthOfSetRun + LengthOfClearRun;
  549. } while (BitMapHint < MiMirrorBitMap->SizeOfBitMap);
  550. }
  551. //
  552. // Phase1 verification is now complete.
  553. //
  554. //
  555. // Notify the HAL that everything's done while still holding
  556. // the PFN lock - the HAL will now complete copying of all pages and
  557. // any other needed state before returning from this call.
  558. //
  559. Status = HalEndMirroring (1);
  560. UNLOCK_PFN2 (ExitIrql);
  561. #if DBG
  562. if (MiMirrorDebug != 0) {
  563. DbgPrint ("Mirror P1: %x pages copied\n", PagesWritten);
  564. if (MmMirroring & MM_MIRRORING_VERIFYING) {
  565. DbgPrint ("Mirror P1: %x pages verified\n", PagesVerified);
  566. }
  567. }
  568. if (PassMaxRun > MiMirrorPassMax[1]) {
  569. MiMirrorPassMax[1] = PassMaxRun;
  570. }
  571. #endif
  572. MiZeroingDisabled = FALSE;
  573. MmUnlockPagableImageSection(ExPageLockHandle);
  574. ExReleaseFastMutex (&MmDynamicMemoryMutex);
  575. return Status;
  576. }