summaryrefslogblamecommitdiffstats
path: root/private/ntos/mm/shutdown.c
blob: 333d16e4f2ad5a6321b0149579f8972f07b71d6a (plain) (tree)













































































































































































































































































































































































                                                                                         
/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    shutdown.c

Abstract:

    This module contains the initialization for the memory management
    system.

Author:

    Lou Perazzoli (loup) 21-Aug-1991

Revision History:

--*/

#include "mi.h"

extern ULONG MmSystemShutdown;

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGELK,MmShutdownSystem)
#endif

ULONG MmZeroPageFile;



BOOLEAN
MmShutdownSystem (
    IN BOOLEAN RebootPending
    )

/*++

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.

Arguments:

    RebootPending - Indicates whether or not a reboot is to be performed after the system
        has been shut down.  This parameter is ignored by this routine.

Return Value:

    TRUE if the pages were successfully written, FALSE otherwise.

--*/

{
    ULONG ModifiedPage;
    PMMPFN Pfn1;
    PSUBSECTION Subsection;
    PCONTROL_AREA ControlArea;
    PULONG Page;
    ULONG MdlHack[(sizeof(MDL)/4) + MM_MAXIMUM_WRITE_CLUSTER];
    PMDL Mdl;
    NTSTATUS Status;
    KEVENT IoEvent;
    IO_STATUS_BLOCK IoStatus;
    KIRQL OldIrql;
    LARGE_INTEGER StartingOffset;
    ULONG count;
    ULONG j, k;
    ULONG first;
    ULONG write;
    PMMPAGING_FILE PagingFile;

    UNREFERENCED_PARAMETER( RebootPending );

    //
    // Don't do this more than once.
    //

    if (!MmSystemShutdown) {

        MmLockPagableSectionByHandle(ExPageLockHandle);

        Mdl = (PMDL)&MdlHack;
        Page = (PULONG)(Mdl + 1);

        KeInitializeEvent (&IoEvent, NotificationEvent, FALSE);

        MmInitializeMdl(Mdl,
                        NULL,
                        PAGE_SIZE);

        Mdl->MdlFlags |= MDL_PAGES_LOCKED;

        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.
                    //

                    Pfn1->u3.e1.Modified = 0;

                    //
                    // Up the reference count for the physical page as there
                    // is I/O in progress.
                    //

                    Pfn1->u3.e2.ReferenceCount += 1;

                    *Page = ModifiedPage;
                    ControlArea->NumberOfMappedViews += 1;
                    ControlArea->NumberOfPfnReferences += 1;

                    UNLOCK_PFN (OldIrql);

                    StartingOffset.QuadPart = MI_STARTING_OFFSET (Subsection,
                                                                  Pfn1->PteAddress);

                    Mdl->StartVa = (PVOID)(Pfn1->u3.e1.PageColor << PAGE_SHIFT);
                    KeClearEvent (&IoEvent);
                    Status = IoSynchronousPageWrite (
                                            ControlArea->FilePointer,
                                            Mdl,
                                            &StartingOffset,
                                            &IoEvent,
                                            &IoStatus );

                    //
                    // Ignore all I/O failures - there is nothing that can be
                    // done at this point.
                    //

                    if (!NT_SUCCESS(Status)) {
                        KeSetEvent (&IoEvent, 0, FALSE);
                    }

                    Status = KeWaitForSingleObject (&IoEvent,
                                                    WrPageOut,
                                                    KernelMode,
                                                    FALSE,
                                                    &MmTwentySeconds);

                    if (Status == STATUS_TIMEOUT) {

                        //
                        // The write did not complete in 20 seconds, assume
                        // that the file systems are hung and return an
                        // error.
                        //

                        Pfn1->u3.e1.Modified = 1;
                        return(FALSE);
                    }

                    if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
                        MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
                    }

                    LOCK_PFN (OldIrql);
                    MiDecrementReferenceCount (ModifiedPage);
                    ControlArea->NumberOfMappedViews -= 1;
                    ControlArea->NumberOfPfnReferences -= 1;
                    if (ControlArea->NumberOfPfnReferences == 0) {

                        //
                        // This routine return with the PFN lock released!.
                        //

                        MiCheckControlArea (ControlArea, NULL, OldIrql);
                        LOCK_PFN (OldIrql);
                    }

                    //
                    // Restart scan at the front of the list.
                    //

                    ModifiedPage = MmModifiedPageListHead.Flink;
                    continue;
                }
            }
            ModifiedPage = Pfn1->u1.Flink;
        }

        UNLOCK_PFN (OldIrql);

        //
        // If a high number of modified pages still exist, start the
        // modified page writer and wait for 5 seconds.
        //

        if (MmAvailablePages < (MmFreeGoal * 2)) {
            LARGE_INTEGER FiveSeconds = {(ULONG)(-5 * 1000 * 1000 * 10), -1};

            KeSetEvent (&MmModifiedPageWriterEvent, 0, FALSE);
            KeDelayExecutionThread (KernelMode,
                                    FALSE,
                                    &FiveSeconds);
        }

        //
        // 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) {

            //
            // Get pages to complete the write request.
            //

            Mdl->StartVa = NULL;
            j = 0;
            Page = (PULONG)(Mdl + 1);

            LOCK_PFN (OldIrql);

            if (MmAvailablePages < (MmModifiedWriteClusterSize + 20)) {
                UNLOCK_PFN(OldIrql);
                return TRUE;
            }

            do {
                *Page = MiRemoveZeroPage (j);
                Pfn1 = MI_PFN_ELEMENT (*Page);
                Pfn1->u3.e2.ReferenceCount = 1;
                Pfn1->u2.ShareCount = 0;
                Pfn1->OriginalPte.u.Long = 0;
                MI_SET_PFN_DELETED (Pfn1);
                Page += 1;
                j += 1;
            } while (j < MmModifiedWriteClusterSize);

            k = 0;

            while (k < MmNumberOfPagingFiles) {

                PagingFile = MmPagingFile[k];

                count = 0;
                write = FALSE;

                for (j = 1; j < PagingFile->Size; j++) {

                    if (RtlCheckBit (PagingFile->Bitmap, j) == 0) {

                        if (count == 0) {
                            first = j;
                        }
                        count += 1;
                        if (count == MmModifiedWriteClusterSize) {
                            write = TRUE;
                        }
                    } else {
                        if (count != 0) {

                            //
                            // Issue a write.
                            //

                            write = TRUE;
                        }
                    }

                    if ((j == (PagingFile->Size - 1)) &&
                        (count != 0)) {
                        write = TRUE;
                    }

                    if (write) {

                        UNLOCK_PFN (OldIrql);

                        StartingOffset.QuadPart = (LONGLONG)first << PAGE_SHIFT;
                        Mdl->ByteCount = count << PAGE_SHIFT;
                        KeClearEvent (&IoEvent);

                        Status = IoSynchronousPageWrite (
                                                PagingFile->File,
                                                Mdl,
                                                &StartingOffset,
                                                &IoEvent,
                                                &IoStatus);

                        //
                        // Ignore all I/O failures - there is nothing that can be
                        // done at this point.
                        //

                        if (!NT_SUCCESS(Status)) {
                            KeSetEvent (&IoEvent, 0, FALSE);
                        }

                        Status = KeWaitForSingleObject (&IoEvent,
                                                        WrPageOut,
                                                        KernelMode,
                                                        FALSE,
                                                        &MmTwentySeconds);

                        if (Status == STATUS_TIMEOUT) {

                            //
                            // The write did not complete in 20 seconds, assume
                            // that the file systems are hung and return an
                            // error.
                            //

                            return(FALSE);
                        }

                        if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
                            MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
                        }

                        LOCK_PFN (OldIrql);
                        count = 0;
                        write = FALSE;
                    }
                }
                k += 1;
            }
            j = 0;
            Page = (PULONG)(Mdl + 1);
            do {
                MiDecrementReferenceCount (*Page);
                Page += 1;
                j += 1;
            } while (j < MmModifiedWriteClusterSize);
            UNLOCK_PFN (OldIrql);
        }
        MmUnlockPagableImageSection(ExPageLockHandle);
    }
    return TRUE;
}