/*++ Copyright (c) 1996 Microsoft Corporation Module Name: rmnotify.c Abstract: Interfaces with the resource monitor to detect notification of resource state changes. Author: John Vert (jvert) 12-Jan-1996 Revision History: --*/ #include "fmp.h" #define LOG_MODULE RMNOTIFY // // Local Data // CL_QUEUE NotifyQueue; HANDLE RmNotifyThread; // // Local Functions // DWORD FmpRmWorkerThread( IN LPVOID lpThreadParameter ); VOID FmpRmWorkItemHandler( IN PCLRTL_WORK_ITEM WorkItem, IN DWORD Ignored1, IN DWORD Ignored2, IN ULONG_PTR Ignored3 ); DWORD FmpInitializeNotify( VOID ) /*++ Routine Description: Initialization routine for notification engine Arguments: None. Return Value: ERROR_SUCCESS if successful Win32 error code otherwise. --*/ { DWORD ThreadId; DWORD Status; Status = ClRtlInitializeQueue(&NotifyQueue); if (Status != ERROR_SUCCESS) { CL_LOGFAILURE(Status); return(Status); } RmNotifyThread = CreateThread(NULL, 0, FmpRmWorkerThread, NULL, 0, &ThreadId); if (RmNotifyThread == NULL) { CsInconsistencyHalt(GetLastError()); } return(ERROR_SUCCESS); } DWORD FmpRmWorkerThread( IN LPVOID lpThreadParameter ) /*++ Routine Description: This thread processes deferred Resource Monitor events. Arguments: lpThreadParameter - not used. Return Value: None. --*/ { DWORD status = ERROR_SUCCESS; PRM_EVENT event; PLIST_ENTRY entry; while (TRUE) { entry = ClRtlRemoveHeadQueue(&NotifyQueue); if ( entry == NULL ) { break; } event = CONTAINING_RECORD(entry, RM_EVENT, Linkage); if (event->EventType == RmWorkerTerminate) { LocalFree(event); break; } status = FmpRmDoHandleCriticalResourceStateChange( event, NULL, event->Parameters.ResourceTransition.NewState); LocalFree(event); if (status != ERROR_SUCCESS) { break; } } return(status); } BOOL FmpPostNotification( IN RM_NOTIFY_KEY NotifyKey, IN DWORD NotifyEvent, IN CLUSTER_RESOURCE_STATE CurrentState ) /*++ Routine Description: Callback routine used by resource monitor for resource state change notification. This routine queues the notification to a worker thread for deferred processing. Arguments: NotifyKey - Supplies the notification key for the resource that changed NotifyEvent - The event type. CurrentState - Supplies the (new) current state of the resource Return Value: TRUE - continue receiving notifications FALSE - abort notifications --*/ { PRM_EVENT event; event = LocalAlloc(LMEM_FIXED, sizeof(RM_EVENT)); if (event != NULL) { ClRtlLogPrint(LOG_NOISE, "[FM] NotifyCallBackRoutine: enqueuing event\n"); event->EventType = NotifyEvent; event->Parameters.ResourceTransition.NotifyKey = NotifyKey; event->Parameters.ResourceTransition.NewState = CurrentState; // // Enqueue the event for the worker thread. // ClRtlInsertTailQueue(&NotifyQueue, &event->Linkage); } else { ClRtlLogPrint(LOG_UNUSUAL, "[FM] NotifyCallBackRoutine: Unable to post item, memory alloc failure %1!u!\n", GetLastError()); } return(TRUE); } DWORD FmpRmDoHandleCriticalResourceStateChange( IN PRM_EVENT pEvent, IN OPTIONAL PFM_RESOURCE pTransitionedResource, IN CLUSTER_RESOURCE_STATE NewState ) /*++ Routine Description: Does an interlocked decrement of the gdwQuoBlockingResources variable. Handle the transition of the quorum resource state via a separate thread. Arguments: pEvent - The Resource Monitor Event pTransitionedResource - The resource whose state has changed. NewState - New state of the resource. Return Value: ERROR_SUCCESS on success, a Win32 error code otherwise. Comments: DO NOT hold any locks (such as group lock, gQuoChangeLock, etc.) in this function. You could deadlock the system quite easily. --*/ { RM_NOTIFY_KEY NotifyKey; DWORD dwOldBlockingFlag; PFM_RESOURCE pResource = pTransitionedResource; DWORD status = ERROR_SUCCESS; // // Chittur Subbaraman (chitturs) - 4/19/99 // // This function decrements the blocking resources count when the // resource state has stabilized. It is important to do this // decrement in a non-blocking mode so that the quorum resource // does not get caught forever waiting for this count to go down to // zero in the offline call, FmpRmOfflineResource. This code was // originally located in FmpHandleResourceTransition and was moved // here since you could run out of FmpRmWorkItemHandler threads // (which service the CsDelayedWorkQueue) since all of them could // get blocked on the local resource lock in // FmpHandleResourceTransition and consequently any new notifications // from resmon which could potentially decrement this count will // not get serviced. // if ( !ARGUMENT_PRESENT ( pTransitionedResource ) ) { //SS: have no idea what this was for, but only do this check if pEvent is passed in if (pEvent) { NotifyKey = pEvent->Parameters.ResourceResuscitate.NotifyKey; pResource = FmpFindResourceByNotifyKey( NotifyKey ); if ( pResource == NULL ) { ClRtlLogPrint(LOG_UNUSUAL, "[FM] FmpRmDoHandleCriticalResourceStateChange, bad resource NotifyKey %1!u!\n", NotifyKey); goto FnExit; } if ( pEvent->EventType != ResourceTransition ) { goto FnExit; } } } if ( pResource->QuorumResource ) { // // Chittur Subbaraman (chitturs) - 6/25/99 // // If this resource is the quorum resource, then let // FmpHandleResourceTransition take care of the sync notifications. // Note that this function only does the notifications for the // non-quorum resources as well as does the decrement on the // blocking resources count. The decrement MUST be done // without holding any locks to avoid potential deadlocks with // the quorum resource offline getting stuck in FmpRmOfflineResource // waiting for the blocking resources count to go to 0. // As far as the quorum resource goes, the sync notifications // must be done with gQuoChangeLock held since we want to // synchronize with other threads such as the FmCheckQuorumState // called by the DM node down handler. FmpHandleResourceTransition // does hold the gQuoChangeLock. // // Note also that for the quorum resource a separate thread // handles the resource transition since if we depend on the // worker threads servicing the CsDelayedWorkQueue to do this, // this notification could be starved from being processed since // some thread could hold the group lock and be stuck in the // resource onlining waiting for the quorum resource to go // online and all the worker threads servicing the CsDelayedWorkQueue // could be blocked on the group lock preventing the propagation // of the quorum resource state. // FmpCreateResStateChangeHandler( pResource, NewState, pResource->State ); goto FnExit; } // // Comments from sunitas: Call the synchronous notifications. // This is done before the count is decremented as the synchronous // callbacks like the registry replication must get a chance to // finish before the quorum resource state is allowed to change. // // Note, there is no synchronization here with the resmon's // online/offline code. They are using the LocalResourceLocks. // FmpCallResourceNotifyCb( pResource, NewState ); dwOldBlockingFlag = InterlockedExchange( &pResource->BlockingQuorum, 0 ); if ( dwOldBlockingFlag ) { ClRtlLogPrint(LOG_NOISE, "[FM] FmpRmDoHandleCriticalResourceStateChange: call InterlockedDecrement on gdwQuoBlockingResources, Resource %1!ws!\n", OmObjectId(pResource)); InterlockedDecrement( &gdwQuoBlockingResources ); } //post a work item to the fm worker thread to handle the rest OmReferenceObject(pResource); FmpPostWorkItem(FM_EVENT_RES_RESOURCE_TRANSITION, pResource, NewState); FnExit: return( status ); }