1388 lines
27 KiB
C
1388 lines
27 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
clusnet.c
|
||
|
||
Abstract:
|
||
|
||
Intialization and dispatch routines for the Cluster Network Driver.
|
||
|
||
Author:
|
||
|
||
Mike Massa (mikemas) July 29, 1996
|
||
|
||
Revision History:
|
||
|
||
Who When What
|
||
-------- -------- ----------------------------------------------
|
||
mikemas 07-29-96 created
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
#include "clusnet.tmh"
|
||
|
||
#include <sspi.h>
|
||
|
||
//
|
||
// Global Data
|
||
//
|
||
PDRIVER_OBJECT CnDriverObject = NULL;
|
||
PDEVICE_OBJECT CnDeviceObject = NULL;
|
||
KSPIN_LOCK CnDeviceObjectStackSizeLock = 0;
|
||
PDEVICE_OBJECT CdpDeviceObject = NULL;
|
||
PKPROCESS CnSystemProcess = NULL;
|
||
CN_STATE CnState = CnStateShutdown;
|
||
PERESOURCE CnResource = NULL;
|
||
CL_NODE_ID CnMinValidNodeId = ClusterInvalidNodeId;
|
||
CL_NODE_ID CnMaxValidNodeId = ClusterInvalidNodeId;
|
||
CL_NODE_ID CnLocalNodeId = ClusterInvalidNodeId;
|
||
KSPIN_LOCK CnShutdownLock = 0;
|
||
BOOLEAN CnShutdownScheduled = FALSE;
|
||
PKEVENT CnShutdownEvent = NULL;
|
||
WORK_QUEUE_ITEM CnShutdownWorkItem = {{NULL, NULL}, NULL, NULL};
|
||
HANDLE ClussvcProcessHandle = NULL;
|
||
|
||
|
||
//
|
||
// vars for managing Events. The lookaside list generates Event data structs
|
||
// that are used to carry the data back to user mode. EventLock is the only
|
||
// lock and synchronizes all access to any event structure (both here and in
|
||
// CN_FSCONTEXT). EventFileHandles is a list of CN_FSCONTEXT structs that
|
||
// are interested in receiving event notifications. To avoid synchronization
|
||
// problems between clusnet and mm in clussvc, events have an epoch associated
|
||
// with them. MM increments the epoch at the beginning of regroup event and
|
||
// updates clusnet at the end of regroup. Any events still pending in the
|
||
// event queue with a stale epoch are ignored by MM.
|
||
//
|
||
// EventDeliveryInProgress is a count of threads that are currently
|
||
// iterating through the EventFileHandles list and delivering events.
|
||
// The EventFileHandles list cannot be modified while EventDeliveryInProgress
|
||
// is greater than zero. EventDeliveryComplete is a notification event
|
||
// that is signalled when the EventDeliveryInProgress count reaches zero.
|
||
// EventRevisitRequired indicates whether a new event IRP arrived during
|
||
// event delivery. To avoid delivering events out of order, the IRP cannot
|
||
// be completed immediately.
|
||
//
|
||
|
||
PNPAGED_LOOKASIDE_LIST EventLookasideList = NULL;
|
||
LIST_ENTRY EventFileHandles = {0,0};
|
||
#if DBG
|
||
CN_LOCK EventLock = {0,0};
|
||
#else
|
||
CN_LOCK EventLock = 0;
|
||
#endif
|
||
ULONG EventEpoch;
|
||
LONG EventDeliveryInProgress = 0;
|
||
KEVENT EventDeliveryComplete;
|
||
BOOLEAN EventRevisitRequired = FALSE;
|
||
|
||
#if DBG
|
||
ULONG CnDebug = 0;
|
||
#endif // DBG
|
||
|
||
//
|
||
// Private Types
|
||
//
|
||
|
||
//
|
||
// Private Data
|
||
//
|
||
|
||
SECURITY_STATUS
|
||
SEC_ENTRY
|
||
SecSetPagingMode(
|
||
BOOLEAN Pageable
|
||
);
|
||
|
||
BOOLEAN SecurityPagingModeSet = FALSE;
|
||
|
||
//
|
||
// Local Prototypes
|
||
//
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
);
|
||
|
||
VOID
|
||
DriverUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
);
|
||
|
||
NTSTATUS
|
||
CnCreateDeviceObjects(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
);
|
||
|
||
VOID
|
||
CnDeleteDeviceObjects(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
CnAdjustDeviceObjectStackSize(
|
||
PDEVICE_OBJECT ClusnetDeviceObject,
|
||
PDEVICE_OBJECT TargetDeviceObject
|
||
);
|
||
|
||
//
|
||
// Mark init code as discardable.
|
||
//
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#pragma alloc_text(INIT, DriverEntry)
|
||
#pragma alloc_text(INIT, CnCreateDeviceObjects)
|
||
|
||
#pragma alloc_text(PAGE, DriverUnload)
|
||
#pragma alloc_text(PAGE, CnDeleteDeviceObjects)
|
||
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
//
|
||
// Function definitions
|
||
//
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialization routine for the driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to the driver object created by the system.
|
||
RegistryPath - The driver's registry key.
|
||
|
||
Return Value:
|
||
|
||
An NT status code.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
USHORT i;
|
||
|
||
#if DBG
|
||
volatile BOOLEAN DontLoad = FALSE;
|
||
|
||
if ( DontLoad )
|
||
return STATUS_UNSUCCESSFUL;
|
||
#endif
|
||
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[ClusNet] Loading...\n"));
|
||
}
|
||
|
||
WPP_INIT_TRACING(DriverObject, RegistryPath);
|
||
|
||
//
|
||
// Save a pointer to the system process so that we can open
|
||
// handles in the context of this process later.
|
||
//
|
||
CnSystemProcess = (PKPROCESS) IoGetCurrentProcess();
|
||
|
||
//
|
||
// Allocate a synchronization resource.
|
||
//
|
||
CnResource = CnAllocatePool(sizeof(ERESOURCE));
|
||
|
||
if (CnResource == NULL) {
|
||
return(STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
status = ExInitializeResourceLite(CnResource);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// initialize the mechanisms used to deliver event callbacks
|
||
// to user mode
|
||
//
|
||
EventLookasideList = CnAllocatePool(sizeof(NPAGED_LOOKASIDE_LIST));
|
||
|
||
if (EventLookasideList == NULL) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
ExInitializeNPagedLookasideList(EventLookasideList,
|
||
NULL,
|
||
NULL,
|
||
0,
|
||
sizeof( CLUSNET_EVENT_ENTRY ),
|
||
CN_EVENT_SIGNATURE,
|
||
0);
|
||
|
||
CnInitializeLock( &EventLock, CNP_EVENT_LOCK );
|
||
InitializeListHead( &EventFileHandles );
|
||
KeInitializeEvent( &EventDeliveryComplete, NotificationEvent, TRUE );
|
||
|
||
//
|
||
// Initialize miscellaneous other items.
|
||
//
|
||
KeInitializeSpinLock(&CnShutdownLock);
|
||
KeInitializeSpinLock(&CnDeviceObjectStackSizeLock);
|
||
|
||
//
|
||
// Initialize the driver object
|
||
//
|
||
CnDriverObject = DriverObject;
|
||
|
||
DriverObject->DriverUnload = DriverUnload;
|
||
DriverObject->FastIoDispatch = NULL;
|
||
|
||
for (i=0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
|
||
DriverObject->MajorFunction[i] = CnDispatch;
|
||
}
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
|
||
CnDispatchDeviceControl;
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
|
||
CnDispatchInternalDeviceControl;
|
||
|
||
//
|
||
// Create all the devices exported by this driver.
|
||
//
|
||
status = CnCreateDeviceObjects(DriverObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto error_exit;
|
||
}
|
||
|
||
#ifdef MEMLOGGING
|
||
//
|
||
// initialize the in-memory log
|
||
//
|
||
|
||
CnInitializeMemoryLog();
|
||
#endif // MEMLOGGING
|
||
|
||
//
|
||
// Load the IP Address and NetBT support.
|
||
// This must be done before the transport registers for PnP events.
|
||
//
|
||
status = IpaLoad();
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto error_exit;
|
||
}
|
||
|
||
status = NbtIfLoad();
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto error_exit;
|
||
}
|
||
|
||
//
|
||
// Load the transport component
|
||
//
|
||
status = CxLoad(RegistryPath);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto error_exit;
|
||
}
|
||
|
||
#ifdef MM_IN_CLUSNET
|
||
|
||
//
|
||
// Load the membership component
|
||
//
|
||
status = CmmLoad(RegistryPath);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto error_exit;
|
||
}
|
||
|
||
#endif // MM_IN_CLUSNET
|
||
|
||
//
|
||
// make ksecdd non-pagable so we can sign and verify
|
||
// signatures at raised IRQL
|
||
//
|
||
|
||
SecSetPagingMode( FALSE );
|
||
SecurityPagingModeSet = TRUE;
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[ClusNet] Loaded.\n"));
|
||
}
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
|
||
error_exit:
|
||
|
||
DriverUnload(CnDriverObject);
|
||
|
||
return(status);
|
||
}
|
||
|
||
|
||
VOID
|
||
DriverUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Unloads the driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to the driver object created by the system.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[ClusNet] Unloading...\n"));
|
||
}
|
||
|
||
CnTrace(HBEAT_ERROR,0, "[ClusNet] Unloading...\n");
|
||
|
||
//
|
||
// First, force a shutdown.
|
||
//
|
||
CnShutdown();
|
||
|
||
//
|
||
// Now unload the components.
|
||
//
|
||
#ifdef MM_IN_CLUSNET
|
||
|
||
CmmUnload();
|
||
|
||
#endif // MM_IN_CLUSNET
|
||
|
||
CxUnload();
|
||
|
||
#ifdef MEMLOGGING
|
||
//
|
||
// initialize the in-memory log
|
||
//
|
||
|
||
CnFreeMemoryLog();
|
||
#endif // MEMLOGGING
|
||
|
||
CnDeleteDeviceObjects();
|
||
|
||
if (CnResource != NULL) {
|
||
ExDeleteResourceLite(CnResource);
|
||
CnFreePool(CnResource); CnResource = NULL;
|
||
}
|
||
|
||
CnDriverObject = NULL;
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[ClusNet] Unloaded.\n"));
|
||
}
|
||
|
||
if (EventLookasideList != NULL) {
|
||
ExDeleteNPagedLookasideList( EventLookasideList );
|
||
CnFreePool( EventLookasideList ); EventLookasideList = NULL;
|
||
}
|
||
|
||
//
|
||
// finally, allow the security driver to return to nonpaged mode
|
||
//
|
||
|
||
if ( SecurityPagingModeSet ) {
|
||
SecSetPagingMode( TRUE );
|
||
}
|
||
|
||
WPP_CLEANUP(DriverObject);
|
||
|
||
return;
|
||
|
||
} // DriverUnload
|
||
|
||
|
||
NTSTATUS
|
||
CnCreateDeviceObjects(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates the device objects exported by the driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to the driver object created by the system.
|
||
|
||
Return Value:
|
||
|
||
An NT status code.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING deviceName;
|
||
|
||
|
||
//
|
||
// Create the driver control device
|
||
//
|
||
RtlInitUnicodeString(&deviceName, DD_CLUSNET_DEVICE_NAME);
|
||
|
||
status = IoCreateDevice(
|
||
DriverObject,
|
||
0,
|
||
&deviceName,
|
||
FILE_DEVICE_NETWORK,
|
||
0,
|
||
FALSE,
|
||
&CnDeviceObject
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
CNPRINT((
|
||
"[ClusNet] Failed to create %ws device object, status %lx\n",
|
||
deviceName.Buffer,
|
||
status
|
||
));
|
||
return(status);
|
||
}
|
||
|
||
CnDeviceObject->Flags |= DO_DIRECT_IO;
|
||
CnDeviceObject->StackSize = CN_DEFAULT_IRP_STACK_SIZE;
|
||
|
||
status = IoRegisterShutdownNotification(CnDeviceObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
CNPRINT((
|
||
"[ClusNet] Failed to register for shutdown notification, status %lx\n",
|
||
status
|
||
));
|
||
}
|
||
|
||
#if defined(WMI_TRACING)
|
||
status = IoWMIRegistrationControl (CnDeviceObject, WMIREG_ACTION_REGISTER);
|
||
if (!NT_SUCCESS(status)) {
|
||
CNPRINT(("[ClusNet] Failed to register for WMI Support, %lx\n", status) );
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Create the datagram transport device
|
||
//
|
||
RtlInitUnicodeString(&deviceName, DD_CDP_DEVICE_NAME);
|
||
|
||
status = IoCreateDevice(
|
||
DriverObject,
|
||
0,
|
||
&deviceName,
|
||
FILE_DEVICE_NETWORK,
|
||
0,
|
||
FALSE,
|
||
&CdpDeviceObject
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
CNPRINT((
|
||
"[ClusNet] Failed to create %ws device object, status %lx\n",
|
||
deviceName.Buffer,
|
||
status
|
||
));
|
||
return(status);
|
||
}
|
||
|
||
CdpDeviceObject->Flags |= DO_DIRECT_IO;
|
||
CdpDeviceObject->StackSize = CDP_DEFAULT_IRP_STACK_SIZE;
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
VOID
|
||
CnDeleteDeviceObjects(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Deletes the device objects exported by the driver.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (CnDeviceObject != NULL) {
|
||
#if defined(WMI_TRACING)
|
||
IoWMIRegistrationControl(CnDeviceObject, WMIREG_ACTION_DEREGISTER);
|
||
#endif
|
||
IoDeleteDevice(CnDeviceObject);
|
||
CnDeviceObject = NULL;
|
||
}
|
||
|
||
if (CdpDeviceObject != NULL) {
|
||
IoDeleteDevice(CdpDeviceObject);
|
||
CdpDeviceObject = NULL;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
NTSTATUS
|
||
CnInitialize(
|
||
IN CL_NODE_ID LocalNodeId,
|
||
IN ULONG MaxNodes
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialization routine for the Cluster Network Driver.
|
||
Called when an initialize request is received.
|
||
|
||
Arguments:
|
||
|
||
LocalNodeId - The ID of the local node.
|
||
|
||
MaxNodes - The maximum number of valid cluster nodes.
|
||
|
||
Return Value:
|
||
|
||
An NT status code.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
KIRQL irql;
|
||
|
||
if ( (MaxNodes == 0) ||
|
||
(LocalNodeId < ClusterMinNodeId) ||
|
||
(LocalNodeId > (ClusterMinNodeId + MaxNodes - 1))
|
||
)
|
||
{
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[Clusnet] Initializing...\n"));
|
||
}
|
||
|
||
CnState = CnStateInitializePending;
|
||
|
||
//
|
||
// Reset global values
|
||
//
|
||
CnAssert(CnLocalNodeId == ClusterInvalidNodeId);
|
||
CnAssert(CnMinValidNodeId == ClusterInvalidNodeId);
|
||
CnAssert(CnMaxValidNodeId == ClusterInvalidNodeId);
|
||
|
||
CnMinValidNodeId = ClusterMinNodeId;
|
||
CnMaxValidNodeId = ClusterMinNodeId + MaxNodes - 1;
|
||
CnLocalNodeId = LocalNodeId;
|
||
|
||
//
|
||
// Reenable the halt processing mechanism.
|
||
//
|
||
KeAcquireSpinLock(&CnShutdownLock, &irql);
|
||
CnShutdownScheduled = FALSE;
|
||
CnShutdownEvent = NULL;
|
||
KeReleaseSpinLock(&CnShutdownLock, irql);
|
||
|
||
//
|
||
// Initialize the IP Address support
|
||
//
|
||
status = IpaInitialize();
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
goto error_exit;
|
||
}
|
||
|
||
#ifdef MM_IN_CLUSNET
|
||
|
||
//
|
||
// Call the Membership Manager's init routine. This will in turn call
|
||
// the Transport's init routine.
|
||
//
|
||
status = CmmInitialize();
|
||
|
||
#else // MM_IN_CLUSNET
|
||
|
||
status = CxInitialize();
|
||
|
||
#endif // MM_IN_CLUSNET
|
||
|
||
if (status == STATUS_SUCCESS) {
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[Clusnet] Initialized.\n"));
|
||
}
|
||
|
||
CnState = CnStateInitialized;
|
||
}
|
||
else {
|
||
goto error_exit;
|
||
}
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
error_exit:
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[Clusnet] Initialization failed, Shutting down. Status = %08X\n",
|
||
status));
|
||
}
|
||
|
||
CnShutdown();
|
||
|
||
return(status);
|
||
|
||
} // CnInitialize
|
||
|
||
NTSTATUS
|
||
CnShutdown(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Terminates operation of the Cluster Membership Manager.
|
||
Called when the Cluster Service is shutting down.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
|
||
|
||
if ( (CnState == CnStateInitialized) ||
|
||
(CnState == CnStateInitializePending)
|
||
)
|
||
{
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[Clusnet] Shutting down...\n"));
|
||
}
|
||
|
||
CnState = CnStateShutdownPending;
|
||
|
||
//
|
||
// Shutdown the NetBT and IP Address support.
|
||
//
|
||
NbtIfShutdown();
|
||
IpaShutdown();
|
||
|
||
#ifdef MM_IN_CLUSNET
|
||
|
||
//
|
||
// Shutdown the Membership Manager. This will shutdown the
|
||
// Transport as a side-effect.
|
||
//
|
||
CmmShutdown();
|
||
|
||
#else // MM_IN_CLUSNET
|
||
|
||
CxShutdown();
|
||
|
||
#endif // MM_IN_CLUSNET
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[Clusnet] Shutdown complete.\n"));
|
||
}
|
||
|
||
CnAssert(CnLocalNodeId != ClusterInvalidNodeId);
|
||
|
||
CnMinValidNodeId = ClusterInvalidNodeId;
|
||
CnMaxValidNodeId = ClusterInvalidNodeId;
|
||
CnLocalNodeId = ClusterInvalidNodeId;
|
||
|
||
CnState = CnStateShutdown;
|
||
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
else {
|
||
status = STATUS_DEVICE_NOT_READY;
|
||
}
|
||
|
||
//
|
||
// always test if we have a handle to this process
|
||
// and remove it
|
||
//
|
||
|
||
if ( ClussvcProcessHandle ) {
|
||
|
||
CnCloseProcessHandle( ClussvcProcessHandle );
|
||
ClussvcProcessHandle = NULL;
|
||
}
|
||
|
||
return(status);
|
||
|
||
} // CnShutdown
|
||
|
||
|
||
VOID
|
||
CnShutdownWorkRoutine(
|
||
IN PVOID WorkItem
|
||
)
|
||
{
|
||
BOOLEAN acquired;
|
||
NTSTATUS Status;
|
||
|
||
acquired = CnAcquireResourceExclusive(CnResource, TRUE);
|
||
|
||
if (!acquired) {
|
||
KIRQL irql;
|
||
|
||
CNPRINT(("[Clusnet] Failed to acquire CnResource\n"));
|
||
|
||
KeAcquireSpinLock(&CnShutdownLock, &irql);
|
||
CnShutdownScheduled = FALSE;
|
||
if (CnShutdownEvent != NULL) {
|
||
KeSetEvent(CnShutdownEvent, IO_NO_INCREMENT, FALSE);
|
||
}
|
||
KeReleaseSpinLock(&CnShutdownLock, irql);
|
||
|
||
return;
|
||
}
|
||
|
||
(VOID) CnShutdown();
|
||
|
||
if (CnShutdownEvent != NULL) {
|
||
KeSetEvent(CnShutdownEvent, IO_NO_INCREMENT, FALSE);
|
||
}
|
||
|
||
if (acquired) {
|
||
CnReleaseResourceForThread(
|
||
CnResource,
|
||
(ERESOURCE_THREAD) PsGetCurrentThread()
|
||
);
|
||
}
|
||
|
||
//
|
||
// Leave CnShutdownScheduled = TRUE until we are reinitialized to
|
||
// prevent scheduling unnecessary work items.
|
||
//
|
||
|
||
return;
|
||
|
||
} // CnShutdownWorkRoutine
|
||
|
||
|
||
BOOLEAN
|
||
CnHaltOperation(
|
||
IN PKEVENT ShutdownEvent OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Schedules a critical worker thread to perform clusnet shutdown,
|
||
if a thread is not already scheduled.
|
||
|
||
Arguments:
|
||
|
||
ShutdownEvent - if provided, event to be signalled after
|
||
shutdown is complete
|
||
|
||
Return value:
|
||
|
||
TRUE if shutdown was scheduled. FALSE if shutdown was already
|
||
scheduled (in which case ShutdownEvent will not be signalled).
|
||
|
||
--*/
|
||
{
|
||
KIRQL irql;
|
||
|
||
|
||
KeAcquireSpinLock(&CnShutdownLock, &irql);
|
||
|
||
if (CnShutdownScheduled) {
|
||
KeReleaseSpinLock(&CnShutdownLock, irql);
|
||
|
||
return(FALSE);
|
||
}
|
||
|
||
CnShutdownScheduled = TRUE;
|
||
CnShutdownEvent = ShutdownEvent;
|
||
|
||
KeReleaseSpinLock(&CnShutdownLock, irql);
|
||
|
||
//
|
||
// Schedule a critical worker thread to do the shutdown work.
|
||
//
|
||
ExInitializeWorkItem(
|
||
&CnShutdownWorkItem,
|
||
CnShutdownWorkRoutine,
|
||
&CnShutdownWorkItem
|
||
);
|
||
|
||
ExQueueWorkItem(&CnShutdownWorkItem, CriticalWorkQueue);
|
||
|
||
return(TRUE);
|
||
|
||
} // CnHaltOperation
|
||
|
||
|
||
//
|
||
// ExResource wrappers that disable APCs.
|
||
//
|
||
BOOLEAN
|
||
CnAcquireResourceExclusive(
|
||
IN PERESOURCE Resource,
|
||
IN BOOLEAN Wait
|
||
)
|
||
{
|
||
BOOLEAN acquired;
|
||
|
||
|
||
KeEnterCriticalRegion();
|
||
|
||
acquired = ExAcquireResourceExclusiveLite(Resource, Wait);
|
||
|
||
if (!acquired) {
|
||
KeLeaveCriticalRegion();
|
||
}
|
||
|
||
return(acquired);
|
||
|
||
} // CnAcquireResourceExclusive
|
||
|
||
|
||
BOOLEAN
|
||
CnAcquireResourceShared(
|
||
IN PERESOURCE Resource,
|
||
IN BOOLEAN Wait
|
||
)
|
||
{
|
||
BOOLEAN acquired;
|
||
|
||
|
||
KeEnterCriticalRegion();
|
||
|
||
acquired = ExAcquireResourceSharedLite(Resource, Wait);
|
||
|
||
if (!acquired) {
|
||
KeLeaveCriticalRegion();
|
||
}
|
||
|
||
return(acquired);
|
||
|
||
} // CnAcquireResourceShared
|
||
|
||
|
||
VOID
|
||
CnReleaseResourceForThread(
|
||
IN PERESOURCE Resource,
|
||
IN ERESOURCE_THREAD ResourceThreadId
|
||
)
|
||
{
|
||
ExReleaseResourceForThreadLite(Resource, ResourceThreadId);
|
||
|
||
KeLeaveCriticalRegion();
|
||
|
||
return;
|
||
|
||
} // CnReleaseResourceForThread
|
||
|
||
|
||
|
||
NTSTATUS
|
||
CnCloseProcessHandle(
|
||
HANDLE Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Close the cluster service process handle
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
CnAssert( Handle != NULL );
|
||
|
||
KeAttachProcess( CnSystemProcess );
|
||
Status = ZwClose( Handle );
|
||
KeDetachProcess();
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[Clusnet] Process handle released. status = %08X\n", Status));
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
CnAdjustDeviceObjectStackSize(
|
||
PDEVICE_OBJECT ClusnetDeviceObject,
|
||
PDEVICE_OBJECT TargetDeviceObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
Adjust the StackSize of ClusnetDeviceObject so that we
|
||
can pass client IRPs down to TargetDeviceObject.
|
||
|
||
The StackSize of clusnet device objects is initialized to
|
||
a default that allows for some leeway for attached drivers.
|
||
|
||
Arguments
|
||
|
||
ClusnetDeviceObject - clusnet device object whose StackSize
|
||
should be adjusted
|
||
|
||
TargetDeviceObject - device object clusnet IRPs, originally
|
||
issued to clusnet, will be forwarded to
|
||
|
||
Return value
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
CCHAR defaultStackSize, newStackSize = 0;
|
||
KIRQL irql;
|
||
|
||
if (ClusnetDeviceObject == CnDeviceObject) {
|
||
defaultStackSize = CN_DEFAULT_IRP_STACK_SIZE;
|
||
}
|
||
else if (ClusnetDeviceObject == CdpDeviceObject) {
|
||
defaultStackSize = CDP_DEFAULT_IRP_STACK_SIZE;
|
||
}
|
||
else {
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
CNPRINT(("[Clusnet] CnAdjustDeviceObjectStackSize: "
|
||
"unknown clusnet device object %p.\n",
|
||
ClusnetDeviceObject
|
||
));
|
||
}
|
||
return;
|
||
}
|
||
|
||
KeAcquireSpinLock(&CnDeviceObjectStackSizeLock, &irql);
|
||
|
||
if (ClusnetDeviceObject->StackSize <
|
||
TargetDeviceObject->StackSize + defaultStackSize) {
|
||
|
||
ClusnetDeviceObject->StackSize =
|
||
TargetDeviceObject->StackSize + defaultStackSize;
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
newStackSize = ClusnetDeviceObject->StackSize;
|
||
}
|
||
}
|
||
|
||
KeReleaseSpinLock(&CnDeviceObjectStackSizeLock, irql);
|
||
|
||
IF_CNDBG(CN_DEBUG_INIT) {
|
||
if (newStackSize != 0) {
|
||
CNPRINT(("[Clusnet] Set StackSize of clusnet device "
|
||
"object %p to %d "
|
||
"based on target device object %p.\n",
|
||
ClusnetDeviceObject,
|
||
newStackSize,
|
||
TargetDeviceObject
|
||
));
|
||
}
|
||
}
|
||
|
||
return;
|
||
|
||
} // CnAdjustDeviceObjectStackSize
|
||
|
||
#if DBG
|
||
|
||
//
|
||
// Debug code.
|
||
//
|
||
|
||
ULONG CnCpuLockMask[MAXIMUM_PROCESSORS];
|
||
|
||
VOID
|
||
CnAssertBreak(
|
||
PCHAR FailedStatement,
|
||
PCHAR FileName,
|
||
ULONG LineNumber
|
||
)
|
||
{
|
||
DbgPrint(
|
||
"[Clusnet] Assertion \"%s\" failed in %s line %u\n",
|
||
FailedStatement,
|
||
FileName,
|
||
LineNumber
|
||
);
|
||
DbgBreakPoint();
|
||
|
||
return;
|
||
|
||
} // CnAssertBreak
|
||
|
||
|
||
ULONG
|
||
CnGetCpuLockMask(
|
||
VOID
|
||
)
|
||
{
|
||
ULONG mask;
|
||
|
||
if (KeGetCurrentIrql() != DISPATCH_LEVEL) {
|
||
CnAssert(CnCpuLockMask[KeGetCurrentProcessorNumber()] == 0);
|
||
mask = 0;
|
||
}
|
||
else {
|
||
mask = CnCpuLockMask[KeGetCurrentProcessorNumber()];
|
||
}
|
||
|
||
return(mask);
|
||
}
|
||
|
||
|
||
VOID
|
||
CnVerifyCpuLockMask(
|
||
IN ULONG RequiredLockMask,
|
||
IN ULONG ForbiddenLockMask,
|
||
IN ULONG MaximumLockMask
|
||
)
|
||
{
|
||
ULONG mask;
|
||
|
||
|
||
if (KeGetCurrentIrql() < DISPATCH_LEVEL) {
|
||
mask = 0;
|
||
}
|
||
else {
|
||
mask = CnCpuLockMask[KeGetCurrentProcessorNumber()];
|
||
}
|
||
|
||
if ((mask & RequiredLockMask) != RequiredLockMask) {
|
||
CNPRINT((
|
||
"[Clusnet] Locking bug: Req'd lock mask %lx, actual mask %lx\n",
|
||
RequiredLockMask,
|
||
mask
|
||
));
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
if (mask & ForbiddenLockMask) {
|
||
CNPRINT((
|
||
"[Clusnet] Locking bug: Forbidden mask %lx, actual mask %lx\n",
|
||
ForbiddenLockMask,
|
||
mask
|
||
));
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
if (mask > MaximumLockMask) {
|
||
CNPRINT((
|
||
"[Clusnet] Locking bug: Max lock mask %lx, actual mask %lx\n",
|
||
MaximumLockMask,
|
||
mask
|
||
));
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
CnInitializeLock(
|
||
PCN_LOCK Lock,
|
||
ULONG Rank
|
||
)
|
||
{
|
||
KeInitializeSpinLock(&(Lock->SpinLock));
|
||
Lock->Rank = Rank;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
CnAcquireLock(
|
||
IN PCN_LOCK Lock,
|
||
OUT PCN_IRQL Irql
|
||
)
|
||
{
|
||
KIRQL irql;
|
||
ULONG currentCpu;
|
||
|
||
|
||
|
||
if (KeGetCurrentIrql() != DISPATCH_LEVEL) {
|
||
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
||
}
|
||
else {
|
||
irql = DISPATCH_LEVEL;
|
||
}
|
||
|
||
currentCpu = KeGetCurrentProcessorNumber();
|
||
|
||
if (CnCpuLockMask[currentCpu] >= Lock->Rank) {
|
||
CNPRINT((
|
||
"[Clusnet] CPU %u trying to acquire lock %lx out of order, mask %lx\n",
|
||
currentCpu,
|
||
Lock->Rank,
|
||
CnCpuLockMask[currentCpu]
|
||
));
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
KeAcquireSpinLockAtDpcLevel(&(Lock->SpinLock));
|
||
*Irql = irql;
|
||
|
||
CnCpuLockMask[currentCpu] |= Lock->Rank;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
CnAcquireLockAtDpc(
|
||
IN PCN_LOCK Lock
|
||
)
|
||
{
|
||
ULONG currentCpu = KeGetCurrentProcessorNumber();
|
||
|
||
|
||
if (KeGetCurrentIrql() != DISPATCH_LEVEL) {
|
||
CNPRINT((
|
||
"[Clusnet] CPU %u trying to acquire DPC lock at passive level.\n",
|
||
currentCpu
|
||
));
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
if (CnCpuLockMask[currentCpu] >= Lock->Rank) {
|
||
CNPRINT((
|
||
"[Clusnet] CPU %u trying to acquire lock %lx out of order, mask %lx\n",
|
||
currentCpu,
|
||
Lock->Rank,
|
||
CnCpuLockMask[currentCpu]
|
||
));
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
KeAcquireSpinLockAtDpcLevel(&(Lock->SpinLock));
|
||
|
||
CnCpuLockMask[currentCpu] |= Lock->Rank;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
CnReleaseLock(
|
||
IN PCN_LOCK Lock,
|
||
IN CN_IRQL Irql
|
||
)
|
||
{
|
||
ULONG currentCpu = KeGetCurrentProcessorNumber();
|
||
|
||
if (KeGetCurrentIrql() != DISPATCH_LEVEL) {
|
||
CNPRINT((
|
||
"[Clusnet] CPU %u trying to release lock from passive level.\n",
|
||
currentCpu
|
||
));
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
if ( !(CnCpuLockMask[currentCpu] & Lock->Rank) ) {
|
||
CNPRINT((
|
||
"[Clusnet] CPU %u trying to release lock %lx, which it doesn't hold, mask %lx\n",
|
||
currentCpu,
|
||
Lock->Rank,
|
||
CnCpuLockMask[currentCpu]
|
||
));
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
CnCpuLockMask[currentCpu] &= ~(Lock->Rank);
|
||
|
||
KeReleaseSpinLock(&(Lock->SpinLock), Irql);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
CnReleaseLockFromDpc(
|
||
IN PCN_LOCK Lock
|
||
)
|
||
{
|
||
ULONG currentCpu = KeGetCurrentProcessorNumber();
|
||
|
||
|
||
if (KeGetCurrentIrql() != DISPATCH_LEVEL) {
|
||
CNPRINT((
|
||
"[Clusnet] CPU %u trying to release lock from passive level.\n",
|
||
currentCpu
|
||
));
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
if ( !(CnCpuLockMask[currentCpu] & Lock->Rank) ) {
|
||
CNPRINT((
|
||
"[Clusnet] CPU %u trying to release lock %lx, which it doesn't hold, mask %lx\n",
|
||
currentCpu,
|
||
Lock->Rank,
|
||
CnCpuLockMask[currentCpu]
|
||
));
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
CnCpuLockMask[currentCpu] &= ~(Lock->Rank);
|
||
|
||
KeReleaseSpinLockFromDpcLevel(&(Lock->SpinLock));
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
CnMarkIoCancelLockAcquired(
|
||
VOID
|
||
)
|
||
{
|
||
ULONG currentCpu = KeGetCurrentProcessorNumber();
|
||
|
||
CnAssert(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||
|
||
CnAssert(!(CnCpuLockMask[currentCpu] & CN_IOCANCEL_LOCK));
|
||
CnAssert(CnCpuLockMask[currentCpu] < CN_IOCANCEL_LOCK_MAX);
|
||
|
||
CnCpuLockMask[currentCpu] |= CN_IOCANCEL_LOCK;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
CnAcquireCancelSpinLock(
|
||
OUT PCN_IRQL Irql
|
||
)
|
||
{
|
||
|
||
KIRQL irql;
|
||
KIRQL tempIrql;
|
||
ULONG currentCpu;
|
||
|
||
|
||
if (KeGetCurrentIrql() != DISPATCH_LEVEL) {
|
||
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
||
}
|
||
else {
|
||
irql = DISPATCH_LEVEL;
|
||
}
|
||
|
||
currentCpu = KeGetCurrentProcessorNumber();
|
||
|
||
if (CnCpuLockMask[currentCpu] >= CN_IOCANCEL_LOCK) {
|
||
CNPRINT((
|
||
"[Clusnet] CPU %u trying to acquire IoCancel lock out of order, mask %lx\n",
|
||
currentCpu,
|
||
CnCpuLockMask[currentCpu]
|
||
));
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
IoAcquireCancelSpinLock(&tempIrql);
|
||
|
||
CnAssert(tempIrql == DISPATCH_LEVEL);
|
||
|
||
*Irql = irql;
|
||
|
||
CnCpuLockMask[currentCpu] |= CN_IOCANCEL_LOCK;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
CnReleaseCancelSpinLock(
|
||
IN CN_IRQL Irql
|
||
)
|
||
{
|
||
ULONG currentCpu = KeGetCurrentProcessorNumber();
|
||
|
||
|
||
if (KeGetCurrentIrql() != DISPATCH_LEVEL) {
|
||
CNPRINT((
|
||
"[Clusnet] CPU %u trying to release lock from passive level.\n",
|
||
currentCpu
|
||
));
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
if ( !(CnCpuLockMask[currentCpu] & CN_IOCANCEL_LOCK) ) {
|
||
CNPRINT((
|
||
"[Clusnet] CPU %u trying to release IoCancel lock, which it doesn't hold, mask %lx\n",
|
||
currentCpu,
|
||
CnCpuLockMask[currentCpu]
|
||
));
|
||
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
CnCpuLockMask[currentCpu] &= ~(CN_IOCANCEL_LOCK);
|
||
|
||
IoReleaseCancelSpinLock(Irql);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
#endif // DEBUG
|