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.
1741 lines
37 KiB
1741 lines
37 KiB
/*++
|
|
|
|
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;
|
|
PSECURITY_DESCRIPTOR CdpAdminSecurityDescriptor = 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
|
|
);
|
|
|
|
NTSTATUS
|
|
CnBuildDeviceAcl(
|
|
OUT PACL *DeviceAcl
|
|
);
|
|
|
|
NTSTATUS
|
|
CnCreateSecurityDescriptor(
|
|
VOID
|
|
);
|
|
|
|
//
|
|
// Mark init code as discardable.
|
|
//
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(INIT, CnCreateDeviceObjects)
|
|
#pragma alloc_text(INIT, CnCreateSecurityDescriptor)
|
|
#pragma alloc_text(INIT, CnBuildDeviceAcl)
|
|
|
|
#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;
|
|
}
|
|
|
|
//
|
|
// Initialize the CDP security descriptor.
|
|
//
|
|
status = CnCreateSecurityDescriptor();
|
|
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
|
|
//
|
|
|
|
status = SecSetPagingMode( FALSE );
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto error_exit;
|
|
}
|
|
|
|
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
|
|
|
|
if (CdpAdminSecurityDescriptor != NULL) {
|
|
ExFreePool(CdpAdminSecurityDescriptor);
|
|
CdpAdminSecurityDescriptor = NULL;
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
//
|
|
// 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;
|
|
|
|
// Disable further processing of Clussvc to Clusnet Hbs.
|
|
ClussvcClusnetHbTimeoutAction = ClussvcHangActionDisable;
|
|
InterlockedExchange(&ClussvcClusnetHbTickCount, 0);
|
|
ClussvcClusnetHbTimeoutTicks = 0;
|
|
|
|
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
|
|
CnEnableHaltProcessing(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes global data for halt processing.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
KIRQL irql;
|
|
|
|
KeAcquireSpinLock(&CnShutdownLock, &irql);
|
|
CnShutdownScheduled = FALSE;
|
|
CnShutdownEvent = NULL;
|
|
KeReleaseSpinLock(&CnShutdownLock, irql);
|
|
|
|
return;
|
|
|
|
} // CnEnableHaltProcessing
|
|
|
|
|
|
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
|
|
|
|
|
|
NTSTATUS
|
|
CnBuildDeviceAcl(
|
|
OUT PACL *DeviceAcl
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine builds an ACL which gives Administrators,
|
|
LocalSystem, and NetworkService principals full access.
|
|
All other principals have no access.
|
|
|
|
Arguments:
|
|
|
|
DeviceAcl - Output pointer to the new ACL.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or an appropriate error code.
|
|
|
|
Notes:
|
|
|
|
This code was lifted from AFD.
|
|
|
|
--*/
|
|
{
|
|
PGENERIC_MAPPING genericMapping;
|
|
ULONG aclLength;
|
|
NTSTATUS status;
|
|
ACCESS_MASK accessMask = GENERIC_ALL;
|
|
PACL newAcl;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Enable access to all the globally defined SIDs
|
|
//
|
|
|
|
genericMapping = IoGetFileObjectGenericMapping();
|
|
|
|
RtlMapGenericMask( &accessMask, genericMapping );
|
|
|
|
aclLength = sizeof( ACL ) +
|
|
3 * FIELD_OFFSET (ACCESS_ALLOWED_ACE, SidStart) +
|
|
RtlLengthSid( SeExports->SeAliasAdminsSid ) +
|
|
RtlLengthSid( SeExports->SeLocalSystemSid ) +
|
|
RtlLengthSid( SeExports->SeNetworkServiceSid );
|
|
|
|
newAcl = ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
aclLength,
|
|
CN_POOL_TAG
|
|
);
|
|
|
|
if (newAcl == NULL) {
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
status = RtlCreateAcl (newAcl, aclLength, ACL_REVISION );
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePoolWithTag(
|
|
newAcl,
|
|
CN_POOL_TAG
|
|
);
|
|
return (status);
|
|
}
|
|
|
|
status = RtlAddAccessAllowedAce (
|
|
newAcl,
|
|
ACL_REVISION2,
|
|
accessMask,
|
|
SeExports->SeAliasAdminsSid
|
|
);
|
|
|
|
CnAssert(NT_SUCCESS(status));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
CNPRINT((
|
|
"[ClusNet] Failed to add Admin to ACL, error: %lx\n",
|
|
status
|
|
));
|
|
return(status);
|
|
}
|
|
|
|
status = RtlAddAccessAllowedAce (
|
|
newAcl,
|
|
ACL_REVISION2,
|
|
accessMask,
|
|
SeExports->SeLocalSystemSid
|
|
);
|
|
|
|
CnAssert(NT_SUCCESS(status));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
CNPRINT((
|
|
"[ClusNet] Failed to add LocalSystem to ACL, error: %lx\n",
|
|
status
|
|
));
|
|
return(status);
|
|
}
|
|
|
|
status = RtlAddAccessAllowedAce (
|
|
newAcl,
|
|
ACL_REVISION2,
|
|
accessMask,
|
|
SeExports->SeNetworkServiceSid
|
|
);
|
|
|
|
CnAssert(NT_SUCCESS(status));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
CNPRINT((
|
|
"[ClusNet] Failed to add NetworkService to ACL, error: %lx. "
|
|
"(Non-fatal error)\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
*DeviceAcl = newAcl;
|
|
|
|
return (STATUS_SUCCESS);
|
|
|
|
} // CnBuildDeviceAcl
|
|
|
|
|
|
NTSTATUS
|
|
CnCreateSecurityDescriptor(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a security descriptor which gives access
|
|
only to certain priviliged accounts. This descriptor is used
|
|
to access check CDP socket opens.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or an appropriate error code.
|
|
|
|
Notes:
|
|
|
|
This code was lifted from AFD.
|
|
|
|
--*/
|
|
{
|
|
PACL devAcl = NULL;
|
|
NTSTATUS status;
|
|
BOOLEAN memoryAllocated = FALSE;
|
|
PSECURITY_DESCRIPTOR cdpSecurityDescriptor;
|
|
ULONG cdpSecurityDescriptorLength;
|
|
CHAR buffer[SECURITY_DESCRIPTOR_MIN_LENGTH];
|
|
PSECURITY_DESCRIPTOR localSecurityDescriptor =
|
|
(PSECURITY_DESCRIPTOR)buffer;
|
|
PSECURITY_DESCRIPTOR localCdpAdminSecurityDescriptor;
|
|
SECURITY_INFORMATION securityInformation = DACL_SECURITY_INFORMATION;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Get a pointer to the security descriptor from the CDP device object.
|
|
//
|
|
status = ObGetObjectSecurity(
|
|
CdpDeviceObject,
|
|
&cdpSecurityDescriptor,
|
|
&memoryAllocated
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
CNPRINT((
|
|
"[ClusNet] Failed to get CDP device object security descriptor, "
|
|
"status %lx\n",
|
|
status
|
|
));
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Build a local security descriptor with an ACL giving only
|
|
// certain priviliged accounts.
|
|
//
|
|
status = CnBuildDeviceAcl(&devAcl);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
CNPRINT((
|
|
"[ClusNet] Failed to create Raw ACL, error: %lx\n",
|
|
status
|
|
));
|
|
goto error_exit;
|
|
}
|
|
|
|
(VOID) RtlCreateSecurityDescriptor(
|
|
localSecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION
|
|
);
|
|
|
|
(VOID) RtlSetDaclSecurityDescriptor(
|
|
localSecurityDescriptor,
|
|
TRUE,
|
|
devAcl,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Make a copy of the CDP descriptor. This copy will be
|
|
// the raw descriptor.
|
|
//
|
|
cdpSecurityDescriptorLength = RtlLengthSecurityDescriptor(
|
|
cdpSecurityDescriptor
|
|
);
|
|
|
|
localCdpAdminSecurityDescriptor = ExAllocatePoolWithTag (
|
|
PagedPool,
|
|
cdpSecurityDescriptorLength,
|
|
CN_POOL_TAG
|
|
);
|
|
|
|
if (localCdpAdminSecurityDescriptor == NULL) {
|
|
CNPRINT((
|
|
"[ClusNet]: failed to allocate security descriptor "
|
|
"of size %d.\n",
|
|
cdpSecurityDescriptorLength
|
|
));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto error_exit;
|
|
}
|
|
|
|
RtlMoveMemory(
|
|
localCdpAdminSecurityDescriptor,
|
|
cdpSecurityDescriptor,
|
|
cdpSecurityDescriptorLength
|
|
);
|
|
|
|
CdpAdminSecurityDescriptor = localCdpAdminSecurityDescriptor;
|
|
|
|
//
|
|
// Now apply the local descriptor to the raw descriptor.
|
|
//
|
|
status = SeSetSecurityDescriptorInfo(
|
|
NULL,
|
|
&securityInformation,
|
|
localSecurityDescriptor,
|
|
&CdpAdminSecurityDescriptor,
|
|
PagedPool,
|
|
IoGetFileObjectGenericMapping()
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
CNPRINT((
|
|
"[ClusNet]: SeSetSecurity failed for CDP admin "
|
|
"security descriptor, %lx\n",
|
|
status
|
|
));
|
|
CnAssert(CdpAdminSecurityDescriptor == localCdpAdminSecurityDescriptor);
|
|
ExFreePool(CdpAdminSecurityDescriptor);
|
|
CdpAdminSecurityDescriptor = NULL;
|
|
goto error_exit;
|
|
}
|
|
|
|
if (CdpAdminSecurityDescriptor != localCdpAdminSecurityDescriptor) {
|
|
ExFreePool(localCdpAdminSecurityDescriptor);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
error_exit:
|
|
|
|
ObReleaseObjectSecurity(
|
|
cdpSecurityDescriptor,
|
|
memoryAllocated
|
|
);
|
|
|
|
if (devAcl != NULL) {
|
|
ExFreePoolWithTag(
|
|
devAcl,
|
|
CN_POOL_TAG
|
|
);
|
|
}
|
|
|
|
return(status);
|
|
|
|
} // CnCreateSecurityDescriptor
|
|
|
|
|
|
|
|
#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
|