1737 lines
46 KiB
C
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
|
|
|