|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
cnpnode.c
Abstract:
Node management routines for the Cluster Network Protocol.
Author:
Mike Massa (mikemas) July 29, 1996
Revision History:
Who When What -------- -------- ---------------------------------------------- mikemas 07-29-96 created
Notes:
--*/
#include "precomp.h"
#pragma hdrstop
#include "cnpnode.tmh"
//
// Global Node Data
//
PCNP_NODE * CnpNodeTable = NULL; LIST_ENTRY CnpDeletingNodeList = {NULL, NULL}; #if DBG
CN_LOCK CnpNodeTableLock = {0, 0}; #else // DBG
CN_LOCK CnpNodeTableLock = 0; #endif // DBG
PCNP_NODE CnpLocalNode = NULL; BOOLEAN CnpIsNodeShutdownPending = FALSE; PKEVENT CnpNodeShutdownEvent = NULL;
//
// static data
//
//
// Membership state table. This table is used to determine the validity
// of membership state transitions. Row is current state; col is the state
// to which a transition is made. Dead to Unconfigured is currently illegal,
// but someday, if we support dynamically shrinking the size of the
// cluster, we'd need to allow this transition.
//
typedef enum _MM_ACTION { MMActionIllegal = 0, MMActionWarning, MMActionNodeAlive, MMActionNodeDead, MMActionConfigured, MMActionUnconfigured } MM_ACTION;
MM_ACTION MembershipStateTable[ClusnetNodeStateLastEntry][ClusnetNodeStateLastEntry] = { // Alive Joining Dead NC'ed
/* Alive */ { MMActionWarning, MMActionIllegal, MMActionNodeDead, MMActionIllegal }, /* Join */ { MMActionNodeAlive, MMActionIllegal, MMActionNodeDead, MMActionIllegal }, /* Dead */ { MMActionNodeAlive, MMActionNodeAlive, MMActionWarning, MMActionIllegal }, /* NC'ed */ { MMActionIllegal, MMActionIllegal, MMActionConfigured, MMActionIllegal } };
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, CnpLoadNodes)
#pragma alloc_text(PAGE, CnpInitializeNodes)
#endif // ALLOC_PRAGMA
//
// Private utility routines
//
VOID CnpDestroyNode( PCNP_NODE Node ) /*++
Notes:
Called with no locks held. There should be no outstanding references to the target node.
Synchronization with CnpCancelDeregisterNode() is achieved via CnpNodeTableLock.
--*/ { PLIST_ENTRY entry; CN_IRQL tableIrql; BOOLEAN setCleanupEvent = FALSE;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
IF_CNDBG( CN_DEBUG_NODEOBJ ) CNPRINT(("[CNP] Destroying node %u\n", Node->Id));
CnAcquireLock(&CnpNodeTableLock, &tableIrql);
//
// Remove the node from the deleting list.
//
#if DBG
{ PCNP_NODE node = NULL;
//
// Verify that the node object is on the deleting list.
//
for (entry = CnpDeletingNodeList.Flink; entry != &CnpDeletingNodeList; entry = entry->Flink ) { node = CONTAINING_RECORD(entry, CNP_NODE, Linkage);
if (node == Node) { break; } }
CnAssert(node == Node); }
#endif // DBG
RemoveEntryList(&(Node->Linkage));
if (CnpIsNodeShutdownPending) { if (IsListEmpty(&CnpDeletingNodeList)) { setCleanupEvent = TRUE; } }
CnReleaseLock(&CnpNodeTableLock, tableIrql);
if (Node->PendingDeleteIrp != NULL) { CnAcquireCancelSpinLock(&(Node->PendingDeleteIrp->CancelIrql));
CnCompletePendingRequest(Node->PendingDeleteIrp, STATUS_SUCCESS, 0);
//
// The IoCancelSpinLock was released by CnCompletePendingRequest()
//
}
CnFreePool(Node);
if (setCleanupEvent) { IF_CNDBG(CN_DEBUG_CLEANUP) { CNPRINT(("[CNP] Setting node cleanup event.\n")); }
KeSetEvent(CnpNodeShutdownEvent, 0, FALSE); }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return;
} // CnpDestroyNode
BOOLEAN CnpDeleteNode( IN PCNP_NODE Node, IN PVOID Unused, IN CN_IRQL NodeTableIrql ) /*++
Routine Description:
Deletes a node object.
Arguments:
Node - A pointer to the node object to be deleted.
Unused - An umused parameter.
NodeTableIrql - The IRQL value at which the CnpNodeTable lock was acquired,
Return Value:
Returns TRUE if the CnpNodeTable lock is still held. Returns FALSE if the CnpNodeTable lock is released.
Notes:
Called with CnpNodeTable and node object locks held. Releases both locks.
Conforms to the calling convention for PCNP_NODE_UPDATE_ROUTINE
--*/ { PLIST_ENTRY entry; PCNP_INTERFACE interface; PCNP_NETWORK network; CL_NODE_ID nodeId = Node->Id;
CnVerifyCpuLockMask( (CNP_NODE_TABLE_LOCK | CNP_NODE_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
IF_CNDBG( CN_DEBUG_NODEOBJ ) CNPRINT(("[CNP] Deleting node %u\n", nodeId));
if (CnpLocalNode == Node) { CnAssert(CnLocalNodeId == Node->Id); CnpLocalNode = NULL; }
//
// Move the node to the deleting list.
//
CnpNodeTable[nodeId] = NULL; InsertTailList(&CnpDeletingNodeList, &(Node->Linkage));
IF_CNDBG( CN_DEBUG_NODEOBJ ) CNPRINT(( "[CNP] Moved node %u to deleting list\n", nodeId ));
CnReleaseLockFromDpc(&CnpNodeTableLock); Node->Irql = NodeTableIrql;
//
// From this point on, the cancel routine may run and
// complete the irp.
//
Node->Flags |= CNP_NODE_FLAG_DELETING;
Node->CommState = ClusnetNodeCommStateOffline;
//
// Delete all the node's interfaces.
//
IF_CNDBG( CN_DEBUG_NODEOBJ ) CNPRINT(( "[CNP] Deleting all interfaces on node %u\n", Node->Id ));
while (!IsListEmpty(&(Node->InterfaceList))) {
interface = CONTAINING_RECORD( Node->InterfaceList.Flink, CNP_INTERFACE, NodeLinkage );
network = interface->Network;
CnAcquireLockAtDpc(&(network->Lock)); network->Irql = DISPATCH_LEVEL;
CnpDeleteInterface(interface);
//
// The network object lock was released.
//
}
//
// Remove initial reference on node object. When the reference
// count goes to zero, the node will be deleted. This releases
// the node lock.
//
CnpDereferenceNode(Node);
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(FALSE); }
//
// CNP Internal Routines
//
VOID CnpWalkNodeTable( PCNP_NODE_UPDATE_ROUTINE UpdateRoutine, PVOID UpdateContext ) { ULONG i; CN_IRQL tableIrql; PCNP_NODE node; BOOLEAN isNodeTableLockHeld;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
CnAcquireLock(&CnpNodeTableLock, &tableIrql);
CnAssert(CnMinValidNodeId != ClusterInvalidNodeId); CnAssert(CnMaxValidNodeId != ClusterInvalidNodeId);
for (i=CnMinValidNodeId; i <= CnMaxValidNodeId; i++) { node = CnpNodeTable[i];
if (node != NULL) {
CnAcquireLockAtDpc(&(node->Lock)); node->Irql = DISPATCH_LEVEL;
isNodeTableLockHeld = (*UpdateRoutine)( node, UpdateContext, tableIrql );
//
// The node object lock was released.
// The node table lock may also have been released.
//
if (!isNodeTableLockHeld) { CnAcquireLock(&CnpNodeTableLock, &tableIrql); } } }
CnReleaseLock(&CnpNodeTableLock, tableIrql);
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return;
} // CnpWalkNodeTable
NTSTATUS CnpValidateAndFindNode( IN CL_NODE_ID NodeId, OUT PCNP_NODE * Node ) { NTSTATUS status; CN_IRQL tableIrql; PCNP_NODE node = NULL;
CnVerifyCpuLockMask( 0, // Required
CNP_LOCK_RANGE, // Forbidden
CNP_PRECEEDING_LOCK_RANGE // Maximum
);
if (CnIsValidNodeId(NodeId)) { CnAcquireLock(&CnpNodeTableLock, &tableIrql);
node = CnpNodeTable[NodeId];
if (node != NULL) { CnAcquireLockAtDpc(&(node->Lock)); CnReleaseLockFromDpc(&CnpNodeTableLock); node->Irql = tableIrql;
*Node = node;
CnVerifyCpuLockMask( CNP_NODE_OBJECT_LOCK, // Required
CNP_NODE_TABLE_LOCK, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return(STATUS_SUCCESS); } else { status = STATUS_CLUSTER_NODE_NOT_FOUND; }
CnReleaseLock(&CnpNodeTableLock, tableIrql); } else { status = STATUS_CLUSTER_INVALID_NODE; }
CnVerifyCpuLockMask( 0, // Required
CNP_LOCK_RANGE, // Forbidden
CNP_PRECEEDING_LOCK_RANGE // Maximum
);
return(status);
} // CnpValidateAndFindNode
PCNP_NODE CnpLockedFindNode( IN CL_NODE_ID NodeId, IN CN_IRQL NodeTableIrql ) /*++
Routine Description:
Searches the node table for a specified node object.
Arguments:
NodeId - The ID of the node object to locate.
NodeTableIrql - The IRQL level at which the node table lock was acquired before calling this routine.
Return Value:
A pointer to the requested node object, if it exists. NULL otherwise.
Notes:
Called with CnpNodeTableLock held. Returns with CnpNodeTableLock released. If return value is non-NULL, returns with node object lock held.
--*/ { NTSTATUS status; CN_IRQL tableIrql; PCNP_NODE node;
CnVerifyCpuLockMask( CNP_NODE_TABLE_LOCK, // Required
0, // Forbidden
CNP_NODE_TABLE_LOCK_MAX // Maximum
);
node = CnpNodeTable[NodeId];
if (node != NULL) { CnAcquireLockAtDpc(&(node->Lock)); CnReleaseLockFromDpc(&CnpNodeTableLock); node->Irql = NodeTableIrql;
CnVerifyCpuLockMask( CNP_NODE_OBJECT_LOCK, // Required
CNP_NODE_TABLE_LOCK, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return(node); }
CnReleaseLock(&CnpNodeTableLock, NodeTableIrql);
CnVerifyCpuLockMask( 0, // Required
(CNP_NODE_TABLE_LOCK | CNP_NODE_OBJECT_LOCK), // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return(NULL);
} // CnpLockedFindNode
PCNP_NODE CnpFindNode( IN CL_NODE_ID NodeId ) { CN_IRQL tableIrql;
CnVerifyCpuLockMask( 0, // Required
CNP_LOCK_RANGE, // Forbidden
CNP_PRECEEDING_LOCK_RANGE // Maximum
);
CnAcquireLock(&CnpNodeTableLock, &tableIrql);
return(CnpLockedFindNode(NodeId, tableIrql));
} // CnpFindNode
VOID CnpDeclareNodeUnreachable( PCNP_NODE Node ) /*++
Notes:
Called with node object lock held.
--*/ { CnVerifyCpuLockMask( CNP_NODE_OBJECT_LOCK, // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
if ( (Node->CommState == ClusnetNodeCommStateOnline) && !CnpIsNodeUnreachable(Node) ) { IF_CNDBG( CN_DEBUG_NODEOBJ ) CNPRINT(("[CNP] Declaring node %u unreachable\n", Node->Id));
Node->Flags |= CNP_NODE_FLAG_UNREACHABLE; }
return;
} // CnpDeclareNodeUnreachable
VOID CnpDeclareNodeReachable( PCNP_NODE Node ) /*++
Notes:
Called with node object lock held.
--*/ { CnVerifyCpuLockMask( CNP_NODE_OBJECT_LOCK, // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
if ( (Node->CommState == ClusnetNodeCommStateOnline) && CnpIsNodeUnreachable(Node) ) { IF_CNDBG( CN_DEBUG_NODEOBJ ) CNPRINT(("[CNP] Declaring node %u reachable again\n", Node->Id));
Node->Flags &= ~(CNP_NODE_FLAG_UNREACHABLE); }
return;
} // CnpDeclareNodeUnreachable
VOID CnpReferenceNode( PCNP_NODE Node ) /*++
Notes:
Called with node object lock held.
--*/ { CnVerifyCpuLockMask( CNP_NODE_OBJECT_LOCK, // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
CnAssert(Node->RefCount != 0xFFFFFFFF);
Node->RefCount++;
IF_CNDBG( CN_DEBUG_CNPREF ) CNPRINT(( "[CNP] Referencing node %u, new refcount %u\n", Node->Id, Node->RefCount ));
return;
} // CnpReferenceNode
VOID CnpDereferenceNode( PCNP_NODE Node ) /*++
Notes:
Called with node object lock held. Returns with node object lock released.
--*/ { BOOLEAN isDeleting = FALSE; ULONG newRefCount;
CnVerifyCpuLockMask( CNP_NODE_OBJECT_LOCK, // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
CnAssert(Node->RefCount != 0);
newRefCount = --(Node->RefCount);
IF_CNDBG( CN_DEBUG_CNPREF ) CNPRINT(( "[CNP] Dereferencing node %u, new refcount %u\n", Node->Id, newRefCount ));
CnReleaseLock(&(Node->Lock), Node->Irql);
if (newRefCount > 0) { CnVerifyCpuLockMask( 0, // Required
CNP_NODE_OBJECT_LOCK, // Forbidden
CNP_NODE_TABLE_LOCK_MAX // Maximum
);
return; }
CnpDestroyNode(Node);
CnVerifyCpuLockMask( 0, // Required
CNP_NODE_OBJECT_LOCK, // Forbidden
CNP_NODE_TABLE_LOCK_MAX // Maximum
);
return;
} // CnpDereferenceNode
//
// Cluster Transport Public Routines
//
NTSTATUS CnpLoadNodes( VOID ) /*++
Routine Description:
Called when the Cluster Network driver is loading. Initializes static node-related data structures.
Arguments:
None.
Return Value:
None.
--*/ { NTSTATUS status; ULONG i;
CnInitializeLock(&CnpNodeTableLock, CNP_NODE_TABLE_LOCK); InitializeListHead(&CnpDeletingNodeList);
return(STATUS_SUCCESS);
} // CnpLoadNodes
NTSTATUS CnpInitializeNodes( VOID ) /*++
Routine Description:
Called when the Cluster Network driver is being (re)initialized. Initializes dynamic node-related data structures.
Arguments:
None.
Return Value:
None.
--*/ { NTSTATUS status; ULONG i;
PAGED_CODE();
CnAssert(CnLocalNodeId != ClusterInvalidNodeId); CnAssert(CnMinValidNodeId != ClusterInvalidNodeId); CnAssert(CnMaxValidNodeId != ClusterInvalidNodeId); CnAssert(CnpNodeTable == NULL); CnAssert(CnpNodeShutdownEvent == NULL); CnAssert(IsListEmpty(&CnpDeletingNodeList));
CnpNodeShutdownEvent = CnAllocatePool(sizeof(KEVENT));
if (CnpNodeShutdownEvent == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); }
KeInitializeEvent(CnpNodeShutdownEvent, NotificationEvent, FALSE); CnpIsNodeShutdownPending = FALSE;
CnpNodeTable = CnAllocatePool( (sizeof(PCNP_NODE) * (CnMaxValidNodeId + 1)) );
if (CnpNodeTable == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); }
RtlZeroMemory(CnpNodeTable, (sizeof(PCNP_NODE) * (CnMaxValidNodeId + 1)) );
//
// Register the local node.
//
status = CxRegisterNode(CnLocalNodeId);
if (!NT_SUCCESS(status)) { return(status); }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(STATUS_SUCCESS);
} // CnpInitializeNodes
VOID CnpShutdownNodes( VOID ) /*++
Routine Description:
Called when a shutdown request is issued to the Cluster Network Driver. Deletes all node objects.
Arguments:
None.
Return Value:
None.
--*/ { ULONG i; CN_IRQL tableIrql; PCNP_NODE node; PCNP_NODE * table; BOOLEAN waitEvent = FALSE; NTSTATUS status;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
if (CnpNodeShutdownEvent != NULL) { CnAssert(CnpIsNodeShutdownPending == FALSE);
IF_CNDBG(CN_DEBUG_CLEANUP) { CNPRINT(("[CNP] Cleaning up nodes...\n")); }
if (CnpNodeTable != NULL) {
CnpWalkNodeTable(CnpDeleteNode, NULL);
CnAcquireLock(&CnpNodeTableLock, &tableIrql);
if (!IsListEmpty(&CnpDeletingNodeList)) { CnpIsNodeShutdownPending = TRUE; waitEvent = TRUE; }
CnReleaseLock(&CnpNodeTableLock, tableIrql);
if (waitEvent) { IF_CNDBG(CN_DEBUG_CLEANUP) { CNPRINT(("[CNP] Node deletes are pending...\n")); }
status = KeWaitForSingleObject( CnpNodeShutdownEvent, Executive, KernelMode, FALSE, // not alertable
NULL // no timeout
); CnAssert(status == STATUS_SUCCESS); }
CnAssert(IsListEmpty(&CnpDeletingNodeList));
IF_CNDBG(CN_DEBUG_CLEANUP) { CNPRINT(("[CNP] All nodes deleted.\n")); }
CnFreePool(CnpNodeTable); CnpNodeTable = NULL; }
CnFreePool(CnpNodeShutdownEvent); CnpNodeShutdownEvent = NULL;
IF_CNDBG(CN_DEBUG_CLEANUP) { CNPRINT(("[CNP] Nodes cleaned up.\n")); } }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return;
} // CnpShutdownNodes
NTSTATUS CxRegisterNode( CL_NODE_ID NodeId ) { NTSTATUS status = STATUS_SUCCESS; CN_IRQL tableIrql; PCNP_NODE node = NULL;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
if (CnIsValidNodeId(NodeId)) { //
// Allocate and initialize a node object.
//
node = CnAllocatePool(sizeof(CNP_NODE));
if (node == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); }
RtlZeroMemory(node, sizeof(CNP_NODE));
CN_INIT_SIGNATURE(node, CNP_NODE_SIG); node->Id = NodeId; node->CommState = ClusnetNodeCommStateOffline; node->MMState = ClusnetNodeStateDead; node->RefCount = 1;
//
// NodeDownIssued is init'ed to true so that the first recv'd
// heart beat msg will cause a node up event to be triggered
//
node->NodeDownIssued = TRUE; InitializeListHead(&(node->InterfaceList)); CnInitializeLock(&(node->Lock), CNP_NODE_OBJECT_LOCK);
CnAcquireLock(&CnpNodeTableLock, &tableIrql);
//
// Make sure this isn't a duplicate registration
//
if (CnpNodeTable[NodeId] == NULL) { if (NodeId == CnLocalNodeId) { node->Flags |= CNP_NODE_FLAG_LOCAL; CnpLocalNode = node; }
CnpNodeTable[NodeId] = node;
status = STATUS_SUCCESS; } else { status = STATUS_CLUSTER_NODE_EXISTS; }
CnReleaseLock(&CnpNodeTableLock, tableIrql);
if (!NT_SUCCESS(status)) { CnFreePool(node); } else { IF_CNDBG( CN_DEBUG_NODEOBJ ) CNPRINT(("[CNP] Registered node %u\n", NodeId)); } } else { status = STATUS_CLUSTER_INVALID_NODE; }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxRegisterNode
VOID CxCancelDeregisterNode( PDEVICE_OBJECT DeviceObject, PIRP Irp ) /*++
Routine Description:
Cancellation handler for DeregisterNode requests.
Return Value:
None.
Notes:
Called with cancel spinlock held. Returns with cancel spinlock released.
--*/
{ PFILE_OBJECT fileObject; CN_IRQL cancelIrql = Irp->CancelIrql; PLIST_ENTRY entry; PCNP_NODE node;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
CnMarkIoCancelLockAcquired();
IF_CNDBG( CN_DEBUG_IRP ) CNPRINT(( "[CNP] Attempting to cancel DeregisterNode irp %p\n", Irp ));
CnAssert(DeviceObject == CnDeviceObject);
fileObject = CnBeginCancelRoutine(Irp);
CnAcquireLockAtDpc(&CnpNodeTableLock);
CnReleaseCancelSpinLock(DISPATCH_LEVEL);
//
// We can only complete the irp if we can find it stashed in a
// deleting node object. The deleting node object could have
// been destroyed and the IRP completed before we acquired the
// CnpNetworkListLock.
//
for (entry = CnpDeletingNodeList.Flink; entry != &CnpDeletingNodeList; entry = entry->Flink ) { node = CONTAINING_RECORD(entry, CNP_NODE, Linkage);
if (node->PendingDeleteIrp == Irp) { IF_CNDBG( CN_DEBUG_IRP ) CNPRINT(( "[CNP] Found dereg irp on node %u\n", node->Id ));
//
// Found the Irp. Now take it away and complete it.
//
node->PendingDeleteIrp = NULL;
CnReleaseLock(&CnpNodeTableLock, cancelIrql);
CnAcquireCancelSpinLock(&(Irp->CancelIrql));
CnEndCancelRoutine(fileObject);
CnCompletePendingRequest(Irp, STATUS_CANCELLED, 0);
//
// IoCancelSpinLock was released by CnCompletePendingRequest().
//
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return; } }
CnReleaseLock(&CnpNodeTableLock, cancelIrql);
CnAcquireCancelSpinLock(&cancelIrql);
CnEndCancelRoutine(fileObject);
CnReleaseCancelSpinLock(cancelIrql);
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return;
} // CnpCancelApiDeregisterNode
NTSTATUS CxDeregisterNode( CL_NODE_ID NodeId, PIRP Irp, PIO_STACK_LOCATION IrpSp ) { NTSTATUS status; CN_IRQL cancelIrql; PCNP_NODE node = NULL; BOOLEAN isNodeTableLockHeld;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
if (CnIsValidNodeId(NodeId)) { if (NodeId != CnLocalNodeId) { CnAcquireCancelSpinLock(&cancelIrql); CnAcquireLockAtDpc(&CnpNodeTableLock);
node = CnpNodeTable[NodeId];
if (node != NULL) { status = CnMarkRequestPending( Irp, IrpSp, CxCancelDeregisterNode );
if (status != STATUS_CANCELLED) {
CnReleaseCancelSpinLock(DISPATCH_LEVEL);
CnAssert(status == STATUS_SUCCESS);
CnAcquireLockAtDpc(&(node->Lock));
IF_CNDBG( CN_DEBUG_NODEOBJ ) CNPRINT(("[CNP] Deregistering node %u\n", NodeId));
//
// Save a pointer to pending irp. Note this is protected
// by the table lock, not the object lock.
//
node->PendingDeleteIrp = Irp;
isNodeTableLockHeld = CnpDeleteNode(node, NULL, cancelIrql);
if (isNodeTableLockHeld) { CnReleaseLock(&CnpNodeTableLock, cancelIrql); }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(STATUS_PENDING); } } else { status = STATUS_CLUSTER_NODE_NOT_FOUND; }
CnReleaseLockFromDpc(&CnpNodeTableLock); CnReleaseCancelSpinLock(cancelIrql); } else { status = STATUS_CLUSTER_INVALID_REQUEST; } } else { status = STATUS_CLUSTER_INVALID_NODE; }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxDeregisterNode
NTSTATUS CxOnlineNodeComm( CL_NODE_ID NodeId ) { NTSTATUS status; PCNP_NODE node;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpValidateAndFindNode(NodeId, &node);
if (status == STATUS_SUCCESS) {
if (node->CommState == ClusnetNodeCommStateOffline) { IF_CNDBG( CN_DEBUG_NODEOBJ ) CNPRINT(( "[CNP] Moving node %u comm state to online.\n", NodeId ));
CnTrace( CNP_NODE_DETAIL, CnpTraceOnlineNodeComm, "[CNP] Moving node %u comm state to online.\n", NodeId );
node->CommState = ClusnetNodeCommStateOnline;
CnpWalkInterfacesOnNode(node, CnpResetAndOnlinePendingInterface);
} else { status = STATUS_CLUSTER_NODE_ALREADY_UP; }
CnReleaseLock(&(node->Lock), node->Irql); }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxOnlineNodeComm
NTSTATUS CxOfflineNodeComm( IN CL_NODE_ID NodeId, IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp ) /*++
Notes:
--*/ { PCNP_NODE node; NTSTATUS status;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpValidateAndFindNode(NodeId, &node);
if (status == STATUS_SUCCESS) { if (node->CommState == ClusnetNodeCommStateOnline) { IF_CNDBG( CN_DEBUG_NODEOBJ ) CNPRINT(( "[CNP] Moving node %u comm state to offline.\n", NodeId ));
CnTrace( CNP_NODE_DETAIL, CnpTraceOfflineNodeComm, "[CNP] Moving node %u comm state to offline.\n", NodeId ); node->CommState = ClusnetNodeCommStateOffline;
CnpWalkInterfacesOnNode(node, CnpOfflineInterfaceWrapper);
} else { status = STATUS_CLUSTER_NODE_ALREADY_DOWN; }
CnReleaseLock(&(node->Lock), node->Irql); } else { status = STATUS_CLUSTER_NODE_NOT_FOUND; }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxOfflineNodeComm
NTSTATUS CxGetNodeCommState( IN CL_NODE_ID NodeId, OUT PCLUSNET_NODE_COMM_STATE CommState ) { NTSTATUS status; PCNP_NODE node;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpValidateAndFindNode(NodeId, &node);
if (status == STATUS_SUCCESS) { if (CnpIsNodeUnreachable(node)) { *CommState = ClusnetNodeCommStateUnreachable; } else { *CommState = node->CommState; }
CnReleaseLock(&(node->Lock), node->Irql); }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxGetNodeCommState
NTSTATUS CxGetNodeMembershipState( IN CL_NODE_ID NodeId, OUT PCLUSNET_NODE_STATE State ) { NTSTATUS status; PCNP_NODE node;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpValidateAndFindNode(NodeId, &node);
if (status == STATUS_SUCCESS) {
*State = node->MMState;
CnReleaseLock(&(node->Lock), node->Irql); }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxGetNodeMembershipState
NTSTATUS CxSetNodeMembershipState( IN CL_NODE_ID NodeId, IN CLUSNET_NODE_STATE State ) { NTSTATUS status; PCNP_NODE node; MM_ACTION MMAction; BOOLEAN nodeLockAcquired = FALSE;
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
status = CnpValidateAndFindNode(NodeId, &node);
if (status == STATUS_SUCCESS) { nodeLockAcquired = TRUE;
IF_CNDBG( CN_DEBUG_MMSTATE ) { CNPRINT(("[Clusnet] Changing Node %u (%08X) MMState from %u to %u\n", node->Id, node, node->MMState, State)); }
//
// look up the routine to call (if any) based on the old and new
// state
//
switch ( MembershipStateTable[ node->MMState ][ State ] ) {
case MMActionIllegal: status = STATUS_CLUSTER_INVALID_REQUEST; break;
case MMActionWarning:
//
// warning about null transitions
//
if ( node->MMState == ClusnetNodeStateAlive && State == ClusnetNodeStateAlive ) {
status = STATUS_CLUSTER_NODE_ALREADY_UP; } else if ( node->MMState == ClusnetNodeStateDead && State == ClusnetNodeStateDead ) {
status = STATUS_CLUSTER_NODE_ALREADY_DOWN; } break;
case MMActionNodeAlive: node->MMState = State; //
// if we're transitioning our own node from Dead to
// Joining or Alive then start heartbeat code
//
if (( node->MMState != ClusnetNodeStateJoining || State != ClusnetNodeStateAlive ) && CnpIsNodeLocal( node )) {
node->MissedHBs = 0; node->HBWasMissed = FALSE;
//
// Release the node lock before starting heartbeats. Note
// that we are holding the global resource here, which will
// synchronize this code with shutdown.
//
CnReleaseLock(&(node->Lock), node->Irql); nodeLockAcquired = FALSE;
CnpStartHeartBeats(); }
break;
case MMActionNodeDead:
//
// reset this flag so when node is being brought
// online again, we'll issue a Node Up event on
// first HB received from this node.
//
node->NodeDownIssued = TRUE; node->MMState = State;
if ( CnpIsNodeLocal( node )) { //
// Release the node lock before stopping heartbeats. Note
// that we are holding the global resource here, which will
// synchronize this code with shutdown.
//
CnReleaseLock(&(node->Lock), node->Irql); nodeLockAcquired = FALSE;
CnpStopHeartBeats(); }
break;
case MMActionConfigured: node->MMState = State; break; }
if ( NT_ERROR( status )) {
CN_DBGCHECK; }
if (nodeLockAcquired) { CnReleaseLock(&(node->Lock), node->Irql); } }
CnVerifyCpuLockMask( 0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return(status);
} // CxSetNodeMembershipState
|