windows-nt/Source/XPSP1/NT/base/cluster/service/fm/tree.c

746 lines
20 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
tree.c
Abstract:
Cluster resource tree management routines.
Author:
Rod Gamache (rodga) 17-Apr-1996
Notes:
WARNING: All of the routines in this file assume that the resource
lock is held when they are called.
Revision History:
--*/
#include "fmp.h"
#define LOG_MODULE TREE
//
// Global Data
//
//
// Local function prototypes
//
BOOL
FmpAddResourceToDependencyTree(
IN PFM_RESOURCE Resource,
IN PFM_DEPENDENCY_TREE Tree
);
BOOL
FmpIsResourceInDependencyTree(
IN PFM_RESOURCE Resource,
IN PFM_DEPENDENCY_TREE Tree
);
DWORD
FmpOfflineWaitingResourceTree(
IN PFM_RESOURCE Resource,
IN BOOL BringQuorumOffline
);
DWORD
FmpRestartResourceTree(
IN PFM_RESOURCE Resource
)
/*++
Routine Description:
This routine brings back part of a dependency tree, starting from the
point of the last failure.
Arguments:
Resource - A pointer to the resource object that last failed and is
restarting.
Returns:
ERROR_SUCCESS - if the request is successful.
A Win32 error if the request fails.
--*/
{
PLIST_ENTRY entry;
PDEPENDENCY dependency;
DWORD status;
FmpAcquireLocalResourceLock( Resource );
//
// Tell the resource monitor to restart this resource if needed.
//
//
// If the current state is not online and we want it to be online, then
// bring it online.
//
if ( (Resource->State != ClusterResourceOnline) &&
((Resource->PersistentState == ClusterResourceOnline)) ) {
ClRtlLogPrint(LOG_NOISE,
"[FM] RestartResourceTree, Restart resource %1!ws!\n",
OmObjectId(Resource));
status = FmpOnlineResource(Resource, FALSE);
}
//
// If this resource has any dependents, start them if needed.
//
for ( entry = Resource->ProvidesFor.Flink;
entry != &(Resource->ProvidesFor);
entry = entry->Flink
)
{
dependency = CONTAINING_RECORD(entry, DEPENDENCY, ProviderLinkage);
//
// Recursively restart the dependent resource.
//
ClRtlLogPrint(LOG_NOISE,
"[FM] RestartResourceTree, %1!ws! depends on %2!ws!. Restart first\n",
OmObjectId(dependency->DependentResource),
OmObjectId(Resource));
status = FmpRestartResourceTree(dependency->DependentResource);
}
FmpReleaseLocalResourceLock( Resource );
return(ERROR_SUCCESS);
} // FmpRestartResourceTree
DWORD
FmpOnlineWaitingTree(
IN PFM_RESOURCE Resource
)
/*++
Routine Description:
This routine brings back part of a dependency tree, starting from the
point of the last waiting resource.
Arguments:
Resource - A pointer to the resource object that is now online.
Returns:
ERROR_SUCCESS - if the request is successful.
A Win32 error if the request fails.
Notes:
This routine is only called when the given resource is online.
--*/
{
PLIST_ENTRY entry;
PDEPENDENCY dependency;
DWORD status;
FmpAcquireLocalResourceLock( Resource );
//if shutdown is in progress, dont bring resources online
if (FmpShutdown)
{
//
// If this resource has any dependents, and they are in online pending state
// mark them as offline.
//
for ( entry = Resource->ProvidesFor.Flink;
entry != &(Resource->ProvidesFor);
entry = entry->Flink
)
{
dependency = CONTAINING_RECORD(entry, DEPENDENCY, ProviderLinkage);
if ((dependency->DependentResource->State == ClusterResourceOnlinePending) &&
(dependency->DependentResource->Flags & RESOURCE_WAITING))
{
//set the state of the all dependent resources to be offline again
FmpPropagateResourceState(dependency->DependentResource, ClusterResourceOffline);
//set the resource to be not waiting
dependency->DependentResource->Flags &= ~RESOURCE_WAITING;
//
// Recursively set the state of all dependent resources to offline
//
ClRtlLogPrint(LOG_NOISE,
"[FM] OnlineWaitingTree, %1!ws! (%2!u!) depends on %3!ws! (%4!u!). Shutdown others\n",
OmObjectId(dependency->DependentResource),
dependency->DependentResource->State,
OmObjectId(Resource),
Resource->State);
status = FmpOnlineWaitingTree(dependency->DependentResource);
}
}
//
// Chittur Subbaraman (chitturs) - 11/5/1999
//
// Ensure that the resource state itself is made
// ClusterResourceOffline if FM is asked to shutdown. Note that
// this function is recursively called from below, not just from
// the FM worker thread. So, if FM happened to be shutdown
// while executing this function called from below, then we
// offline all the dependent resources above, but not the
// resource itself. This is done here.
//
if ( ( Resource->State == ClusterResourceOnlinePending ) &&
( Resource->Flags & RESOURCE_WAITING ) )
{
FmpPropagateResourceState( Resource, ClusterResourceOffline );
Resource->Flags &= ~RESOURCE_WAITING;
ClRtlLogPrint( LOG_NOISE,
"[FM] OnlineWaitingTree, Resource <%1!ws!> forcibly brought offline...\n",
OmObjectId( Resource ) );
}
FmpReleaseLocalResourceLock( Resource );
return(ERROR_SUCCESS);
}
//for normal-non shutdown case
//
// Tell the resource monitor to restart this resource if needed.
//
//
// If the current state is not online and it is waiting, then it probably
// needs to be brought online now.
//
if ( (Resource->State == ClusterResourceOnlinePending) &&
(Resource->Flags & RESOURCE_WAITING) ) {
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpOnlineWaitingTree, Start resource %1!ws!\n",
OmObjectId(Resource));
Resource->State = ClusterResourceOffline;
status = FmpOnlineResource(Resource, FALSE);
if ( status == ERROR_SUCCESS ) {
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpOnlineWaitingTree, online for resource %1!ws! succeeded, online the dependents\r\n",
OmObjectId(Resource));
}
else if (status == ERROR_QUORUM_RESOURCE_ONLINE_FAILED)
{
PRESOURCE_ENUM pResourceEnum;
LPWSTR pszNewId;
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpOnlineWaitingTree, online for resource %1!ws!, status = %2!u!.\n",
OmObjectId(Resource),
status);
pResourceEnum = (PRESOURCE_ENUM)LocalAlloc(LMEM_FIXED,
sizeof(RESOURCE_ENUM));
if (!pResourceEnum)
{
CL_UNEXPECTED_ERROR(ERROR_NOT_ENOUGH_MEMORY);
CsInconsistencyHalt(ERROR_NOT_ENOUGH_MEMORY);
}
pResourceEnum->EntryCount = 1;
pResourceEnum->ContainsQuorum = (Resource == gpQuoResource);
pszNewId = LocalAlloc(LMEM_FIXED, (lstrlenW(OmObjectId(Resource))+1) * sizeof(WCHAR));
if ( pszNewId == NULL )
{
CsInconsistencyHalt(ERROR_NOT_ENOUGH_MEMORY);
}
lstrcpyW(pszNewId, OmObjectId(Resource));
pResourceEnum->Entry[0].Id = pszNewId;
pResourceEnum->Entry[0].State = Resource->PersistentState;
FmpSubmitRetryOnline(pResourceEnum);
FmpReleaseLocalResourceLock(Resource);
LocalFree(pszNewId);
LocalFree(pResourceEnum);
return(status);
}
else
{
FmpReleaseLocalResourceLock( Resource );
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpOnlineWaitingTree, online for resource %1!ws! returned = %2!u!.\n",
OmObjectId(Resource),
status);
return(status);
}
}
//
// If this resource has any dependents, start them if needed.
//
for ( entry = Resource->ProvidesFor.Flink;
entry != &(Resource->ProvidesFor);
entry = entry->Flink
)
{
dependency = CONTAINING_RECORD(entry, DEPENDENCY, ProviderLinkage);
//
// Recursively restart the dependent resource.
//
ClRtlLogPrint(LOG_NOISE,
"[FM] OnlineWaitingTree, %1!ws! (%2!u!) depends on %3!ws! (%4!u!). Start now\n",
OmObjectId(dependency->DependentResource),
dependency->DependentResource->State,
OmObjectId(Resource),
Resource->State);
status = FmpOnlineWaitingTree(dependency->DependentResource);
}
FmpReleaseLocalResourceLock( Resource );
return(ERROR_SUCCESS);
} // FmpOnlineWaitingTree
DWORD
FmpOfflineWaitingTree(
IN PFM_RESOURCE Resource
)
{
PLIST_ENTRY entry;
PDEPENDENCY dependency;
DWORD status;
FmpAcquireLocalResourceLock( Resource );
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpOfflineWaitingTree: Entry for <%1!ws!>.\n",
OmObjectName( Resource ) );
//
// Tell the resource monitor to stop this resource if needed.
// Make sure that the quorum resource is the last one brought offline
//
status = FmpOfflineWaitingResourceTree(Resource, FALSE);
//the quorum resource might still need to come offline, if it is in this group
if ((status == ERROR_SUCCESS) && (Resource->Group == gpQuoResource->Group))
{
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpOfflineWaitingTree: Quorum resource is in the same group,Moving list=0x%1!08lx!\n",
Resource->Group->MovingList);
//if a move is pending bring the quorum resource offline if all resources
// in the group are offline
// else dont bring the quorum resource offline
// this is because we dont bring the quorum resource offline on group offlines
if (Resource->Group->MovingList)
{
PLIST_ENTRY listEntry;
DWORD BringQuorumOffline = TRUE;
PFM_RESOURCE pGroupResource;
for ( listEntry = Resource->Group->Contains.Flink;
listEntry != &(Resource->Group->Contains);
listEntry = listEntry->Flink )
{
pGroupResource = CONTAINING_RECORD(listEntry,
FM_RESOURCE,
ContainsLinkage );
// if this is the quorum resource continue
if (pGroupResource->QuorumResource)
continue;
//if the state is not offline or failed, dont try
//and bring the quorum resource offline
if ((pGroupResource->State != ClusterResourceOffline) &&
(pGroupResource->State != ClusterResourceFailed))
{
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpOfflineWaitingTree: Quorum cannot be brought offline now for <%1!ws!>, state=%2!u!\n",
OmObjectName(pGroupResource), pGroupResource->State);
BringQuorumOffline = FALSE;
break;
}
}
if (BringQuorumOffline)
{
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpOfflineWaitingTree: bring quorum resource offline\n");
status = FmpOfflineResource(gpQuoResource, FALSE);
}
}
}
FmpReleaseLocalResourceLock( Resource );
ClRtlLogPrint(LOG_NOISE,
"[FM] FmpOfflineWaitingTree: returned status %1!u! for <%2!ws!>.\n",
status, OmObjectName( Resource ) );
return(status);
}
DWORD
FmpOfflineWaitingResourceTree(
IN PFM_RESOURCE Resource,
IN BOOL BringQuorumOffline
)
/*++
Routine Description:
This routine offlines a dependency tree, starting from the
point of the last waiting resource.
Arguments:
Resource - A pointer to the resource object that is now offline.
Returns:
ERROR_SUCCESS - if the request is successful.
A Win32 error if the request fails.
Notes:
This routine is only called when the given resource is offline.
--*/
{
PLIST_ENTRY entry;
PDEPENDENCY dependency;
DWORD status = ERROR_SUCCESS;
FmpAcquireLocalResourceLock( Resource );
//
// Tell the resource monitor to stop this resource if needed.
//
//
// If the current state is not offline and it is waiting, then it probably
// needs to be brought offline now.
//
if ((Resource->State == ClusterResourceOfflinePending) &&
(Resource->Flags & RESOURCE_WAITING)) {
ClRtlLogPrint(LOG_NOISE,
"[FM] OfflineWaitingResourceTree, Offline resource %1!ws!\n",
OmObjectId(Resource));
Resource->State = ClusterResourceOnline;
status = FmpOfflineResource(Resource, FALSE);
if ( status == ERROR_IO_PENDING ) {
FmpReleaseLocalResourceLock( Resource );
ClRtlLogPrint(LOG_NOISE,
"[FM] OfflineWaitingResourceTree, offline for resource %1!ws! returned pending.\n",
OmObjectId(Resource));
return(status);
} else {
ClRtlLogPrint(LOG_NOISE,
"[FM] OfflineWaitingResourceTree, offline for resource %1!ws!, status = %2!u!.\n",
OmObjectId(Resource),
status);
}
}
//
// If this resource has any providers, stop them if needed.
//
for ( entry = Resource->DependsOn.Flink;
entry != &(Resource->DependsOn);
entry = entry->Flink
)
{
dependency = CONTAINING_RECORD(entry, DEPENDENCY, DependentLinkage);
if (dependency->ProviderResource->QuorumResource && !BringQuorumOffline)
{
continue;
}
//
// Recursively offline the provider resource.
//
ClRtlLogPrint(LOG_NOISE,
"[FM] OfflineWaitingResourceTree, %1!ws! provides for %2!ws!. Offline next.\n",
OmObjectId(dependency->ProviderResource),
OmObjectId(Resource));
//dependency->ProviderResource->Flags |= RESOURCE_WAITING;
status = FmpOfflineWaitingResourceTree(dependency->ProviderResource, BringQuorumOffline);
}
FmpReleaseLocalResourceLock( Resource );
ClRtlLogPrint(LOG_NOISE,
"[FM] OfflineWaitingResourceTree: Exit, status=%1!u! for <%2!ws!>.\n",
status, OmObjectName( Resource ) );
return(status);
} // FmpOfflineWaitingResourceTree
PFM_DEPENDENCY_TREE
FmCreateFullDependencyTree(
IN PFM_RESOURCE Resource
)
/*++
Routine Description:
Creates a full dependency tree containing all the resources
that either depend on or provide for the supplied resource.
Arguments:
Resource - Supplies the resource
Return Value:
Pointer to the dependency tree.
NULL if out of memory.
--*/
{
PFM_DEPENDENCY_TREE Tree;
BOOL Success;
Tree = LocalAlloc(LMEM_FIXED, sizeof(FM_DEPENDENCY_TREE));
if (Tree == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(NULL);
}
InitializeListHead(&Tree->ListHead);
//
// Add the resources that the specified resource depends on.
//
Success = FmpAddResourceToDependencyTree(Resource, Tree);
if (!Success) {
LocalFree(Tree);
return(NULL);
} else {
return(Tree);
}
}
BOOL
FmpIsResourceInDependencyTree(
IN PFM_RESOURCE Resource,
IN PFM_DEPENDENCY_TREE Tree
)
/*++
Routine Description:
Determines whether the specified resource is already in the
dependency tree.
Arguments:
Resource - Supplies the resource to check for
Tree - Supplies the dependency tree.
Return Value:
TRUE if the resource is in the dependency tree
FALSE if the resource is not in the dependency tree
--*/
{
PLIST_ENTRY ListEntry;
PFM_DEPENDTREE_ENTRY Node;
ListEntry = Tree->ListHead.Flink;
while (ListEntry != &Tree->ListHead) {
Node = CONTAINING_RECORD(ListEntry,
FM_DEPENDTREE_ENTRY,
ListEntry);
if (Node->Resource == Resource) {
return(TRUE);
}
ListEntry = ListEntry->Flink;
}
return(FALSE);
}
BOOL
FmpAddResourceToDependencyTree(
IN PFM_RESOURCE Resource,
IN PFM_DEPENDENCY_TREE Tree
)
/*++
Routine Description:
Recursive worker for adding a resource and all resources that
it depends on or provides for into the dependency tree.
Arguments:
Resource - Supplies the resource to add.
Tree - Supplies the tree the resource should be added to.
Return Value:
TRUE - Successfully completed
FALSE - out of memory
--*/
{
PLIST_ENTRY ListEntry;
PDEPENDENCY Dependency;
PFM_DEPENDTREE_ENTRY Node;
//
// First check to see if we are already in the tree.
// If so, we are done.
//
if (FmpIsResourceInDependencyTree(Resource, Tree)) {
return(TRUE);
}
//
// Recursively call ourselves for each entry we depend on.
//
ListEntry = Resource->DependsOn.Flink;
while (ListEntry != &Resource->DependsOn) {
Dependency = CONTAINING_RECORD(ListEntry,
DEPENDENCY,
DependentLinkage);
ListEntry = ListEntry->Flink;
//
// Recursively add this resource to the tree
//
if (!FmpAddResourceToDependencyTree(Dependency->ProviderResource, Tree)) {
return(FALSE);
}
}
//
// Add ourselves to the list now if we are not already in it.
//
if (!FmpIsResourceInDependencyTree(Resource, Tree)) {
//
// Add ourselves to the end of the list.
//
Node = LocalAlloc(LMEM_FIXED, sizeof(FM_DEPENDTREE_ENTRY));
if (Node == NULL) {
return(FALSE);
}
OmReferenceObject(Resource);
Node->Resource = Resource;
InsertTailList(&Tree->ListHead, &Node->ListEntry);
}
//
// Now add the resources that this resource provides for to the list.
//
ListEntry = Resource->ProvidesFor.Flink;
while (ListEntry != &Resource->ProvidesFor) {
Dependency = CONTAINING_RECORD(ListEntry,
DEPENDENCY,
ProviderLinkage);
ListEntry = ListEntry->Flink;
//
// Recursively add this resource to the tree
//
if (!FmpAddResourceToDependencyTree(Dependency->DependentResource, Tree)) {
return(FALSE);
}
}
return(TRUE);
}
VOID
FmDestroyFullDependencyTree(
IN PFM_DEPENDENCY_TREE Tree
)
/*++
Routine Description:
Destroys a dependency tree
Arguments:
Tree - Supplies the dependency tree
Return Value:
None
--*/
{
PFM_DEPENDTREE_ENTRY Entry;
PLIST_ENTRY ListEntry;
while (!IsListEmpty(&Tree->ListHead)) {
ListEntry = RemoveHeadList(&Tree->ListHead);
Entry = CONTAINING_RECORD(ListEntry,
FM_DEPENDTREE_ENTRY,
ListEntry);
OmDereferenceObject(Entry->Resource);
LocalFree(Entry);
}
LocalFree(Tree);
}