windows-nt/Source/XPSP1/NT/base/cluster/clusnet/xport/cnpnode.c

1530 lines
34 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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