windows-nt/Source/XPSP1/NT/base/cluster/resmon/reslist.c

1571 lines
46 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1995-1997 Microsoft Corporation
Module Name:
reslist.c
Abstract:
Implements the management of the resource list. This includes
adding resources to the list and deleting them from the list.
Author:
John Vert (jvert) 1-Dec-1995
Revision History:
Sivaprasad Padisetty (sivapad) 06-18-1997 Added the COM support
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include "resmonp.h"
#include "stdio.h" //RNGBUG - remove all of these in all .c files
#define RESMON_MODULE RESMON_MODULE_RESLIST
DWORD RmpLogLevel = LOG_ERROR;
//
// Function prototypes local to this module
//
BOOL
RmpChkdskNotRunning(
IN PRESOURCE Resource
);
DWORD
RmpSetResourceStatus(
IN RESOURCE_HANDLE ResourceHandle,
IN PRESOURCE_STATUS ResourceStatus
)
/*++
Routine Description:
Update the status of a resource.
Arguments:
ResourceHandle - Handle (pointer to) the resource to update.
ResourceStatus - Pointer to a resource status structure for update.
Returns:
ResourceExitStateContinue - if the thread does not have to terminate.
ResourceExitStateTerminate - if the thread must terminat now.
--*/
{
BOOL bSuccess;
DWORD status;
PRESOURCE resource = (PRESOURCE)ResourceHandle;
DWORD retryCount = ( resource->PendingTimeout/100 >= 600 ) ?
( resource->PendingTimeout/100 - 400 ):200;
PPOLL_EVENT_LIST eventList;
HANDLE OnlineEvent;
//
// Check if we're only updating the checkpoint value. If so, then
// don't use any locks.
//
if ( ResourceStatus->ResourceState >= ClusterResourcePending ) {
resource->CheckPoint = ResourceStatus->CheckPoint;
return ResourceExitStateContinue;
}
//
// Acquire the lock first to prevent race conditions if the resource
// DLL manages to set resource status from a different thread before
// returning PENDING from its online/offline entrypoint.
//
eventList = (PPOLL_EVENT_LIST) resource->EventList;
status = TryEnterCriticalSection( &eventList->ListLock );
while ( !status &&
retryCount-- ) {
//
// Chittur Subbaraman (chitturs) - 10/18/99
//
// Comment out this unprotected check. The same check is done
// protected further downstream. Unprotected checking could
// cause a resource to attempt to enter here even before
// the timer event has been created by s_RmOnlineResource or
// s_RmOfflineResource and in such a case the resource will
// not be able to set the resource status. This will cause
// the resmon to wrongly time out the resource.
//
#if 0
//
// Check if the resource is shutting down as we're waiting.
//
if ( resource->TimerEvent == NULL ) {
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] Resource (%1!ws!) TimerEvent became NULL, state (%2!d!)!\n",
resource->ResourceName,
resource->State);
if ( resource->OnlineEvent ) {
CloseHandle( resource->OnlineEvent );
resource->OnlineEvent = NULL;
}
return ResourceExitStateTerminate;
}
#endif
//
// Chittur Subbaraman (chitturs) - 12/8/99
//
// Check if the "Terminate" or "Close" entry point has been
// called for this resource. If so, then no need to set the
// resource status. Moreover, those entry points could be
// blocked waiting for the pending thread to terminate and
// if the pending thread is stuck looping here waiting for
// the lock held by the former thread, we are in a deadlock-like
// situation. [Note that the fact that you are at this point
// means that it is not the "Terminate"/"Close" itself calling
// this function since the eventlist lock can be obtained since
// it was obtained by resmon prior to calling the resdll entry.]
//
if( ( resource->dwEntryPoint ) &
( RESDLL_ENTRY_TERMINATE | RESDLL_ENTRY_CLOSE ) )
{
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] RmpSetResourceStatus: Resource <%1!ws!> not setting status since "
"%2!ws! is called, lock owner=0x%3!x!, resource=%4!ws!, state=%5!u!...\n",
resource->ResourceName,
(resource->dwEntryPoint == RESDLL_ENTRY_TERMINATE) ?
L"Terminate":L"Close",
eventList->ListLock.OwningThread,
(eventList->LockOwnerResource != NULL) ? eventList->LockOwnerResource->ResourceName:L"Unknown resource",
eventList->MonitorState);
if ( resource->OnlineEvent ) {
CloseHandle( resource->OnlineEvent );
resource->OnlineEvent = NULL;
}
return ResourceExitStateTerminate;
}
Sleep(100); // Delay a little
status = TryEnterCriticalSection( &eventList->ListLock );
}
//
// If we couldn't proceed, we're stuck. Just return now.
//
if ( !status ) {
//
// We're unsynchronized, but clean up a bit.
//
if ( resource->OnlineEvent ) {
CloseHandle( resource->OnlineEvent );
resource->OnlineEvent = NULL;
}
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] Resource (%1!ws!) Failed TryEnterCriticalSection after too many "
"tries, state=%2!d!, lock owner=%3!x!, resource=%4!ws!, state=%5!u!\n",
resource->ResourceName,
resource->State,
eventList->ListLock.OwningThread,
(eventList->LockOwnerResource != NULL) ? eventList->LockOwnerResource->ResourceName:L"Unknown resource",
eventList->MonitorState);
//
// SS: Why do we let the resource continue ?
//
return ResourceExitStateContinue;
}
//
// SS: If the timer thread has timed us out, there is no
// point in continuing.
//
// First check if the resource is shutting down.
//
if ( resource->TimerEvent == NULL ) {
//
// Just return asking the resource dll to terminate, but clean
// up a bit.
//
if ( resource->OnlineEvent ) {
CloseHandle( resource->OnlineEvent );
resource->OnlineEvent = NULL;
}
ReleaseEventListLock( eventList );
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] Timer Event is NULL when resource (%1!ws!) tries to set state=%2!d! !\n",
resource->ResourceName,
resource->State);
return ResourceExitStateTerminate;
}
//
// Synchronize with the online thread.
//
if ( resource->OnlineEvent != NULL ) {
OnlineEvent = resource->OnlineEvent;
resource->OnlineEvent = NULL;
ReleaseEventListLock( eventList );
WaitForSingleObject( OnlineEvent, INFINITE );
AcquireEventListLock( eventList );
CloseHandle( OnlineEvent );
}
//
// If the state of the resource is not pending, then return immediately
//
if ( resource->State < ClusterResourcePending ) {
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] Resource (%1!ws!) attempted to set status while state was not pending (%2!d!)!\n",
resource->ResourceName,
resource->State);
CL_LOGFAILURE(ERROR_INVALID_SERVER_STATE);
ReleaseEventListLock( eventList );
return ResourceExitStateContinue;
}
resource->State = ResourceStatus->ResourceState;
// resource->WaitHint = ResourceStatus->WaitHint;
resource->CheckPoint = ResourceStatus->CheckPoint;
//
// If the state has stabilized, stop the timer thread.
//
if ( resource->State < ClusterResourcePending ) {
//
// Add any events to our eventlist if the resource is reporting its state as online.
//
if ( ResourceStatus->EventHandle ) {
if ( resource->State == ClusterResourceOnline ) {
if ( (ULONG_PTR)ResourceStatus->EventHandle > 0x2000 ) {
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] SetResourceStatus: Resource <%1!ws!> attempted to set a bogus event %2!lx!.\n",
resource->ResourceName,
ResourceStatus->EventHandle );
} else {
status = RmpAddPollEvent( eventList,
ResourceStatus->EventHandle,
resource );
if ( status != ERROR_SUCCESS ) {
resource->State = ClusterResourceFailed;
ClRtlLogPrint( LOG_UNUSUAL, "[RM] ResourceStatus, failed to add event to list.\n");
}
//
// Signal poller that event list changed.
//
if ( status == ERROR_SUCCESS ) {
RmpSignalPoller( eventList );
}
}
} else {
ClRtlLogPrint(LOG_ERROR,
"[RM] RmpSetResourceStatus: Resource '%1!ws!' supplies event handle 0x%2!08lx! while reporting state %3!u!...\n",
resource->ResourceName,
ResourceStatus->EventHandle,
resource->State );
}
}
//
// The event may have been closed by the timer thread
// if this is happening too late, ignore the error.
//
if( resource->TimerEvent != NULL )
{
bSuccess = SetEvent( resource->TimerEvent );
if ( !bSuccess )
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] RmpSetResourceStatus, Error %1!u! calling SetEvent to wake timer thread\n",
GetLastError());
}
//
// Chittur Subbaraman (chitturs) - 1/12/99
//
// Post a notification to the cluster service regarding a state
// change AFTER sending a signal to a timer. This will reduce
// the probability of the cluster service sending in another
// request before the timer thread had a chance to close out
// the event handle.
//
ClRtlLogPrint(LOG_NOISE,
"[RM] RmpSetResourceStatus, Posting state %2!u! notification for resource <%1!ws!>\n",
resource->ResourceName,
resource->State);
RmpPostNotify( resource, NotifyResourceStateChange );
}
ReleaseEventListLock( eventList );
return ResourceExitStateContinue;
} // RmpSetResourceStatus
VOID
RmpLogEvent(
IN RESOURCE_HANDLE ResourceHandle,
IN LOG_LEVEL LogLevel,
IN LPCWSTR FormatString,
...
)
/*++
Routine Description:
Log an event for the given resource.
Arguments:
ResourceHandle - Handle (pointer to) the resource to update.
LogLevel - Supplies the level of this log event.
FormatString - Supplies a format string for this log message.
Returns:
None.
--*/
{
LPWSTR headerBuffer;
LPWSTR messageBuffer;
DWORD bufferLength;
PRESOURCE resource = (PRESOURCE)ResourceHandle;
PVOID argArray[2];
HKEY resKey;
DWORD status;
DWORD valueType;
#ifdef SLOW_RMP_LOG_EVENT
WCHAR resourceName[128];
#endif
va_list argList;
ULONG rtlLogLevel;
//
// map resmon log levels to those used by ClRtlLogPrint
//
switch ( LogLevel ) {
case LOG_INFORMATION:
rtlLogLevel = LOG_NOISE;
break;
case LOG_WARNING:
rtlLogLevel = LOG_UNUSUAL;
break;
case LOG_ERROR:
case LOG_SEVERE:
default:
rtlLogLevel = LOG_CRITICAL;
}
if ( (resource == NULL) ||
(resource->Signature != RESOURCE_SIGNATURE) ) {
LPWSTR resourcePrefix = (LPWSTR)ResourceHandle;
//
// Some resource DLLs have threads that do some
// work on behalf of this resource DLL, but has no
// relation to a particular resource. Thus they cannot
// provide a resource handle, necessary to log an event.
//
// The following hack allows them to supply a string
// to be printed before the message.
//
// This string should start with unicode 'r' and 't'
// characters. "rt" is interpreted as a signature and is not printed.
//
if (resourcePrefix &&
resourcePrefix[0] == L'r' &&
resourcePrefix[1] == L't')
{
resourcePrefix += 2; // skip the signature
} else {
resourcePrefix = L"<Unknown Resource>";
//CL_LOGFAILURE((DWORD)resource);
}
va_start( argList, FormatString );
//
// Print out the actual message
//
if ( bufferLength = FormatMessageW(FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
FormatString,
0,
0,
(LPWSTR)&messageBuffer,
0,
&argList) )
{
ClRtlLogPrint( rtlLogLevel, "%1!ws!: %2!ws!", resourcePrefix, messageBuffer);
LocalFree(messageBuffer);
}
va_end( argList );
return;
}
//CL_ASSERT(resource->Signature == RESOURCE_SIGNATURE);
#ifdef SLOW_RMP_LOG_EVENT
status = ClusterRegOpenKey( RmpResourcesKey,
resource->ResourceId,
KEY_READ,
&resKey );
if ( status != ERROR_SUCCESS ) {
return;
}
bufferLength = 128;
status = ClusterRegQueryValue( resKey,
CLUSREG_NAME_RES_NAME,
&valueType,
(LPBYTE)&resourceName,
&bufferLength );
ClusterRegCloseKey( resKey );
if ( status != ERROR_SUCCESS ) {
return;
}
#endif
//
// Print out the prefix string
//
argArray[0] = resource->ResourceType;
#ifdef SLOW_RMP_LOG_EVENT
argArray[1] = resourceName;
#else
argArray[1] = resource->ResourceName;
#endif
if ( bufferLength = FormatMessageW(FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
L"%1!ws! <%2!ws!>: ",
0,
0,
(LPWSTR)&headerBuffer,
0,
(va_list*)&argArray) ) {
} else {
return;
}
va_start( argList, FormatString );
//
// Print out the actual message
//
if ( bufferLength = FormatMessageW(FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
FormatString,
0,
0,
(LPWSTR)&messageBuffer,
0,
&argList) )
{
ClRtlLogPrint( rtlLogLevel, "%1!ws!%2!ws!", headerBuffer, messageBuffer);
LocalFree(messageBuffer);
}
LocalFree(headerBuffer);
va_end( argList );
} // RmpLogEvent
VOID
RmpLostQuorumResource(
IN RESOURCE_HANDLE ResourceHandle
)
/*++
Routine Description:
Stop the cluster service... since we lost our quorum resource.
Arguments:
ResourceHandle - Handle (pointer to) the resource to update.
Returns:
None.
--*/
{
PRESOURCE resource = (PRESOURCE)ResourceHandle;
//
// Kill the cluster service alone. Take no action for this process since the main
// thread in resmon.c would detect the termination of the cluster service process
// and cleanly shut down hosted resources and the process itself.
//
TerminateProcess( RmpClusterProcess, 1 );
ClRtlLogPrint( LOG_CRITICAL, "[RM] LostQuorumResource, cluster service terminated...\n");
return;
} // RmpLostQuorumResource
BOOL
RmpChkdskNotRunning(
IN PRESOURCE Resource
)
/*++
Routine Description:
If this is a storage class resource, make sure CHKDSK is not running.
Arguments:
Resource - A pointer to the resource to check.
Returns:
TRUE - if this is not a STORAGE resource or CHKDSK is not running.
FALSE - if this is a STORAGE resource AND CHKDSK is running.
--*/
{
PSYSTEM_PROCESS_INFORMATION processInfo;
NTSTATUS ntStatus;
DWORD status;
DWORD size = 4096;
ANSI_STRING pname;
PCHAR commonBuffer = NULL;
PCHAR ptr;
DWORD totalOffset = 0;
CLUS_RESOURCE_CLASS_INFO resClassInfo;
DWORD bytesReturned;
#if 1
//
// Get the class of resource... if not a STORAGE class then fail now.
//
if ( Resource->dwType == RESMON_TYPE_DLL ) {
status = (Resource->pResourceTypeControl)( Resource->Id,
CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO,
NULL,
0,
&resClassInfo,
sizeof(resClassInfo),
&bytesReturned );
} else {
HRESULT hr ;
VARIANT vtIn, vtOut ;
SAFEARRAY sfIn = {1, 0, 1, 0, NULL, {0, 0} } ;
SAFEARRAY sfOut = {1, FADF_FIXEDSIZE, 1, 0, &resClassInfo, {sizeof(resClassInfo), 0} } ;
SAFEARRAY *psfOut = &sfOut ;
vtIn.vt = VT_ARRAY | VT_UI1 ;
vtOut.vt = VT_ARRAY | VT_UI1 | VT_BYREF ;
vtIn.parray = &sfIn ;
vtOut.pparray = &psfOut ;
hr = IClusterResControl_ResourceControl (
Resource->pClusterResControl,
(OLERESID)Resource->Id,
(long)CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO,
&vtIn,
&vtOut,
(long *)&bytesReturned,
&status);
if (FAILED(hr)) {
CL_LOGFAILURE(hr); // Use the default processing
status = ERROR_INVALID_FUNCTION;
}
}
if ( (status != ERROR_SUCCESS) ||
(resClassInfo.rc != CLUS_RESCLASS_STORAGE) ) {
return TRUE; // fail now
}
#endif
retry:
RmpFree( commonBuffer );
commonBuffer = RmpAlloc( size );
if ( !commonBuffer ) {
return TRUE; // fail now
}
ntStatus = NtQuerySystemInformation(
SystemProcessInformation,
commonBuffer,
size,
NULL );
if ( ntStatus == STATUS_INFO_LENGTH_MISMATCH ) {
size += 4096;
goto retry;
}
if ( !NT_SUCCESS(ntStatus) ) {
return TRUE; // fail now
}
processInfo = (PSYSTEM_PROCESS_INFORMATION)commonBuffer;
while ( TRUE ) {
if ( processInfo->ImageName.Buffer ) {
if ( ( ntStatus = RtlUnicodeStringToAnsiString( &pname,
&processInfo->ImageName,
TRUE ) ) != STATUS_SUCCESS ) {
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] ChkdskNotRunning: Unable to convert Unicode string to Ansi, status = 0x%lx...\n",
ntStatus);
break;
}
ptr = strrchr( pname.Buffer, '\\' );
if ( ptr ) {
ptr++;
} else {
ptr = pname.Buffer;
}
if ( lstrcmpiA( ptr, "CHKDSK.EXE" ) == 0 ) {
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] ChkdskNotRunning: Found process %1!ws!.\n",
processInfo->ImageName.Buffer );
RmpFree( pname.Buffer );
RmpFree( commonBuffer );
return FALSE; // chkdsk is running
}
RmpFree( pname.Buffer );
}
if ( processInfo->NextEntryOffset == 0 ) break;
totalOffset += processInfo->NextEntryOffset;
processInfo = (PSYSTEM_PROCESS_INFORMATION)&commonBuffer[totalOffset];
}
RmpFree( commonBuffer );
return TRUE; // CHKDSK is not running
} // RmpChkdskNotRunning
DWORD
RmpTimerThread(
IN LPVOID Context
)
/*++
Routine Description:
Thread to wait on transition of a resource from pending to a stable state.
Arguments:
Context - A pointer to the resource being timed.
Returns:
Win32 error code.
--*/
{
PRESOURCE resource = (PRESOURCE)Context;
DWORD status;
HANDLE timerEvent;
DWORD prevCheckPoint;
CL_ASSERT( resource != NULL );
timerEvent = resource->TimerEvent;
if ( !timerEvent ) {
return(ERROR_SUCCESS);
}
//
// Loop waiting for resource to complete pending operation or to
// shutdown processing.
//
while ( timerEvent ) {
prevCheckPoint = resource->CheckPoint;
status = WaitForSingleObject( timerEvent,
resource->PendingTimeout );
//
// If we were asked to stop, then exit quietly.
//
if ( status != WAIT_TIMEOUT ) {
//
// The thread that cleans the timer event must close the handle.
//
CloseHandle(timerEvent);
resource->TimerEvent = NULL;
return(ERROR_SUCCESS);
}
//
// Check if the resource has not made forward progress... if not,
// then let break out now.
//
// Also if this is a storage class resource make sure that
// CHKDSK is not running.
//
if ( (prevCheckPoint == resource->CheckPoint) &&
RmpChkdskNotRunning( resource ) ) {
break;
}
ClRtlLogPrint(LOG_NOISE,
"[RM] RmpTimerThread: Giving a reprieve for resource %1!ws!...\n",
resource->ResourceName);
}
//
// Indicate that this resource failed!
//
AcquireEventListLock( (PPOLL_EVENT_LIST)resource->EventList );
if ( resource->TimerEvent != NULL ) {
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] RmpTimerThread: Resource %1!ws! pending timed out "
"- setting state to failed.\n",
resource->ResourceName);
CloseHandle(resource->TimerEvent);
resource->TimerEvent = NULL;
resource->State = ClusterResourceFailed;
//
// Log an event
//
status = ERROR_TIMEOUT;
ClusterLogEvent1(LOG_CRITICAL,
LOG_CURRENT_MODULE,
__FILE__,
__LINE__,
RMON_RESOURCE_TIMEOUT,
sizeof(status),
&status,
resource->ResourceName);
//
// Chittur Subbaraman (chitturs) - 4/5/99
//
// Since the resource has failed, there is no point in having
// the OnlineEvent hanging around. If the OnlineEvent is not
// closed out, then you cannot call s_RmOnlineResource or
// s_RmOfflineResource.
//
if ( resource->OnlineEvent != NULL ) {
CloseHandle( resource->OnlineEvent );
resource->OnlineEvent = NULL;
}
RmpPostNotify( resource, NotifyResourceStateChange );
}
ReleaseEventListLock( (PPOLL_EVENT_LIST)resource->EventList );
return(ERROR_SUCCESS);
} // RmpTimerThread
DWORD
RmpOfflineResource(
IN RESID ResourceId,
IN BOOL Shutdown,
OUT DWORD *pdwState
)
/*++
Routine Description:
Brings the specified resource into the offline state.
Arguments:
ResourceId - Supplies the resource to be brought online.
Shutdown - Specifies whether the resource is to be shutdown gracefully
TRUE - resource will be shutdown gracefully
FALSE - resource will be immediately taken offline
pdwState - the new resource state is returned in here.
Return Value:
The new state of the resource.
Notes:
The resource's eventlist lock must NOT be held.
--*/
{
DWORD status=ERROR_SUCCESS;
BOOL success;
PRESOURCE Resource;
HANDLE timerThread;
DWORD threadId;
DWORD loopCount;
BOOL fLockAcquired;
Resource = (PRESOURCE)ResourceId;
CL_ASSERT(Resource->Signature == RESOURCE_SIGNATURE);
*pdwState = Resource->State;
//if this is a graceful close,create the online/offline
//event such that if a resource calls rmpsetresourcestatus
//soon after online for that resource is called and before
//the timer thread/event is even created then we wont have
//an event leak and an abadoned thread
if (Shutdown)
{
//
// We should not be in a Pending state.
//
if ( Resource->State > ClusterResourcePending )
{
return(ERROR_INVALID_STATE);
}
//
// Create an event to allow the SetResourceStatus callback to synchronize
// execution with this thread.
//
if ( Resource->OnlineEvent )
{
return(ERROR_NOT_READY);
}
Resource->OnlineEvent = CreateEvent( NULL,
FALSE,
FALSE,
NULL );
if ( Resource->OnlineEvent == NULL )
{
return(GetLastError());
}
}
//
// Lock the EventList Lock, insert the
// resource into the list, and take the resource offline.
// The lock is required to synchronize access to the resource list and
// to serialize any calls to resource DLLs. Only one thread may be
// calling a resource DLL at any time. This prevents resource DLLs
// from having to worry about being thread-safe.
//
AcquireListLock(); // We need this lock for the potential remove below!
AcquireEventListLock( (PPOLL_EVENT_LIST)Resource->EventList );
//
// Stop any previous timer threads. Should this be done before the lock
// is held?
//
if ( Resource->TimerEvent != NULL ) {
success = SetEvent( Resource->TimerEvent );
}
//
// Update shared state to indicate we are taking a resource offline
//
RmpSetMonitorState(RmonOfflineResource, Resource);
//
// If we have an error signal event, then remove it from our lists.
//
if ( Resource->EventHandle ) {
RmpRemovePollEvent( Resource->EventHandle );
}
//
// Call Offline entrypoint.
//
if ( Shutdown )
{
CL_ASSERT( (Resource->State < ClusterResourcePending) );
try {
#ifdef COMRES
status = RESMON_OFFLINE (Resource) ;
#else
status = (Resource->Offline)(Resource->Id);
#endif
} except (EXCEPTION_EXECUTE_HANDLER) {
status = GetExceptionCode();
}
//
// If the Resource DLL returns pending, then start a timer.
//
if (status == ERROR_SUCCESS) {
//close the event
SetEvent( Resource->OnlineEvent );
CloseHandle( Resource->OnlineEvent );
Resource->OnlineEvent = NULL;
Resource->State = ClusterResourceOffline;
}
else if ( status == ERROR_IO_PENDING ) {
CL_ASSERT(Resource->TimerEvent == NULL );
Resource->TimerEvent = CreateEvent( NULL,
FALSE,
FALSE,
NULL );
if ( Resource->TimerEvent == NULL ) {
CL_UNEXPECTED_ERROR(status = GetLastError());
} else {
timerThread = CreateThread( NULL,
0,
RmpTimerThread,
Resource,
0,
&threadId );
if ( timerThread == NULL ) {
CL_UNEXPECTED_ERROR(status = GetLastError());
} else {
Resource->State = ClusterResourceOfflinePending;
//Resource->WaitHint = PENDING_TIMEOUT;
//Resource->CheckPoint = 0;
//
// Chittur Subbaraman (chitturs) - 1/12/99
//
// Raise the timer thread priority to highest. This
// is necessary to avoid certain cases in which the
// timer thread is sluggish to close out the timer event
// handle before a second offline. Note that there are
// no major performance implications by doing this since
// the timer thread is in a wait state most of the time.
//
if ( !SetThreadPriority( timerThread, THREAD_PRIORITY_HIGHEST ) )
{
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] RmpOfflineResource:Error setting priority of timer "
"thread for resource %1!ws!\n",
Resource->ResourceName);
CL_LOGFAILURE( GetLastError() );
}
CloseHandle( timerThread );
}
}
Resource->State = ClusterResourceOfflinePending;
SetEvent(Resource->OnlineEvent);
}
else {
CloseHandle( Resource->OnlineEvent );
Resource->OnlineEvent = NULL;
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] OfflinelineResource failed, resource %1!ws!, status = %2!u!.\n",
Resource->ResourceName,
status);
ClusterLogEvent1(LOG_CRITICAL,
LOG_CURRENT_MODULE,
__FILE__,
__LINE__,
RMON_OFFLINE_FAILED,
sizeof(status),
&status,
Resource->ResourceName);
Resource->State = ClusterResourceFailed;
}
} else {
Resource->dwEntryPoint = RESDLL_ENTRY_TERMINATE;
try {
#ifdef COMRES
RESMON_TERMINATE (Resource) ;
#else
(Resource->Terminate)(Resource->Id);
#endif
} except (EXCEPTION_EXECUTE_HANDLER) {
}
Resource->dwEntryPoint = 0;
Resource->State = ClusterResourceOffline;
}
RmpSetMonitorState(RmonIdle, NULL);
ReleaseEventListLock( (PPOLL_EVENT_LIST)Resource->EventList );
ReleaseListLock();
*pdwState = Resource->State;
return(status);
} // RmpOfflineResource
VOID
RmpRemoveResourceList(
IN PRESOURCE Resource
)
/*++
Routine Description:
Removes a resource into the monitoring list.
Arguments:
Resource - Supplies the resource to be removed from the list.
Return Value:
None.
--*/
{
PPOLL_EVENT_LIST EventList = (PPOLL_EVENT_LIST)Resource->EventList;
AcquireEventListLock( EventList );
//
// Make sure it is really in the list.
//
CL_ASSERT(Resource->Flags & RESOURCE_INSERTED);
CL_ASSERT(Resource->ListEntry.Flink->Blink == &Resource->ListEntry);
CL_ASSERT(Resource->ListEntry.Blink->Flink == &Resource->ListEntry);
CL_ASSERT(EventList->NumberOfResources);
RemoveEntryList(&Resource->ListEntry);
Resource->Flags &= ~RESOURCE_INSERTED;
--EventList->NumberOfResources;
ReleaseEventListLock( EventList );
} // RmpRemoveResourceList
DWORD
RmpInsertResourceList(
IN PRESOURCE Resource,
IN OPTIONAL PPOLL_EVENT_LIST pPollEventList
)
/*++
Routine Description:
Inserts a resource into the monitoring list.
Each resource is placed in a list along with other resources with the
same poll interval. The IsAlive and LooksAlive timeouts are adjusted
so that the IsAlive interval is an even multiple of the LooksAlive interval.
Thus, the IsAlive poll can simply be done every Nth poll instead of the normal
LooksAlive poll.
Arguments:
Resource - Supplies the resource to be added to the list.
pPollEventList - Supplies the eventlist in which the resource is to
be added. Optional.
Return Value:
None.
--*/
{
DWORD Temp1, Temp2;
ULONG i;
PMONITOR_BUCKET NewBucket;
PMONITOR_BUCKET Bucket;
DWORDLONG PollInterval;
PPOLL_EVENT_LIST EventList;
PPOLL_EVENT_LIST MinEventList;
PLIST_ENTRY ListEntry;
DWORD dwError = ERROR_SUCCESS;
CL_ASSERT((Resource->Flags & RESOURCE_INSERTED) == 0);
//
// If we have no LooksAlivePollInterval, then poll the IsAlive on every
// poll interval. Otherwise, poll IsAlive every N LooksAlive poll
// intervals.
//
if ( Resource->LooksAlivePollInterval == 0 ) {
//
// Round IsAlivePollInterval up to the system granularity
//
Temp1 = Resource->IsAlivePollInterval;
Temp1 = Temp1 + POLL_GRANULARITY - 1;
//if this has rolled over
if (Temp1 < Resource->IsAlivePollInterval)
Temp1 = 0xFFFFFFFF;
Temp1 = Temp1 / POLL_GRANULARITY;
Temp1 = Temp1 * POLL_GRANULARITY;
Resource->IsAlivePollInterval = Temp1;
Resource->IsAliveRollover = 1;
//
// Convert poll interval from ms to 100ns units
//
PollInterval = Resource->IsAlivePollInterval * 10 * 1000;
} else {
//
// First round LooksAlivePollInterval up to the system granularity
//
Temp1 = Resource->LooksAlivePollInterval;
Temp1 = (Temp1 + POLL_GRANULARITY - 1) ;
//check for rollover
if (Temp1 < Resource->LooksAlivePollInterval)
Temp1 = 0xFFFFFFFF;
Temp1 = Temp1/POLL_GRANULARITY;
Temp1 = Temp1 * POLL_GRANULARITY;
Resource->LooksAlivePollInterval = Temp1;
//
// Now round IsAlivePollInterval to a multiple of LooksAlivePollInterval
//
Temp2 = Resource->IsAlivePollInterval;
Temp2 = (Temp2 + Temp1 - 1) / Temp1;
Temp2 = Temp2 * Temp1;
Resource->IsAlivePollInterval = Temp2;
Resource->IsAliveRollover = (ULONG)(Temp2 / Temp1);
CL_ASSERT((Temp2 / Temp1) * Temp1 == Temp2);
//
// Convert poll interval from ms to 100ns units
//
PollInterval = Resource->LooksAlivePollInterval * 10 * 1000;
}
if ( PollInterval > 0xFFFFFFFF ) {
PollInterval = 0xFFFFFFFF;
}
Resource->IsAliveCount = 0;
//
// Chittur Subbaraman (chitturs) - 1/30/2000
//
// If an eventlist is supplied as parameter, do not attempt to
// find a new eventlist.
//
if( ARGUMENT_PRESENT( pPollEventList ) ) {
MinEventList = pPollEventList;
goto skip_eventlist_find;
}
//
// First find the EventList with the fewest number of entries.
//
AcquireListLock();
ListEntry = RmpEventListHead.Flink;
MinEventList = CONTAINING_RECORD(ListEntry,
POLL_EVENT_LIST,
Next );
CL_ASSERT( ListEntry != &RmpEventListHead );
for ( ListEntry = RmpEventListHead.Flink;
ListEntry != &RmpEventListHead;
ListEntry = ListEntry->Flink ) {
EventList = CONTAINING_RECORD( ListEntry, POLL_EVENT_LIST, Next );
if ( EventList->NumberOfResources < MinEventList->NumberOfResources ) {
MinEventList = EventList;
}
}
ReleaseListLock();
if ( MinEventList->NumberOfResources >= MAX_RESOURCES_PER_THREAD ) {
MinEventList = RmpCreateEventList();
}
skip_eventlist_find:
if ( MinEventList == NULL ) {
dwError = GetLastError();
goto FnExit;
}
EventList = MinEventList;
AcquireEventListLock( EventList );
Resource->EventList = (PVOID)EventList;
//
// Search the list for a bucket with the same period as this resource.
//
Bucket = CONTAINING_RECORD(EventList->BucketListHead.Flink,
MONITOR_BUCKET,
BucketList);
while (&Bucket->BucketList != &EventList->BucketListHead) {
if (Bucket->Period == PollInterval) {
break;
}
Bucket = CONTAINING_RECORD(Bucket->BucketList.Flink,
MONITOR_BUCKET,
BucketList);
}
if (&Bucket->BucketList == &EventList->BucketListHead) {
//
// Need to add a new bucket with this period.
//
Bucket = RmpAlloc(sizeof(MONITOR_BUCKET));
if (Bucket == NULL) {
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
}
InsertTailList(&EventList->BucketListHead, &Bucket->BucketList);
InitializeListHead(&Bucket->ResourceList);
GetSystemTimeAsFileTime((LPFILETIME)&Bucket->DueTime);
Bucket->Period = PollInterval;
if ( PollInterval == 0 ) {
// The following constant should be over 136 years
Bucket->DueTime += (DWORDLONG)((DWORDLONG)1000 * (DWORD) -1);
} else {
Bucket->DueTime += Bucket->Period;
}
EventList->NumberOfBuckets++;
}
InsertHeadList(&Bucket->ResourceList, &Resource->ListEntry);
Resource->Flags |= RESOURCE_INSERTED;
++EventList->NumberOfResources;
ReleaseEventListLock( EventList );
FnExit:
return (dwError);
} // RmpInsertResourceList
VOID
RmpRundownResources(
VOID
)
/*++
Routine Description:
Runs down the list of active resources and terminates/closes
each one.
Arguments:
None
Return Value:
None.
--*/
{
PLIST_ENTRY ListEntry;
PMONITOR_BUCKET Bucket;
PRESOURCE Resource;
PPOLL_EVENT_LIST EventList;
DWORD i;
BOOL fLockAcquired;
AcquireListLock();
while (!IsListEmpty(&RmpEventListHead)) {
ListEntry = RemoveHeadList(&RmpEventListHead);
EventList = CONTAINING_RECORD(ListEntry,
POLL_EVENT_LIST,
Next);
AcquireEventListLock( EventList );
//
// Find all resources on the bucket list and close them.
//
while (!IsListEmpty(&EventList->BucketListHead)) {
ListEntry = RemoveHeadList(&EventList->BucketListHead);
Bucket = CONTAINING_RECORD(ListEntry,
MONITOR_BUCKET,
BucketList);
while (!IsListEmpty(&Bucket->ResourceList)) {
ListEntry = RemoveHeadList(&Bucket->ResourceList);
Resource = CONTAINING_RECORD(ListEntry,
RESOURCE,
ListEntry);
//
// Acquire spin lock for synchronizing with arbitrate.
//
fLockAcquired = RmpAcquireSpinLock( Resource, TRUE );
//
// If the resource is in online or in pending state, terminate it. Note that
// we need to terminate pending resources as well, otherwise our close call
// down below would cause those threads to AV.
//
if ((Resource->State == ClusterResourceOnline) ||
(Resource->State > ClusterResourcePending)) {
Resource->dwEntryPoint = RESDLL_ENTRY_TERMINATE;
ClRtlLogPrint( LOG_NOISE,
"[RM] RundownResources, terminate resource <%1!ws!>.\n",
Resource->ResourceName );
#ifdef COMRES
RESMON_TERMINATE (Resource) ;
#else
(Resource->Terminate)(Resource->Id);
#endif
Resource->dwEntryPoint = 0;
}
//
// If the resource has been arbitrated for, release it.
//
if (Resource->IsArbitrated) {
#ifdef COMRES
RESMON_RELEASE (Resource) ;
#else
(Resource->Release)(Resource->Id);
#endif
}
//
// Close the resource.
//
Resource->dwEntryPoint = RESDLL_ENTRY_CLOSE;
#ifdef COMRES
RESMON_CLOSE (Resource) ;
#else
(Resource->Close)(Resource->Id);
#endif
Resource->dwEntryPoint = 0;
//
// Zero the resource links so that RmCloseResource can tell
// that this resource is already terminated and closed.
//
Resource->ListEntry.Flink = Resource->ListEntry.Blink = NULL;
if ( fLockAcquired ) RmpReleaseSpinLock ( Resource );
}
}
//
// Stop the thread processing this event list, and the notify event.
//
CL_ASSERT(EventList->ThreadHandle != NULL);
CL_ASSERT( EventList->ListNotify != NULL );
SetEvent(EventList->ListNotify);
ReleaseEventListLock( EventList );
//
// Wait for the thread to finish up before freeing the memory it is
// referencing.
//
ReleaseListLock(); // Release the list lock while waiting...
//
// Wait for 60 seconds for the threads to finish up.
//
WaitForSingleObject(EventList->ThreadHandle, 60000);
AcquireListLock();
CloseHandle(EventList->ThreadHandle);
EventList->ThreadHandle = NULL;
//
// We will assume that all of the event handles were closed as a
// result of calling the Close entrypoint.
//
RmpFree( EventList );
}
ReleaseListLock();
//
// Post shutdown of notification thread.
//
ClRtlLogPrint( LOG_NOISE, "[RM] RundownResources posting shutdown notification.\n");
RmpPostNotify( NULL, NotifyShutdown );
return;
} // RmpRundownResources
VOID
RmpSetEventListLockOwner(
IN PRESOURCE pResource,
IN DWORD dwMonitorState
)
/*++
Routine Description:
Saves the resource and the resource DLL entry point into the eventlist structure just
before the resource DLL entry point is called.
Arguments:
pResource - Pointer to the resource structure.
dwMonitorState - The state of the resource monitor, what resource DLL entry point it has called.
Return Value:
None.
--*/
{
PPOLL_EVENT_LIST pEventList;
if ( pResource == NULL ) return;
pEventList = (PPOLL_EVENT_LIST) pResource->EventList;
if ( pEventList != NULL )
{
pEventList->LockOwnerResource = pResource;
pEventList->MonitorState = dwMonitorState;
}
}
BOOL
RmpAcquireSpinLock(
IN PRESOURCE pResource,
IN BOOL fSpin
)
/*++
Routine Description:
Acquire a spin lock.
Arguments:
pResource - Pointer to the resource structure.
fSpin - TRUE if we must spin if the lock is unavailable. FALSE we shouldn't spin but return
a failure.
Return Value:
TRUE - Lock is acquired.
FALSE - Lock is not acquired.
--*/
{
DWORD dwRetryCount = 0;
//
// This resource is not one that supports arbitrate. Return failure. Note that resources
// other than the quorum resource support this entry point. We use this instead of the
// pResource->IsArbitrate since that variable is set only after the first arbitrate is called
// and we need to use this function before the arbitrate entry point is called.
//
if ( pResource->pArbitrate == NULL ) return FALSE;
//
// Initial check for lock availability.
//
if ( InterlockedCompareExchange( &pResource->fArbLock, 1, 0 ) == 0 ) return TRUE;
//
// Did not get the lock. Check if we must spin.
//
if ( fSpin == FALSE )
{
ClRtlLogPrint(LOG_UNUSUAL,
"[RM] RmpAcquireSpinLock: Could not get spinlock for resource %1!ws! after no wait...\n",
pResource->ResourceName);
return FALSE;
}
//
// We must spin. Spin until timeout.
//
while ( ( dwRetryCount < RESMON_MAX_SLOCK_RETRIES ) &&
( InterlockedCompareExchange( &pResource->fArbLock, 1, 0 ) ) )
{
Sleep ( 500 );
dwRetryCount ++;
}
if ( dwRetryCount == RESMON_MAX_SLOCK_RETRIES )
{
ClRtlLogPrint(LOG_ERROR,
"[RM] RmpAcquireSpinLock: Could not get spinlock for resource %1!ws! after spinning...\n",
pResource->ResourceName);
return FALSE;
}
return TRUE;
} // RmpAcquireSpinLock
VOID
RmpReleaseSpinLock(
IN PRESOURCE pResource
)
/*++
Routine Description:
Release a spin lock.
Arguments:
pResource - Pointer to the resource structure.
Return Value:
None.
--*/
{
DWORD dwRetryCount = 0;
//
// This resource is not one that supports arbitrate. Return failure.
//
if ( pResource->pArbitrate == NULL ) return;
InterlockedExchange( &pResource->fArbLock, 0 );
} // RmpReleaseSpinLock