|
|
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
poshtdwn.c
Abstract:
Shutdown-related routines and structures
Author:
Rob Earhart (earhart) 01-Feb-2000
Revision History:
--*/
#include "pop.h"
#if DBG
BOOLEAN PopDumpFileObject( IN PVOID Object, IN PUNICODE_STRING ObjectName, IN ULONG_PTR HandleCount, IN ULONG_PTR PointerCount, IN PVOID Parameter ); #endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, PopInitShutdownList)
#pragma alloc_text(PAGE, PoRequestShutdownEvent)
#pragma alloc_text(PAGE, PoRequestShutdownWait)
#pragma alloc_text(PAGE, PoQueueShutdownWorkItem)
#pragma alloc_text(PAGELK, PopGracefulShutdown)
#if DBG
#pragma alloc_text(PAGELK, PopDumpFileObject)
#endif
#endif
KEVENT PopShutdownEvent; KGUARDED_MUTEX PopShutdownListMutex;
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg("PAGEDATA")
#endif
BOOLEAN PopShutdownListAvailable = FALSE;
//
// This list will contain a set of threads that we need to wait
// for when we're about to shutdown.
//
SINGLE_LIST_ENTRY PopShutdownThreadList;
//
// List containing a set of worker routines that
// we need to process before we can shutdown the
// machine.
//
LIST_ENTRY PopShutdownQueue;
typedef struct _PoShutdownThreadListEntry { SINGLE_LIST_ENTRY ShutdownThreadList; PETHREAD Thread; } POSHUTDOWNLISTENTRY, *PPOSHUTDOWNLISTENTRY;
NTSTATUS PopInitShutdownList( VOID ) { PAGED_CODE();
KeInitializeEvent(&PopShutdownEvent, NotificationEvent, FALSE); PopShutdownThreadList.Next = NULL; InitializeListHead(&PopShutdownQueue); KeInitializeGuardedMutex(&PopShutdownListMutex);
PopShutdownListAvailable = TRUE;
return STATUS_SUCCESS; }
NTSTATUS PoRequestShutdownWait( IN PETHREAD Thread ) /*++
Routine Description:
This function will add the caller's thread onto an internal list that we'll wait for before we shutdown.
Arguments:
Thread - Pointer to the caller's thread.
Return Value:
NTSTATUS.
--*/ { PPOSHUTDOWNLISTENTRY Entry;
PAGED_CODE();
Entry = (PPOSHUTDOWNLISTENTRY) ExAllocatePoolWithTag(PagedPool|POOL_COLD_ALLOCATION, sizeof(POSHUTDOWNLISTENTRY), 'LSoP'); if (! Entry) { return STATUS_NO_MEMORY; }
Entry->Thread = Thread; ObReferenceObject(Thread);
KeAcquireGuardedMutex(&PopShutdownListMutex);
if (! PopShutdownListAvailable) { ObDereferenceObject(Thread); ExFreePool(Entry); KeReleaseGuardedMutex(&PopShutdownListMutex); return STATUS_UNSUCCESSFUL; }
PushEntryList(&PopShutdownThreadList, &Entry->ShutdownThreadList);
KeReleaseGuardedMutex(&PopShutdownListMutex);
return STATUS_SUCCESS; }
NTSTATUS PoRequestShutdownEvent( OUT PVOID *Event OPTIONAL ) /*++
Routine Description:
This function will add the caller's thread onto an internal list that we'll wait for before we shutdown.
Arguments:
Event - If the parameter exists, it will recieve a pointer to our PopShutdownEvent.
Return Value:
NTSTATUS.
--*/ { NTSTATUS Status;
PAGED_CODE();
if (Event != NULL) { *Event = NULL; }
Status = PoRequestShutdownWait(PsGetCurrentThread()); if (!NT_SUCCESS(Status)) { return Status; }
if (Event != NULL) { *Event = &PopShutdownEvent; }
return STATUS_SUCCESS; }
NTKERNELAPI NTSTATUS PoQueueShutdownWorkItem( IN PWORK_QUEUE_ITEM WorkItem ) /*++
Routine Description:
This function appends WorkItem onto our internal list of things to run down when we're about to shutdown. Subsystems can use this as a mechanism to get notified whey we're going down so they can do any last minute cleanup.
Arguments:
WorkItem - Pointer to work item to be added onto our list which will be run down before we shutdown.
Return Value:
NTSTATUS.
--*/ { NTSTATUS Status;
PAGED_CODE();
ASSERT(WorkItem); ASSERT(WorkItem->WorkerRoutine);
KeAcquireGuardedMutex(&PopShutdownListMutex);
if (PopShutdownListAvailable) { InsertTailList(&PopShutdownQueue, &WorkItem->List); Status = STATUS_SUCCESS; } else { Status = STATUS_SYSTEM_SHUTDOWN; }
KeReleaseGuardedMutex(&PopShutdownListMutex);
return Status; }
#if DBG
extern POBJECT_TYPE IoFileObjectType;
BOOLEAN PopDumpFileObject( IN PVOID Object, IN PUNICODE_STRING ObjectName, IN ULONG_PTR HandleCount, IN ULONG_PTR PointerCount, IN PVOID Parameter ) { PFILE_OBJECT File; PULONG NumberOfFilesFound;
UNREFERENCED_PARAMETER(ObjectName); ASSERT(Object); ASSERT(Parameter);
File = (PFILE_OBJECT) Object; NumberOfFilesFound = (PULONG) Parameter;
++*NumberOfFilesFound; DbgPrint("\t0x%0p : HC %d, PC %d, Name %.*ls\n", Object, HandleCount, PointerCount, File->FileName.Length, File->FileName.Buffer);
return TRUE; } #endif // DBG
VOID PopGracefulShutdown ( IN PVOID WorkItemParameter ) /*++
Routine Description:
This function is only called as a HyperCritical work queue item. It's responsible for gracefully shutting down the system.
Return Value:
This function never returns.
--*/ { PVOID Context;
UNREFERENCED_PARAMETER(WorkItemParameter);
//
// Shutdown executive components (there's no turning back after this).
//
PERFINFO_SHUTDOWN_LOG_LAST_MEMORY_SNAPSHOT();
if (!PopAction.ShutdownBugCode) { HalEndOfBoot(); }
if (PoCleanShutdownEnabled()) { //
// Terminate all processes. This will close all the handles and delete
// all the address spaces. Note the system process is kept alive.
//
PsShutdownSystem (); //
// Notify every system thread that we're shutting things
// down...
//
KeSetEvent(&PopShutdownEvent, 0, FALSE);
//
// ... and give all threads which requested notification a
// chance to clean up and exit.
//
KeAcquireGuardedMutex(&PopShutdownListMutex);
PopShutdownListAvailable = FALSE;
KeReleaseGuardedMutex(&PopShutdownListMutex);
{ PLIST_ENTRY Next; PWORK_QUEUE_ITEM WorkItem;
while (PopShutdownQueue.Flink != &PopShutdownQueue) { Next = RemoveHeadList(&PopShutdownQueue); WorkItem = CONTAINING_RECORD(Next, WORK_QUEUE_ITEM, List); WorkItem->WorkerRoutine(WorkItem->Parameter); } }
{ PSINGLE_LIST_ENTRY Next; PPOSHUTDOWNLISTENTRY ShutdownEntry;
while (TRUE) { Next = PopEntryList(&PopShutdownThreadList); if (! Next) { break; }
ShutdownEntry = CONTAINING_RECORD(Next, POSHUTDOWNLISTENTRY, ShutdownThreadList); KeWaitForSingleObject(ShutdownEntry->Thread, Executive, KernelMode, FALSE, NULL); ObDereferenceObject(ShutdownEntry->Thread); ExFreePool(ShutdownEntry); } } }
//
// Terminate Plug-N-Play.
//
PpShutdownSystem (TRUE, 0, &Context);
ExShutdownSystem (0);
//
// Send first-chance shutdown IRPs to all drivers that asked for it.
//
IoShutdownSystem (0);
if (PoCleanShutdownEnabled()) { //
// Wait for all the user mode processes to exit.
//
PsWaitForAllProcesses (); }
//
// Scrub the object directories
//
if (PoCleanShutdownEnabled() & PO_CLEAN_SHUTDOWN_OB) { ObShutdownSystem (0); }
//
// Close the registry and the associated handles/file objects.
//
CmShutdownSystem ();
//
// Swap in the worker threads, to keep them from paging
//
ExShutdownSystem(1);
//
// This call to MmShutdownSystem will flush all the mapped data and empty
// the cache. This gets the data out and dereferences all the file objects
// so the drivers (ie: the network stack) can be cleanly unloaded. The
// pagefile handles are closed, however the file objects backing them are
// still referenced. Paging can continue, but no pagefile handles will
// exist in the system handle table.
//
MmShutdownSystem (0);
//
// Flush the lazy writer cache
//
CcWaitForCurrentLazyWriterActivity();
//
// Send out last-chance shutdown IRPs, including shutdown IRPs to
// filesystems. This is for notifications only - the filesystems are
// still active and usable after this call. It is expected however that
// no subsequent writes will be cached.
//
// ISSUE - 2002/02/21 - ADRIAO: Shutdown messages incomplete for filesystems
// Ideally we'd have a message to tell filesystems that the FS is no
// longer in use. However, this needs to be done on a *per-device* basis
// and ordering!
// The FS shutdown IRPs cannot be used in this fashion as filesystems
// only register once against their control objects for this message. A
// future solution might be to forward the powers IRP to mounted filesystems
// with the expectation that the bottom of the FS stack will forward the
// IRP back to the underlying storage stack. This would be symmetric with
// how removals work in PnP.
//
IoShutdownSystem(1);
//
// Push any lazy writes that snuck in before we shutdown the filesystem
// to the hardware.
//
CcWaitForCurrentLazyWriterActivity();
//
// This prevents us from making any more calls out to GDI.
//
PopFullWake = 0;
ASSERT(PopAction.DevState); PopAction.DevState->Thread = KeGetCurrentThread();
//
// Inform drivers of the system shutdown state.
// This will finish shutting down Io and Mm.
// After this is complete,
// NO MORE REFERENCES TO PAGABLE CODE OR DATA MAY BE MADE.
//
PopSetDevicesSystemState(FALSE);
#if DBG
if (PoCleanShutdownEnabled()) { ULONG NumberOfFilesFoundAtShutdown = 0; // As of this time, no files should be open.
DbgPrint("Looking for open files...\n"); ObEnumerateObjectsByType(IoFileObjectType, &PopDumpFileObject, &NumberOfFilesFoundAtShutdown); DbgPrint("Found %d open files.\n", NumberOfFilesFoundAtShutdown); ASSERT(NumberOfFilesFoundAtShutdown == 0); } #endif
IoFreePoDeviceNotifyList(&PopAction.DevState->Order);
//
// Disable any wake alarms.
//
HalSetWakeEnable(FALSE);
//
// If this is a controlled shutdown bugcheck sequence, issue the
// bugcheck now
// ISSUE-2000/01/30-earhart Placement of ShutdownBugCode BugCheck
// I dislike the fact that we're doing this controlled shutdown
// bugcheck so late in the shutdown process; at this stage, too
// much state has been torn down for this to be really useful.
// Maybe if there's a debugger attached, we could shut down
// sooner...
if (PopAction.ShutdownBugCode) { KeBugCheckEx (PopAction.ShutdownBugCode->Code, PopAction.ShutdownBugCode->Parameter1, PopAction.ShutdownBugCode->Parameter2, PopAction.ShutdownBugCode->Parameter3, PopAction.ShutdownBugCode->Parameter4); }
PERFINFO_SHUTDOWN_DUMP_PERF_BUFFER();
PpShutdownSystem (TRUE, 1, &Context);
ExShutdownSystem (2);
if (PoCleanShutdownEnabled() & PO_CLEAN_SHUTDOWN_OB) { ObShutdownSystem (2); }
//
// Any allocated pool left at this point is a leak.
//
MmShutdownSystem (2);
//
// Implement shutdown style action -
// N.B. does not return (will bugcheck in preference to returning).
//
PopShutdownSystem(PopAction.Action); }
#ifdef ALLOC_DATA_PRAGMA
#pragma data_seg()
#endif
|