/*++ 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