|
|
/*++
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;
typedef struct { LIST_ENTRY Linkage; RM_EVENT_TYPE EventType; union { struct { RM_NOTIFY_KEY NotifyKey; CLUSTER_RESOURCE_STATE NewState; } ResourceTransition; struct { RM_NOTIFY_KEY NotifyKey; } ResourceResuscitate; } Parameters; } RM_EVENT, *PRM_EVENT;
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 FmpRmDoHandleCriticalResourceStateChange( IN PRM_EVENT pEvent );
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); if (status != ERROR_SUCCESS) { break; } } return(status); }
#if 0
VOID FmpRmWorkItemHandler( IN PCLRTL_WORK_ITEM WorkItem, IN DWORD Ignored1, IN DWORD Ignored2, IN ULONG_PTR Ignored3 ) {
PFM_RESOURCE resource; ULONG_PTR notifyKey; PRM_EVENT event; DWORD status; BOOL bQuoChangeLockHeld = FALSE; event = (PRM_EVENT)WorkItem->Context;
// It is assumed that NotifyKey is always the first field of the struct
// within the union in RM_EVENT
notifyKey = event->Parameters.ResourceResuscitate.NotifyKey;
resource = FmpFindResourceByNotifyKey( notifyKey );
if (resource == NULL) { ClRtlLogPrint(LOG_NOISE, "[FM] FmpRmWorkItemHandler, bad resource NotifyKey %1!u!\n", notifyKey ); goto FnExit; }
ClRtlLogPrint(LOG_NOISE, "[FM] FmpRmWorkItemHandler, Resource=<%1!ws!>, Event=%2!u!\n", OmObjectId(resource), event->EventType);
ChkFMState: if (!FmpFMGroupsInited) { DWORD dwRetryCount = 50; ACQUIRE_SHARED_LOCK(gQuoChangeLock);
//FmFormNewClusterPhaseProcessing is in progress
if (FmpFMFormPhaseProcessing) { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpRmWorkItemHandler, resource notification from quorum resource " "during phase processing. Sleep and retry\n"); RELEASE_LOCK(gQuoChangeLock); Sleep(500); if (dwRetryCount--) goto ChkFMState; else { ClRtlLogPrint(LOG_CRITICAL, "[FM] FmpRmWorkItemHandler, waited for too long\n"); //terminate the process
CL_ASSERT(FALSE); } } else { bQuoChangeLockHeld = TRUE; } //this can only come from the quorum resource
CL_ASSERT(resource->QuorumResource); } switch(event->EventType) {
case ResourceTransition: { CLUSTER_RESOURCE_STATE newState = event->Parameters.ResourceTransition.NewState;
FmpHandleResourceTransition( resource, newState ); break; }
#if 0
//
// Chittur Subbaraman (chitturs) - 4/19/99
//
// Commenting out - case ResourceResuscitate is not called from anywhere.
//
case ResourceResuscitate: ClRtlLogPrint(LOG_NOISE, "[FM] FmpRmWorkItemHandler, processing ResourceResuscitate event\n"); FmpAcquireLocalResourceLock( resource ); FmpRestartResourceTree( resource ); FmpReleaseLocalResourceLock( resource ); break; //
// Chittur Subbaraman (chitturs) - 4/19/99
//
// Commenting out - case RmUpdateResource is now handled by FmpWorkerThread.
//
case RmUpdateResource:
//
// Now tell the resource monitor about the changes.
//
FmpAcquireLocalResourceLock( resource ); status = FmpRmChangeResourceParams( resource ); FmpReleaseLocalResourceLock( resource ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_UNUSUAL, "[FM] FmpRmWorkerThread, failed to change resource " "parameters for %1!ws!, error %2!u!.\n", OmObjectId(resource), status ); } break; //
// Chittur Subbaraman (chitturs) - 4/19/99
//
// Commenting out - Since the producer of this notification is commented
// out in fmreg.c.
//
case RmRestartResource: FmpAcquireLocalResourceLock( resource ); status = FmpRmCloseResource( resource ); if ( status == ERROR_SUCCESS ) { if ( resource->Flags & RESOURCE_SEPARATE_MONITOR ) { resource->Flags &= ~RESOURCE_SEPARATE_MONITOR; } else { resource->Flags |= RESOURCE_SEPARATE_MONITOR; } status = FmpRmCreateResource( resource ); if ( status != ERROR_SUCCESS ) { ClRtlLogPrint(LOG_UNUSUAL, "[FM] FmpRmWorkItemhandler: Separate resource monitor " "changed for '%1!ws!', but failed to re-open the resource, " "error %2!u!.\n", OmObjectId(resource), status ); } } else { ClRtlLogPrint(LOG_UNUSUAL, "[FM] FmpRmWorkItemHandler :Separate resource monitor " "changed for '%1!ws!', but failed to close the resource, " "error %2!u!.\n", OmObjectId(resource), status ); } FmpReleaseLocalResourceLock( resource ); break; #endif
default: ClRtlLogPrint(LOG_NOISE, "[FM] FmpRmWorkerThread, Unknown event type %1!u!\n", event->EventType ); break; }
FnExit: if (bQuoChangeLockHeld) { RELEASE_LOCK(gQuoChangeLock); }
LocalFree(event); LocalFree(WorkItem);
ClRtlLogPrint(LOG_NOISE,"[FM] FmpRmWorkItemHandler: Exit\n");
return; }
#endif
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); }
return(TRUE); }
DWORD FmpRmDoHandleCriticalResourceStateChange( IN PRM_EVENT pEvent )
/*++
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
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; DWORD status = ERROR_SUCCESS; PCLRTL_WORK_ITEM pWorkItem; CLUSTER_RESOURCE_STATE NewState = pEvent->Parameters.ResourceTransition.NewState;
//
// 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.
//
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 );
LocalFree( pEvent ); goto FnExit; }
pWorkItem = LocalAlloc( LMEM_FIXED, sizeof( CLRTL_WORK_ITEM ) ); if ( pWorkItem == NULL ) { status = ERROR_NOT_ENOUGH_MEMORY; CL_UNEXPECTED_ERROR( status ); 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);
#if 0
ClRtlInitializeWorkItem( pWorkItem, FmpRmWorkItemHandler, (PVOID) pEvent );
status = ClRtlPostItemWorkQueue( CsDelayedWorkQueue, pWorkItem, 0, 0 );
if ( status ) { LocalFree( pWorkItem ); CL_UNEXPECTED_ERROR( status ); }
#endif
FnExit: return( status ); }
|