VOID MiFlushUnusedSections ( VOID ) /*++ Routine Description: This routine rumages through the PFN database and attempts to close any unused sections. Arguments: None. Return Value: None. --*/ { PMMPFN LastPfn; PMMPFN Pfn1; PSUBSECTION Subsection; KIRQL OldIrql; LOCK_PFN (OldIrql); Pfn1 = MI_PFN_ELEMENT (MmLowestPhysicalPage + 1); LastPfn = MI_PFN_ELEMENT(MmHighestPhysicalPage); while (Pfn1 < LastPfn) { if (Pfn1->OriginalPte.u.Soft.Prototype == 1) { if ((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList)) { // // Make sure the PTE is not waiting for I/O to complete. // if (MI_IS_PFN_DELETED (Pfn1)) { Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte); MiFlushUnusedSectionInternal (Subsection->ControlArea); } } } Pfn1++; } UNLOCK_PFN (OldIrql); return; }
VOID MiFormatPte ( IN PMMPTE PointerPte ) { // int j; // unsigned long pte; PMMPTE proto_pte; PSUBSECTION subsect; // struct a_bit { // unsigned long biggies : 31; // unsigned long bitties : 1; // }; // // struct a_bit print_pte; if (MmIsAddressValid (PointerPte) == FALSE) { DbgPrint(" cannot dump PTE %p - it's not valid\n\n", (ULONG_PTR)PointerPte); return; } proto_pte = MiPteToProto(PointerPte); subsect = MiGetSubsectionAddress(PointerPte); DbgPrint("***DumpPTE at %p contains %p\n", (ULONG_PTR)PointerPte, PointerPte->u.Long); DbgPrint(" protoaddr %p subsectaddr %p\n\n", (ULONG_PTR)proto_pte, (ULONG_PTR)subsect); return; // DbgPrint("page frame number 0x%lx proto PTE address 0x%lx\n", // // DbgPrint("PTE is 0x%lx\n", PTETOULONG(the_pte)); // // proto_pte = MiPteToProto(PointerPte); // // DbgPrint("page frame number 0x%lx proto PTE address 0x%lx\n", // PointerPte->u.Hard.PageFrameNumber,*(PULONG)&proto_pte); // // DbgPrint(" 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 \n"); // DbgPrint(" +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \n"); // DbgPrint(" | pfn |c|p|t|r|r|d|a|c|p|o|w|v| \n"); // DbgPrint(" | |o|r|r|s|s|t|c|a|b|w|r|l| \n"); // DbgPrint(" | |w|o|n|v|v|y|c|c|o|n|t|d| \n"); // DbgPrint(" +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ \n "); // pte = PTETOULONG(the_pte); // // for (j = 0; j < 32; j++) { // *(PULONG)& print_pte = pte; // DbgPrint(" %lx",print_pte.bitties); // pte = pte << 1; // } // DbgPrint("\n"); // }
BOOLEAN MiShutdownSystem ( VOID ) /*++ Routine Description: This function performs the shutdown of memory management. This is accomplished by writing out all modified pages which are destined for files other than the paging file. All processes have already been killed, the registry shutdown and shutdown IRPs already sent. On return from this phase all mapped file data must be flushed and the unused segment list emptied. This releases all the Mm references to file objects, allowing many drivers (especially the network) to unload. Arguments: None. Return Value: TRUE if the pages were successfully written, FALSE otherwise. --*/ { SIZE_T ImportListSize; PLOAD_IMPORTS ImportList; PLOAD_IMPORTS ImportListNonPaged; PLIST_ENTRY NextEntry; PKLDR_DATA_TABLE_ENTRY DataTableEntry; PFN_NUMBER ModifiedPage; PMMPFN Pfn1; PSUBSECTION Subsection; PCONTROL_AREA ControlArea; PPFN_NUMBER Page; PFILE_OBJECT FilePointer; ULONG ConsecutiveFileLockFailures; PFN_NUMBER MdlHack[(sizeof(MDL)/sizeof(PFN_NUMBER)) + MM_MAXIMUM_WRITE_CLUSTER]; PMDL Mdl; NTSTATUS Status; KEVENT IoEvent; IO_STATUS_BLOCK IoStatus; KIRQL OldIrql; LARGE_INTEGER StartingOffset; ULONG count; ULONG i; // // Don't do this more than once. // if (MmSystemShutdown == 0) { Mdl = (PMDL) MdlHack; Page = (PPFN_NUMBER)(Mdl + 1); KeInitializeEvent (&IoEvent, NotificationEvent, FALSE); MmInitializeMdl (Mdl, NULL, PAGE_SIZE); Mdl->MdlFlags |= MDL_PAGES_LOCKED; MmLockPageableSectionByHandle (ExPageLockHandle); LOCK_PFN (OldIrql); ModifiedPage = MmModifiedPageListHead.Flink; while (ModifiedPage != MM_EMPTY_LIST) { // // There are modified pages. // Pfn1 = MI_PFN_ELEMENT (ModifiedPage); if (Pfn1->OriginalPte.u.Soft.Prototype == 1) { // // This page is destined for a file. // Subsection = MiGetSubsectionAddress (&Pfn1->OriginalPte); ControlArea = Subsection->ControlArea; if ((!ControlArea->u.Flags.Image) && (!ControlArea->u.Flags.NoModifiedWriting)) { MiUnlinkPageFromList (Pfn1); // // Issue the write. // MI_SET_MODIFIED (Pfn1, 0, 0x28); // // Up the reference count for the physical page as there // is I/O in progress. // MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE (Pfn1); *Page = ModifiedPage; ControlArea->NumberOfMappedViews += 1; ControlArea->NumberOfPfnReferences += 1; UNLOCK_PFN (OldIrql); StartingOffset.QuadPart = MiStartingOffset (Subsection, Pfn1->PteAddress); Mdl->StartVa = NULL; ConsecutiveFileLockFailures = 0; FilePointer = ControlArea->FilePointer; retry: KeClearEvent (&IoEvent); Status = FsRtlAcquireFileForCcFlushEx (FilePointer); if (NT_SUCCESS(Status)) { Status = IoSynchronousPageWrite (FilePointer, Mdl, &StartingOffset, &IoEvent, &IoStatus); // // Release the file we acquired. // FsRtlReleaseFileForCcFlush (FilePointer); } if (!NT_SUCCESS(Status)) { // // Only try the request more than once if the // filesystem said it had a deadlock. // if (Status == STATUS_FILE_LOCK_CONFLICT) { ConsecutiveFileLockFailures += 1; if (ConsecutiveFileLockFailures < 5) { KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime); goto retry; } goto wait_complete; } // // Ignore all I/O failures - there is nothing that // can be done at this point. // KeSetEvent (&IoEvent, 0, FALSE); } Status = KeWaitForSingleObject (&IoEvent, WrPageOut, KernelMode, FALSE, (PLARGE_INTEGER)&MmTwentySeconds); wait_complete: if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) { MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl); } if (Status == STATUS_TIMEOUT) { // // The write did not complete in 20 seconds, assume // that the file systems are hung and return an // error. // LOCK_PFN (OldIrql); MI_SET_MODIFIED (Pfn1, 1, 0xF); MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF (Pfn1); ControlArea->NumberOfMappedViews -= 1; ControlArea->NumberOfPfnReferences -= 1; // // This routine returns with the PFN lock released! // MiCheckControlArea (ControlArea, OldIrql); MmUnlockPageableImageSection (ExPageLockHandle); return FALSE; } LOCK_PFN (OldIrql); MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF (Pfn1); ControlArea->NumberOfMappedViews -= 1; ControlArea->NumberOfPfnReferences -= 1; // // This routine returns with the PFN lock released! // MiCheckControlArea (ControlArea, OldIrql); LOCK_PFN (OldIrql); // // Restart scan at the front of the list. // ModifiedPage = MmModifiedPageListHead.Flink; continue; } } ModifiedPage = Pfn1->u1.Flink; } UNLOCK_PFN (OldIrql); // // Indicate to the modified page writer that the system has // shutdown. // MmSystemShutdown = 1; // // Check to see if the paging file should be overwritten. // Only free blocks are written. // if (MmZeroPageFile) { MiZeroAllPageFiles (); } MmUnlockPageableImageSection (ExPageLockHandle); } if (PoCleanShutdownEnabled ()) { // // Empty the unused segment list. // LOCK_PFN (OldIrql); MmUnusedSegmentForceFree = (ULONG)-1; KeSetEvent (&MmUnusedSegmentCleanup, 0, FALSE); // // Give it 5 seconds to empty otherwise assume the filesystems are // hung and march on. // for (count = 0; count < 500; count += 1) { if (IsListEmpty(&MmUnusedSegmentList)) { break; } UNLOCK_PFN (OldIrql); KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime); LOCK_PFN (OldIrql); #if DBG if (count == 400) { // // Everything should have been flushed by now. Give the // filesystem team a chance to debug this on checked builds. // ASSERT (FALSE); } #endif // // Resignal if needed in case more closed file objects triggered // additional entries. // if (MmUnusedSegmentForceFree == 0) { MmUnusedSegmentForceFree = (ULONG)-1; KeSetEvent (&MmUnusedSegmentCleanup, 0, FALSE); } } UNLOCK_PFN (OldIrql); // // Get rid of any paged pool references as they will be illegal // by the time MmShutdownSystem is called again since the filesystems // will have shutdown. // KeWaitForSingleObject (&MmSystemLoadLock, WrVirtualMemory, KernelMode, FALSE, (PLARGE_INTEGER)NULL); NextEntry = PsLoadedModuleList.Flink; while (NextEntry != &PsLoadedModuleList) { DataTableEntry = CONTAINING_RECORD (NextEntry, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks); ImportList = (PLOAD_IMPORTS)DataTableEntry->LoadedImports; if ((ImportList != (PVOID)LOADED_AT_BOOT) && (ImportList != (PVOID)NO_IMPORTS_USED) && (!SINGLE_ENTRY(ImportList))) { ImportListSize = ImportList->Count * sizeof(PVOID) + sizeof(SIZE_T); ImportListNonPaged = (PLOAD_IMPORTS) ExAllocatePoolWithTag (NonPagedPool, ImportListSize, 'TDmM'); if (ImportListNonPaged != NULL) { RtlCopyMemory (ImportListNonPaged, ImportList, ImportListSize); ExFreePool (ImportList); DataTableEntry->LoadedImports = ImportListNonPaged; } else { // // Don't bother with the clean shutdown at this point. // PopShutdownCleanly = FALSE; break; } } // // Free the full DLL name as it is pageable. // if (DataTableEntry->FullDllName.Buffer != NULL) { ExFreePool (DataTableEntry->FullDllName.Buffer); DataTableEntry->FullDllName.Buffer = NULL; } NextEntry = NextEntry->Flink; } KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE); // // Close all the pagefile handles, note we still have an object // reference to each keeping the underlying object resident. // At the end of Phase1 shutdown we'll release those references // to trigger the storage stack unload. The handle close must be // done here however as it will reference pageable structures. // for (i = 0; i < MmNumberOfPagingFiles; i += 1) { // // Free each pagefile name now as it resides in paged pool and // may need to be inpaged to be freed. Since the paging files // are going to be shutdown shortly, now is the time to access // pageable stuff and get rid of it. Zeroing the buffer pointer // is sufficient as the only accesses to this are from the // try-except-wrapped GetSystemInformation APIs and all the // user processes are gone already. // ASSERT (MmPagingFile[i]->PageFileName.Buffer != NULL); ExFreePool (MmPagingFile[i]->PageFileName.Buffer); MmPagingFile[i]->PageFileName.Buffer = NULL; ZwClose (MmPagingFile[i]->FileHandle); } } return TRUE; }