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