/*++ 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); }