windows-nt/Source/XPSP1/NT/base/cluster/clusnet/xport/cxpnp.c
2020-09-26 16:20:57 +08:00

1737 lines
46 KiB
C

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
cxpnp.c
Abstract:
PnP handling code for the Cluster Network Driver.
Author:
Mike Massa (mikemas) March 21, 1998
Revision History:
Who When What
-------- -------- ----------------------------------------------
mikemas 03-22-98 created
Notes:
--*/
#include "precomp.h"
#include <ntddk.h>
#include <wmistr.h>
#include <ndisguid.h>
#include <ntddndis.h>
#include <ntpnpapi.h>
#include <zwapi.h>
#pragma hdrstop
#include "cxpnp.tmh"
//
// Local data structures
//
typedef struct _CNP_WMI_RECONNECT_WORKER_CONTEXT {
PIO_WORKITEM WorkItem;
CL_NETWORK_ID NetworkId;
} CNP_WMI_RECONNECT_WORKER_CONTEXT, *PCNP_WMI_RECONNECT_WORKER_CONTEXT;
//
// WMI Data
//
PERESOURCE CnpWmiNdisMediaStatusResource = NULL;
PVOID CnpWmiNdisMediaStatusConnectObject = NULL;
PVOID CnpWmiNdisMediaStatusDisconnectObject = NULL;
HANDLE CnpIpMediaSenseFileHandle = NULL;
PIRP CnpIpDisableMediaSenseIrp = NULL;
PKEVENT CnpIpDisableMediaSenseEvent = NULL;
//
// Local prototypes
//
NTSTATUS
CnpWmiPnpDisableMediaSenseCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
VOID
CnpWmiNdisMediaStatusConnectCallback(
IN PVOID Wnode,
IN PVOID Context
);
VOID
CnpWmiNdisMediaStatusDisconnectCallback(
IN PVOID Wnode,
IN PVOID Context
);
VOID
CnpReconnectLocalInterfaceWrapper(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
);
VOID
CnpDisconnectLocalInterface(
PCNP_INTERFACE Interface,
PCNP_NETWORK Network
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, CxWmiPnpLoad)
#pragma alloc_text(PAGE, CxWmiPnpUnload)
#pragma alloc_text(PAGE, CxWmiPnpInitialize)
#pragma alloc_text(PAGE, CxWmiPnpShutdown)
#endif // ALLOC_PRAGMA
//
// Exported Routines
//
VOID
CxTdiAddAddressHandler(
IN PTA_ADDRESS TaAddress,
IN PUNICODE_STRING DeviceName,
IN PTDI_PNP_CONTEXT Context
)
{
if (TaAddress->AddressType == TDI_ADDRESS_TYPE_IP) {
NTSTATUS status;
PTDI_ADDRESS_IP tdiAddressIp = (PTDI_ADDRESS_IP)
&(TaAddress->Address[0]);
IF_CNDBG(CN_DEBUG_CONFIG) {
CNPRINT((
"[CX] Processing PnP add event for IP address %lx\n",
tdiAddressIp->in_addr
));
}
//
// Ensure that this is a valid address, and that it is not one
// that we brought online for a cluster ip address resource.
//
if (tdiAddressIp->in_addr != 0) {
if (!IpaIsAddressRegistered(tdiAddressIp->in_addr)) {
IF_CNDBG(CN_DEBUG_CONFIG) {
CNPRINT((
"[CX] Issuing address add event to cluster svc for IP address %lx\n",
tdiAddressIp->in_addr
));
}
CnIssueEvent(
ClusnetEventAddAddress,
0,
(CL_NETWORK_ID) tdiAddressIp->in_addr
);
}
else {
IF_CNDBG(CN_DEBUG_CONFIG) {
CNPRINT((
"[CX] PnP add event is for an IP address resource, skip.\n"
));
}
}
}
}
return;
} // CxTdiAddAddressHandler
VOID
CxTdiDelAddressHandler(
IN PTA_ADDRESS TaAddress,
IN PUNICODE_STRING DeviceName,
IN PTDI_PNP_CONTEXT Context
)
{
if (TaAddress->AddressType == TDI_ADDRESS_TYPE_IP) {
NTSTATUS status;
PCNP_INTERFACE interface;
PCNP_NETWORK network;
PLIST_ENTRY entry;
PTA_IP_ADDRESS taIpAddress;
CN_IRQL nodeTableIrql;
CL_NODE_ID i;
PTDI_ADDRESS_IP tdiAddressIp = (PTDI_ADDRESS_IP)
&(TaAddress->Address[0]);
IF_CNDBG(CN_DEBUG_CONFIG) {
CNPRINT((
"[CX] Processing PnP delete event for IP address %lx.\n",
tdiAddressIp->in_addr
));
}
if (tdiAddressIp->in_addr != 0) {
//
// Figure out if this is the address for one of this node's
// registered interfaces.
//
CnAcquireLock(&CnpNodeTableLock, &nodeTableIrql);
if (CnpLocalNode != NULL) {
CnAcquireLockAtDpc(&(CnpLocalNode->Lock));
CnReleaseLockFromDpc(&CnpNodeTableLock);
CnpLocalNode->Irql = nodeTableIrql;
network = NULL;
for (entry = CnpLocalNode->InterfaceList.Flink;
entry != &(CnpLocalNode->InterfaceList);
entry = entry->Flink
)
{
interface = CONTAINING_RECORD(
entry,
CNP_INTERFACE,
NodeLinkage
);
taIpAddress = (PTA_IP_ADDRESS) &(interface->TdiAddress);
if (taIpAddress->Address[0].Address[0].in_addr ==
tdiAddressIp->in_addr
)
{
//
// Found the local interface corresponding to this
// address. Be proactive - destroy the corresponding
// network now.
//
network = interface->Network;
CnAcquireLockAtDpc(&CnpNetworkListLock);
CnAcquireLockAtDpc(&(network->Lock));
CnReleaseLockFromDpc(&(CnpLocalNode->Lock));
network->Irql = DISPATCH_LEVEL;
IF_CNDBG(CN_DEBUG_CONFIG) {
CNPRINT((
"[CX] Deleting network ID %u after PnP "
"delete event for IP address %lx.\n",
network->Id, tdiAddressIp->in_addr
));
}
CnpDeleteNetwork(network, nodeTableIrql);
//
// Both locks were released.
//
break;
}
}
if (network == NULL) {
CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
}
//
// Post an event to the service.
//
CnIssueEvent(
ClusnetEventDelAddress,
0,
(CL_NETWORK_ID) tdiAddressIp->in_addr
);
}
else {
CnReleaseLock(&CnpNodeTableLock, nodeTableIrql);
}
}
}
return;
} // CxTdiDelAddressHandler
NTSTATUS
CxWmiPnpLoad(
VOID
)
/*++
Notes:
Called when clusnet driver is loaded.
--*/
{
PDEVICE_OBJECT ipDeviceObject = NULL;
PFILE_OBJECT ipFileObject = NULL;
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
//
// Allocate a synchronization resource.
//
CnpWmiNdisMediaStatusResource = CnAllocatePool(sizeof(ERESOURCE));
if (CnpWmiNdisMediaStatusResource == NULL) {
return(STATUS_INSUFFICIENT_RESOURCES);
}
status = ExInitializeResourceLite(CnpWmiNdisMediaStatusResource);
if (!NT_SUCCESS(status)) {
return(status);
}
//
// Get a handle to the IP device object to disable media sense
//
status = CnpOpenDevice(
DD_IP_DEVICE_NAME,
&CnpIpMediaSenseFileHandle
);
if (!NT_SUCCESS(status)) {
IF_CNDBG(CN_DEBUG_INIT) {
CNPRINT(("[CX] Failed to open IP device to "
"disable media sense, status %lx\n",
status));
}
return(status);
}
//
// Disable IP media sense. This works by submitting an
// IOCTL_IP_DISABLE_MEDIA_SENSE_REQUEST IRP. The IRP
// will pend until we cancel it (re-enabling media sense).
//
CnpIpDisableMediaSenseEvent = CnAllocatePool(sizeof(KEVENT));
if (CnpIpDisableMediaSenseEvent != NULL) {
KeInitializeEvent(
CnpIpDisableMediaSenseEvent,
SynchronizationEvent,
FALSE
);
//
// Reference the IP file object and get the device object
//
status = ObReferenceObjectByHandle(
CnpIpMediaSenseFileHandle,
0,
NULL,
KernelMode,
&ipFileObject,
NULL
);
if (NT_SUCCESS(status)) {
ipDeviceObject = IoGetRelatedDeviceObject(ipFileObject);
//
// File object reference is no longer needed
// because the handle is still open.
//
ObDereferenceObject(ipFileObject);
CnpIpDisableMediaSenseIrp = IoAllocateIrp(
ipDeviceObject->StackSize,
FALSE
);
if (CnpIpDisableMediaSenseIrp != NULL) {
irpSp = IoGetNextIrpStackLocation(CnpIpDisableMediaSenseIrp);
irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
irpSp->Parameters.DeviceIoControl.IoControlCode
= IOCTL_IP_DISABLE_MEDIA_SENSE_REQUEST;
irpSp->DeviceObject = ipDeviceObject;
irpSp->FileObject = ipFileObject;
IoSetCompletionRoutine(
CnpIpDisableMediaSenseIrp,
CnpWmiPnpDisableMediaSenseCompletion,
NULL,
TRUE,
TRUE,
TRUE
);
status = IoCallDriver(
ipDeviceObject,
CnpIpDisableMediaSenseIrp
);
if (status != STATUS_PENDING) {
IF_CNDBG(CN_DEBUG_INIT) {
CNPRINT(("[CX] Failed to disable IP media "
"sense, status %lx\n", status));
}
KeWaitForSingleObject(
CnpIpDisableMediaSenseEvent,
Executive,
KernelMode,
FALSE,
NULL
);
CnFreePool(CnpIpDisableMediaSenseEvent);
CnpIpDisableMediaSenseEvent = NULL;
IoFreeIrp(CnpIpDisableMediaSenseIrp);
CnpIpDisableMediaSenseIrp = NULL;
//
// Cannot risk simply returning status
// because we need the driver load to
// fail.
//
if (NT_SUCCESS(status)) {
status = STATUS_UNSUCCESSFUL;
}
} else {
//
// Need to return STATUS_SUCCESS so that
// the driver load will not fail.
//
status = STATUS_SUCCESS;
IF_CNDBG(CN_DEBUG_INIT) {
CNPRINT(("[CX] IP media sense disabled.\n"));
}
CnTrace(
CXPNP, CxWmiPnpIPMediaSenseDisabled,
"[CXPNP] IP media sense disabled.\n"
);
}
} else {
IF_CNDBG(CN_DEBUG_INIT) {
CNPRINT(("[CX] Failed to allocate IP media sense "
"disable IRP.\n"));
}
CnFreePool(CnpIpDisableMediaSenseEvent);
CnpIpDisableMediaSenseEvent = NULL;
status = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
IF_CNDBG(CN_DEBUG_INIT) {
CNPRINT(("[CX] Failed to reference IP device "
"file handle, status %lx\n", status));
}
CnFreePool(CnpIpDisableMediaSenseEvent);
CnpIpDisableMediaSenseEvent = NULL;
}
} else {
IF_CNDBG(CN_DEBUG_INIT) {
CNPRINT(("[CX] Failed to allocate IP media sense "
"disable event.\n"));
}
status = STATUS_INSUFFICIENT_RESOURCES;
}
return(status);
} // CxWmiPnpLoad
VOID
CxWmiPnpUnload(
VOID
)
/*++
Notes:
Called when clusnet driver is unloaded.
--*/
{
CnAssert(CnpWmiNdisMediaStatusConnectObject == NULL);
CnAssert(CnpWmiNdisMediaStatusDisconnectObject == NULL);
//
// Re-enable IP media sense. This works by cancelling our
// IOCTL_IP_DISABLE_MEDIA_SENSE_REQUEST IRP, which should
// still be pending.
//
if (CnpIpDisableMediaSenseIrp != NULL) {
if (!IoCancelIrp(CnpIpDisableMediaSenseIrp)) {
//
// Our disable media sense IRP could not be cancelled. This
// probably means that it was completed because somebody
// else submitted a media sense enable request.
//
CnTrace(
CXPNP, CnpWmiPnpDisableMediaSenseCompletionUnexpected,
"[CXPNP] IP media sense re-enabled unexpectedly.\n"
);
} else {
//
// Irp was cancelled, and media sense is disabled as
// expected.
//
CnTrace(
CXPNP, CnpWmiPnpDisableMediaSenseCompletion,
"[CXPNP] IP media sense re-enabled.\n"
);
}
//
// Regardless of who re-enabled media sense, we need to free
// the media sense IRP and event. First we wait on the event,
// which is signalled in our completion routine.
//
KeWaitForSingleObject(
CnpIpDisableMediaSenseEvent,
Executive,
KernelMode,
FALSE,
NULL
);
CnFreePool(CnpIpDisableMediaSenseEvent);
CnpIpDisableMediaSenseEvent = NULL;
IoFreeIrp(CnpIpDisableMediaSenseIrp);
CnpIpDisableMediaSenseIrp = NULL;
}
CnAssert(CnpIpDisableMediaSenseIrp == NULL);
if (CnpIpMediaSenseFileHandle != NULL) {
ZwClose(CnpIpMediaSenseFileHandle);
CnpIpMediaSenseFileHandle = NULL;
}
if (CnpWmiNdisMediaStatusResource != NULL) {
ExDeleteResourceLite(CnpWmiNdisMediaStatusResource);
CnFreePool(CnpWmiNdisMediaStatusResource);
CnpWmiNdisMediaStatusResource = NULL;
}
} // CxWmiPnpUnload
NTSTATUS
CxWmiPnpInitialize(
VOID
)
/*++
Notes:
Called in response to initialize ioctl.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
BOOLEAN acquired = FALSE;
GUID wmiGuid;
PAGED_CODE();
acquired = CnAcquireResourceExclusive(
CnpWmiNdisMediaStatusResource,
TRUE
);
CnAssert(acquired == TRUE);
//
// Register WMI callbacks for NDIS media status events
//
if (CnpWmiNdisMediaStatusConnectObject == NULL) {
wmiGuid = GUID_NDIS_STATUS_MEDIA_CONNECT;
status = IoWMIOpenBlock(
&wmiGuid,
WMIGUID_NOTIFICATION,
&CnpWmiNdisMediaStatusConnectObject
);
if (!NT_SUCCESS(status)) {
CNPRINT((
"[CX] Unable to open WMI NDIS status media connect "
"datablock, status %lx\n",
status
));
CnpWmiNdisMediaStatusConnectObject = NULL;
goto error_exit;
}
status = IoWMISetNotificationCallback(
CnpWmiNdisMediaStatusConnectObject,
CnpWmiNdisMediaStatusConnectCallback,
NULL
);
if (!NT_SUCCESS(status)) {
CNPRINT((
"[CX] Unable to register WMI NDIS status media connect "
"callback, status %lx\n",
status
));
goto error_exit;
}
}
if (CnpWmiNdisMediaStatusDisconnectObject == NULL) {
wmiGuid = GUID_NDIS_STATUS_MEDIA_DISCONNECT;
status = IoWMIOpenBlock(
&wmiGuid,
WMIGUID_NOTIFICATION,
&CnpWmiNdisMediaStatusDisconnectObject
);
if (!NT_SUCCESS(status)) {
CNPRINT((
"[CX] Unable to open WMI NDIS status media disconnect "
"datablock, status %lx\n",
status
));
CnpWmiNdisMediaStatusDisconnectObject = NULL;
goto error_exit;
}
status = IoWMISetNotificationCallback(
CnpWmiNdisMediaStatusDisconnectObject,
CnpWmiNdisMediaStatusDisconnectCallback,
NULL
);
if (!NT_SUCCESS(status)) {
CNPRINT((
"[CX] Unable to register WMI NDIS status media disconnect "
"callback, status %lx\n",
status
));
goto error_exit;
}
}
goto release_exit;
error_exit:
if (CnpWmiNdisMediaStatusConnectObject != NULL) {
ObDereferenceObject(CnpWmiNdisMediaStatusConnectObject);
CnpWmiNdisMediaStatusConnectObject = NULL;
}
if (CnpWmiNdisMediaStatusDisconnectObject != NULL) {
ObDereferenceObject(CnpWmiNdisMediaStatusDisconnectObject);
CnpWmiNdisMediaStatusDisconnectObject = NULL;
}
release_exit:
//
// Release resource
//
if (acquired) {
CnReleaseResourceForThread(
CnpWmiNdisMediaStatusResource,
(ERESOURCE_THREAD) PsGetCurrentThread()
);
}
return(status);
} // CxWmiPnpInitialize
VOID
CxWmiPnpShutdown(
VOID
)
/*++
Notes:
Called in response to clusnet shutdown.
--*/
{
BOOLEAN acquired = FALSE;
PAGED_CODE();
acquired = CnAcquireResourceExclusive(
CnpWmiNdisMediaStatusResource,
TRUE
);
CnAssert(acquired == TRUE);
if (CnpWmiNdisMediaStatusConnectObject != NULL) {
ObDereferenceObject(CnpWmiNdisMediaStatusConnectObject);
CnpWmiNdisMediaStatusConnectObject = NULL;
}
if (CnpWmiNdisMediaStatusDisconnectObject != NULL) {
ObDereferenceObject(CnpWmiNdisMediaStatusDisconnectObject);
CnpWmiNdisMediaStatusDisconnectObject = NULL;
}
//
// Release resource
//
if (acquired) {
CnReleaseResourceForThread(
CnpWmiNdisMediaStatusResource,
(ERESOURCE_THREAD) PsGetCurrentThread()
);
}
return;
} // CxWmiPnpShutdown
VOID
CxReconnectLocalInterface(
IN CL_NETWORK_ID NetworkId
)
/**
Routine Description:
Queues a worker thread to set the local interface
associated with NetworkId to connected. Called when
a heartbeat is received over a network that is marked
locally disconnected.
Arguments:
NetworkId - network ID of network to be reconnected
Return value:
None
Notes:
Can fail without reporting an error if either
allocation fails.
--*/
{
PCNP_WMI_RECONNECT_WORKER_CONTEXT context;
context = CnAllocatePool(sizeof(CNP_WMI_RECONNECT_WORKER_CONTEXT));
if (context != NULL) {
context->WorkItem = IoAllocateWorkItem(CnDeviceObject);
if (context->WorkItem != NULL) {
context->NetworkId = NetworkId;
CnTrace(
CXPNP, CxReconnectLocalInterface,
"[CXPNP] Queueing worker thread to reconnect local "
"interface for network ID %u.\n",
NetworkId // LOGULONG
);
IoQueueWorkItem(
context->WorkItem,
CnpReconnectLocalInterfaceWrapper,
DelayedWorkQueue,
context
);
} else {
CnFreePool(context);
}
}
return;
}
VOID
CxQueryMediaStatus(
IN HANDLE AdapterDeviceHandle,
IN CL_NETWORK_ID NetworkId,
OUT PULONG MediaStatus
)
/**
Routine Description:
Queries the status of the adapter device. Used to
determine whether a local interface is initially
connected or disconnected.
Arguments:
AdapterHandle - adapter device object handle
NetworkId - network ID of adapter to be queried
Return value:
None
Notes:
NDIS query formation modeled after ndis\lib\ndisapi.c
--*/
{
BOOLEAN acquired = FALSE;
NTSTATUS status;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
//
// Set default
//
*MediaStatus = NdisMediaStateDisconnected;
//
// Acquire resource
//
acquired = CnAcquireResourceExclusive(
CnpWmiNdisMediaStatusResource,
TRUE
);
CnAssert(acquired == TRUE);
if (AdapterDeviceHandle != NULL) {
//
// Construct NDIS statistics query
//
NDIS_OID statsOidList[] =
{
OID_GEN_MEDIA_CONNECT_STATUS // | NDIS_OID_PRIVATE
};
UCHAR statsBuf[
FIELD_OFFSET(NDIS_STATISTICS_VALUE, Data)
+ sizeof(LARGE_INTEGER)
];
PNDIS_STATISTICS_VALUE pStatsBuf;
LARGE_INTEGER value;
IF_CNDBG( CN_DEBUG_CONFIG ) {
CNPRINT((
"[CXPNP] Querying NDIS for local adapter "
"on network %u (handle %p).\n",
NetworkId,
AdapterDeviceHandle
));
}
pStatsBuf = (PNDIS_STATISTICS_VALUE) &statsBuf[0];
status = CnpZwDeviceControl(
AdapterDeviceHandle,
IOCTL_NDIS_QUERY_SELECTED_STATS,
statsOidList,
sizeof(statsOidList),
pStatsBuf,
sizeof(statsBuf)
);
IF_CNDBG( CN_DEBUG_CONFIG ) {
CNPRINT((
"[CXPNP] NDIS query for local adapter "
"on network %u returned status %lx.\n",
NetworkId,
status
));
}
if (pStatsBuf->DataLength == sizeof(LARGE_INTEGER)) {
value.QuadPart = *(PULONGLONG)(&pStatsBuf->Data[0]);
} else {
value.LowPart = *(PULONG)(&pStatsBuf->Data[0]);
}
*MediaStatus = value.LowPart; // NdisMediaState{Disc|C}onnected
IF_CNDBG( CN_DEBUG_CONFIG ) {
CNPRINT((
"[CXPNP] NDIS query for local adapter "
"on network %u returned media status %lx.\n",
NetworkId,
*MediaStatus
));
}
CnTrace(
CXPNP, CxQueryMediaStatus,
"[CXPNP] Found media status %u for local network ID %u.\n",
*MediaStatus, // LOGULONG
NetworkId // LOGULONG
);
}
//
// If the media status is disconnected, we must disconnect the
// local interface and network.
//
if (*MediaStatus == NdisMediaStateDisconnected) {
PCNP_NETWORK network = NULL;
PCNP_INTERFACE interface = NULL;
CN_IRQL nodeTableIrql;
PLIST_ENTRY entry;
CnAcquireLock(&CnpNodeTableLock, &nodeTableIrql);
if (CnpLocalNode != NULL) {
CnAcquireLockAtDpc(&(CnpLocalNode->Lock));
CnReleaseLockFromDpc(&CnpNodeTableLock);
CnpLocalNode->Irql = nodeTableIrql;
network = CnpFindNetwork(NetworkId);
if (network != NULL) {
//
// Only go through the disconnect if the network
// is currently marked as locally connected.
// It is possible that we have already received
// and processed a WMI disconnect event.
//
if (!CnpIsNetworkLocalDisconn(network)) {
for (entry = CnpLocalNode->InterfaceList.Flink;
entry != &(CnpLocalNode->InterfaceList);
entry = entry->Flink
)
{
interface = CONTAINING_RECORD(
entry,
CNP_INTERFACE,
NodeLinkage
);
if (interface->Network == network) {
CnpDisconnectLocalInterface(
interface,
network
);
//
// Both node and network locks
// were released.
//
break;
} else {
interface = NULL;
}
}
} else {
CnTrace(
CXPNP, CxQueryMediaStatusDisconnectRedundant,
"[CXPNP] Network ID %u is already disconnected; "
"aborting disconnect.\n",
network->Id // LOGULONG
);
}
if (interface == NULL) {
CnReleaseLock(&(network->Lock), network->Irql);
}
}
if (interface == NULL) {
CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
}
} else {
CnReleaseLock(&CnpNodeTableLock, nodeTableIrql);
}
}
//
// Release resource
//
if (acquired) {
CnReleaseResourceForThread(
CnpWmiNdisMediaStatusResource,
(ERESOURCE_THREAD) PsGetCurrentThread()
);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return;
} // CxQueryMediaStatus
//
// Local Routines
//
NTSTATUS
CnpWmiPnpDisableMediaSenseCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
//
// Irp is always freed by our disable routine to prevent a race
// condition where we don't know if we have called IoCancelIrp
// yet or not.
//
KeSetEvent(CnpIpDisableMediaSenseEvent, IO_NO_INCREMENT, FALSE);
return(STATUS_MORE_PROCESSING_REQUIRED);
} // CnpWmiPnpDisableMediaSenseCompletion
VOID
CnpWmiPnpUpdateCurrentInterface(
IN PCNP_INTERFACE UpdateInterface
)
/*++
Routine Description:
Updates the CurrentInterface for interfaces after the local
interface is connected or disconnected. Called in response
to WMI NDIS media status events.
Arguments:
Interface - A pointer to the interface on which to operate.
Return Value:
None.
Notes:
Called with associated node and network locks held.
Returns with network lock released.
Conforms to calling convention for PCNP_INTERFACE_UPDATE_ROUTINE.
--*/
{
PCNP_NODE node = UpdateInterface->Node;
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
//
// We don't really need the network lock. It's just part of
// the calling convention.
//
CnReleaseLockFromDpc(&(UpdateInterface->Network->Lock));
CnpUpdateNodeCurrentInterface(node);
if ( (node->CurrentInterface == NULL)
||
( node->CurrentInterface->State <
ClusnetInterfaceStateOnlinePending
)
)
{
//
// This node is now unreachable.
//
CnpDeclareNodeUnreachable(node);
}
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NODE_OBJECT_LOCK_MAX // Maximum
);
return;
} // CnpWmiPnpUpdateCurrentInterface
VOID
CnpReconnectLocalInterface(
PCNP_INTERFACE Interface,
PCNP_NETWORK Network
)
/*++
Routine Description:
Changes a local interface from being disconnected to
connected. Called in response to a WMI NDIS media status
connect event or a heartbeat received on a disconnected
interface.
Arguments:
Interface - local interface that is reconnected
Network - network associated with Interface
Return value:
None
Notes:
Called with CnpWmiNdisMediaStatusResource, local node lock,
and Network lock held.
Returns with CnpWmiNdisMediaStatusResource held but neither
lock held.
--*/
{
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
CnTrace(
CXPNP, CnpReconnectLocalInterface,
"[CXPNP] Reconnecting local interface for "
"network ID %u.\n",
Network->Id // LOGULONG
);
//
// Clear the local disconnect flag in the network
// object
//
Network->Flags &= ~CNP_NET_FLAG_LOCALDISCONN;
//
// Reference the network so it can't go away while we
// reprioritize the associated interfaces.
//
CnpReferenceNetwork(Network);
//
// Bring the interface online. This call releases the
// network lock.
//
CnpOnlineInterface(Interface);
//
// Release the node lock before walking the interfaces
// on the network.
//
CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
//
// Update the CurrentInterface for the other
// nodes in the cluster to reflect the connected
// status of the local interface.
//
CnpWalkInterfacesOnNetwork(
Network,
CnpWmiPnpUpdateCurrentInterface
);
//
// Issue InterfaceUp event to the cluster
// service.
//
CnTrace(
CXPNP, CxWmiNdisReconnectIssueEvent,
"[CXPNP] Issuing InterfaceUp event "
"for node %u on net %u, previous I/F state = %!ifstate!.",
Interface->Node->Id, // LOGULONG
Interface->Network->Id, // LOGULONG
Interface->State // LOGIfState
);
CnIssueEvent(
ClusnetEventNetInterfaceUp,
Interface->Node->Id,
Interface->Network->Id
);
//
// Release the reference on the network object.
//
CnAcquireLock(&(Network->Lock), &(Network->Irql));
CnpDereferenceNetwork(Network);
return;
} // CnpReconnectLocalInterface
VOID
CnpReconnectLocalInterfaceWrapper(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
{
PCNP_WMI_RECONNECT_WORKER_CONTEXT context = Context;
PCNP_NETWORK network = NULL;
PCNP_INTERFACE interface = NULL;
CN_IRQL nodeTableIrql;
BOOLEAN acquired = FALSE;
PLIST_ENTRY entry;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
acquired = CnAcquireResourceExclusive(
CnpWmiNdisMediaStatusResource,
TRUE
);
CnAssert(acquired == TRUE);
CnAcquireLock(&CnpNodeTableLock, &nodeTableIrql);
if (CnpLocalNode != NULL) {
CnAcquireLockAtDpc(&(CnpLocalNode->Lock));
CnReleaseLockFromDpc(&CnpNodeTableLock);
CnpLocalNode->Irql = nodeTableIrql;
network = CnpFindNetwork(context->NetworkId);
if (network != NULL) {
//
// Only go through the reconnect if the network
// is currently marked as locally disconnected.
// It is possible that we have already received
// and processed a WMI connect event.
//
if (CnpIsNetworkLocalDisconn(network)) {
for (entry = CnpLocalNode->InterfaceList.Flink;
entry != &(CnpLocalNode->InterfaceList);
entry = entry->Flink
)
{
interface = CONTAINING_RECORD(
entry,
CNP_INTERFACE,
NodeLinkage
);
if (interface->Network == network) {
CnpReconnectLocalInterface(
interface,
network
);
//
// Both node and network locks
// were released.
//
break;
} else {
interface = NULL;
}
}
} else {
CnTrace(
CXPNP, CnpReconnectLocalInterfaceWrapperRedundant,
"[CXPNP] Network ID %u is already connected; "
"aborting reconnect in wrapper.\n",
network->Id // LOGULONG
);
}
if (interface == NULL) {
CnReleaseLock(&(network->Lock), network->Irql);
}
}
if (interface == NULL) {
CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
}
} else {
CnReleaseLock(&CnpNodeTableLock, nodeTableIrql);
}
//
// Release resource
//
if (acquired) {
CnReleaseResourceForThread(
CnpWmiNdisMediaStatusResource,
(ERESOURCE_THREAD) PsGetCurrentThread()
);
}
//
// Free the workitem and context
//
IoFreeWorkItem(context->WorkItem);
CnFreePool(context);
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return;
} // CnpReconnectLocalInterfaceWrapper
VOID
CnpDisconnectLocalInterface(
PCNP_INTERFACE Interface,
PCNP_NETWORK Network
)
/*++
Routine Description:
Changes a local interface from being connected to
disconnected. Called in response to a WMI NDIS media status
disconnect event or an NDIS query that returns media
disconnected.
Arguments:
Interface - local interface that is reconnected
Network - network associated with Interface
Return value:
None
Notes:
Called with CnpWmiNdisMediaStatusResource, local node lock,
and Network lock held.
Returns with CnpWmiNdisMediaStatusResource held but neither
lock held.
--*/
{
CnVerifyCpuLockMask(
(CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK), // Required
0, // Forbidden
CNP_NETWORK_OBJECT_LOCK_MAX // Maximum
);
CnTrace(
CXPNP, CnpDisconnectLocalInterface,
"[CXPNP] Interface for network ID %u "
"disconnected.\n",
Network->Id // LOGULONG
);
//
// Set the local disconnect flag in the network
// object
//
Network->Flags |= CNP_NET_FLAG_LOCALDISCONN;
//
// Reference the network so it can't go away while we
// reprioritize the associated interfaces.
//
CnpReferenceNetwork(Network);
//
// Fail the interface. This call releases the
// network lock.
//
CnpFailInterface(Interface);
//
// Release the node lock before walking the interfaces
// on the network.
//
CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
//
// Update the CurrentInterface for the other
// nodes in the cluster to reflect the disconnected
// status of the local interface.
//
CnpWalkInterfacesOnNetwork(
Network,
CnpWmiPnpUpdateCurrentInterface
);
//
// Issue InterfaceFailed event to the cluster
// service.
//
CnTrace(
CXPNP, CnpLocalDisconnectIssueEvent,
"[CXPNP] Issuing InterfaceFailed event "
"for node %u on net %u, previous I/F state = %!ifstate!.",
Interface->Node->Id, // LOGULONG
Interface->Network->Id, // LOGULONG
Interface->State // LOGIfState
);
CnIssueEvent(
ClusnetEventNetInterfaceFailed,
Interface->Node->Id,
Interface->Network->Id
);
//
// Release the reference on the network object.
//
CnAcquireLock(&(Network->Lock), &(Network->Irql));
CnpDereferenceNetwork(Network);
return;
} // CnpDisconnectLocalInterface
VOID
CnpWmiNdisMediaStatusConnectCallback(
IN PVOID Wnode,
IN PVOID Context
)
{
PWNODE_SINGLE_INSTANCE wnode = (PWNODE_SINGLE_INSTANCE) Wnode;
PCNP_INTERFACE interface;
PCNP_NETWORK network;
PLIST_ENTRY entry;
CN_IRQL nodeTableIrql;
BOOLEAN acquired = FALSE;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
IF_CNDBG(CN_DEBUG_CONFIG) {
CNPRINT((
"[CX] Received WMI NDIS status media connect event.\n"
));
}
//
// Serialize events as much as possible, since clusnet spinlocks
// may be acquired and released repeatedly.
//
// Note that there may not be any guarantees that WMI event
// ordering is guaranteed. The fallback mechanism for clusnet
// is heartbeats -- if a heartbeat is received on an interface,
// we know the interface is connected.
//
acquired = CnAcquireResourceExclusive(
CnpWmiNdisMediaStatusResource,
TRUE
);
CnAssert(acquired == TRUE);
//
// Figure out if this callback is for one of this node's
// registered interfaces by comparing the WMI provider ID
// in the WNODE header to the WMI provider IDs of this
// node's adapters.
//
CnAcquireLock(&CnpNodeTableLock, &nodeTableIrql);
if (CnpLocalNode != NULL) {
CnAcquireLockAtDpc(&(CnpLocalNode->Lock));
CnReleaseLockFromDpc(&CnpNodeTableLock);
CnpLocalNode->Irql = nodeTableIrql;
network = NULL;
for (entry = CnpLocalNode->InterfaceList.Flink;
entry != &(CnpLocalNode->InterfaceList);
entry = entry->Flink
)
{
interface = CONTAINING_RECORD(
entry,
CNP_INTERFACE,
NodeLinkage
);
if (wnode->WnodeHeader.ProviderId
== interface->AdapterWMIProviderId) {
//
// Found the local interface corresponding to this
// address.
//
network = interface->Network;
//
// Start by checking if we believe the network is
// currently disconnected.
//
CnAcquireLockAtDpc(&(network->Lock));
network->Irql = DISPATCH_LEVEL;
if (CnpIsNetworkLocalDisconn(network)) {
CnTrace(
CXPNP, CxWmiNdisConnectNet,
"[CXPNP] Interface for network ID %u "
"connected.\n",
network->Id // LOGULONG
);
CnpReconnectLocalInterface(interface, network);
//
// Node and network locks were released
//
} else {
CnTrace(
CXPNP, CxWmiNdisConnectNetRedundant,
"[CXPNP] Ignoring redundant WMI NDIS connect "
"event for interface for network ID %u.\n",
network->Id // LOGULONG
);
CnReleaseLockFromDpc(&(network->Lock));
CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
}
break;
}
}
if (network == NULL) {
CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
}
}
else {
CnReleaseLock(&CnpNodeTableLock, nodeTableIrql);
}
IF_CNDBG(CN_DEBUG_CONFIG) {
if (network != NULL) {
CNPRINT((
"[CX] Interface for network ID %u connected.\n",
network->Id
));
} else {
CNPRINT((
"[CX] Unknown interface connected, provider id %lx\n",
wnode->WnodeHeader.ProviderId
));
}
}
//
// Release resource
//
if (acquired) {
CnReleaseResourceForThread(
CnpWmiNdisMediaStatusResource,
(ERESOURCE_THREAD) PsGetCurrentThread()
);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return;
} // CnpWmiNdisMediaStatusConnectCallback
VOID
CnpWmiNdisMediaStatusDisconnectCallback(
IN PVOID Wnode,
IN PVOID Context
)
{
PWNODE_SINGLE_INSTANCE wnode = (PWNODE_SINGLE_INSTANCE) Wnode;
PCNP_INTERFACE interface;
PCNP_NETWORK network;
PLIST_ENTRY entry;
CN_IRQL nodeTableIrql;
BOOLEAN acquired = FALSE;
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
IF_CNDBG(CN_DEBUG_CONFIG) {
CNPRINT((
"[CX] Received WMI NDIS status media disconnect event.\n"
));
}
CnTrace(CXPNP, CxWmiNdisDisconnect,
"[CXPNP] Received WMI NDIS status media disconnect event.\n"
);
//
// Serialize events as much as possible, since clusnet spinlocks
// may be acquired and released repeatedly.
//
// Note that there may not be any guarantees that WMI event
// ordering is guaranteed. The fallback mechanism for clusnet
// is heartbeats -- if a heartbeat is received on an interface,
// we know the interface is connected.
//
acquired = CnAcquireResourceExclusive(
CnpWmiNdisMediaStatusResource,
TRUE
);
CnAssert(acquired == TRUE);
//
// Figure out if this callback is for one of this node's
// registered interfaces by comparing the WMI provider ID
// in the WNODE header to the WMI provider IDs of this
// node's adapters.
//
CnAcquireLock(&CnpNodeTableLock, &nodeTableIrql);
if (CnpLocalNode != NULL) {
CnAcquireLockAtDpc(&(CnpLocalNode->Lock));
CnReleaseLockFromDpc(&CnpNodeTableLock);
CnpLocalNode->Irql = nodeTableIrql;
network = NULL;
for (entry = CnpLocalNode->InterfaceList.Flink;
entry != &(CnpLocalNode->InterfaceList);
entry = entry->Flink
)
{
interface = CONTAINING_RECORD(
entry,
CNP_INTERFACE,
NodeLinkage
);
if (wnode->WnodeHeader.ProviderId
== interface->AdapterWMIProviderId) {
//
// Found the local interface object corresponding
// to this adapter.
//
network = interface->Network;
CnAcquireLockAtDpc(&(network->Lock));
network->Irql = DISPATCH_LEVEL;
CnpDisconnectLocalInterface(interface, network);
break;
}
}
if (network == NULL) {
CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
}
}
else {
CnReleaseLock(&CnpNodeTableLock, nodeTableIrql);
}
IF_CNDBG(CN_DEBUG_CONFIG) {
if (network != NULL) {
CNPRINT((
"[CX] Interface for network ID %u disconnected.\n",
network->Id
));
} else {
CNPRINT((
"[CX] Unknown interface disconnected, provider id %lx\n",
wnode->WnodeHeader.ProviderId
));
}
}
//
// Release resource
//
if (acquired) {
CnReleaseResourceForThread(
CnpWmiNdisMediaStatusResource,
(ERESOURCE_THREAD) PsGetCurrentThread()
);
}
CnVerifyCpuLockMask(
0, // Required
0xFFFFFFFF, // Forbidden
0 // Maximum
);
return;
} // CnpWmiNdisMediaStatusDisconnectCallback