569 lines
15 KiB
C
569 lines
15 KiB
C
/*++
|
||
|
||
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 );
|
||
}
|